Skip to content

Commit 33107c2

Browse files
authored
Merge pull request #22417 from MathiasVP/js-parse-weird-module-import
JS: Parse alternative import syntax
2 parents 67a362a + df35070 commit 33107c2

3 files changed

Lines changed: 158 additions & 0 deletions

File tree

javascript/extractor/src/com/semmle/jcorn/Parser.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,9 @@
118118
* 4.0.3</a>, but does not support plugins, and always tracks full source locations.
119119
*/
120120
public class Parser {
121+
private static final Pattern LEGACY_MODULE_IMPORT_TAIL =
122+
Pattern.compile("\\s+[A-Za-z_$][A-Za-z0-9_$]*\\s+from\\s*['\"]");
123+
121124
protected final Options options;
122125
protected final Set<String> keywords;
123126
private final Set<String> reservedWords, reservedWordsStrict, reservedWordsStrictBind;
@@ -2718,6 +2721,18 @@ boolean isUsingDecl() {
27182721
&& Identifiers.isIdentifierChar(this.input.codePointAt(next), false);
27192722
}
27202723

2724+
/**
2725+
* Checks for the abandoned ES6 draft namespace-import syntax:
2726+
* {@code module namespace from "module-name";}.
2727+
*/
2728+
boolean isLegacyModuleImport() {
2729+
if (this.type != TokenType.name || !this.value.equals("module")) return false;
2730+
2731+
Matcher matcher = LEGACY_MODULE_IMPORT_TAIL.matcher(this.input);
2732+
matcher.region(this.pos, this.input.length());
2733+
return matcher.lookingAt();
2734+
}
2735+
27212736
/**
27222737
* Parse a single statement.
27232738
*
@@ -2780,6 +2795,10 @@ protected Statement parseStatement(boolean declaration, boolean topLevel, Set<St
27802795
return this.parseBlock(false);
27812796
} else if (starttype == TokenType.semi) {
27822797
return this.parseEmptyStatement(startLoc);
2798+
} else if (topLevel && this.isLegacyModuleImport()) {
2799+
if (!this.options.allowImportExportEverywhere() && !this.inModule)
2800+
this.raise(this.start, "Legacy module imports may appear only with 'sourceType: module'");
2801+
return this.parseLegacyModuleImport(startLoc);
27832802
} else if (starttype == TokenType._export || starttype == TokenType._import) {
27842803
if (!this.options.allowImportExportEverywhere()) {
27852804
if (!topLevel)
@@ -3577,6 +3596,29 @@ protected Statement parseImport(Position startLoc) {
35773596
return parseImportRest(loc);
35783597
}
35793598

3599+
/**
3600+
* Parses {@code module namespace from "module-name";} as the equivalent namespace import
3601+
* {@code import * as namespace from "module-name";}.
3602+
*/
3603+
protected ImportDeclaration parseLegacyModuleImport(Position startLoc) {
3604+
SourceLocation loc = new SourceLocation(startLoc);
3605+
this.next();
3606+
3607+
SourceLocation specifierLoc = new SourceLocation(this.startLoc);
3608+
Identifier local = this.parseIdent(false);
3609+
this.checkLVal(local, true, null);
3610+
this.expectContextual("from");
3611+
if (this.type != TokenType.string) this.unexpected();
3612+
Literal source = (Literal) this.parseExprAtom(null);
3613+
this.semicolon();
3614+
3615+
List<ImportSpecifier> specifiers = new ArrayList<ImportSpecifier>();
3616+
specifiers.add(this.finishNode(new ImportNamespaceSpecifier(specifierLoc, local)));
3617+
return this.finishNode(
3618+
new ImportDeclaration(
3619+
loc, specifiers, source, null, ImportPhaseModifier.NONE));
3620+
}
3621+
35803622
protected Expression parseImportOrExportAttributesAndSemicolon() {
35813623
Expression result = null;
35823624
if (!this.eagerlyTrySemicolon()) {
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module Utils from "./utils";
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
#10000=@"/import8.js;sourcefile"
2+
files(#10000,"/import8.js")
3+
#10001=@"/;folder"
4+
folders(#10001,"/")
5+
containerparent(#10001,#10000)
6+
#10002=@"loc,{#10000},0,0,0,0"
7+
locations_default(#10002,#10000,0,0,0,0)
8+
hasLocation(#10000,#10002)
9+
#20000=@"global_scope"
10+
scopes(#20000,0)
11+
#20001=@"script;{#10000},1,1"
12+
#20002=*
13+
lines(#20002,#20001,"module Utils from ""./utils"";","")
14+
#20003=@"loc,{#10000},1,1,1,28"
15+
locations_default(#20003,#10000,1,1,1,28)
16+
hasLocation(#20002,#20003)
17+
numlines(#20001,1,1,0)
18+
#20004=*
19+
tokeninfo(#20004,6,#20001,0,"module")
20+
#20005=@"loc,{#10000},1,1,1,6"
21+
locations_default(#20005,#10000,1,1,1,6)
22+
hasLocation(#20004,#20005)
23+
#20006=*
24+
tokeninfo(#20006,6,#20001,1,"Utils")
25+
#20007=@"loc,{#10000},1,8,1,12"
26+
locations_default(#20007,#10000,1,8,1,12)
27+
hasLocation(#20006,#20007)
28+
#20008=*
29+
tokeninfo(#20008,6,#20001,2,"from")
30+
#20009=@"loc,{#10000},1,14,1,17"
31+
locations_default(#20009,#10000,1,14,1,17)
32+
hasLocation(#20008,#20009)
33+
#20010=*
34+
tokeninfo(#20010,4,#20001,3,"""./utils""")
35+
#20011=@"loc,{#10000},1,19,1,27"
36+
locations_default(#20011,#10000,1,19,1,27)
37+
hasLocation(#20010,#20011)
38+
#20012=*
39+
tokeninfo(#20012,8,#20001,4,";")
40+
#20013=@"loc,{#10000},1,28,1,28"
41+
locations_default(#20013,#10000,1,28,1,28)
42+
hasLocation(#20012,#20013)
43+
#20014=*
44+
tokeninfo(#20014,0,#20001,5,"")
45+
#20015=@"loc,{#10000},1,29,1,28"
46+
locations_default(#20015,#10000,1,29,1,28)
47+
hasLocation(#20014,#20015)
48+
toplevels(#20001,0)
49+
hasLocation(#20001,#20003)
50+
#20016=@"var;{Utils};{#20000}"
51+
variables(#20016,"Utils",#20000)
52+
#20017=@"local_type_name;{Utils};{#20000}"
53+
local_type_names(#20017,"Utils",#20000)
54+
#20018=@"local_namespace_name;{Utils};{#20000}"
55+
local_namespace_names(#20018,"Utils",#20000)
56+
variables(#20016,"Utils",#20000)
57+
local_type_names(#20017,"Utils",#20000)
58+
local_namespace_names(#20018,"Utils",#20000)
59+
#20019=@"var;{this};{#20000}"
60+
variables(#20019,"this",#20000)
61+
#20020=*
62+
stmts(#20020,27,#20001,0,"module ... utils"";")
63+
hasLocation(#20020,#20003)
64+
stmt_containers(#20020,#20001)
65+
#20021=*
66+
exprs(#20021,4,#20020,-1,"""./utils""")
67+
hasLocation(#20021,#20011)
68+
enclosing_stmt(#20021,#20020)
69+
expr_containers(#20021,#20001)
70+
literals("./utils","""./utils""",#20021)
71+
#20022=*
72+
regexpterm(#20022,1,#20021,0,"./utils")
73+
#20023=@"loc,{#10000},1,20,1,26"
74+
locations_default(#20023,#10000,1,20,1,26)
75+
hasLocation(#20022,#20023)
76+
#20024=*
77+
regexpterm(#20024,12,#20022,0,".")
78+
#20025=@"loc,{#10000},1,20,1,20"
79+
locations_default(#20025,#10000,1,20,1,20)
80+
hasLocation(#20024,#20025)
81+
#20026=*
82+
regexpterm(#20026,14,#20022,1,"/utils")
83+
#20027=@"loc,{#10000},1,21,1,26"
84+
locations_default(#20027,#10000,1,21,1,26)
85+
hasLocation(#20026,#20027)
86+
regexp_const_value(#20026,"/utils")
87+
#20028=*
88+
exprs(#20028,85,#20020,0,"Utils f ... utils"";")
89+
#20029=@"loc,{#10000},1,8,1,28"
90+
locations_default(#20029,#10000,1,8,1,28)
91+
hasLocation(#20028,#20029)
92+
enclosing_stmt(#20028,#20020)
93+
expr_containers(#20028,#20001)
94+
#20030=*
95+
exprs(#20030,78,#20028,1,"Utils")
96+
hasLocation(#20030,#20007)
97+
enclosing_stmt(#20030,#20020)
98+
expr_containers(#20030,#20001)
99+
literals("Utils","Utils",#20030)
100+
decl(#20030,#20016)
101+
typedecl(#20030,#20017)
102+
namespacedecl(#20030,#20018)
103+
#20031=*
104+
entry_cfg_node(#20031,#20001)
105+
#20032=@"loc,{#10000},1,1,1,0"
106+
locations_default(#20032,#10000,1,1,1,0)
107+
hasLocation(#20031,#20032)
108+
#20033=*
109+
exit_cfg_node(#20033,#20001)
110+
hasLocation(#20033,#20015)
111+
successor(#20020,#20033)
112+
successor(#20028,#20020)
113+
successor(#20031,#20028)
114+
numlines(#10000,1,1,0)
115+
filetype(#10000,"javascript")

0 commit comments

Comments
 (0)