Skip to content

Commit 245a0dc

Browse files
authored
improve performance on error path in JSON.parse (#90)
* improve performance on SyntaxErrors * add unit test * fix resetting stackTraceLimit * fix lnt error * use finally block more efficiently
1 parent 9ac2ea9 commit 245a0dc

File tree

3 files changed

+37
-7
lines changed

3 files changed

+37
-7
lines changed

benchmarks/throw.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const internals = {
1111
const suite = new Benchmark.Suite()
1212

1313
suite
14-
.add('JSON.parse', () => {
14+
.add('JSON.parse valid', () => {
1515
JSON.parse(internals.text)
1616
})
1717
.add('JSON.parse error', () => {
@@ -21,15 +21,15 @@ suite
2121
})
2222
.add('secure-json-parse parse', () => {
2323
try {
24-
sjson.parse(internals.text)
24+
sjson.parse(internals.invalid)
2525
} catch (ignoreErr) { }
2626
})
2727
.add('secure-json-parse safeParse', () => {
28-
sjson.safeParse(internals.text)
28+
sjson.safeParse(internals.invalid)
2929
})
3030
.add('reviver', () => {
3131
try {
32-
JSON.parse(internals.text, internals.reviver)
32+
JSON.parse(internals.invalid, internals.reviver)
3333
} catch (ignoreErr) { }
3434
})
3535
.on('cycle', (event) => {

index.js

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const hasBuffer = typeof Buffer !== 'undefined'
44
const suspectProtoRx = /"(?:_|\\u005[Ff])(?:_|\\u005[Ff])(?:p|\\u0070)(?:r|\\u0072)(?:o|\\u006[Ff])(?:t|\\u0074)(?:o|\\u006[Ff])(?:_|\\u005[Ff])(?:_|\\u005[Ff])"\s*:/
55
const suspectConstructorRx = /"(?:c|\\u0063)(?:o|\\u006[Ff])(?:n|\\u006[Ee])(?:s|\\u0073)(?:t|\\u0074)(?:r|\\u0072)(?:u|\\u0075)(?:c|\\u0063)(?:t|\\u0074)(?:o|\\u006[Ff])(?:r|\\u0072)"\s*:/
66

7-
function parse (text, reviver, options) {
7+
function _parse (text, reviver, options) {
88
// Normalize arguments
99
if (options == null) {
1010
if (reviver !== null && typeof reviver === 'object') {
@@ -97,11 +97,25 @@ function filter (obj, { protoAction = 'error', constructorAction = 'error', safe
9797
return obj
9898
}
9999

100+
function parse (text, reviver, options) {
101+
const stackTraceLimit = Error.stackTraceLimit
102+
Error.stackTraceLimit = 0
103+
try {
104+
return _parse(text, reviver, options)
105+
} finally {
106+
Error.stackTraceLimit = stackTraceLimit
107+
}
108+
}
109+
100110
function safeParse (text, reviver) {
111+
const stackTraceLimit = Error.stackTraceLimit
112+
Error.stackTraceLimit = 0
101113
try {
102-
return parse(text, reviver, { safe: true })
103-
} catch (ignoreError) {
114+
return _parse(text, reviver, { safe: true })
115+
} catch (_e) {
104116
return null
117+
} finally {
118+
Error.stackTraceLimit = stackTraceLimit
105119
}
106120
}
107121

test/index.test.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,14 @@ test('parse', t => {
145145
t.end()
146146
})
147147

148+
t.test('should reset stackTraceLimit', t => {
149+
const text = '{ "a": 5, "b": 6, "__proto__": { "x": 7 }, "c": { "d": 0, "e": "text", "__proto__": { "y": 8 }, "f": { "g": 2 } } }'
150+
Error.stackTraceLimit = 42
151+
t.throws(() => j.parse(text))
152+
t.same(Error.stackTraceLimit, 42)
153+
t.end()
154+
})
155+
148156
t.end()
149157
})
150158

@@ -372,6 +380,14 @@ test('safeParse', t => {
372380
t.end()
373381
})
374382

383+
t.test('should reset stackTraceLimit', t => {
384+
const text = '{ "a": 5, "b": 6, "__proto__": { "x": 7 }, "c": { "d": 0, "e": "text", "__proto__": { "y": 8 }, "f": { "g": 2 } } }'
385+
Error.stackTraceLimit = 42
386+
t.same(j.safeParse(text), null)
387+
t.same(Error.stackTraceLimit, 42)
388+
t.end()
389+
})
390+
375391
t.test('sanitizes nested object string', t => {
376392
const text = '{ "a": 5, "b": 6, "__proto__": { "x": 7 }, "c": { "d": 0, "e": "text", "__proto__": { "y": 8 }, "f": { "g": 2 } } }'
377393

0 commit comments

Comments
 (0)