Skip to content

Commit 04a0740

Browse files
committed
JS: Extractor: More robust ES2015 checking
Created shared AbstractDetector to not duplicate all the tedious logic ;) I took inspiration from the tests in `javascript/extractor/tests/esnext/input/dynamic-import.js`
1 parent cd84500 commit 04a0740

File tree

5 files changed

+114
-1
lines changed

5 files changed

+114
-1
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.semmle.js.extractor;
2+
3+
import com.semmle.js.ast.DynamicImport;
4+
import com.semmle.js.ast.ExportDeclaration;
5+
import com.semmle.js.ast.Expression;
6+
import com.semmle.js.ast.ImportDeclaration;
7+
import com.semmle.js.ast.Node;
8+
import com.semmle.js.ast.Statement;
9+
10+
/** A utility class for detecting Node.js code. */
11+
public class ES2015Detector extends AbstractDetector {
12+
/**
13+
* Is {@code ast} a program that uses ES2015 import/export code?
14+
*/
15+
public static boolean looksLikeES2015(Node ast) {
16+
return new ES2015Detector().programDetection(ast);
17+
}
18+
19+
@Override
20+
protected boolean visitStatement(Statement stmt) {
21+
if (stmt instanceof ImportDeclaration || stmt instanceof ExportDeclaration) {
22+
return true;
23+
}
24+
return super.visitStatement(stmt);
25+
}
26+
27+
@Override
28+
protected boolean visitExpression(Expression e) {
29+
if (e instanceof DynamicImport) {
30+
return true;
31+
}
32+
return super.visitExpression(e);
33+
}
34+
}

javascript/extractor/src/com/semmle/js/extractor/JSExtractor.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,26 @@ public Pair<Label, ParseResultInfo> extract(
5858

5959
JSParser.Result parserRes =
6060
JSParser.parse(config, sourceType, source, textualExtractor.getMetrics());
61+
62+
// Check if we guessed wrong with the regex in `establishSourceType`, (which could
63+
// happen due to a block-comment line starting with ' import').
64+
if (config.getSourceType() == SourceType.AUTO && sourceType != SourceType.SCRIPT) {
65+
boolean wrongGuess = false;
66+
67+
if (sourceType == SourceType.MODULE) {
68+
// check that we did see an import/export declaration
69+
wrongGuess = ES2015Detector.looksLikeES2015(parserRes.getAST()) == false;
70+
} else if (sourceType == SourceType.CLOSURE_MODULE ) {
71+
// TODO
72+
}
73+
74+
if (wrongGuess) {
75+
sourceType = SourceType.SCRIPT;
76+
parserRes =
77+
JSParser.parse(config, sourceType, source, textualExtractor.getMetrics());
78+
}
79+
}
80+
6181
return extract(textualExtractor, source, toplevelKind, scopeManager, sourceType, parserRes);
6282
}
6383

javascript/extractor/test/com/semmle/js/extractor/test/AllTests.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
@SuiteClasses({
1919
JSXTests.class,
2020
NodeJSDetectorTests.class,
21+
ES2015DetectorTests.class,
2122
TrapTests.class,
2223
ObjectRestSpreadTests.class,
2324
ClassPropertiesTests.class,
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.semmle.js.extractor.test;
2+
3+
import com.semmle.js.ast.Node;
4+
import com.semmle.js.extractor.ES2015Detector;
5+
import com.semmle.js.extractor.ExtractionMetrics;
6+
import com.semmle.js.extractor.ExtractorConfig;
7+
import com.semmle.js.extractor.ExtractorConfig.SourceType;
8+
import com.semmle.js.parser.JSParser;
9+
import com.semmle.js.parser.JSParser.Result;
10+
import org.junit.Assert;
11+
import org.junit.Test;
12+
13+
public class ES2015DetectorTests {
14+
// using `experimental: true` as we do in real extractor, see `extractSource` method
15+
// in `AutoBuild.java`
16+
private static final ExtractorConfig CONFIG = new ExtractorConfig(true);
17+
18+
private void isES2015(String src, boolean expected) {
19+
Result res = JSParser.parse(CONFIG, SourceType.MODULE, src, new ExtractionMetrics());
20+
Node ast = res.getAST();
21+
Assert.assertNotNull(ast);
22+
Assert.assertTrue(ES2015Detector.looksLikeES2015(ast) == expected);
23+
}
24+
25+
@Test
26+
public void testImport() {
27+
isES2015("import * as fs from 'fs';", true);
28+
}
29+
30+
@Test
31+
public void testExport() {
32+
isES2015("export function foo() { };", true);
33+
}
34+
35+
@Test
36+
public void testDynamicImport() {
37+
isES2015("import('fs');", true);
38+
}
39+
40+
@Test
41+
public void testDynamicImportAssign() {
42+
isES2015("var fs = import('fs');", true);
43+
}
44+
45+
@Test
46+
public void testDynamicImportThen() {
47+
isES2015("import('o').then((o) => {});", true);
48+
}
49+
50+
@Test
51+
public void importInBlockComment() {
52+
isES2015("/*\n"
53+
+ " import * from 'fs';\n"
54+
+ "*/\n"
55+
+ "const fs = require('fs');",
56+
false);
57+
}
58+
}

javascript/extractor/tests/node/output/trap/detection.js.trap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,6 @@ variables(#20046,"__dirname",#20041)
137137
#20047=@"var;{arguments};{#20041}"
138138
variables(#20047,"arguments",#20041)
139139
is_module(#20001)
140-
is_es2015_module(#20001)
141140
#20048=@"var;{fs};{#20041}"
142141
variables(#20048,"fs",#20041)
143142
#20049=*
@@ -199,5 +198,6 @@ successor(#20053,#20050)
199198
successor(#20052,#20055)
200199
successor(#20050,#20061)
201200
successor(#20059,#20049)
201+
is_nodejs(#20001)
202202
numlines(#10000,6,1,5)
203203
filetype(#10000,"javascript")

0 commit comments

Comments
 (0)