Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ads BigInt support #48

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ node_js:
- '8'
- '9'
- '10'
- '12'
77 changes: 42 additions & 35 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ var replacerStack = []

// Regular stringify
function stringify (obj, replacer, spacer) {
decirc(obj, '', [], undefined)
jsonSafe(obj, '', [], undefined)
var res
if (replacerStack.length === 0) {
res = JSON.stringify(obj, replacer, spacer)
Expand All @@ -25,40 +25,33 @@ function stringify (obj, replacer, spacer) {
}
return res
}
function decirc (val, k, stack, parent) {
function jsonSafe (val, k, stack, parent) {
var i
if (typeof val === 'object' && val !== null) {
var type = typeof val

if (type === 'object' && val !== null) {
for (i = 0; i < stack.length; i++) {
if (stack[i] === val) {
var propertyDescriptor = Object.getOwnPropertyDescriptor(parent, k)
if (propertyDescriptor.get !== undefined) {
if (propertyDescriptor.configurable) {
Object.defineProperty(parent, k, { value: '[Circular]' })
arr.push([parent, k, val, propertyDescriptor])
} else {
replacerStack.push([val, k])
}
} else {
parent[k] = '[Circular]'
arr.push([parent, k, val])
}
replaceProperty(parent, k, val, '[Circular]', type)
return
}
}
stack.push(val)
// Optimize for Arrays. Big arrays could kill the performance otherwise!
if (Array.isArray(val)) {
for (i = 0; i < val.length; i++) {
decirc(val[i], i, stack, val)
jsonSafe(val[i], i, stack, val)
}
} else {
var keys = Object.keys(val)
for (i = 0; i < keys.length; i++) {
var key = keys[i]
decirc(val[key], key, stack, val)
jsonSafe(val[key], key, stack, val)
}
}
stack.pop()
} else if (type === 'bigint') {
replaceProperty(parent, k, val, val.toString(), type)
}
}

Expand All @@ -74,7 +67,7 @@ function compareFunction (a, b) {
}

function deterministicStringify (obj, replacer, spacer) {
var tmp = deterministicDecirc(obj, '', [], undefined) || obj
var tmp = deterministicJsonSafe(obj, '', [], undefined) || obj
var res
if (replacerStack.length === 0) {
res = JSON.stringify(tmp, replacer, spacer)
Expand All @@ -92,23 +85,14 @@ function deterministicStringify (obj, replacer, spacer) {
return res
}

function deterministicDecirc (val, k, stack, parent) {
function deterministicJsonSafe (val, k, stack, parent) {
var i
if (typeof val === 'object' && val !== null) {
var type = typeof val

if (type === 'object' && val !== null) {
for (i = 0; i < stack.length; i++) {
if (stack[i] === val) {
var propertyDescriptor = Object.getOwnPropertyDescriptor(parent, k)
if (propertyDescriptor.get !== undefined) {
if (propertyDescriptor.configurable) {
Object.defineProperty(parent, k, { value: '[Circular]' })
arr.push([parent, k, val, propertyDescriptor])
} else {
replacerStack.push([val, k])
}
} else {
parent[k] = '[Circular]'
arr.push([parent, k, val])
}
replaceProperty(parent, k, val, '[Circular]', type)
return
}
}
Expand All @@ -119,15 +103,15 @@ function deterministicDecirc (val, k, stack, parent) {
// Optimize for Arrays. Big arrays could kill the performance otherwise!
if (Array.isArray(val)) {
for (i = 0; i < val.length; i++) {
deterministicDecirc(val[i], i, stack, val)
deterministicJsonSafe(val[i], i, stack, val)
}
} else {
// Create a temporary object in the required way
var tmp = {}
var keys = Object.keys(val).sort(compareFunction)
for (i = 0; i < keys.length; i++) {
var key = keys[i]
deterministicDecirc(val[key], key, stack, val)
deterministicJsonSafe(val[key], key, stack, val)
tmp[key] = val[key]
}
if (parent !== undefined) {
Expand All @@ -138,6 +122,23 @@ function deterministicDecirc (val, k, stack, parent) {
}
}
stack.pop()
} else if (type === 'bigint') {
replaceProperty(parent, k, val, val.toString(), type)
}
}

function replaceProperty (parent, k, val, cVal, type) {
var propertyDescriptor = Object.getOwnPropertyDescriptor(parent, k)
if (propertyDescriptor.get !== undefined) {
if (propertyDescriptor.configurable) {
Object.defineProperty(parent, k, { value: cVal })
arr.push([parent, k, val, propertyDescriptor])
} else {
replacerStack.push([val, k, type])
}
} else {
parent[k] = cVal
arr.push([parent, k, val])
}
}

Expand All @@ -150,7 +151,13 @@ function replaceGetterValues (replacer) {
for (var i = 0; i < replacerStack.length; i++) {
var part = replacerStack[i]
if (part[1] === key && part[0] === val) {
val = '[Circular]'
switch (part[2]) {
case 'bigint':
val = val.toString()
break
default:
val = '[Circular]'
}
replacerStack.splice(i, 1)
break
}
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"benchmark": "^2.1.4",
"clone": "^2.1.0",
"json-stringify-safe": "^5.0.1",
"standard": "^11.0.0",
"standard": "^14.0.0",
"tap": "^12.0.0"
},
"repository": {
Expand Down
30 changes: 30 additions & 0 deletions test-stable.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ const fss = require('./').stable
const clone = require('clone')
const s = JSON.stringify

// "polyfill" BigInt for standard and node < 10.4
var BigInt = global.BigInt || function BigInt (value) { return value }

test('circular reference to root', function (assert) {
const fixture = { name: 'Tywin Lannister' }
fixture.circle = fixture
Expand Down Expand Up @@ -309,3 +312,30 @@ test('getter child circular reference', function (assert) {
assert.is(actual, expected)
assert.end()
})

test('bigint should be serialized to string', function (assert) {
const fixture = { name: 'Tywin Lannister', age: BigInt('62') }
const expected = s(
{ age: '62', name: 'Tywin Lannister' }
)
const actual = fss(fixture)
assert.is(actual, expected)
assert.end()
})

test('non-configurable bigint getters use a replacer', function (assert) {
const fixture = { name: 'Tywin Lannister' }
Object.defineProperty(fixture, 'age', {
configurable: false,
get: function () { return BigInt('62') },
enumerable: true
})

const expected = s(
{ age: '62', name: 'Tywin Lannister' }
)

const actual = fss(fixture)
assert.is(actual, expected)
assert.end()
})
29 changes: 29 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ const fss = require('./')
const clone = require('clone')
const s = JSON.stringify

// "polyfill" BigInt for standard and node < 10.4
var BigInt = global.BigInt || function BigInt (value) { return value }

test('circular reference to root', function (assert) {
const fixture = { name: 'Tywin Lannister' }
fixture.circle = fixture
Expand Down Expand Up @@ -302,3 +305,29 @@ test('getter child circular reference are replaced instead of marked', function
assert.is(actual, expected)
assert.end()
})

test('bigint should be serialized to string', function (assert) {
const fixture = { name: 'Tywin Lannister', age: BigInt('62') }
const expected = s(
{ name: 'Tywin Lannister', age: '62' }
)
const actual = fss(fixture)
assert.is(actual, expected)
assert.end()
})

test('non-configurable bigint getters use a replacer', function (assert) {
const fixture = { name: 'Tywin Lannister' }
Object.defineProperty(fixture, 'age', {
configurable: false,
get: function () { return BigInt('62') },
enumerable: true
})

const expected = s(
{ name: 'Tywin Lannister', age: '62' }
)
const actual = fss(fixture)
assert.is(actual, expected)
assert.end()
})