From 5871b87fe676394bccd11db563d5ac319afb1497 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Mon, 24 Aug 2026 20:08:48 +0100 Subject: [PATCH 1/2] JS: Add a test for an unusual import syntax which is not support in any modern JS implementations, but may be supported by transpilers. --- .../extractor/tests/es2015/input/import8.js | 1 + .../tests/es2015/output/trap/import8.js.trap | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 javascript/extractor/tests/es2015/input/import8.js create mode 100644 javascript/extractor/tests/es2015/output/trap/import8.js.trap diff --git a/javascript/extractor/tests/es2015/input/import8.js b/javascript/extractor/tests/es2015/input/import8.js new file mode 100644 index 000000000000..90b02b5fc001 --- /dev/null +++ b/javascript/extractor/tests/es2015/input/import8.js @@ -0,0 +1 @@ +module Utils from "./utils"; \ No newline at end of file diff --git a/javascript/extractor/tests/es2015/output/trap/import8.js.trap b/javascript/extractor/tests/es2015/output/trap/import8.js.trap new file mode 100644 index 000000000000..3dc96cd68731 --- /dev/null +++ b/javascript/extractor/tests/es2015/output/trap/import8.js.trap @@ -0,0 +1,28 @@ +#10000=@"/import8.js;sourcefile" +files(#10000,"/import8.js") +#10001=@"/;folder" +folders(#10001,"/") +containerparent(#10001,#10000) +#10002=@"loc,{#10000},0,0,0,0" +locations_default(#10002,#10000,0,0,0,0) +hasLocation(#10000,#10002) +#20000=@"global_scope" +scopes(#20000,0) +#20001=@"script;{#10000},1,1" +toplevels(#20001,0) +#20002=@"loc,{#10000},1,1,1,1" +locations_default(#20002,#10000,1,1,1,1) +hasLocation(#20001,#20002) +#20003=* +js_parse_errors(#20003,#20001,"Error: Unexpected token","module Utils from ""./utils"";") +#20004=@"loc,{#10000},1,8,1,8" +locations_default(#20004,#10000,1,8,1,8) +hasLocation(#20003,#20004) +#20005=* +lines(#20005,#20001,"module Utils from ""./utils"";","") +#20006=@"loc,{#10000},1,1,1,28" +locations_default(#20006,#10000,1,1,1,28) +hasLocation(#20005,#20006) +numlines(#20001,1,0,0) +numlines(#10000,1,0,0) +filetype(#10000,"javascript") From df35070d0892a3466d45d230df4b2061d9e38275 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Mon, 24 Aug 2026 20:10:52 +0100 Subject: [PATCH 2/2] JS: Support an alternative syntax for imports and accept test changes. --- .../src/com/semmle/jcorn/Parser.java | 42 +++++++ .../tests/es2015/output/trap/import8.js.trap | 117 +++++++++++++++--- 2 files changed, 144 insertions(+), 15 deletions(-) diff --git a/javascript/extractor/src/com/semmle/jcorn/Parser.java b/javascript/extractor/src/com/semmle/jcorn/Parser.java index 3ae1491af10e..a248a82dd140 100644 --- a/javascript/extractor/src/com/semmle/jcorn/Parser.java +++ b/javascript/extractor/src/com/semmle/jcorn/Parser.java @@ -118,6 +118,9 @@ * 4.0.3, but does not support plugins, and always tracks full source locations. */ public class Parser { + private static final Pattern LEGACY_MODULE_IMPORT_TAIL = + Pattern.compile("\\s+[A-Za-z_$][A-Za-z0-9_$]*\\s+from\\s*['\"]"); + protected final Options options; protected final Set keywords; private final Set reservedWords, reservedWordsStrict, reservedWordsStrictBind; @@ -2718,6 +2721,18 @@ boolean isUsingDecl() { && Identifiers.isIdentifierChar(this.input.codePointAt(next), false); } + /** + * Checks for the abandoned ES6 draft namespace-import syntax: + * {@code module namespace from "module-name";}. + */ + boolean isLegacyModuleImport() { + if (this.type != TokenType.name || !this.value.equals("module")) return false; + + Matcher matcher = LEGACY_MODULE_IMPORT_TAIL.matcher(this.input); + matcher.region(this.pos, this.input.length()); + return matcher.lookingAt(); + } + /** * Parse a single statement. * @@ -2780,6 +2795,10 @@ protected Statement parseStatement(boolean declaration, boolean topLevel, Set specifiers = new ArrayList(); + specifiers.add(this.finishNode(new ImportNamespaceSpecifier(specifierLoc, local))); + return this.finishNode( + new ImportDeclaration( + loc, specifiers, source, null, ImportPhaseModifier.NONE)); + } + protected Expression parseImportOrExportAttributesAndSemicolon() { Expression result = null; if (!this.eagerlyTrySemicolon()) { diff --git a/javascript/extractor/tests/es2015/output/trap/import8.js.trap b/javascript/extractor/tests/es2015/output/trap/import8.js.trap index 3dc96cd68731..f794ba427c7e 100644 --- a/javascript/extractor/tests/es2015/output/trap/import8.js.trap +++ b/javascript/extractor/tests/es2015/output/trap/import8.js.trap @@ -9,20 +9,107 @@ hasLocation(#10000,#10002) #20000=@"global_scope" scopes(#20000,0) #20001=@"script;{#10000},1,1" +#20002=* +lines(#20002,#20001,"module Utils from ""./utils"";","") +#20003=@"loc,{#10000},1,1,1,28" +locations_default(#20003,#10000,1,1,1,28) +hasLocation(#20002,#20003) +numlines(#20001,1,1,0) +#20004=* +tokeninfo(#20004,6,#20001,0,"module") +#20005=@"loc,{#10000},1,1,1,6" +locations_default(#20005,#10000,1,1,1,6) +hasLocation(#20004,#20005) +#20006=* +tokeninfo(#20006,6,#20001,1,"Utils") +#20007=@"loc,{#10000},1,8,1,12" +locations_default(#20007,#10000,1,8,1,12) +hasLocation(#20006,#20007) +#20008=* +tokeninfo(#20008,6,#20001,2,"from") +#20009=@"loc,{#10000},1,14,1,17" +locations_default(#20009,#10000,1,14,1,17) +hasLocation(#20008,#20009) +#20010=* +tokeninfo(#20010,4,#20001,3,"""./utils""") +#20011=@"loc,{#10000},1,19,1,27" +locations_default(#20011,#10000,1,19,1,27) +hasLocation(#20010,#20011) +#20012=* +tokeninfo(#20012,8,#20001,4,";") +#20013=@"loc,{#10000},1,28,1,28" +locations_default(#20013,#10000,1,28,1,28) +hasLocation(#20012,#20013) +#20014=* +tokeninfo(#20014,0,#20001,5,"") +#20015=@"loc,{#10000},1,29,1,28" +locations_default(#20015,#10000,1,29,1,28) +hasLocation(#20014,#20015) toplevels(#20001,0) -#20002=@"loc,{#10000},1,1,1,1" -locations_default(#20002,#10000,1,1,1,1) -hasLocation(#20001,#20002) -#20003=* -js_parse_errors(#20003,#20001,"Error: Unexpected token","module Utils from ""./utils"";") -#20004=@"loc,{#10000},1,8,1,8" -locations_default(#20004,#10000,1,8,1,8) -hasLocation(#20003,#20004) -#20005=* -lines(#20005,#20001,"module Utils from ""./utils"";","") -#20006=@"loc,{#10000},1,1,1,28" -locations_default(#20006,#10000,1,1,1,28) -hasLocation(#20005,#20006) -numlines(#20001,1,0,0) -numlines(#10000,1,0,0) +hasLocation(#20001,#20003) +#20016=@"var;{Utils};{#20000}" +variables(#20016,"Utils",#20000) +#20017=@"local_type_name;{Utils};{#20000}" +local_type_names(#20017,"Utils",#20000) +#20018=@"local_namespace_name;{Utils};{#20000}" +local_namespace_names(#20018,"Utils",#20000) +variables(#20016,"Utils",#20000) +local_type_names(#20017,"Utils",#20000) +local_namespace_names(#20018,"Utils",#20000) +#20019=@"var;{this};{#20000}" +variables(#20019,"this",#20000) +#20020=* +stmts(#20020,27,#20001,0,"module ... utils"";") +hasLocation(#20020,#20003) +stmt_containers(#20020,#20001) +#20021=* +exprs(#20021,4,#20020,-1,"""./utils""") +hasLocation(#20021,#20011) +enclosing_stmt(#20021,#20020) +expr_containers(#20021,#20001) +literals("./utils","""./utils""",#20021) +#20022=* +regexpterm(#20022,1,#20021,0,"./utils") +#20023=@"loc,{#10000},1,20,1,26" +locations_default(#20023,#10000,1,20,1,26) +hasLocation(#20022,#20023) +#20024=* +regexpterm(#20024,12,#20022,0,".") +#20025=@"loc,{#10000},1,20,1,20" +locations_default(#20025,#10000,1,20,1,20) +hasLocation(#20024,#20025) +#20026=* +regexpterm(#20026,14,#20022,1,"/utils") +#20027=@"loc,{#10000},1,21,1,26" +locations_default(#20027,#10000,1,21,1,26) +hasLocation(#20026,#20027) +regexp_const_value(#20026,"/utils") +#20028=* +exprs(#20028,85,#20020,0,"Utils f ... utils"";") +#20029=@"loc,{#10000},1,8,1,28" +locations_default(#20029,#10000,1,8,1,28) +hasLocation(#20028,#20029) +enclosing_stmt(#20028,#20020) +expr_containers(#20028,#20001) +#20030=* +exprs(#20030,78,#20028,1,"Utils") +hasLocation(#20030,#20007) +enclosing_stmt(#20030,#20020) +expr_containers(#20030,#20001) +literals("Utils","Utils",#20030) +decl(#20030,#20016) +typedecl(#20030,#20017) +namespacedecl(#20030,#20018) +#20031=* +entry_cfg_node(#20031,#20001) +#20032=@"loc,{#10000},1,1,1,0" +locations_default(#20032,#10000,1,1,1,0) +hasLocation(#20031,#20032) +#20033=* +exit_cfg_node(#20033,#20001) +hasLocation(#20033,#20015) +successor(#20020,#20033) +successor(#20028,#20020) +successor(#20031,#20028) +numlines(#10000,1,1,0) filetype(#10000,"javascript")