blob: c4019f7e30631f2e668a341a8c026bf6debd7904 [file] [edit]
// Differential test: fast path JSON.stringify(v, null, space) must match the
// general Stringifier, which we force via an identity callable replacer.
let failures = 0;
const values = [
{},
[],
{ a: 1 },
[1, 2, 3],
[[]],
[{}],
{ a: {} },
{ a: [] },
{ a: { b: { c: [1, [2, [3, {}]]] } } },
{ id: 1, name: 'user1', tags: ['alpha', 'beta'], nested: { active: true, score: 1.5, items: [1, 2, 3] } },
{ s: 'esc"ape\\\n\t', t: 'plain' },
{ u: undefined, v: 1 },
{ u: undefined },
[undefined, null, true, false],
[function () {}, Symbol('x'), undefined],
{ f: function () {}, sym: Symbol('x'), ok: 1 },
'top-level string',
42,
1.25,
-0,
NaN,
Infinity,
true,
null,
[1e21, 1e-7, 0.1, -2147483648, 2147483647],
{ 'k"ey': 1 },
{ '': 2 },
{ 'ユニコード': 'マルチバイト文字列' },
['16bit→string', { a: '€' }],
[[[[[[[[[[1]]]]]]]]]],
new Date(0),
{ d: new Date(0) },
[0.5, [0.25, { x: 0.125 }]],
];
const spaces = [
undefined, null, 2, 0, -1, 1, 10, 11, 100, 2.7, 0.5, -2.5, NaN, Infinity, -Infinity,
'\t', ' ', 'ab', '', '0123456789ABC', '', '€', 'あ',
true, false, {}, [], new Number(3), new String('xy'), Symbol('s'), 10n, 3n,
];
function show(x) {
if (typeof x === 'symbol') return 'Symbol';
if (typeof x === 'bigint') return x + 'n';
try { return JSON.stringify(x) ?? String(x); } catch { return String(x); }
}
for (const space of spaces) {
for (const value of values) {
let fast, slow;
let fastErr, slowErr;
try { fast = JSON.stringify(value, null, space); } catch (e) { fastErr = String(e); }
try { slow = JSON.stringify(value, (k, x) => x, space); } catch (e) { slowErr = String(e); }
if (fast !== slow || fastErr !== slowErr) {
failures++;
print('MISMATCH space=' + show(space) + ' value=' + show(value));
print(' fast: ' + (fastErr ?? JSON.stringify(fast)));
print(' slow: ' + (slowErr ?? JSON.stringify(slow)));
}
}
}
// Large outputs: force StaticBuffer overflow into DynamicBuffer with gap.
{
const big = [];
for (let i = 0; i < 2000; i++)
big.push({ idx: i, name: 'n' + i, vals: [i, i + 1, i + 2], deep: { x: i * 0.5 } });
for (const space of [2, '\t', 10]) {
const fast = JSON.stringify(big, null, space);
const slow = JSON.stringify(big, (k, x) => x, space);
if (fast !== slow) {
failures++;
print('MISMATCH big space=' + show(space) + ' lens ' + fast.length + ' vs ' + slow.length);
}
}
}
// 16-bit content with gap (char16_t fast path).
{
const v = { 'キー': ['値', { nested: '🎉 emoji', n: 1 }] };
for (const space of [2, '\t', 4]) {
const fast = JSON.stringify(v, null, space);
const slow = JSON.stringify(v, (k, x) => x, space);
if (fast !== slow) {
failures++;
print('MISMATCH 16bit space=' + show(space));
print(' fast: ' + fast);
print(' slow: ' + slow);
}
}
}
// Deep nesting with gap.
{
let v = 1;
for (let i = 0; i < 200; i++) v = [v];
const fast = JSON.stringify(v, null, 2);
const slow = JSON.stringify(v, (k, x) => x, 2);
if (fast !== slow) { failures++; print('MISMATCH deep nesting'); }
}
if (failures)
throw new Error('FAILED: ' + failures + ' mismatches');