Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions lexer.js
Original file line number Diff line number Diff line change
Expand Up @@ -999,12 +999,16 @@ function tryParseLiteralExports () {
pos++;
ch = commentWhitespace();
// nothing more complex than identifier expressions for now
if (!identifier()) {
if (!identifier() && source.charCodeAt(pos) !== 92/*\*/) {
pos = revertPos;
return;
}
ch = source.charCodeAt(pos);
}
else if (ch !== 44/*,*/ && ch !== 125/*}*/ && ch !== 61/*=*/ && ch !== 40/*(*/) {
pos = revertPos;
return;
}
_exports.add(decode(source.slice(startPos, endPos)));
}
else if (ch === 46/*.*/ && source.startsWith('..', pos + 1)) {
Expand All @@ -1027,7 +1031,7 @@ function tryParseLiteralExports () {
pos++;
ch = commentWhitespace();
// nothing more complex than identifier expressions for now
if (!identifier()) {
if (!identifier() && source.charCodeAt(pos) !== 92/*\*/) {
pos = revertPos;
return;
}
Expand Down Expand Up @@ -1305,14 +1309,14 @@ function isIdentifierChar(code, astral) {

function identifier () {
let ch = source.codePointAt(pos);
if (!isIdentifierStart(ch, true) || ch === '\\')
if (!isIdentifierStart(ch, true))
return false;
pos += codePointLen(ch);
while (ch = source.codePointAt(pos)) {
if (isIdentifierChar(ch, true)) {
pos += codePointLen(ch);
}
else if (ch === '\\') {
else if (ch === 92/*\*/) {
// no identifier escapes support for now
return false;
}
Expand Down
Binary file modified lib/lexer.wasm
Binary file not shown.
10 changes: 7 additions & 3 deletions src/lexer.c
Original file line number Diff line number Diff line change
Expand Up @@ -1007,12 +1007,16 @@ void tryParseLiteralExports () {
pos++;
ch = commentWhitespace();
// nothing more complex than identifier expressions for now
if (!identifier(ch)) {
if (!identifier(ch) && *pos != '\\') {
pos = revertPos;
return;
}
ch = *pos;
}
else if (ch != ',' && ch != '}' && ch != '=' && ch != '(') {
pos = revertPos;
return;
}
addExport(startPos, endPos);
}
else if (ch == '\'' || ch == '"') {
Expand All @@ -1024,7 +1028,7 @@ void tryParseLiteralExports () {
pos++;
ch = commentWhitespace();
// nothing more complex than identifier expressions for now
if (!identifier(ch)) {
if (!identifier(ch) && *pos != '\\') {
pos = revertPos;
return;
}
Expand Down Expand Up @@ -1161,7 +1165,7 @@ uint32_t fullCharCode(uint16_t ch) {

bool identifier (uint16_t startCh) {
uint32_t ch = fullCharCode(startCh);
if (!isIdentifierStart(ch) && ch != '\\')
if (!isIdentifierStart(ch))
return false;
pos += charCodeByteLen(ch);
while (ch = fullCharCode(*pos)) {
Expand Down
2 changes: 1 addition & 1 deletion src/lexer.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export function parse (source, name = '@') {

while (wasm.rre()) {
const reexptStr = decode(source.slice(wasm.res(), wasm.ree()));
if (reexptStr)
if (reexptStr !== undefined)
reexports.add(reexptStr);
}
while (wasm.ru()) {
Expand Down
73 changes: 73 additions & 0 deletions test/_unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,79 @@ suite('Lexer', () => {
}
});

test('does not detect method prefixes as object exports', () => {
for (const property of ['get value () { return value; }', 'set value (value) {}',
'async value () {}', 'async *value () {}',
'get "value" () { return value; }', 'get [value] () { return value; }',
'get /* comment */ value () { return value; }']) {
assert.deepStrictEqual(parse(`module.exports = { before, ${property}, after };`), {
exports: ['before'], reexports: []
});
}
for (const property of ['get', 'get: value', '"get": value', "'get': value"]) {
assert.deepStrictEqual(parse(`module.exports = { ${property}, after };`), {
exports: ['get', 'after'], reexports: []
});
}
for (const name of ['value', 'get', 'set', 'async']) {
assert.deepStrictEqual(parse(`module.exports = { before, ${name} () {}, after };`), {
exports: ['before', name], reexports: []
});
}
});

test('does not detect escaped identifiers', () => {
for (const name of ['\\u0061', '\\u{61}', 'a\\u0062', 'a\\u{62}']) {
assert.deepStrictEqual(parse(`exports.${name} = value; module.exports = { ${name} };`), {
exports: [], reexports: []
});
}
assert.deepStrictEqual(parse('exports.α = value; exports.𐊧 = value;'), {
exports: ['α', '𐊧'], reexports: []
});
assert.deepStrictEqual(parse('exports["\\u0061"] = value; module.exports = {"\\u0062": value};'), {
exports: ['a', 'b'], reexports: []
});
});

test('preserves empty reexport specifiers', () => {
for (const source of ['module.exports = require("");', "module.exports = require('');",
'__exportStar(require(""), exports);', 'module.exports = { ...require("") };']) {
assert.deepStrictEqual(parse(source), { exports: [], reexports: [''] });
}
assert.deepStrictEqual(parse('module.exports = require("\\uD800");'), {
exports: [], reexports: []
});
});

test('preserves export keys before unsupported identifier value expressions', () => {
for (const value of ['a.b', '\\u0061', '\\u{61}', 'a\\u0062', 'a\\u{62}']) {
for (const key of ['value', '"value"', "'value'"]) {
assert.deepStrictEqual(parse(`module.exports = { ${key}: ${value} };`), {
exports: ['value'], reexports: []
});
assert.deepStrictEqual(parse(`module.exports = { before, ${key}: ${value}, after }; exports.later = later;`), {
exports: ['before', 'value', 'later'], reexports: []
});
}
}
});

test('does not detect getters with escaped identifier returns', () => {
for (const name of ['\\u0061', '\\u{61}', 'a\\u0062', 'a\\u{62}']) {
const getter = `Object.defineProperty(exports, 'value', { get () { return ${name}; } });`;
assert.deepStrictEqual(parse(getter), {
exports: [], reexports: []
});
assert.deepStrictEqual(parse(`exports.value = value; ${getter}`), {
exports: [], reexports: []
});
}
assert.deepStrictEqual(parse("Object.defineProperty(exports, 'value', { get () { return module['\\u0061']; } });"), {
exports: ['value'], reexports: []
});
});

test('automatic Wasm initialization', async () => {
const lexer = await import('../dist/lexer.mjs?automatic-initialization');

Expand Down
Loading