Skip to content

Commit 4b32d8c

Browse files
committed
JS: refactor SourceType/Platform
1 parent ac6b9d1 commit 4b32d8c

4 files changed

Lines changed: 75 additions & 21 deletions

File tree

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

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ private class V extends DefaultVisitor<Context, Label> {
306306
public V(Platform platform, SourceType sourceType) {
307307
this.platform = platform;
308308
this.sourceType = sourceType;
309-
this.isStrict = sourceType == SourceType.MODULE || sourceType == SourceType.CLOSURE_MODULE;
309+
this.isStrict = sourceType.isStrictMode();
310310
}
311311

312312
private Label visit(INode child, Label parent, int childIndex) {
@@ -546,37 +546,37 @@ public Label visit(Program nd, Context c) {
546546

547547
isStrict = hasUseStrict(nd.getBody());
548548

549-
// if we're extracting a Node.js/ES2015 module, introduce module scope
550-
if (platform == Platform.NODE) {
551-
// add node.js-specific globals
552-
scopeManager.addVariables("global", "process", "console", "Buffer");
549+
// Add platform-specific globals.
550+
scopeManager.addVariables(platform.getPredefinedGlobals());
553551

552+
// Introduce local scope if there is one.
553+
if (sourceType.hasLocalScope()) {
554554
Label moduleScopeKey = trapwriter.globalID("module;{" + locationManager.getFileLabel() + "}," + locationManager.getStartLine() + "," + locationManager.getStartColumn());
555555
scopeManager.enterScope(3, moduleScopeKey, toplevelLabel);
556-
// special variables aren't available in `.mjs` modules
557-
if (!".mjs".equals(locationManager.getSourceFileExtension()))
558-
scopeManager.addVariables("require", "module", "exports", "__filename", "__dirname", "arguments");
559-
trapwriter.addTuple("isModule", toplevelLabel);
560-
} else if (sourceType == SourceType.MODULE || sourceType == SourceType.CLOSURE_MODULE) {
561-
Label moduleScopeKey = trapwriter.globalID("module;{" + locationManager.getFileLabel() + "}," + locationManager.getStartLine() + "," + locationManager.getStartColumn());
562-
scopeManager.enterScope(3, moduleScopeKey, toplevelLabel);
563-
if (sourceType == SourceType.CLOSURE_MODULE) {
564-
scopeManager.addVariables("exports");
565-
trapwriter.addTuple("isClosureModule", toplevelLabel);
566-
} else {
567-
trapwriter.addTuple("isES2015Module", toplevelLabel);
568-
}
556+
scopeManager.addVariables(sourceType.getPredefinedLocals());
569557
trapwriter.addTuple("isModule", toplevelLabel);
570558
}
571559

560+
// Emit the specific source type.
561+
switch (sourceType) {
562+
case CLOSURE_MODULE:
563+
trapwriter.addTuple("isClosureModule", toplevelLabel);
564+
break;
565+
case MODULE:
566+
trapwriter.addTuple("isES2015Module", toplevelLabel);
567+
break;
568+
default:
569+
break;
570+
}
571+
572572
// add all declared global (or module-scoped) names, both non-lexical and lexical
573573
scopeManager.addNames(scopeManager.collectDeclaredNames(nd, isStrict, false, DeclKind.none));
574574
scopeManager.addNames(scopeManager.collectDeclaredNames(nd, isStrict, true, DeclKind.none));
575575

576576
visitAll(nd.getBody(), toplevelLabel);
577577

578-
// if we're extracting a module, leave its scope
579-
if (platform == Platform.NODE || sourceType == SourceType.MODULE || sourceType == SourceType.CLOSURE_MODULE)
578+
// Leave the local scope again.
579+
if (sourceType.hasLocalScope())
580580
scopeManager.leaveScope();
581581

582582
contextManager.leaveContainer();

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

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
import java.nio.charset.IllegalCharsetNameException;
55
import java.nio.charset.StandardCharsets;
66
import java.nio.charset.UnsupportedCharsetException;
7+
import java.util.Arrays;
8+
import java.util.Collections;
9+
import java.util.LinkedHashSet;
10+
import java.util.Set;
711

812
import com.semmle.js.parser.JcornWrapper;
913
import com.semmle.util.data.StringUtil;
@@ -69,13 +73,50 @@ public static enum SourceType {
6973
/** A Closure-Library module, defined using `goog.module()`. */
7074
CLOSURE_MODULE,
7175

76+
/** A CommonJS module that is not also an ES2015 module. */
77+
COMMONJS_MODULE,
78+
7279
/** Automatically determined source type. */
7380
AUTO;
7481

7582
@Override
7683
public String toString() {
7784
return StringUtil.lc(name());
7885
}
86+
87+
/**
88+
* Returns true if this source is executed directly in the global scope,
89+
* or false if it has its own local scope.
90+
*/
91+
public boolean hasLocalScope() {
92+
return this != SCRIPT;
93+
}
94+
95+
/**
96+
* Returns true if this source is implicitly in strict mode.
97+
*/
98+
public boolean isStrictMode() {
99+
return this == MODULE;
100+
}
101+
102+
private static final Set<String> closureLocals = Collections.singleton("exports");
103+
private static final Set<String> commonJsLocals = new LinkedHashSet<>(Arrays.asList("require", "module", "exports", "__filename", "__dirname", "arguments"));
104+
105+
/**
106+
* Returns the set of local variables in scope at the top-level of this module.
107+
* <p/>
108+
* If this source type has no local scope, the empty set is returned.
109+
*/
110+
public Set<String> getPredefinedLocals() {
111+
switch (this) {
112+
case CLOSURE_MODULE:
113+
return closureLocals;
114+
case COMMONJS_MODULE:
115+
return commonJsLocals;
116+
default:
117+
return Collections.emptySet();
118+
}
119+
}
79120
};
80121

81122
public static enum Platform {
@@ -85,6 +126,15 @@ public static enum Platform {
85126
public String toString() {
86127
return StringUtil.lc(name());
87128
}
129+
130+
private static final Set<String> nodejsGlobals = new LinkedHashSet<>(Arrays.asList("global", "process", "console", "Buffer"));
131+
132+
/**
133+
* Gets the set of predefined globals for this platform.
134+
*/
135+
public Set<String> getPredefinedGlobals() {
136+
return this == NODE ? nodejsGlobals : Collections.emptySet();
137+
}
88138
}
89139

90140
/**

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ public Pair<Label, LoCInfo> extract(TextualExtractor textualExtractor,
9090
LoCInfo loc;
9191
if (ast != null) {
9292
platform = getPlatform(platform, ast);
93+
if (sourceType == SourceType.SCRIPT && platform == Platform.NODE) {
94+
sourceType = SourceType.COMMONJS_MODULE;
95+
}
9396

9497
lexicalExtractor = new LexicalExtractor(textualExtractor, parserRes.getTokens(), parserRes.getComments());
9598
ASTExtractor scriptExtractor = new ASTExtractor(lexicalExtractor, scopeManager);
@@ -126,7 +129,7 @@ public Pair<Label, LoCInfo> extract(TextualExtractor textualExtractor,
126129

127130
if (config.isExterns())
128131
textualExtractor.getTrapwriter().addTuple("isExterns", toplevelLabel);
129-
if (platform == Platform.NODE && sourceType != SourceType.MODULE && sourceType != SourceType.CLOSURE_MODULE)
132+
if (platform == Platform.NODE && sourceType == SourceType.COMMONJS_MODULE)
130133
textualExtractor.getTrapwriter().addTuple("isNodejs", toplevelLabel);
131134

132135
return Pair.make(toplevelLabel, loc);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
| a.js:5:31:5:31 | o |
2+
| es2015_require.js:1:11:1:17 | require |
23
| exports.js:3:9:3:15 | exports |
34
| tst.html:6:3:6:7 | alert |

0 commit comments

Comments
 (0)