Skip to content

Commit 1e661a4

Browse files
committed
fixes
1 parent d722a79 commit 1e661a4

File tree

2 files changed

+59
-30
lines changed

2 files changed

+59
-30
lines changed

src/parse.js

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -12,64 +12,69 @@ import {
1212
* @param {string} serialized
1313
*/
1414
export function parse(serialized) {
15-
const values = JSON.parse(serialized);
15+
const parsed = JSON.parse(serialized);
1616

17-
if (typeof values === 'number') return get_value(values);
17+
if (typeof parsed === 'number') return hydrate(parsed);
18+
19+
const values = /** @type {any[]} */ (parsed);
20+
const hydrated = Array(values.length);
1821

1922
/** @param {number} index */
20-
function get_value(index) {
23+
function hydrate(index) {
2124
if (index === UNDEFINED) return undefined;
2225
if (index === NAN) return NaN;
2326
if (index === POSITIVE_INFINITY) return Infinity;
2427
if (index === NEGATIVE_INFINITY) return -Infinity;
2528
if (index === NEGATIVE_ZERO) return -0;
2629

27-
return values[index];
28-
}
29-
30-
let i = values.length;
31-
while (i--) {
32-
const value = values[i];
30+
if (index in hydrated) return hydrated[index];
3331

34-
if (!value || typeof value !== 'object') continue;
32+
const value = values[index];
3533

36-
if (Array.isArray(value)) {
34+
if (!value || typeof value !== 'object') {
35+
hydrated[index] = value;
36+
} else if (Array.isArray(value)) {
3737
if (typeof value[0] === 'string') {
3838
const type = value[0];
3939

4040
switch (type) {
4141
case 'Date':
42-
values[i] = new Date(value[1]);
42+
hydrated[index] = new Date(value[1]);
4343
break;
4444

4545
case 'Set':
4646
const set = new Set();
47-
values[i] = set;
48-
for (const n of value[1]) set.add(get_value(n));
47+
hydrated[index] = set;
48+
for (const n of value[1]) set.add(hydrate(n));
4949
break;
5050

5151
case 'Map':
5252
const map = new Map();
53-
values[i] = map;
53+
hydrated[index] = map;
5454
for (let i = 0; i < value[1].length; i += 2) {
55-
map.set(get_value(value[i]), get_value(value[i + 1]));
55+
map.set(hydrate(value[i]), hydrate(value[i + 1]));
5656
}
5757
break;
5858

5959
case 'RegExp':
60-
values[i] = new RegExp(value[1], value[2]);
60+
hydrated[index] = new RegExp(value[1], value[2]);
6161
break;
6262

6363
case 'Object':
64-
values[i] = Object(value[1]);
64+
hydrated[index] = Object(value[1]);
6565
break;
6666

6767
case 'BigInt':
68-
values[i] = BigInt(value[1]);
68+
hydrated[index] = BigInt(value[1]);
6969
break;
7070

7171
case 'null':
72-
const object = Object.create(null);
72+
const obj = Object.create(null);
73+
hydrated[index] = obj;
74+
for (let i = 1; i < value.length; i += 2) {
75+
obj[value[i]] = hydrate(value[i + 1]);
76+
}
77+
break;
7378
}
7479
} else {
7580
const array = new Array(value.length);
@@ -78,23 +83,25 @@ export function parse(serialized) {
7883
const n = value[i];
7984
if (n === HOLE) continue;
8085

81-
array[i] = get_value(n);
86+
array[i] = hydrate(n);
8287
}
8388

84-
values[i] = array;
89+
hydrated[index] = array;
8590
}
8691
} else {
8792
/** @type {Record<string, any>} */
8893
const object = {};
8994

9095
for (const key in value) {
9196
const n = value[key];
92-
object[key] = get_value(n);
97+
object[key] = hydrate(n);
9398
}
9499

95-
values[i] = object;
100+
hydrated[index] = object;
96101
}
102+
103+
return hydrated[index];
97104
}
98105

99-
return values[0];
106+
return hydrate(0);
100107
}

test/test.js

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,12 @@ const fixtures = {
227227
name: 'Set (cyclical)',
228228
value: set,
229229
js: '(function(a){a.add(a).add(42);return a}(new Set))',
230-
json: '[["Set",[0,1]],42]'
230+
json: '[["Set",[0,1]],42]',
231+
validate: (value) => {
232+
assert.equal(value.size, 2);
233+
assert.ok(value.has(42));
234+
assert.ok(value.has(value));
235+
}
231236
};
232237
})(new Set()),
233238

@@ -257,7 +262,11 @@ const fixtures = {
257262
name: 'Object with null prototype (cyclical)',
258263
value: obj,
259264
js: '(function(a){a.self=a;return a}(Object.create(null)))',
260-
json: '[["null","self",0]]'
265+
json: '[["null","self",0]]',
266+
validate: (value) => {
267+
assert.equal(Object.getPrototypeOf(value), null);
268+
assert.is(value.self, value);
269+
}
261270
};
262271
})(Object.create(null)),
263272

@@ -308,13 +317,21 @@ const fixtures = {
308317
name: 'Object without prototype',
309318
value: Object.create(null),
310319
js: 'Object.create(null)',
311-
json: '[["null"]]'
320+
json: '[["null"]]',
321+
validate: (value) => {
322+
assert.equal(Object.getPrototypeOf(value), null);
323+
assert.equal(Object.keys(value).length, 0);
324+
}
312325
},
313326
{
314327
name: 'cross-realm POJO',
315328
value: vm.runInNewContext('({})'),
316329
js: '{}',
317-
json: '[{}]'
330+
json: '[{}]',
331+
validate: (value) => {
332+
assert.equal(Object.getPrototypeOf(value), Object.prototype);
333+
assert.equal(Object.keys(value).length, 0);
334+
}
318335
}
319336
]
320337
};
@@ -349,7 +366,12 @@ for (const [name, tests] of Object.entries(fixtures)) {
349366
test(t.name, () => {
350367
const actual = parse(t.json);
351368
const expected = t.value;
352-
assert.equal(actual, expected);
369+
370+
if (t.validate) {
371+
t.validate(actual);
372+
} else {
373+
assert.equal(actual, expected);
374+
}
353375
});
354376
}
355377
test.run();

0 commit comments

Comments
 (0)