Revert "Merge pull request #897 from Semmle/revert-817-closure-modules"

This reverts commit 95185345fd, reversing
changes made to b8be66ec48.
This commit is contained in:
Asger F
2019-02-07 11:58:38 +00:00
parent 383e82a3f3
commit e4b230ba60
105 changed files with 3526 additions and 133 deletions

View File

@@ -306,7 +306,7 @@ public class ASTExtractor {
public V(Platform platform, SourceType sourceType) {
this.platform = platform;
this.sourceType = sourceType;
this.isStrict = sourceType == SourceType.MODULE;
this.isStrict = sourceType.isStrictMode();
}
private Label visit(INode child, Label parent, int childIndex) {
@@ -546,31 +546,37 @@ public class ASTExtractor {
isStrict = hasUseStrict(nd.getBody());
// if we're extracting a Node.js/ES2015 module, introduce module scope
if (platform == Platform.NODE) {
// add node.js-specific globals
scopeManager.addVariables("global", "process", "console", "Buffer");
// Add platform-specific globals.
scopeManager.addVariables(platform.getPredefinedGlobals());
// Introduce local scope if there is one.
if (sourceType.hasLocalScope()) {
Label moduleScopeKey = trapwriter.globalID("module;{" + locationManager.getFileLabel() + "}," + locationManager.getStartLine() + "," + locationManager.getStartColumn());
scopeManager.enterScope(3, moduleScopeKey, toplevelLabel);
// special variables aren't available in `.mjs` modules
if (!".mjs".equals(locationManager.getSourceFileExtension()))
scopeManager.addVariables("require", "module", "exports", "__filename", "__dirname", "arguments");
trapwriter.addTuple("isModule", toplevelLabel);
} else if (sourceType == SourceType.MODULE) {
Label moduleScopeKey = trapwriter.globalID("module;{" + locationManager.getFileLabel() + "}," + locationManager.getStartLine() + "," + locationManager.getStartColumn());
scopeManager.enterScope(3, moduleScopeKey, toplevelLabel);
scopeManager.addVariables(sourceType.getPredefinedLocals(platform, locationManager.getSourceFileExtension()));
trapwriter.addTuple("isModule", toplevelLabel);
}
// Emit the specific source type.
switch (sourceType) {
case CLOSURE_MODULE:
trapwriter.addTuple("isClosureModule", toplevelLabel);
break;
case MODULE:
trapwriter.addTuple("isES2015Module", toplevelLabel);
break;
default:
break;
}
// add all declared global (or module-scoped) names, both non-lexical and lexical
scopeManager.addNames(scopeManager.collectDeclaredNames(nd, isStrict, false, DeclKind.none));
scopeManager.addNames(scopeManager.collectDeclaredNames(nd, isStrict, true, DeclKind.none));
visitAll(nd.getBody(), toplevelLabel);
// if we're extracting a Node.js/ES2015 module, leave its scope
if (platform == Platform.NODE || sourceType == SourceType.MODULE)
// Leave the local scope again.
if (sourceType.hasLocalScope())
scopeManager.leaveScope();
contextManager.leaveContainer();

View File

@@ -4,7 +4,12 @@ import java.nio.charset.Charset;
import java.nio.charset.IllegalCharsetNameException;
import java.nio.charset.StandardCharsets;
import java.nio.charset.UnsupportedCharsetException;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import com.semmle.js.parser.JcornWrapper;
import com.semmle.util.data.StringUtil;
import com.semmle.util.exception.UserError;
@@ -50,13 +55,75 @@ public class ExtractorConfig {
}
};
/**
* The type of a source file, which together with the {@link Platform}
* determines how the top-level scope of the file behaves, and whether ES2015
* module syntax should be allowed.
* <p>
* Note that the names of these enum members are depended on by {@link Main},
* {@link AutoBuild}, and {@link JcornWrapper}.
*/
public static enum SourceType {
SCRIPT, MODULE, AUTO;
/** A script executed in the global scope. */
SCRIPT,
/** An ES2015 module. */
MODULE,
/** A Closure-Library module, defined using `goog.module()`. */
CLOSURE_MODULE,
/** A CommonJS module that is not also an ES2015 module. */
COMMONJS_MODULE,
/** Automatically determined source type. */
AUTO;
@Override
public String toString() {
return StringUtil.lc(name());
}
/**
* Returns true if this source is executed directly in the global scope,
* or false if it has its own local scope.
*/
public boolean hasLocalScope() {
return this != SCRIPT;
}
/**
* Returns true if this source is implicitly in strict mode.
*/
public boolean isStrictMode() {
return this == MODULE;
}
private static final Set<String> closureLocals = Collections.singleton("exports");
private static final Set<String> commonJsLocals = new LinkedHashSet<>(Arrays.asList("require", "module", "exports", "__filename", "__dirname", "arguments"));
/**
* Returns the set of local variables in scope at the top-level of this module.
* <p/>
* If this source type has no local scope, the empty set is returned.
*/
public Set<String> getPredefinedLocals(Platform platform, String extension) {
switch (this) {
case CLOSURE_MODULE:
return closureLocals;
case COMMONJS_MODULE:
return commonJsLocals;
case MODULE:
if (platform == Platform.NODE && !extension.equals(".mjs")) {
// An ES2015 module that is compiled to a Node.js module effectively has the locals
// from Node.js even if they are not part of the ES2015 standard.
return commonJsLocals;
}
return Collections.emptySet();
default:
return Collections.emptySet();
}
}
};
public static enum Platform {
@@ -66,6 +133,15 @@ public class ExtractorConfig {
public String toString() {
return StringUtil.lc(name());
}
private static final Set<String> nodejsGlobals = new LinkedHashSet<>(Arrays.asList("global", "process", "console", "Buffer"));
/**
* Gets the set of predefined globals for this platform.
*/
public Set<String> getPredefinedGlobals() {
return this == NODE ? nodejsGlobals : Collections.emptySet();
}
}
/**

View File

@@ -29,8 +29,8 @@ public class JSExtractor {
this.config = config;
}
// heuristic: if `import` or `export` appears at the beginning of a line, it's probably a module
private static final Pattern containsImportOrExport = Pattern.compile("(?m)^([ \t]*)(import|export)\\b");
// heuristic: if `import`, `export`, or `goog.module` appears at the beginning of a line, it's probably a module
private static final Pattern containsModuleIndicator = Pattern.compile("(?m)^([ \t]*)(import|export|goog\\.module)\\b");
public Pair<Label, LoCInfo> extract(TextualExtractor textualExtractor, String source, int toplevelKind, ScopeManager scopeManager) throws ParseError {
// if the file starts with `{ "<string>":` it won't parse as JavaScript; try parsing as JSON instead
@@ -69,9 +69,10 @@ public class JSExtractor {
if (sourceType != SourceType.AUTO)
return sourceType;
if (config.getEcmaVersion().compareTo(ECMAVersion.ECMA2015) >= 0) {
Matcher m = containsImportOrExport.matcher(source);
if (m.find() && (allowLeadingWS || m.group(1).isEmpty()))
return SourceType.MODULE;
Matcher m = containsModuleIndicator.matcher(source);
if (m.find() && (allowLeadingWS || m.group(1).isEmpty())) {
return m.group(2).startsWith("goog") ? SourceType.CLOSURE_MODULE : SourceType.MODULE;
}
}
return SourceType.SCRIPT;
}
@@ -89,6 +90,9 @@ public class JSExtractor {
LoCInfo loc;
if (ast != null) {
platform = getPlatform(platform, ast);
if (sourceType == SourceType.SCRIPT && platform == Platform.NODE) {
sourceType = SourceType.COMMONJS_MODULE;
}
lexicalExtractor = new LexicalExtractor(textualExtractor, parserRes.getTokens(), parserRes.getComments());
ASTExtractor scriptExtractor = new ASTExtractor(lexicalExtractor, scopeManager);
@@ -125,7 +129,7 @@ public class JSExtractor {
if (config.isExterns())
textualExtractor.getTrapwriter().addTuple("isExterns", toplevelLabel);
if (platform == Platform.NODE && sourceType != SourceType.MODULE)
if (platform == Platform.NODE && sourceType == SourceType.COMMONJS_MODULE)
textualExtractor.getTrapwriter().addTuple("isNodejs", toplevelLabel);
return Pair.make(toplevelLabel, loc);

View File

@@ -41,7 +41,7 @@ public class Main {
* such a way that it may produce different tuples for the same file under the same
* {@link ExtractorConfig}.
*/
public static final String EXTRACTOR_VERSION = "2019-01-29";
public static final String EXTRACTOR_VERSION = "2019-02-04";
public static final Pattern NEWLINE = Pattern.compile("\n");