From 0895c5f2a00466a905616e66b597f0757f604bcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Mon, 1 Mar 2021 17:24:07 +0100 Subject: [PATCH 1/2] Detect Babel reexports with conflict filter for other exports --- README.md | 3 ++ demo.mjs | 26 ++++++++++++ include-wasm/cjs-module-lexer.h | 1 + lexer.js | 68 ++++++++++++++++++++++++++++- lib/lexer.wasm | Bin 22766 -> 23511 bytes src/lexer.c | 73 ++++++++++++++++++++++++++++++++ test/_unit.js | 41 +++++++++++++++--- 7 files changed, 204 insertions(+), 8 deletions(-) create mode 100644 demo.mjs diff --git a/README.md b/README.md index 375a491..5f449d7 100755 --- a/README.md +++ b/README.md @@ -111,6 +111,9 @@ EXPORT_STAR_LIB: `Object.keys(` IDENTIFIER$1 `).forEach(function (` IDENTIFIER$2 `if (` IDENTIFIER$2 `===` ( `'default'` | `"default"` ) `||` IDENTIFIER$2 `===` ( '__esModule' | `"__esModule"` ) `) return` `;`? | `if (` IDENTIFIER$2 `!==` ( `'default'` | `"default"` ) `)` ) + ( + `if (Object.prototype.hasOwnProperty.call(` IDENTIFIER `, ` IDENTIFIER$2 `)) return` `;`? + )? ( `if (` IDENTIFIER$2 `in` EXPORTS_IDENTIFIER `&&` EXPORTS_IDENTIFIER `[` IDENTIFIER$2 `] ===` IDENTIFIER$1 `[` IDENTIFIER$2 `]) return` `;`? )? diff --git a/demo.mjs b/demo.mjs new file mode 100644 index 0000000..b789414 --- /dev/null +++ b/demo.mjs @@ -0,0 +1,26 @@ +import { parse, init } from "./lexer.js"; + +await init(); + +const code = ` +"use strict"; + +exports.__esModule = true; +var _exportNames = { + named: true +}; +exports.named = void 0; + +var _reExport = require("./re-export"); + +Object.keys(_reExport).forEach(function (key) { + if (key === "default" || key === "__esModule") return; + if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; + if (key in exports && exports[key] === _reExport[key]) return; + exports[key] = _reExport[key]; +}); +var named; +exports.named = named; +` + +console.log(parse(code)); \ No newline at end of file diff --git a/include-wasm/cjs-module-lexer.h b/include-wasm/cjs-module-lexer.h index 1900d92..9f80228 100755 --- a/include-wasm/cjs-module-lexer.h +++ b/include-wasm/cjs-module-lexer.h @@ -213,6 +213,7 @@ bool str_eq4 (uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4) bool str_eq5 (uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4, uint16_t c5); bool str_eq6 (uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4, uint16_t c5, uint16_t c6); bool str_eq7 (uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4, uint16_t c5, uint16_t c6, uint16_t c7); +bool str_eq8 (uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4, uint16_t c5, uint16_t c6, uint16_t c7, uint16_t c8); bool str_eq9 (uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4, uint16_t c5, uint16_t c6, uint16_t c7, uint16_t c8, uint16_t c9); bool str_eq10 (uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4, uint16_t c5, uint16_t c6, uint16_t c7, uint16_t c8, uint16_t c9, uint16_t c10); bool str_eq13 (uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4, uint16_t c5, uint16_t c6, uint16_t c7, uint16_t c8, uint16_t c9, uint16_t c10, uint16_t c11, uint16_t c12, uint16_t c13); diff --git a/lexer.js b/lexer.js index ed81460..a0cf98f 100755 --- a/lexer.js +++ b/lexer.js @@ -489,6 +489,73 @@ function tryParseObjectDefineOrKeys (keys) { } else break; + // `if (Object.prototype.hasOwnProperty.call(` IDENTIFIER `, ` IDENTIFIER$2 `)) return` `;`? + currentIfStatement: if (ch === 105/*i*/ && source.charCodeAt(pos + 1) === 102/*f*/) { + let ifStartPos = pos; + + pos += 2; + ch = commentWhitespace(); + if (ch !== 40/*(*/) break; + pos++; + ch = commentWhitespace(); + if (ch !== 79/*O*/ || !source.startsWith('bject', pos + 1)) { + // Revert parsing the current optional if statement, but don't bail + // out since we can try parse the next possible if statement. + pos = ifStartPos; + ch = 105/*i*/; + break currentIfStatement; + } + pos += 6; + ch = commentWhitespace(); + if (ch !== 46/*.*/) { + // Revert parsing the current optional if statement, but don't bail + // out since we can try parse the next possible if statement. + pos = ifStartPos; + ch = 105/*i*/; + break currentIfStatement; + } + pos++; + ch = commentWhitespace(); + if (ch !== 112/*p*/ || !source.startsWith('rototype', pos + 1)) break; + pos += 9; + ch = commentWhitespace(); + if (ch !== 46/*.*/) break; + pos++; + ch = commentWhitespace(); + if (ch !== 104/*h*/ || !source.startsWith('asOwnProperty', pos + 1)) break; + pos += 14; + ch = commentWhitespace(); + if (ch !== 46/*.*/) break; + pos++; + ch = commentWhitespace(); + if (ch !== 99/*c*/ || !source.startsWith('all', pos + 1)) break; + pos += 4; + ch = commentWhitespace(); + if (ch !== 40/*(*/) break; + pos++; + ch = commentWhitespace(); + if (!identifier()) break; + ch = commentWhitespace(); + if (ch !== 44/*,*/) break; + pos++; + ch = commentWhitespace(); + if (!source.startsWith(it_id, pos)) break; + pos += it_id.length; + ch = commentWhitespace(); + if (ch !== 41/*)*/) break; + pos++; + ch = commentWhitespace(); + if (ch !== 41/*)*/) break; + pos++; + ch = commentWhitespace(); + if (ch !== 114/*r*/ || !source.startsWith('eturn', pos + 1)) break; + pos += 6; + ch = commentWhitespace(); + if (ch === 59/*;*/) + pos++; + ch = commentWhitespace(); + } + // `if (` IDENTIFIER$2 `in` EXPORTS_IDENTIFIER `&&` EXPORTS_IDENTIFIER `[` IDENTIFIER$2 `] ===` IDENTIFIER$1 `[` IDENTIFIER$2 `]) return` `;`? if (ch === 105/*i*/ && source.charCodeAt(pos + 1) === 102/*f*/) { pos += 2; @@ -1039,7 +1106,6 @@ function throwIfImportStatement () { // import.meta case 46/*.*/: throw new Error('Unexpected import.meta in CJS module.'); - return; default: // no space after "import" -> not an import keyword diff --git a/lib/lexer.wasm b/lib/lexer.wasm index a8a5a54afb85f9a9ac2e1327f752b024230aaf21..d9dd5e3668cb0d1216371f7c4c67638f7fc0d671 100755 GIT binary patch delta 851 zcmZWmO-vI(6rQ);?T_u!Q7|q2bGI0lfKZ$AqZMSIQoQ0tV^AXp4>r+@f{8Kqq7V`j zQ(lZ7BziHN%^r-27xiSscrkje2qGZ}s4?E0*=-NRJ-m7Q=9}-E`M!;3@cB7J*u>S# zz!-xn@M>f#9jY`ws9aEGFIGiX-!ZJt6*OIK3Ph`KtLnl$pmQFcMwCzOj@C5m2b=D- zd;{CLzV|U42p~+FLB-})mf?4HLT8GNtV4PE1hQHV6kL%rWQK++Z6-2j6kCGvgE#ho z43#o|VYtR{nc)({UyL{F5SGXKB~`@j8Ai(PuglOg>hU%Jw3TY<-4wLGhQ3R?&G3gL ztk=H`H>C9jr0@$;3VJ55o;wOmx%zM7*%H11BF?lM{pGK*jY& z>-SohKPaAUs@+14#*zujC8H7BlQ#Sa*XUi!q>rgMY3V%WTGQ=yAEEvVQws}UP2t22 zH|cmRFOryy&5QaX-c5GA7!Gh-SEQ&Bq&x9~yYpVunbwmna!>X4gsSHwoo($DLBq?MOMFM&wE|)tulU< zV5=<+hp5z6Z1zZQmL+o4MfTBL+bj$_sr{A@jyfaOBM?ii;}YyX91SCwU@%^kSL8=% zymKNjD5-go-gLf(A)4+g!H~1qRZ@fJ04m7L5WTWWw4U4LA2mZ^BjoUdGXR6m!~7}h EA002$Hvj+t delta 499 zcmYL@F-#Oe6o%j1UEB+Idpj3S65znygX2`>jO=l62fFtX2_2=7*g!$y1&Im4!Wan& znousUpy2`yp(axhh?S)nwIjw31+>vq2Y&t(4Z&Ho?m;g4NRz|6v91OQw? zJ4ieg#*$jNG_8fIQ#IK148t;$!BJqmTgR%L!|jCj$(1e1)hp<8J$;70e0g;f`ZPe&AdIl%P@7wO6zt&z4CCyVEZG6~Xi8l$~(g+-I zOcov9rMa*Bmcc2fm2YOs5o9!=`HXkz$%BlIs?yuedNS5XbwR5-F}c|F5Th}@G4}bI z`kQU%7n+Q2m#%VU)+J4NmI_xj=_k-{5Jg_-p2tBx?k-@FQ$0IM_j~SJB|EOLXEpi) zqQuR)S;aibIauN#m+tc-_eG7qD);}LPJoVq{s5gt`vk-gf6P@{{xQ#MbPn{bk=xn3 ziudGBZ(hf{^23b{RL5N3BTNn3aY7dz5aqC|sPO5n8FM6z5oMm}e}ki(DvaT%oGgqP P@?zi>;)q->KFI$COkaL$ diff --git a/src/lexer.c b/src/lexer.c index bcfd9a9..f9ebaa1 100755 --- a/src/lexer.c +++ b/src/lexer.c @@ -508,6 +508,75 @@ void tryParseObjectDefineOrKeys (bool keys) { else break; + // `if (Object.prototype.hasOwnProperty.call(` IDENTIFIER `, ` IDENTIFIER$2 `)) return` `;`? + if (ch == 'i' && *(pos + 1) == 'f') { + uint16_t *ifStartPos = pos; + + pos += 2; + ch = commentWhitespace(); + if (ch != '(') break; + pos++; + ch = commentWhitespace(); + if (ch != 'O' || !str_eq5(pos + 1, 'b', 'j', 'e', 'c', 't')) { + // Revert parsing the current optional if statement, but don't bail + // out since we can try parse the next possible if statement. + pos = ifStartPos; + ch = 'i'; + goto currentIfStatementEnd; + } + pos += 6; + ch = commentWhitespace(); + if (ch != '.') { + // Revert parsing the current optional if statement, but don't bail + // out since we can try parse the next possible if statement. + pos = ifStartPos; + ch = 'i'; + goto currentIfStatementEnd; + } + pos++; + ch = commentWhitespace(); + if (ch != 'p' || !str_eq8(pos + 1, 'r', 'o', 't', 'o', 't', 'y', 'p', 'e')) break; + pos += 9; + ch = commentWhitespace(); + if (ch != '.') break; + pos++; + ch = commentWhitespace(); + if (ch != 'h' || !str_eq13(pos + 1, 'a', 's', 'O', 'w', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'y')) break; + pos += 14; + ch = commentWhitespace(); + if (ch != '.') break; + pos++; + ch = commentWhitespace(); + if (ch != 'c' || !str_eq3(pos + 1, 'a', 'l', 'l')) break; + pos += 4; + ch = commentWhitespace(); + if (ch != '(') break; + pos++; + ch = commentWhitespace(); + if (!identifier(ch)) break; + ch = commentWhitespace(); + if (ch != ',') break; + pos++; + ch = commentWhitespace(); + if (memcmp(pos, it_id_start, it_id_len * sizeof(uint16_t)) != 0) break; + pos += it_id_len; + ch = commentWhitespace(); + if (ch != ')') break; + pos++; + ch = commentWhitespace(); + if (ch != ')') break; + pos++; + ch = commentWhitespace(); + if (ch != 'r' || !str_eq5(pos + 1, 'e', 't', 'u', 'r', 'n')) break; + pos += 6; + ch = commentWhitespace(); + if (ch == ';') + pos++; + ch = commentWhitespace(); + } + currentIfStatementEnd:; + + // `if (` IDENTIFIER$2 `in` EXPORTS_IDENTIFIER `&&` EXPORTS_IDENTIFIER `[` IDENTIFIER$2 `] ===` IDENTIFIER$1 `[` IDENTIFIER$2 `]) return` `;`? if (ch == 'i' && *(pos + 1) == 'f') { pos += 2; @@ -1257,6 +1326,10 @@ bool str_eq7 (uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4, return *pos == c1 && *(pos + 1) == c2 && *(pos + 2) == c3 && *(pos + 3) == c4 && *(pos + 4) == c5 && *(pos + 5) == c6 && *(pos + 6) == c7; } +bool str_eq8 (uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4, uint16_t c5, uint16_t c6, uint16_t c7, uint16_t c8) { + return *pos == c1 && *(pos + 1) == c2 && *(pos + 2) == c3 && *(pos + 3) == c4 && *(pos + 4) == c5 && *(pos + 5) == c6 && *(pos + 6) == c7 && *(pos + 7) == c8; +} + bool str_eq9 (uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4, uint16_t c5, uint16_t c6, uint16_t c7, uint16_t c8, uint16_t c9) { return *pos == c1 && *(pos + 1) == c2 && *(pos + 2) == c3 && *(pos + 3) == c4 && *(pos + 4) == c5 && *(pos + 5) == c6 && *(pos + 6) == c7 && *(pos + 7) == c8 && *(pos + 8) == c9; } diff --git a/test/_unit.js b/test/_unit.js index 71af716..99f8eda 100755 --- a/test/_unit.js +++ b/test/_unit.js @@ -144,9 +144,19 @@ suite('Lexer', () => { exports[key] = _external001[key]; }); + var _external003 = require("external003"); + + // Babel >=7.12.0, loose mode, reexports conflicts filter + Object.keys(_external003).forEach(function (key) { + if (key === "default" || key === "__esModule") return; + if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; + if (key in exports && exports[key] === _external003[key]) return; + exports[key] = _external003[key]; + }); + var _external002 = require("external002"); - // Babel >=7.12.0, loose mode + // Babel >=7.12.0 Object.keys(_external002).forEach(function (key) { if (key === "default" || key === "__esModule") return; if (key in exports && exports[key] === _external002[key]) return; @@ -158,6 +168,21 @@ suite('Lexer', () => { }); }); + var _external004 = require("external004"); + + // Babel >=7.12.0, reexports conflict filter + Object.keys(_external004).forEach(function (key) { + if (key === "default" || key === "__esModule") return; + if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; + if (key in exports && exports[key] === _external004[key]) return; + Object.defineProperty(exports, key, { + enumerable: true, + get: function () { + return _external004[key]; + } + }); + }); + let external3 = require('external3'); const external4 = require('external4'); @@ -285,15 +310,17 @@ suite('Lexer', () => { `); assert.equal(exports.length, 1); assert.equal(exports[0], '__esModule'); - assert.equal(reexports.length, 8); + assert.equal(reexports.length, 10); assert.equal(reexports[0], 'external'); assert.equal(reexports[1], 'external2'); assert.equal(reexports[2], 'external001'); - assert.equal(reexports[3], 'external002'); - assert.equal(reexports[4], 'external3'); - assert.equal(reexports[5], 'external4'); - assert.equal(reexports[6], 'external😃'); - assert.equal(reexports[7], 'external𤭢'); + assert.equal(reexports[3], 'external003'); + assert.equal(reexports[4], 'external002'); + assert.equal(reexports[5], 'external004'); + assert.equal(reexports[6], 'external3'); + assert.equal(reexports[7], 'external4'); + assert.equal(reexports[8], 'external😃'); + assert.equal(reexports[9], 'external𤭢'); }); test('invalid exports cases', () => { From 01c36a4e7fb649495785f3cdb6990530008d4d5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Mon, 1 Mar 2021 17:38:03 +0100 Subject: [PATCH 2/2] Delete demo.mjs --- demo.mjs | 26 -------------------------- 1 file changed, 26 deletions(-) delete mode 100644 demo.mjs diff --git a/demo.mjs b/demo.mjs deleted file mode 100644 index b789414..0000000 --- a/demo.mjs +++ /dev/null @@ -1,26 +0,0 @@ -import { parse, init } from "./lexer.js"; - -await init(); - -const code = ` -"use strict"; - -exports.__esModule = true; -var _exportNames = { - named: true -}; -exports.named = void 0; - -var _reExport = require("./re-export"); - -Object.keys(_reExport).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; - if (key in exports && exports[key] === _reExport[key]) return; - exports[key] = _reExport[key]; -}); -var named; -exports.named = named; -` - -console.log(parse(code)); \ No newline at end of file