diff --git a/lexer.js b/lexer.js index 1614fd9..664b729 100755 --- a/lexer.js +++ b/lexer.js @@ -1,4 +1,5 @@ let source, pos, end; +const STACK_DEPTH = 2048; let openTokenDepth, templateDepth, lastTokenPos, @@ -37,6 +38,20 @@ const Import = 0; const ExportAssign = 1; const ExportStar = 2; +/** @param {number} tokenPos */ +function pushOpenToken (tokenPos) { + if (openTokenDepth === STACK_DEPTH) + throw new Error('Maximum nesting depth exceeded.'); + openTokenPosStack[openTokenDepth++] = tokenPos; +} + +function pushTemplate () { + if (templateStackDepth === STACK_DEPTH || openTokenDepth === STACK_DEPTH) + throw new Error('Maximum nesting depth exceeded.'); + templateStack[templateStackDepth++] = templateDepth; + templateDepth = ++openTokenDepth; +} + function parseCJS (source, name = '@') { resetState(); try { @@ -125,7 +140,7 @@ function parseSource (cjsSource) { pos += 23; if (source.charCodeAt(pos) === 40/*(*/) { pos++; - openTokenPosStack[openTokenDepth++] = lastTokenPos; + pushOpenToken(lastTokenPos); if (tryParseRequire(Import) && keywordStart(startPos)) { tryBacktrackAddStarExportBinding(startPos - 1); } @@ -136,7 +151,7 @@ function parseSource (cjsSource) { if (source.startsWith('Star', pos)) pos += 4; if (source.charCodeAt(pos) === 40/*(*/) { - openTokenPosStack[openTokenDepth++] = lastTokenPos; + pushOpenToken(lastTokenPos); if (source.charCodeAt(pos + 1) === 114/*r*/) { pos++; tryParseRequire(ExportStar); @@ -170,7 +185,7 @@ function parseSource (cjsSource) { tryParseObjectDefineOrKeys(openTokenDepth === 0); break; case 40/*(*/: - openTokenPosStack[openTokenDepth++] = lastTokenPos; + pushOpenToken(lastTokenPos); break; case 41/*)*/: if (openTokenDepth === 0) @@ -178,9 +193,9 @@ function parseSource (cjsSource) { openTokenDepth--; break; case 123/*{*/: - openClassPosStack[openTokenDepth] = nextBraceIsClass; + pushOpenToken(lastTokenPos); + openClassPosStack[openTokenDepth - 1] = nextBraceIsClass; nextBraceIsClass = false; - openTokenPosStack[openTokenDepth++] = lastTokenPos; break; case 125/*}*/: if (openTokenDepth === 0) @@ -1324,7 +1339,7 @@ function throwIfImportStatement () { switch (ch) { // dynamic import case 40/*(*/: - openTokenPosStack[openTokenDepth++] = startPos; + pushOpenToken(startPos); return; // import.meta case 46/*.*/: @@ -1382,8 +1397,7 @@ function templateString () { const ch = source.charCodeAt(pos); if (ch === 36/*$*/ && source.charCodeAt(pos + 1) === 123/*{*/) { pos++; - templateStack[templateStackDepth++] = templateDepth; - templateDepth = ++openTokenDepth; + pushTemplate(); return; } if (ch === 96/*`*/) diff --git a/lib/lexer.wasm b/lib/lexer.wasm index 3ff889c..577fbed 100755 Binary files a/lib/lexer.wasm and b/lib/lexer.wasm differ diff --git a/lib/lexer.wat b/lib/lexer.wat index 7f1fad4..85155c9 100644 --- a/lib/lexer.wat +++ b/lib/lexer.wat @@ -439,53 +439,250 @@ block ;; label = @18 block ;; label = @19 block ;; label = @20 - i32.const 0 - i32.load16_u offset=8198 - local.tee 3 - br_if 0 (;@20;) block ;; label = @21 block ;; label = @22 + i32.const 0 + i32.load16_u offset=8198 + local.tee 3 + br_if 0 (;@22;) block ;; label = @23 - local.get 1 - i32.const -95 - i32.add - br_table 2 (;@21;) 16 (;@7;) 19 (;@4;) 19 (;@4;) 6 (;@17;) 19 (;@4;) 5 (;@18;) 19 (;@4;) 19 (;@4;) 19 (;@4;) 1 (;@22;) 19 (;@4;) 19 (;@4;) 19 (;@4;) 4 (;@19;) 0 (;@23;) - end - block ;; label = @23 - local.get 1 - i32.const -39 - i32.add - br_table 14 (;@9;) 8 (;@15;) 9 (;@14;) 19 (;@4;) 19 (;@4;) 19 (;@4;) 19 (;@4;) 19 (;@4;) 15 (;@8;) 0 (;@23;) - end - block ;; label = @23 - local.get 1 - i32.const -123 - i32.add - br_table 11 (;@12;) 19 (;@4;) 12 (;@11;) 0 (;@23;) + block ;; label = @24 + block ;; label = @25 + local.get 1 + i32.const -95 + i32.add + br_table 2 (;@23;) 18 (;@7;) 21 (;@4;) 21 (;@4;) 6 (;@19;) 21 (;@4;) 5 (;@20;) 21 (;@4;) 21 (;@4;) 21 (;@4;) 1 (;@24;) 21 (;@4;) 21 (;@4;) 21 (;@4;) 4 (;@21;) 0 (;@25;) + end + block ;; label = @25 + local.get 1 + i32.const -39 + i32.add + br_table 16 (;@9;) 9 (;@16;) 10 (;@15;) 21 (;@4;) 21 (;@4;) 21 (;@4;) 21 (;@4;) 21 (;@4;) 17 (;@8;) 0 (;@25;) + end + block ;; label = @25 + local.get 1 + i32.const -123 + i32.add + br_table 13 (;@12;) 21 (;@4;) 14 (;@11;) 0 (;@25;) + end + local.get 1 + i32.const 34 + i32.eq + br_if 15 (;@9;) + local.get 1 + i32.const 79 + i32.eq + br_if 6 (;@18;) + local.get 1 + i32.const 114 + i32.ne + br_if 20 (;@4;) + block ;; label = @25 + i32.const 0 + call 16 + i32.eqz + br_if 0 (;@25;) + local.get 0 + call 17 + i32.eqz + br_if 0 (;@25;) + local.get 4 + call 18 + end + i32.const 0 + i32.const 0 + i32.load offset=20540 + i32.store offset=8204 + br 21 (;@3;) + end + block ;; label = @24 + local.get 4 + i32.const 4 + i32.add + i32.const 109 + i32.const 112 + i32.const 111 + i32.const 114 + i32.const 116 + call 19 + i32.eqz + br_if 0 (;@24;) + local.get 0 + call 17 + i32.eqz + br_if 0 (;@24;) + call 20 + i32.const 0 + i32.load offset=20540 + local.set 0 + end + i32.const 0 + local.get 0 + i32.store offset=8204 + br 20 (;@3;) end - local.get 1 - i32.const 34 - i32.eq - br_if 13 (;@9;) - local.get 1 - i32.const 79 - i32.eq - br_if 6 (;@16;) - local.get 1 - i32.const 114 - i32.ne - br_if 18 (;@4;) block ;; label = @23 - i32.const 0 - call 16 + block ;; label = @24 + local.get 4 + i32.const 4 + i32.add + local.tee 3 + i32.const 105 + i32.const 110 + i32.const 116 + i32.const 101 + i32.const 114 + i32.const 111 + i32.const 112 + i32.const 82 + i32.const 101 + i32.const 113 + i32.const 117 + i32.const 105 + i32.const 114 + i32.const 101 + i32.const 87 + i32.const 105 + i32.const 108 + i32.const 100 + i32.const 99 + i32.const 97 + i32.const 114 + i32.const 100 + call 21 + i32.eqz + br_if 0 (;@24;) + block ;; label = @25 + local.get 0 + call 17 + br_if 0 (;@25;) + local.get 4 + i32.load16_u + i32.const 46 + i32.ne + br_if 1 (;@24;) + end + i32.const 0 + local.get 4 + i32.const 48 + i32.add + i32.store offset=20540 + local.get 4 + i32.load16_u offset=48 + i32.const 40 + i32.ne + br_if 1 (;@23;) + i32.const 0 + local.get 4 + i32.const 50 + i32.add + i32.store offset=20540 + i32.const 0 + i32.const 1 + i32.store16 offset=8198 + i32.const 0 + i32.load offset=20528 + i32.const 0 + i32.load offset=8204 + i32.store + i32.const 0 + call 16 + i32.eqz + br_if 1 (;@23;) + local.get 0 + call 17 + i32.eqz + br_if 1 (;@23;) + local.get 4 + call 18 + br 1 (;@23;) + end + local.get 3 + i32.const 95 + i32.const 101 + i32.const 120 + i32.const 112 + i32.const 111 + i32.const 114 + i32.const 116 + call 22 i32.eqz br_if 0 (;@23;) - local.get 0 - call 17 - i32.eqz + block ;; label = @24 + local.get 0 + call 17 + br_if 0 (;@24;) + local.get 4 + i32.load16_u + i32.const 46 + i32.ne + br_if 1 (;@23;) + end + i32.const 0 + local.get 4 + i32.const 18 + i32.add + i32.store offset=20540 + block ;; label = @24 + local.get 4 + i32.load16_u offset=18 + local.tee 3 + i32.const 83 + i32.ne + br_if 0 (;@24;) + local.get 4 + i32.load16_u offset=20 + i32.const 116 + i32.ne + br_if 1 (;@23;) + local.get 4 + i32.load16_u offset=22 + i32.const 97 + i32.ne + br_if 1 (;@23;) + local.get 4 + i32.load16_u offset=24 + i32.const 114 + i32.ne + br_if 1 (;@23;) + i32.const 0 + local.get 4 + i32.const 26 + i32.add + i32.store offset=20540 + local.get 4 + i32.load16_u offset=26 + local.set 3 + end + local.get 3 + i32.const 65535 + i32.and + i32.const 40 + i32.ne br_if 0 (;@23;) + i32.const 0 + i32.load offset=20528 + i32.const 0 + i32.load offset=8204 + i32.store + i32.const 0 + i32.const 1 + i32.store16 offset=8198 + i32.const 0 + i32.load offset=20540 + local.tee 4 + i32.load16_u offset=2 + i32.const 114 + i32.ne + br_if 0 (;@23;) + i32.const 0 local.get 4 - call 18 + i32.const 2 + i32.add + i32.store offset=20540 + i32.const 2 + call 16 + drop end i32.const 0 i32.const 0 @@ -494,256 +691,125 @@ br 19 (;@3;) end block ;; label = @22 - local.get 4 - i32.const 4 + local.get 1 + i32.const -39 i32.add - i32.const 109 - i32.const 112 - i32.const 111 - i32.const 114 - i32.const 116 - call 19 - i32.eqz - br_if 0 (;@22;) - local.get 0 - call 17 - i32.eqz - br_if 0 (;@22;) - call 20 - i32.const 0 - i32.load offset=20540 - local.set 0 + br_table 13 (;@9;) 5 (;@17;) 8 (;@14;) 18 (;@4;) 18 (;@4;) 18 (;@4;) 18 (;@4;) 18 (;@4;) 14 (;@8;) 0 (;@22;) end - i32.const 0 - local.get 0 - i32.store offset=8204 - br 18 (;@3;) - end - block ;; label = @21 block ;; label = @22 - local.get 4 - i32.const 4 - i32.add - local.tee 3 - i32.const 105 - i32.const 110 - i32.const 116 - i32.const 101 - i32.const 114 - i32.const 111 - i32.const 112 - i32.const 82 - i32.const 101 - i32.const 113 - i32.const 117 - i32.const 105 - i32.const 114 - i32.const 101 - i32.const 87 - i32.const 105 - i32.const 108 - i32.const 100 - i32.const 99 - i32.const 97 - i32.const 114 - i32.const 100 - call 21 - i32.eqz - br_if 0 (;@22;) - block ;; label = @23 - local.get 0 - call 17 - br_if 0 (;@23;) - local.get 4 - i32.load16_u - i32.const 46 - i32.ne - br_if 1 (;@22;) - end - i32.const 0 - local.get 4 - i32.const 48 - i32.add - i32.store offset=20540 - local.get 4 - i32.load16_u offset=48 - i32.const 40 - i32.ne - br_if 1 (;@21;) - i32.const 0 - local.get 4 - i32.const 50 + local.get 1 + i32.const -96 i32.add - i32.store offset=20540 - i32.const 0 - i32.const 1 - i32.store16 offset=8198 - i32.const 0 - i32.load offset=20528 - i32.const 0 - i32.load offset=8204 - i32.store - i32.const 0 - call 16 - i32.eqz - br_if 1 (;@21;) - local.get 0 - call 17 - i32.eqz - br_if 1 (;@21;) - local.get 4 - call 18 - br 1 (;@21;) + br_table 15 (;@7;) 18 (;@4;) 18 (;@4;) 3 (;@19;) 18 (;@4;) 2 (;@20;) 0 (;@22;) end - local.get 3 - i32.const 95 - i32.const 101 - i32.const 120 - i32.const 112 - i32.const 111 - i32.const 114 - i32.const 116 - call 22 - i32.eqz - br_if 0 (;@21;) block ;; label = @22 - local.get 0 - call 17 - br_if 0 (;@22;) - local.get 4 - i32.load16_u - i32.const 46 - i32.ne - br_if 1 (;@21;) - end - i32.const 0 - local.get 4 - i32.const 18 - i32.add - i32.store offset=20540 - block ;; label = @22 - local.get 4 - i32.load16_u offset=18 - local.tee 3 - i32.const 83 - i32.ne - br_if 0 (;@22;) - local.get 4 - i32.load16_u offset=20 - i32.const 116 - i32.ne - br_if 1 (;@21;) - local.get 4 - i32.load16_u offset=22 - i32.const 97 - i32.ne - br_if 1 (;@21;) - local.get 4 - i32.load16_u offset=24 - i32.const 114 - i32.ne - br_if 1 (;@21;) - i32.const 0 - local.get 4 - i32.const 26 + local.get 1 + i32.const -123 i32.add - i32.store offset=20540 - local.get 4 - i32.load16_u offset=26 - local.set 3 + br_table 9 (;@13;) 18 (;@4;) 12 (;@10;) 0 (;@22;) end - local.get 3 - i32.const 65535 - i32.and - i32.const 40 + local.get 1 + i32.const 34 + i32.eq + br_if 12 (;@9;) + local.get 1 + i32.const 79 + i32.eq + br_if 3 (;@18;) + local.get 1 + i32.const 109 i32.ne - br_if 0 (;@21;) - i32.const 0 - i32.load offset=20528 - i32.const 0 - i32.load offset=8204 - i32.store - i32.const 0 - i32.const 1 - i32.store16 offset=8198 - i32.const 0 - i32.load offset=20540 - local.tee 4 - i32.load16_u offset=2 - i32.const 114 - i32.ne - br_if 0 (;@21;) - i32.const 0 - local.get 4 - i32.const 2 - i32.add - i32.store offset=20540 - i32.const 2 - call 16 - drop + br_if 17 (;@4;) end - i32.const 0 - i32.const 0 - i32.load offset=20540 - i32.store offset=8204 - br 17 (;@3;) - end - block ;; label = @20 - local.get 1 - i32.const -39 + local.get 4 + i32.const 4 i32.add - br_table 11 (;@9;) 5 (;@15;) 7 (;@13;) 16 (;@4;) 16 (;@4;) 16 (;@4;) 16 (;@4;) 16 (;@4;) 12 (;@8;) 0 (;@20;) - end - block ;; label = @20 - local.get 1 - i32.const -96 - i32.add - br_table 13 (;@7;) 16 (;@4;) 16 (;@4;) 3 (;@17;) 16 (;@4;) 2 (;@18;) 0 (;@20;) + i32.const 111 + i32.const 100 + i32.const 117 + i32.const 108 + i32.const 101 + call 19 + i32.eqz + br_if 16 (;@4;) + local.get 0 + call 17 + i32.eqz + br_if 16 (;@4;) + call 23 + br 16 (;@4;) end + local.get 4 + i32.const 4 + i32.add + i32.const 120 + i32.const 112 + i32.const 111 + i32.const 114 + i32.const 116 + call 19 + i32.eqz + br_if 15 (;@4;) + local.get 0 + call 17 + i32.eqz + br_if 15 (;@4;) block ;; label = @20 - local.get 1 - i32.const -123 - i32.add - br_table 8 (;@12;) 16 (;@4;) 10 (;@10;) 0 (;@20;) + local.get 4 + i32.load16_u offset=14 + i32.const 115 + i32.ne + br_if 0 (;@20;) + i32.const 0 + call 24 + br 16 (;@4;) end - local.get 1 - i32.const 34 - i32.eq - br_if 10 (;@9;) - local.get 1 - i32.const 79 - i32.eq - br_if 3 (;@16;) - local.get 1 - i32.const 109 - i32.ne + local.get 3 br_if 15 (;@4;) + call 25 + br 15 (;@4;) end + local.get 0 + call 17 + i32.eqz + br_if 14 (;@4;) local.get 4 - i32.const 4 - i32.add - i32.const 111 - i32.const 100 - i32.const 117 + i32.load16_u offset=4 i32.const 108 - i32.const 101 - call 19 - i32.eqz + i32.ne br_if 14 (;@4;) - local.get 0 - call 17 + local.get 4 + i32.load16_u offset=6 + i32.const 97 + i32.ne + br_if 14 (;@4;) + local.get 4 + i32.load16_u offset=8 + i32.const 115 + i32.ne + br_if 14 (;@4;) + local.get 4 + i32.load16_u offset=10 + i32.const 115 + i32.ne + br_if 14 (;@4;) + local.get 4 + i32.load16_u offset=12 + call 26 i32.eqz br_if 14 (;@4;) - call 23 + i32.const 0 + i32.const 1 + i32.store8 offset=20536 br 14 (;@4;) end local.get 4 i32.const 4 i32.add - i32.const 120 - i32.const 112 - i32.const 111 - i32.const 114 + i32.const 98 + i32.const 106 + i32.const 101 + i32.const 99 i32.const 116 call 19 i32.eqz @@ -752,114 +818,65 @@ call 17 i32.eqz br_if 13 (;@4;) - block ;; label = @18 - local.get 4 - i32.load16_u offset=14 - i32.const 115 - i32.ne - br_if 0 (;@18;) - i32.const 0 - call 24 - br 14 (;@4;) - end local.get 3 - br_if 13 (;@4;) - call 25 + i32.eqz + call 27 br 13 (;@4;) end - local.get 0 - call 17 - i32.eqz - br_if 12 (;@4;) - local.get 4 - i32.load16_u offset=4 - i32.const 108 + local.get 3 + i32.const 2048 i32.ne - br_if 12 (;@4;) - local.get 4 - i32.load16_u offset=6 - i32.const 97 - i32.ne - br_if 12 (;@4;) - local.get 4 - i32.load16_u offset=8 - i32.const 115 - i32.ne - br_if 12 (;@4;) - local.get 4 - i32.load16_u offset=10 - i32.const 115 - i32.ne - br_if 12 (;@4;) - local.get 4 - i32.load16_u offset=12 - call 26 - i32.eqz - br_if 12 (;@4;) + br_if 0 (;@16;) + i32.const 12 + call 28 i32.const 0 - i32.const 1 - i32.store8 offset=20536 - br 12 (;@4;) + i32.load offset=4080 + return end - local.get 4 - i32.const 4 + i32.const 0 + local.get 3 + i32.const 1 i32.add - i32.const 98 - i32.const 106 - i32.const 101 - i32.const 99 - i32.const 116 - call 19 - i32.eqz - br_if 11 (;@4;) - local.get 0 - call 17 - i32.eqz - br_if 11 (;@4;) + i32.store16 offset=8198 + i32.const 0 + i32.load offset=20528 local.get 3 - i32.eqz - call 27 + i32.const 2 + i32.shl + i32.add + i32.const 0 + i32.load offset=8204 + i32.store br 11 (;@4;) end + i32.const 8 + call 28 i32.const 0 - local.get 3 - i32.const 1 - i32.add - i32.store16 offset=8198 - i32.const 0 - i32.load offset=20528 - local.get 3 - i32.const 2 - i32.shl - i32.add - i32.const 0 - i32.load offset=8204 - i32.store - br 10 (;@4;) + i32.load offset=4080 + return end - i32.const 8 - call 28 i32.const 0 - i32.load offset=4080 - return + local.get 3 + i32.const -1 + i32.add + i32.store16 offset=8198 + br 9 (;@4;) end - i32.const 0 local.get 3 - i32.const -1 - i32.add - i32.store16 offset=8198 - br 8 (;@4;) + i32.const 2048 + i32.ne + br_if 0 (;@12;) + i32.const 12 + call 28 + i32.const 0 + i32.load offset=4080 + return end - local.get 3 - i32.const 20560 - i32.add - i32.const 0 - i32.load8_u offset=20536 - i32.store8 i32.const 0 local.get 3 i32.const 1 i32.add + local.tee 4 i32.store16 offset=8198 i32.const 0 i32.load offset=20528 @@ -870,6 +887,14 @@ i32.const 0 i32.load offset=8204 i32.store + local.get 4 + i32.const 65535 + i32.and + i32.const 20559 + i32.add + i32.const 0 + i32.load8_u offset=20536 + i32.store8 i32.const 0 i32.const 0 i32.store8 offset=20536 @@ -1498,10 +1523,19 @@ i32.store offset=20540 return end + block ;; label = @4 + i32.const 0 + i32.load16_u offset=8198 + local.tee 2 + i32.const 2048 + i32.ne + br_if 0 (;@4;) + i32.const 12 + call 28 + return + end i32.const 0 - i32.const 0 - i32.load16_u offset=8198 - local.tee 2 + local.get 2 i32.const 1 i32.add i32.store16 offset=8198 @@ -4713,9 +4747,32 @@ i32.ne br_if 1 (;@3;) i32.const 0 + local.get 2 + i32.const 4 + i32.add + i32.store offset=20540 + block ;; label = @5 + block ;; label = @6 + i32.const 0 + i32.load16_u offset=8196 + local.tee 0 + i32.const 2048 + i32.eq + br_if 0 (;@6;) + i32.const 0 + i32.load16_u offset=8198 + i32.const 65535 + i32.and + i32.const 2048 + i32.ne + br_if 1 (;@5;) + end + i32.const 12 + call 28 + return + end i32.const 0 - i32.load16_u offset=8196 - local.tee 0 + local.get 0 i32.const 1 i32.add i32.store16 offset=8196 @@ -4729,11 +4786,6 @@ i32.load16_u offset=8200 i32.store16 i32.const 0 - local.get 2 - i32.const 4 - i32.add - i32.store offset=20540 - i32.const 0 i32.const 0 i32.load16_u offset=8198 i32.const 1 diff --git a/src/lexer.c b/src/lexer.c index fd7ba8b..e688f21 100755 --- a/src/lexer.c +++ b/src/lexer.c @@ -44,6 +44,25 @@ void (*addReexport)(const uint16_t*, const uint16_t*); void (*addUnsafeGetter)(const uint16_t*, const uint16_t*); void (*clearReexports)(); +static inline __attribute__((always_inline)) bool pushOpenToken (uint16_t* tokenPos) { + if (openTokenDepth == STACK_DEPTH) { + syntaxError(12); + return false; + } + openTokenPosStack[openTokenDepth++] = tokenPos; + return true; +} + +static inline __attribute__((always_inline)) bool pushTemplate () { + if (templateStackDepth == STACK_DEPTH || openTokenDepth == STACK_DEPTH) { + syntaxError(12); + return false; + } + templateStack[templateStackDepth++] = templateDepth; + templateDepth = ++openTokenDepth; + return true; +} + // Note: parsing is based on the _assumption_ that the source is already valid uint32_t parseCJS (uint16_t* _source, uint32_t _sourceLen, void (*_addExport)(const uint16_t*, const uint16_t*), void (*_addReexport)(const uint16_t*, const uint16_t*), void (*_addUnsafeGetter)(const uint16_t*, const uint16_t*), void (*_clearReexports)()) { source = _source; @@ -109,7 +128,8 @@ uint32_t parseCJS (uint16_t* _source, uint32_t _sourceLen, void (*_addExport)(co pos += 23; if (*pos == '(') { pos++; - openTokenPosStack[openTokenDepth++] = lastTokenPos; + if (!pushOpenToken(lastTokenPos)) + return error; if (tryParseRequire(Import) && keywordStart(startPos)) tryBacktrackAddStarExportBinding(startPos - 1); } @@ -119,7 +139,8 @@ uint32_t parseCJS (uint16_t* _source, uint32_t _sourceLen, void (*_addExport)(co if (str_eq4(pos, 'S', 't', 'a', 'r')) pos += 4; if (*pos == '(') { - openTokenPosStack[openTokenDepth++] = lastTokenPos; + if (!pushOpenToken(lastTokenPos)) + return error; if (*(pos + 1) == 'r') { pos++; tryParseRequire(ExportStar); @@ -153,7 +174,8 @@ uint32_t parseCJS (uint16_t* _source, uint32_t _sourceLen, void (*_addExport)(co tryParseObjectDefineOrKeys(openTokenDepth == 0); break; case '(': - openTokenPosStack[openTokenDepth++] = lastTokenPos; + if (!pushOpenToken(lastTokenPos)) + return error; break; case ')': if (openTokenDepth == 0) @@ -161,9 +183,10 @@ uint32_t parseCJS (uint16_t* _source, uint32_t _sourceLen, void (*_addExport)(co openTokenDepth--; break; case '{': - openClassPosStack[openTokenDepth] = nextBraceIsClass; + if (!pushOpenToken(lastTokenPos)) + return error; + openClassPosStack[openTokenDepth - 1] = nextBraceIsClass; nextBraceIsClass = false; - openTokenPosStack[openTokenDepth++] = lastTokenPos; break; case '}': if (openTokenDepth == 0) @@ -1152,7 +1175,8 @@ void throwIfImportStatement () { switch (ch) { // dynamic import case '(': - openTokenPosStack[openTokenDepth++] = startPos; + if (!pushOpenToken(startPos)) + return; return; // import.meta case '.': @@ -1211,8 +1235,8 @@ void templateString () { uint16_t ch = *pos; if (ch == '$' && *(pos + 1) == '{') { pos++; - templateStack[templateStackDepth++] = templateDepth; - templateDepth = ++openTokenDepth; + if (!pushTemplate()) + return; return; } if (ch == '`') diff --git a/test/_unit.js b/test/_unit.js index 0458f17..4a3e94c 100755 --- a/test/_unit.js +++ b/test/_unit.js @@ -887,6 +887,69 @@ function x() { `)); }); + test('Nesting depth boundaries', () => { + const stackDepth = 2048; + const expressionNesting = [ + ['(', ')'], + ['import(', ')'], + ['class A{a=', '}'], + ['`${', '}`'] + ]; + + /** + * @param {number} depth + */ + function mixedSource (depth) { + const braceDepth = Math.floor(depth / 5); + const expressionDepth = depth - braceDepth; + const openings = new Array(expressionDepth); + const closings = new Array(expressionDepth); + for (let index = 0; index < expressionDepth; index++) { + const [opening, closing] = expressionNesting[index % expressionNesting.length]; + openings[index] = opening; + closings[expressionDepth - index - 1] = closing; + } + return '{'.repeat(braceDepth) + openings.join('') + '0' + closings.join('') + + ';' + '}'.repeat(braceDepth); + } + + const sources = [ + [ + '('.repeat(stackDepth) + '0' + ')'.repeat(stackDepth), + '('.repeat(stackDepth + 1) + '0' + ')'.repeat(stackDepth + 1) + ], + [ + '{'.repeat(stackDepth) + '0;' + '}'.repeat(stackDepth), + '{'.repeat(stackDepth + 1) + '0;' + '}'.repeat(stackDepth + 1) + ], + [ + '`${'.repeat(stackDepth) + '0' + '}`'.repeat(stackDepth), + '`${'.repeat(stackDepth + 1) + '0' + '}`'.repeat(stackDepth + 1) + ], + [ + 'class A{a='.repeat(stackDepth) + '0' + '}'.repeat(stackDepth), + 'class A{a='.repeat(stackDepth + 1) + '0' + '}'.repeat(stackDepth + 1) + ], + [ + 'import('.repeat(stackDepth) + '"x"' + ')'.repeat(stackDepth), + 'import('.repeat(stackDepth + 1) + '"x"' + ')'.repeat(stackDepth + 1) + ], + [ + mixedSource(stackDepth), + mixedSource(stackDepth + 1) + ] + ]; + const expectedError = process.env.WASM || process.env.WASM_SYNC + ? /^Error: Parse error @/ + : /^Error: Maximum nesting depth exceeded\./; + + for (const [accepted, rejected] of sources) { + assert.deepStrictEqual(parse(accepted), { exports: [], reexports: [] }); + assert.throws(() => parse(rejected), expectedError); + assert.deepStrictEqual(parse('exports.after = 1'), { exports: ['after'], reexports: [] }); + } + }); + test('Division / Regex ambiguity', () => { const source = ` /as)df/; x();