diff --git a/change-notes/1.20/analysis-javascript.md b/change-notes/1.20/analysis-javascript.md index 6167aaa6276..68f08324e35 100644 --- a/change-notes/1.20/analysis-javascript.md +++ b/change-notes/1.20/analysis-javascript.md @@ -12,6 +12,8 @@ * Type inference for function calls has been improved. This may give additional results for queries that rely on type inference. +* The [Closure-Library](https://github.com/google/closure-library/wiki/goog.module:-an-ES6-module-like-alternative-to-goog.provide) module system is now supported. + ## New queries | **Query** | **Tags** | **Purpose** | diff --git a/javascript/extractor/src/com/semmle/js/extractor/ASTExtractor.java b/javascript/extractor/src/com/semmle/js/extractor/ASTExtractor.java index b135f0ce3f3..886cacd87a6 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/ASTExtractor.java +++ b/javascript/extractor/src/com/semmle/js/extractor/ASTExtractor.java @@ -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(); diff --git a/javascript/extractor/src/com/semmle/js/extractor/ExtractorConfig.java b/javascript/extractor/src/com/semmle/js/extractor/ExtractorConfig.java index dfb031021f3..bd42953e240 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/ExtractorConfig.java +++ b/javascript/extractor/src/com/semmle/js/extractor/ExtractorConfig.java @@ -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. + *

+ * 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 closureLocals = Collections.singleton("exports"); + private static final Set 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. + *

+ * If this source type has no local scope, the empty set is returned. + */ + public Set 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 nodejsGlobals = new LinkedHashSet<>(Arrays.asList("global", "process", "console", "Buffer")); + + /** + * Gets the set of predefined globals for this platform. + */ + public Set getPredefinedGlobals() { + return this == NODE ? nodejsGlobals : Collections.emptySet(); + } } /** diff --git a/javascript/extractor/src/com/semmle/js/extractor/JSExtractor.java b/javascript/extractor/src/com/semmle/js/extractor/JSExtractor.java index 9b252011264..2ef02a769fb 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/JSExtractor.java +++ b/javascript/extractor/src/com/semmle/js/extractor/JSExtractor.java @@ -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 extract(TextualExtractor textualExtractor, String source, int toplevelKind, ScopeManager scopeManager) throws ParseError { // if the file starts with `{ "":` 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); diff --git a/javascript/extractor/src/com/semmle/js/extractor/Main.java b/javascript/extractor/src/com/semmle/js/extractor/Main.java index 792823af247..98950f212f8 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/Main.java +++ b/javascript/extractor/src/com/semmle/js/extractor/Main.java @@ -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"); diff --git a/javascript/extractor/tests/closure/input/googDotDeclareModuleId.js b/javascript/extractor/tests/closure/input/googDotDeclareModuleId.js new file mode 100644 index 00000000000..621884a8865 --- /dev/null +++ b/javascript/extractor/tests/closure/input/googDotDeclareModuleId.js @@ -0,0 +1,3 @@ +goog.declareModuleId('test'); + +export let x = 5; diff --git a/javascript/extractor/tests/closure/input/googDotModule.js b/javascript/extractor/tests/closure/input/googDotModule.js new file mode 100644 index 00000000000..dcb994f981d --- /dev/null +++ b/javascript/extractor/tests/closure/input/googDotModule.js @@ -0,0 +1,3 @@ +goog.module('test'); +var x = 5; +exports = { x: x }; diff --git a/javascript/extractor/tests/closure/input/googDotProvide.js b/javascript/extractor/tests/closure/input/googDotProvide.js new file mode 100644 index 00000000000..a194c35957f --- /dev/null +++ b/javascript/extractor/tests/closure/input/googDotProvide.js @@ -0,0 +1,3 @@ +goog.provide('test.x'); + +test.x = 5; diff --git a/javascript/extractor/tests/closure/output/trap/googDotDeclareModuleId.js.trap b/javascript/extractor/tests/closure/output/trap/googDotDeclareModuleId.js.trap new file mode 100644 index 00000000000..729aae779a4 --- /dev/null +++ b/javascript/extractor/tests/closure/output/trap/googDotDeclareModuleId.js.trap @@ -0,0 +1,203 @@ +#10000=@"/googDotDeclareModuleId.js;sourcefile" +files(#10000,"/googDotDeclareModuleId.js","googDotDeclareModuleId","js",0) +#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" +#20002=* +lines(#20002,#20001,"goog.declareModuleId('test');"," +") +#20003=@"loc,{#10000},1,1,1,29" +locations_default(#20003,#10000,1,1,1,29) +hasLocation(#20002,#20003) +#20004=* +lines(#20004,#20001,""," +") +#20005=@"loc,{#10000},2,1,2,0" +locations_default(#20005,#10000,2,1,2,0) +hasLocation(#20004,#20005) +#20006=* +lines(#20006,#20001,"export let x = 5;"," +") +#20007=@"loc,{#10000},3,1,3,17" +locations_default(#20007,#10000,3,1,3,17) +hasLocation(#20006,#20007) +numlines(#20001,3,2,0) +#20008=* +tokeninfo(#20008,6,#20001,0,"goog") +#20009=@"loc,{#10000},1,1,1,4" +locations_default(#20009,#10000,1,1,1,4) +hasLocation(#20008,#20009) +#20010=* +tokeninfo(#20010,8,#20001,1,".") +#20011=@"loc,{#10000},1,5,1,5" +locations_default(#20011,#10000,1,5,1,5) +hasLocation(#20010,#20011) +#20012=* +tokeninfo(#20012,6,#20001,2,"declareModuleId") +#20013=@"loc,{#10000},1,6,1,20" +locations_default(#20013,#10000,1,6,1,20) +hasLocation(#20012,#20013) +#20014=* +tokeninfo(#20014,8,#20001,3,"(") +#20015=@"loc,{#10000},1,21,1,21" +locations_default(#20015,#10000,1,21,1,21) +hasLocation(#20014,#20015) +#20016=* +tokeninfo(#20016,4,#20001,4,"'test'") +#20017=@"loc,{#10000},1,22,1,27" +locations_default(#20017,#10000,1,22,1,27) +hasLocation(#20016,#20017) +#20018=* +tokeninfo(#20018,8,#20001,5,")") +#20019=@"loc,{#10000},1,28,1,28" +locations_default(#20019,#10000,1,28,1,28) +hasLocation(#20018,#20019) +#20020=* +tokeninfo(#20020,8,#20001,6,";") +#20021=@"loc,{#10000},1,29,1,29" +locations_default(#20021,#10000,1,29,1,29) +hasLocation(#20020,#20021) +#20022=* +tokeninfo(#20022,7,#20001,7,"export") +#20023=@"loc,{#10000},3,1,3,6" +locations_default(#20023,#10000,3,1,3,6) +hasLocation(#20022,#20023) +#20024=* +tokeninfo(#20024,7,#20001,8,"let") +#20025=@"loc,{#10000},3,8,3,10" +locations_default(#20025,#10000,3,8,3,10) +hasLocation(#20024,#20025) +#20026=* +tokeninfo(#20026,6,#20001,9,"x") +#20027=@"loc,{#10000},3,12,3,12" +locations_default(#20027,#10000,3,12,3,12) +hasLocation(#20026,#20027) +#20028=* +tokeninfo(#20028,8,#20001,10,"=") +#20029=@"loc,{#10000},3,14,3,14" +locations_default(#20029,#10000,3,14,3,14) +hasLocation(#20028,#20029) +#20030=* +tokeninfo(#20030,3,#20001,11,"5") +#20031=@"loc,{#10000},3,16,3,16" +locations_default(#20031,#10000,3,16,3,16) +hasLocation(#20030,#20031) +#20032=* +tokeninfo(#20032,8,#20001,12,";") +#20033=@"loc,{#10000},3,17,3,17" +locations_default(#20033,#10000,3,17,3,17) +hasLocation(#20032,#20033) +#20034=* +tokeninfo(#20034,0,#20001,13,"") +#20035=@"loc,{#10000},4,1,4,0" +locations_default(#20035,#10000,4,1,4,0) +hasLocation(#20034,#20035) +toplevels(#20001,0) +#20036=@"loc,{#10000},1,1,4,0" +locations_default(#20036,#10000,1,1,4,0) +hasLocation(#20001,#20036) +#20037=@"module;{#10000},1,1" +scopes(#20037,3) +scopenodes(#20001,#20037) +scopenesting(#20037,#20000) +isModule(#20001) +isES2015Module(#20001) +#20038=@"var;{x};{#20037}" +variables(#20038,"x",#20037) +#20039=* +stmts(#20039,2,#20001,0,"goog.de ... test');") +hasLocation(#20039,#20003) +stmtContainers(#20039,#20001) +#20040=* +exprs(#20040,13,#20039,0,"goog.de ... 'test')") +#20041=@"loc,{#10000},1,1,1,28" +locations_default(#20041,#10000,1,1,1,28) +hasLocation(#20040,#20041) +enclosingStmt(#20040,#20039) +exprContainers(#20040,#20001) +#20042=* +exprs(#20042,14,#20040,-1,"goog.declareModuleId") +#20043=@"loc,{#10000},1,1,1,20" +locations_default(#20043,#10000,1,1,1,20) +hasLocation(#20042,#20043) +enclosingStmt(#20042,#20039) +exprContainers(#20042,#20001) +#20044=* +exprs(#20044,79,#20042,0,"goog") +hasLocation(#20044,#20009) +enclosingStmt(#20044,#20039) +exprContainers(#20044,#20001) +literals("goog","goog",#20044) +#20045=@"var;{goog};{#20000}" +variables(#20045,"goog",#20000) +bind(#20044,#20045) +#20046=* +exprs(#20046,0,#20042,1,"declareModuleId") +hasLocation(#20046,#20013) +enclosingStmt(#20046,#20039) +exprContainers(#20046,#20001) +literals("declareModuleId","declareModuleId",#20046) +#20047=* +exprs(#20047,4,#20040,0,"'test'") +hasLocation(#20047,#20017) +enclosingStmt(#20047,#20039) +exprContainers(#20047,#20001) +literals("test","'test'",#20047) +#20048=* +stmts(#20048,30,#20001,1,"export let x = 5;") +hasLocation(#20048,#20007) +stmtContainers(#20048,#20001) +#20049=* +stmts(#20049,23,#20048,-1,"let x = 5;") +#20050=@"loc,{#10000},3,8,3,17" +locations_default(#20050,#10000,3,8,3,17) +hasLocation(#20049,#20050) +stmtContainers(#20049,#20001) +#20051=* +exprs(#20051,64,#20049,0,"x = 5") +#20052=@"loc,{#10000},3,12,3,16" +locations_default(#20052,#10000,3,12,3,16) +hasLocation(#20051,#20052) +enclosingStmt(#20051,#20049) +exprContainers(#20051,#20001) +#20053=* +exprs(#20053,78,#20051,0,"x") +hasLocation(#20053,#20027) +enclosingStmt(#20053,#20049) +exprContainers(#20053,#20001) +literals("x","x",#20053) +decl(#20053,#20038) +#20054=* +exprs(#20054,3,#20051,1,"5") +hasLocation(#20054,#20031) +enclosingStmt(#20054,#20049) +exprContainers(#20054,#20001) +literals("5","5",#20054) +#20055=* +entry_cfg_node(#20055,#20001) +#20056=@"loc,{#10000},1,1,1,0" +locations_default(#20056,#10000,1,1,1,0) +hasLocation(#20055,#20056) +#20057=* +exit_cfg_node(#20057,#20001) +hasLocation(#20057,#20035) +successor(#20048,#20049) +successor(#20049,#20053) +successor(#20054,#20051) +successor(#20053,#20054) +successor(#20051,#20057) +successor(#20039,#20044) +successor(#20047,#20040) +successor(#20046,#20042) +successor(#20044,#20046) +successor(#20042,#20047) +successor(#20040,#20048) +successor(#20055,#20039) +numlines(#10000,3,2,0) +filetype(#10000,"javascript") diff --git a/javascript/extractor/tests/closure/output/trap/googDotModule.js.trap b/javascript/extractor/tests/closure/output/trap/googDotModule.js.trap new file mode 100644 index 00000000000..7da62924256 --- /dev/null +++ b/javascript/extractor/tests/closure/output/trap/googDotModule.js.trap @@ -0,0 +1,283 @@ +#10000=@"/googDotModule.js;sourcefile" +files(#10000,"/googDotModule.js","googDotModule","js",0) +#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" +#20002=* +lines(#20002,#20001,"goog.module('test');"," +") +#20003=@"loc,{#10000},1,1,1,20" +locations_default(#20003,#10000,1,1,1,20) +hasLocation(#20002,#20003) +#20004=* +lines(#20004,#20001,"var x = 5;"," +") +#20005=@"loc,{#10000},2,1,2,10" +locations_default(#20005,#10000,2,1,2,10) +hasLocation(#20004,#20005) +#20006=* +lines(#20006,#20001,"exports = { x: x };"," +") +#20007=@"loc,{#10000},3,1,3,19" +locations_default(#20007,#10000,3,1,3,19) +hasLocation(#20006,#20007) +numlines(#20001,3,3,0) +#20008=* +tokeninfo(#20008,6,#20001,0,"goog") +#20009=@"loc,{#10000},1,1,1,4" +locations_default(#20009,#10000,1,1,1,4) +hasLocation(#20008,#20009) +#20010=* +tokeninfo(#20010,8,#20001,1,".") +#20011=@"loc,{#10000},1,5,1,5" +locations_default(#20011,#10000,1,5,1,5) +hasLocation(#20010,#20011) +#20012=* +tokeninfo(#20012,6,#20001,2,"module") +#20013=@"loc,{#10000},1,6,1,11" +locations_default(#20013,#10000,1,6,1,11) +hasLocation(#20012,#20013) +#20014=* +tokeninfo(#20014,8,#20001,3,"(") +#20015=@"loc,{#10000},1,12,1,12" +locations_default(#20015,#10000,1,12,1,12) +hasLocation(#20014,#20015) +#20016=* +tokeninfo(#20016,4,#20001,4,"'test'") +#20017=@"loc,{#10000},1,13,1,18" +locations_default(#20017,#10000,1,13,1,18) +hasLocation(#20016,#20017) +#20018=* +tokeninfo(#20018,8,#20001,5,")") +#20019=@"loc,{#10000},1,19,1,19" +locations_default(#20019,#10000,1,19,1,19) +hasLocation(#20018,#20019) +#20020=* +tokeninfo(#20020,8,#20001,6,";") +#20021=@"loc,{#10000},1,20,1,20" +locations_default(#20021,#10000,1,20,1,20) +hasLocation(#20020,#20021) +#20022=* +tokeninfo(#20022,7,#20001,7,"var") +#20023=@"loc,{#10000},2,1,2,3" +locations_default(#20023,#10000,2,1,2,3) +hasLocation(#20022,#20023) +#20024=* +tokeninfo(#20024,6,#20001,8,"x") +#20025=@"loc,{#10000},2,5,2,5" +locations_default(#20025,#10000,2,5,2,5) +hasLocation(#20024,#20025) +#20026=* +tokeninfo(#20026,8,#20001,9,"=") +#20027=@"loc,{#10000},2,7,2,7" +locations_default(#20027,#10000,2,7,2,7) +hasLocation(#20026,#20027) +#20028=* +tokeninfo(#20028,3,#20001,10,"5") +#20029=@"loc,{#10000},2,9,2,9" +locations_default(#20029,#10000,2,9,2,9) +hasLocation(#20028,#20029) +#20030=* +tokeninfo(#20030,8,#20001,11,";") +#20031=@"loc,{#10000},2,10,2,10" +locations_default(#20031,#10000,2,10,2,10) +hasLocation(#20030,#20031) +#20032=* +tokeninfo(#20032,6,#20001,12,"exports") +#20033=@"loc,{#10000},3,1,3,7" +locations_default(#20033,#10000,3,1,3,7) +hasLocation(#20032,#20033) +#20034=* +tokeninfo(#20034,8,#20001,13,"=") +#20035=@"loc,{#10000},3,9,3,9" +locations_default(#20035,#10000,3,9,3,9) +hasLocation(#20034,#20035) +#20036=* +tokeninfo(#20036,8,#20001,14,"{") +#20037=@"loc,{#10000},3,11,3,11" +locations_default(#20037,#10000,3,11,3,11) +hasLocation(#20036,#20037) +#20038=* +tokeninfo(#20038,6,#20001,15,"x") +#20039=@"loc,{#10000},3,13,3,13" +locations_default(#20039,#10000,3,13,3,13) +hasLocation(#20038,#20039) +#20040=* +tokeninfo(#20040,8,#20001,16,":") +#20041=@"loc,{#10000},3,14,3,14" +locations_default(#20041,#10000,3,14,3,14) +hasLocation(#20040,#20041) +#20042=* +tokeninfo(#20042,6,#20001,17,"x") +#20043=@"loc,{#10000},3,16,3,16" +locations_default(#20043,#10000,3,16,3,16) +hasLocation(#20042,#20043) +#20044=* +tokeninfo(#20044,8,#20001,18,"}") +#20045=@"loc,{#10000},3,18,3,18" +locations_default(#20045,#10000,3,18,3,18) +hasLocation(#20044,#20045) +#20046=* +tokeninfo(#20046,8,#20001,19,";") +#20047=@"loc,{#10000},3,19,3,19" +locations_default(#20047,#10000,3,19,3,19) +hasLocation(#20046,#20047) +#20048=* +tokeninfo(#20048,0,#20001,20,"") +#20049=@"loc,{#10000},4,1,4,0" +locations_default(#20049,#10000,4,1,4,0) +hasLocation(#20048,#20049) +toplevels(#20001,0) +#20050=@"loc,{#10000},1,1,4,0" +locations_default(#20050,#10000,1,1,4,0) +hasLocation(#20001,#20050) +#20051=@"module;{#10000},1,1" +scopes(#20051,3) +scopenodes(#20001,#20051) +scopenesting(#20051,#20000) +#20052=@"var;{exports};{#20051}" +variables(#20052,"exports",#20051) +isModule(#20001) +isClosureModule(#20001) +#20053=@"var;{x};{#20051}" +variables(#20053,"x",#20051) +#20054=* +stmts(#20054,2,#20001,0,"goog.module('test');") +hasLocation(#20054,#20003) +stmtContainers(#20054,#20001) +#20055=* +exprs(#20055,13,#20054,0,"goog.module('test')") +#20056=@"loc,{#10000},1,1,1,19" +locations_default(#20056,#10000,1,1,1,19) +hasLocation(#20055,#20056) +enclosingStmt(#20055,#20054) +exprContainers(#20055,#20001) +#20057=* +exprs(#20057,14,#20055,-1,"goog.module") +#20058=@"loc,{#10000},1,1,1,11" +locations_default(#20058,#10000,1,1,1,11) +hasLocation(#20057,#20058) +enclosingStmt(#20057,#20054) +exprContainers(#20057,#20001) +#20059=* +exprs(#20059,79,#20057,0,"goog") +hasLocation(#20059,#20009) +enclosingStmt(#20059,#20054) +exprContainers(#20059,#20001) +literals("goog","goog",#20059) +#20060=@"var;{goog};{#20000}" +variables(#20060,"goog",#20000) +bind(#20059,#20060) +#20061=* +exprs(#20061,0,#20057,1,"module") +hasLocation(#20061,#20013) +enclosingStmt(#20061,#20054) +exprContainers(#20061,#20001) +literals("module","module",#20061) +#20062=* +exprs(#20062,4,#20055,0,"'test'") +hasLocation(#20062,#20017) +enclosingStmt(#20062,#20054) +exprContainers(#20062,#20001) +literals("test","'test'",#20062) +#20063=* +stmts(#20063,18,#20001,1,"var x = 5;") +hasLocation(#20063,#20005) +stmtContainers(#20063,#20001) +#20064=* +exprs(#20064,64,#20063,0,"x = 5") +#20065=@"loc,{#10000},2,5,2,9" +locations_default(#20065,#10000,2,5,2,9) +hasLocation(#20064,#20065) +enclosingStmt(#20064,#20063) +exprContainers(#20064,#20001) +#20066=* +exprs(#20066,78,#20064,0,"x") +hasLocation(#20066,#20025) +enclosingStmt(#20066,#20063) +exprContainers(#20066,#20001) +literals("x","x",#20066) +decl(#20066,#20053) +#20067=* +exprs(#20067,3,#20064,1,"5") +hasLocation(#20067,#20029) +enclosingStmt(#20067,#20063) +exprContainers(#20067,#20001) +literals("5","5",#20067) +#20068=* +stmts(#20068,2,#20001,2,"exports = { x: x };") +hasLocation(#20068,#20007) +stmtContainers(#20068,#20001) +#20069=* +exprs(#20069,47,#20068,0,"exports = { x: x }") +#20070=@"loc,{#10000},3,1,3,18" +locations_default(#20070,#10000,3,1,3,18) +hasLocation(#20069,#20070) +enclosingStmt(#20069,#20068) +exprContainers(#20069,#20001) +#20071=* +exprs(#20071,79,#20069,0,"exports") +hasLocation(#20071,#20033) +enclosingStmt(#20071,#20068) +exprContainers(#20071,#20001) +literals("exports","exports",#20071) +bind(#20071,#20052) +#20072=* +exprs(#20072,8,#20069,1,"{ x: x }") +#20073=@"loc,{#10000},3,11,3,18" +locations_default(#20073,#10000,3,11,3,18) +hasLocation(#20072,#20073) +enclosingStmt(#20072,#20068) +exprContainers(#20072,#20001) +#20074=* +properties(#20074,#20072,0,0,"x: x") +#20075=@"loc,{#10000},3,13,3,16" +locations_default(#20075,#10000,3,13,3,16) +hasLocation(#20074,#20075) +#20076=* +exprs(#20076,0,#20074,0,"x") +hasLocation(#20076,#20039) +enclosingStmt(#20076,#20068) +exprContainers(#20076,#20001) +literals("x","x",#20076) +#20077=* +exprs(#20077,79,#20074,1,"x") +hasLocation(#20077,#20043) +enclosingStmt(#20077,#20068) +exprContainers(#20077,#20001) +literals("x","x",#20077) +bind(#20077,#20053) +#20078=* +entry_cfg_node(#20078,#20001) +#20079=@"loc,{#10000},1,1,1,0" +locations_default(#20079,#10000,1,1,1,0) +hasLocation(#20078,#20079) +#20080=* +exit_cfg_node(#20080,#20001) +hasLocation(#20080,#20049) +successor(#20068,#20071) +successor(#20072,#20076) +successor(#20077,#20074) +successor(#20076,#20077) +successor(#20074,#20069) +successor(#20071,#20072) +successor(#20069,#20080) +successor(#20063,#20066) +successor(#20067,#20064) +successor(#20066,#20067) +successor(#20064,#20068) +successor(#20054,#20059) +successor(#20062,#20055) +successor(#20061,#20057) +successor(#20059,#20061) +successor(#20057,#20062) +successor(#20055,#20063) +successor(#20078,#20054) +numlines(#10000,3,3,0) +filetype(#10000,"javascript") diff --git a/javascript/extractor/tests/closure/output/trap/googDotProvide.js.trap b/javascript/extractor/tests/closure/output/trap/googDotProvide.js.trap new file mode 100644 index 00000000000..be568d46f5c --- /dev/null +++ b/javascript/extractor/tests/closure/output/trap/googDotProvide.js.trap @@ -0,0 +1,205 @@ +#10000=@"/googDotProvide.js;sourcefile" +files(#10000,"/googDotProvide.js","googDotProvide","js",0) +#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" +#20002=* +lines(#20002,#20001,"goog.provide('test.x');"," +") +#20003=@"loc,{#10000},1,1,1,23" +locations_default(#20003,#10000,1,1,1,23) +hasLocation(#20002,#20003) +#20004=* +lines(#20004,#20001,""," +") +#20005=@"loc,{#10000},2,1,2,0" +locations_default(#20005,#10000,2,1,2,0) +hasLocation(#20004,#20005) +#20006=* +lines(#20006,#20001,"test.x = 5;"," +") +#20007=@"loc,{#10000},3,1,3,11" +locations_default(#20007,#10000,3,1,3,11) +hasLocation(#20006,#20007) +numlines(#20001,3,2,0) +#20008=* +tokeninfo(#20008,6,#20001,0,"goog") +#20009=@"loc,{#10000},1,1,1,4" +locations_default(#20009,#10000,1,1,1,4) +hasLocation(#20008,#20009) +#20010=* +tokeninfo(#20010,8,#20001,1,".") +#20011=@"loc,{#10000},1,5,1,5" +locations_default(#20011,#10000,1,5,1,5) +hasLocation(#20010,#20011) +#20012=* +tokeninfo(#20012,6,#20001,2,"provide") +#20013=@"loc,{#10000},1,6,1,12" +locations_default(#20013,#10000,1,6,1,12) +hasLocation(#20012,#20013) +#20014=* +tokeninfo(#20014,8,#20001,3,"(") +#20015=@"loc,{#10000},1,13,1,13" +locations_default(#20015,#10000,1,13,1,13) +hasLocation(#20014,#20015) +#20016=* +tokeninfo(#20016,4,#20001,4,"'test.x'") +#20017=@"loc,{#10000},1,14,1,21" +locations_default(#20017,#10000,1,14,1,21) +hasLocation(#20016,#20017) +#20018=* +tokeninfo(#20018,8,#20001,5,")") +#20019=@"loc,{#10000},1,22,1,22" +locations_default(#20019,#10000,1,22,1,22) +hasLocation(#20018,#20019) +#20020=* +tokeninfo(#20020,8,#20001,6,";") +#20021=@"loc,{#10000},1,23,1,23" +locations_default(#20021,#10000,1,23,1,23) +hasLocation(#20020,#20021) +#20022=* +tokeninfo(#20022,6,#20001,7,"test") +#20023=@"loc,{#10000},3,1,3,4" +locations_default(#20023,#10000,3,1,3,4) +hasLocation(#20022,#20023) +#20024=* +tokeninfo(#20024,8,#20001,8,".") +#20025=@"loc,{#10000},3,5,3,5" +locations_default(#20025,#10000,3,5,3,5) +hasLocation(#20024,#20025) +#20026=* +tokeninfo(#20026,6,#20001,9,"x") +#20027=@"loc,{#10000},3,6,3,6" +locations_default(#20027,#10000,3,6,3,6) +hasLocation(#20026,#20027) +#20028=* +tokeninfo(#20028,8,#20001,10,"=") +#20029=@"loc,{#10000},3,8,3,8" +locations_default(#20029,#10000,3,8,3,8) +hasLocation(#20028,#20029) +#20030=* +tokeninfo(#20030,3,#20001,11,"5") +#20031=@"loc,{#10000},3,10,3,10" +locations_default(#20031,#10000,3,10,3,10) +hasLocation(#20030,#20031) +#20032=* +tokeninfo(#20032,8,#20001,12,";") +#20033=@"loc,{#10000},3,11,3,11" +locations_default(#20033,#10000,3,11,3,11) +hasLocation(#20032,#20033) +#20034=* +tokeninfo(#20034,0,#20001,13,"") +#20035=@"loc,{#10000},4,1,4,0" +locations_default(#20035,#10000,4,1,4,0) +hasLocation(#20034,#20035) +toplevels(#20001,0) +#20036=@"loc,{#10000},1,1,4,0" +locations_default(#20036,#10000,1,1,4,0) +hasLocation(#20001,#20036) +#20037=* +stmts(#20037,2,#20001,0,"goog.pr ... st.x');") +hasLocation(#20037,#20003) +stmtContainers(#20037,#20001) +#20038=* +exprs(#20038,13,#20037,0,"goog.pr ... est.x')") +#20039=@"loc,{#10000},1,1,1,22" +locations_default(#20039,#10000,1,1,1,22) +hasLocation(#20038,#20039) +enclosingStmt(#20038,#20037) +exprContainers(#20038,#20001) +#20040=* +exprs(#20040,14,#20038,-1,"goog.provide") +#20041=@"loc,{#10000},1,1,1,12" +locations_default(#20041,#10000,1,1,1,12) +hasLocation(#20040,#20041) +enclosingStmt(#20040,#20037) +exprContainers(#20040,#20001) +#20042=* +exprs(#20042,79,#20040,0,"goog") +hasLocation(#20042,#20009) +enclosingStmt(#20042,#20037) +exprContainers(#20042,#20001) +literals("goog","goog",#20042) +#20043=@"var;{goog};{#20000}" +variables(#20043,"goog",#20000) +bind(#20042,#20043) +#20044=* +exprs(#20044,0,#20040,1,"provide") +hasLocation(#20044,#20013) +enclosingStmt(#20044,#20037) +exprContainers(#20044,#20001) +literals("provide","provide",#20044) +#20045=* +exprs(#20045,4,#20038,0,"'test.x'") +hasLocation(#20045,#20017) +enclosingStmt(#20045,#20037) +exprContainers(#20045,#20001) +literals("test.x","'test.x'",#20045) +#20046=* +stmts(#20046,2,#20001,1,"test.x = 5;") +hasLocation(#20046,#20007) +stmtContainers(#20046,#20001) +#20047=* +exprs(#20047,47,#20046,0,"test.x = 5") +#20048=@"loc,{#10000},3,1,3,10" +locations_default(#20048,#10000,3,1,3,10) +hasLocation(#20047,#20048) +enclosingStmt(#20047,#20046) +exprContainers(#20047,#20001) +#20049=* +exprs(#20049,14,#20047,0,"test.x") +#20050=@"loc,{#10000},3,1,3,6" +locations_default(#20050,#10000,3,1,3,6) +hasLocation(#20049,#20050) +enclosingStmt(#20049,#20046) +exprContainers(#20049,#20001) +#20051=* +exprs(#20051,79,#20049,0,"test") +hasLocation(#20051,#20023) +enclosingStmt(#20051,#20046) +exprContainers(#20051,#20001) +literals("test","test",#20051) +#20052=@"var;{test};{#20000}" +variables(#20052,"test",#20000) +bind(#20051,#20052) +#20053=* +exprs(#20053,0,#20049,1,"x") +hasLocation(#20053,#20027) +enclosingStmt(#20053,#20046) +exprContainers(#20053,#20001) +literals("x","x",#20053) +#20054=* +exprs(#20054,3,#20047,1,"5") +hasLocation(#20054,#20031) +enclosingStmt(#20054,#20046) +exprContainers(#20054,#20001) +literals("5","5",#20054) +#20055=* +entry_cfg_node(#20055,#20001) +#20056=@"loc,{#10000},1,1,1,0" +locations_default(#20056,#10000,1,1,1,0) +hasLocation(#20055,#20056) +#20057=* +exit_cfg_node(#20057,#20001) +hasLocation(#20057,#20035) +successor(#20046,#20051) +successor(#20054,#20047) +successor(#20053,#20049) +successor(#20051,#20053) +successor(#20049,#20054) +successor(#20047,#20057) +successor(#20037,#20042) +successor(#20045,#20038) +successor(#20044,#20040) +successor(#20042,#20044) +successor(#20040,#20045) +successor(#20038,#20046) +successor(#20055,#20037) +numlines(#10000,3,2,0) +filetype(#10000,"javascript") diff --git a/javascript/extractor/tests/es2015/output/trap/export1.js.trap b/javascript/extractor/tests/es2015/output/trap/export1.js.trap index b91210e75b3..d941f194304 100644 --- a/javascript/extractor/tests/es2015/output/trap/export1.js.trap +++ b/javascript/extractor/tests/es2015/output/trap/export1.js.trap @@ -57,6 +57,7 @@ scopes(#20018,3) scopenodes(#20001,#20018) scopenesting(#20018,#20000) isModule(#20001) +isES2015Module(#20001) #20019=@"var;{x};{#20018}" variables(#20019,"x",#20018) #20020=* diff --git a/javascript/extractor/tests/es2015/output/trap/export10.js.trap b/javascript/extractor/tests/es2015/output/trap/export10.js.trap index b3a45738d78..11992095dfe 100644 --- a/javascript/extractor/tests/es2015/output/trap/export10.js.trap +++ b/javascript/extractor/tests/es2015/output/trap/export10.js.trap @@ -85,6 +85,7 @@ scopes(#20029,3) scopenodes(#20001,#20029) scopenesting(#20029,#20000) isModule(#20001) +isES2015Module(#20001) #20030=@"var;{f};{#20029}" variables(#20030,"f",#20029) #20031=* diff --git a/javascript/extractor/tests/es2015/output/trap/export11.js.trap b/javascript/extractor/tests/es2015/output/trap/export11.js.trap index b5864d6de79..25f9caf1b81 100644 --- a/javascript/extractor/tests/es2015/output/trap/export11.js.trap +++ b/javascript/extractor/tests/es2015/output/trap/export11.js.trap @@ -55,6 +55,7 @@ scopes(#20017,3) scopenodes(#20001,#20017) scopenesting(#20017,#20000) isModule(#20001) +isES2015Module(#20001) #20018=* stmts(#20018,29,#20001,0,"export ... lass {}") hasLocation(#20018,#20003) diff --git a/javascript/extractor/tests/es2015/output/trap/export2.js.trap b/javascript/extractor/tests/es2015/output/trap/export2.js.trap index 8ae4aee565e..27ec1b0d1d1 100644 --- a/javascript/extractor/tests/es2015/output/trap/export2.js.trap +++ b/javascript/extractor/tests/es2015/output/trap/export2.js.trap @@ -67,6 +67,7 @@ scopes(#20022,3) scopenodes(#20001,#20022) scopenesting(#20022,#20000) isModule(#20001) +isES2015Module(#20001) #20023=@"var;{f};{#20022}" variables(#20023,"f",#20022) #20024=* diff --git a/javascript/extractor/tests/es2015/output/trap/export3.js.trap b/javascript/extractor/tests/es2015/output/trap/export3.js.trap index 9649c8047c0..bcb6b619e12 100644 --- a/javascript/extractor/tests/es2015/output/trap/export3.js.trap +++ b/javascript/extractor/tests/es2015/output/trap/export3.js.trap @@ -72,6 +72,7 @@ scopes(#20024,3) scopenodes(#20001,#20024) scopenesting(#20024,#20000) isModule(#20001) +isES2015Module(#20001) #20025=@"var;{f};{#20024}" variables(#20025,"f",#20024) #20026=* diff --git a/javascript/extractor/tests/es2015/output/trap/export4.js.trap b/javascript/extractor/tests/es2015/output/trap/export4.js.trap index 789561f2787..541992fd903 100644 --- a/javascript/extractor/tests/es2015/output/trap/export4.js.trap +++ b/javascript/extractor/tests/es2015/output/trap/export4.js.trap @@ -67,6 +67,7 @@ scopes(#20022,3) scopenodes(#20001,#20022) scopenesting(#20022,#20000) isModule(#20001) +isES2015Module(#20001) #20023=* stmts(#20023,29,#20001,0,"export ... n () {}") #20024=@"loc,{#10000},1,1,1,29" diff --git a/javascript/extractor/tests/es2015/output/trap/export5.js.trap b/javascript/extractor/tests/es2015/output/trap/export5.js.trap index cea3d37029e..11aee4bdd59 100644 --- a/javascript/extractor/tests/es2015/output/trap/export5.js.trap +++ b/javascript/extractor/tests/es2015/output/trap/export5.js.trap @@ -125,6 +125,7 @@ scopes(#20045,3) scopenodes(#20001,#20045) scopenesting(#20045,#20000) isModule(#20001) +isES2015Module(#20001) #20046=@"var;{x};{#20045}" variables(#20046,"x",#20045) #20047=@"var;{y};{#20045}" diff --git a/javascript/extractor/tests/es2015/output/trap/export6.js.trap b/javascript/extractor/tests/es2015/output/trap/export6.js.trap index 9667678d047..be811781ee0 100644 --- a/javascript/extractor/tests/es2015/output/trap/export6.js.trap +++ b/javascript/extractor/tests/es2015/output/trap/export6.js.trap @@ -52,6 +52,7 @@ scopes(#20016,3) scopenodes(#20001,#20016) scopenesting(#20016,#20000) isModule(#20001) +isES2015Module(#20001) #20017=* stmts(#20017,28,#20001,0,"export * from 'foo';") hasLocation(#20017,#20003) diff --git a/javascript/extractor/tests/es2015/output/trap/export7.js.trap b/javascript/extractor/tests/es2015/output/trap/export7.js.trap index a2e6ece468e..3856ac50b40 100644 --- a/javascript/extractor/tests/es2015/output/trap/export7.js.trap +++ b/javascript/extractor/tests/es2015/output/trap/export7.js.trap @@ -82,6 +82,7 @@ scopes(#20028,3) scopenodes(#20001,#20028) scopenesting(#20028,#20000) isModule(#20001) +isES2015Module(#20001) #20029=* stmts(#20029,30,#20001,0,"export ... 'foo';") hasLocation(#20029,#20003) diff --git a/javascript/extractor/tests/es2015/output/trap/export8.js.trap b/javascript/extractor/tests/es2015/output/trap/export8.js.trap index a2b073979af..56b6518f11e 100644 --- a/javascript/extractor/tests/es2015/output/trap/export8.js.trap +++ b/javascript/extractor/tests/es2015/output/trap/export8.js.trap @@ -75,6 +75,7 @@ scopes(#20025,3) scopenodes(#20001,#20025) scopenesting(#20025,#20000) isModule(#20001) +isES2015Module(#20001) #20026=* stmts(#20026,2,#20001,0,"foo = 42;") hasLocation(#20026,#20003) diff --git a/javascript/extractor/tests/es2015/output/trap/export9.js.trap b/javascript/extractor/tests/es2015/output/trap/export9.js.trap index 5ce81f3881a..65237923a52 100644 --- a/javascript/extractor/tests/es2015/output/trap/export9.js.trap +++ b/javascript/extractor/tests/es2015/output/trap/export9.js.trap @@ -75,6 +75,7 @@ scopes(#20025,3) scopenodes(#20001,#20025) scopenesting(#20025,#20000) isModule(#20001) +isES2015Module(#20001) #20026=@"var;{C};{#20025}" variables(#20026,"C",#20025) #20027=@"local_type_name;{C};{#20025}" diff --git a/javascript/extractor/tests/es2015/output/trap/import1.js.trap b/javascript/extractor/tests/es2015/output/trap/import1.js.trap index 698f6dc87c4..7e28e03e25f 100644 --- a/javascript/extractor/tests/es2015/output/trap/import1.js.trap +++ b/javascript/extractor/tests/es2015/output/trap/import1.js.trap @@ -52,6 +52,7 @@ scopes(#20016,3) scopenodes(#20001,#20016) scopenesting(#20016,#20000) isModule(#20001) +isES2015Module(#20001) #20017=@"var;{x};{#20016}" variables(#20017,"x",#20016) #20018=@"local_type_name;{x};{#20016}" diff --git a/javascript/extractor/tests/es2015/output/trap/import2.js.trap b/javascript/extractor/tests/es2015/output/trap/import2.js.trap index a59a9455879..b6385936355 100644 --- a/javascript/extractor/tests/es2015/output/trap/import2.js.trap +++ b/javascript/extractor/tests/es2015/output/trap/import2.js.trap @@ -62,6 +62,7 @@ scopes(#20020,3) scopenodes(#20001,#20020) scopenesting(#20020,#20000) isModule(#20001) +isES2015Module(#20001) #20021=@"var;{y};{#20020}" variables(#20021,"y",#20020) #20022=@"local_type_name;{y};{#20020}" diff --git a/javascript/extractor/tests/es2015/output/trap/import3.js.trap b/javascript/extractor/tests/es2015/output/trap/import3.js.trap index 43656eb918f..135adff01a2 100644 --- a/javascript/extractor/tests/es2015/output/trap/import3.js.trap +++ b/javascript/extractor/tests/es2015/output/trap/import3.js.trap @@ -72,6 +72,7 @@ scopes(#20024,3) scopenodes(#20001,#20024) scopenesting(#20024,#20000) isModule(#20001) +isES2015Module(#20001) #20025=@"var;{z};{#20024}" variables(#20025,"z",#20024) #20026=@"local_type_name;{z};{#20024}" diff --git a/javascript/extractor/tests/es2015/output/trap/import4.js.trap b/javascript/extractor/tests/es2015/output/trap/import4.js.trap index bb61d9c5706..2243a8fcd20 100644 --- a/javascript/extractor/tests/es2015/output/trap/import4.js.trap +++ b/javascript/extractor/tests/es2015/output/trap/import4.js.trap @@ -82,6 +82,7 @@ scopes(#20028,3) scopenodes(#20001,#20028) scopenesting(#20028,#20000) isModule(#20001) +isES2015Module(#20001) #20029=@"var;{x};{#20028}" variables(#20029,"x",#20028) #20030=@"var;{z};{#20028}" diff --git a/javascript/extractor/tests/es2015/output/trap/import5.js.trap b/javascript/extractor/tests/es2015/output/trap/import5.js.trap index 94c4cd1bfc2..ea5ea8a3836 100644 --- a/javascript/extractor/tests/es2015/output/trap/import5.js.trap +++ b/javascript/extractor/tests/es2015/output/trap/import5.js.trap @@ -62,6 +62,7 @@ scopes(#20020,3) scopenodes(#20001,#20020) scopenesting(#20020,#20000) isModule(#20001) +isES2015Module(#20001) #20021=@"var;{foo};{#20020}" variables(#20021,"foo",#20020) #20022=@"local_namespace_name;{foo};{#20020}" diff --git a/javascript/extractor/tests/es2015/output/trap/import6.js.trap b/javascript/extractor/tests/es2015/output/trap/import6.js.trap index 7e5540aaf9a..252b32338cf 100644 --- a/javascript/extractor/tests/es2015/output/trap/import6.js.trap +++ b/javascript/extractor/tests/es2015/output/trap/import6.js.trap @@ -42,6 +42,7 @@ scopes(#20012,3) scopenodes(#20001,#20012) scopenesting(#20012,#20000) isModule(#20001) +isES2015Module(#20001) #20013=* stmts(#20013,27,#20001,0,"import 'foo';") hasLocation(#20013,#20003) diff --git a/javascript/extractor/tests/es2015/output/trap/import7.js.trap b/javascript/extractor/tests/es2015/output/trap/import7.js.trap index 5ad6904fee2..974a3cc10b7 100644 --- a/javascript/extractor/tests/es2015/output/trap/import7.js.trap +++ b/javascript/extractor/tests/es2015/output/trap/import7.js.trap @@ -126,6 +126,7 @@ scopes(#20045,3) scopenodes(#20001,#20045) scopenesting(#20045,#20000) isModule(#20001) +isES2015Module(#20001) #20046=@"var;{x};{#20045}" variables(#20046,"x",#20045) #20047=@"var;{z};{#20045}" diff --git a/javascript/extractor/tests/es2015/output/trap/nested_import.js.trap b/javascript/extractor/tests/es2015/output/trap/nested_import.js.trap index cbad5c031dc..074cc3dce4d 100644 --- a/javascript/extractor/tests/es2015/output/trap/nested_import.js.trap +++ b/javascript/extractor/tests/es2015/output/trap/nested_import.js.trap @@ -179,6 +179,7 @@ scopes(#20064,3) scopenodes(#20001,#20064) scopenesting(#20064,#20000) isModule(#20001) +isES2015Module(#20001) #20065=@"var;{x};{#20064}" variables(#20065,"x",#20064) #20066=@"var;{y};{#20064}" diff --git a/javascript/extractor/tests/es2017/output/trap/async-await.js.trap b/javascript/extractor/tests/es2017/output/trap/async-await.js.trap index 067c2b17191..ec4802a50a2 100644 --- a/javascript/extractor/tests/es2017/output/trap/async-await.js.trap +++ b/javascript/extractor/tests/es2017/output/trap/async-await.js.trap @@ -196,6 +196,7 @@ scopes(#20069,3) scopenodes(#20001,#20069) scopenesting(#20069,#20000) isModule(#20001) +isES2015Module(#20001) #20070=@"var;{foo};{#20069}" variables(#20070,"foo",#20069) #20071=* diff --git a/javascript/extractor/tests/es2017/output/trap/export-async-1.js.trap b/javascript/extractor/tests/es2017/output/trap/export-async-1.js.trap index c5598843cdd..89d065a8b85 100644 --- a/javascript/extractor/tests/es2017/output/trap/export-async-1.js.trap +++ b/javascript/extractor/tests/es2017/output/trap/export-async-1.js.trap @@ -80,6 +80,7 @@ scopes(#20027,3) scopenodes(#20001,#20027) scopenesting(#20027,#20000) isModule(#20001) +isES2015Module(#20001) #20028=@"var;{f};{#20027}" variables(#20028,"f",#20027) #20029=* diff --git a/javascript/extractor/tests/es2017/output/trap/export-async-2.js.trap b/javascript/extractor/tests/es2017/output/trap/export-async-2.js.trap index d83028ac5cc..651e897f0ae 100644 --- a/javascript/extractor/tests/es2017/output/trap/export-async-2.js.trap +++ b/javascript/extractor/tests/es2017/output/trap/export-async-2.js.trap @@ -75,6 +75,7 @@ scopes(#20025,3) scopenodes(#20001,#20025) scopenesting(#20025,#20000) isModule(#20001) +isES2015Module(#20001) #20026=* stmts(#20026,29,#20001,0,"export ... n () {}") #20027=@"loc,{#10000},1,1,1,35" diff --git a/javascript/extractor/tests/esnext/output/trap/dynamic-import.js.trap b/javascript/extractor/tests/esnext/output/trap/dynamic-import.js.trap index 888665939e0..5539e654b68 100644 --- a/javascript/extractor/tests/esnext/output/trap/dynamic-import.js.trap +++ b/javascript/extractor/tests/esnext/output/trap/dynamic-import.js.trap @@ -192,6 +192,7 @@ scopes(#20071,3) scopenodes(#20001,#20071) scopenesting(#20071,#20000) isModule(#20001) +isES2015Module(#20001) #20072=* stmts(#20072,2,#20001,0,"import(""m"");") hasLocation(#20072,#20003) diff --git a/javascript/extractor/tests/extensions/output/trap/tst.es6.trap b/javascript/extractor/tests/extensions/output/trap/tst.es6.trap index 1c61912b6f2..26a437438c1 100644 --- a/javascript/extractor/tests/extensions/output/trap/tst.es6.trap +++ b/javascript/extractor/tests/extensions/output/trap/tst.es6.trap @@ -65,6 +65,7 @@ scopes(#20021,3) scopenodes(#20001,#20021) scopenesting(#20021,#20000) isModule(#20001) +isES2015Module(#20001) #20022=* stmts(#20022,2,#20001,0,"console ... ES6"");") hasLocation(#20022,#20003) diff --git a/javascript/extractor/tests/extensions/output/trap/tst2.es.trap b/javascript/extractor/tests/extensions/output/trap/tst2.es.trap index 6910645b5a8..30172094f29 100644 --- a/javascript/extractor/tests/extensions/output/trap/tst2.es.trap +++ b/javascript/extractor/tests/extensions/output/trap/tst2.es.trap @@ -65,6 +65,7 @@ scopes(#20021,3) scopenodes(#20001,#20021) scopenesting(#20021,#20000) isModule(#20001) +isES2015Module(#20001) #20022=* stmts(#20022,2,#20001,0,"console ... o ES"");") hasLocation(#20022,#20003) diff --git a/javascript/extractor/tests/flow/output/trap/anonFunctionWithoutParens.js.trap b/javascript/extractor/tests/flow/output/trap/anonFunctionWithoutParens.js.trap index a1f46308211..3385f035c92 100644 --- a/javascript/extractor/tests/flow/output/trap/anonFunctionWithoutParens.js.trap +++ b/javascript/extractor/tests/flow/output/trap/anonFunctionWithoutParens.js.trap @@ -77,6 +77,7 @@ scopes(#20026,3) scopenodes(#20001,#20026) scopenesting(#20026,#20000) isModule(#20001) +isES2015Module(#20001) #20027=* entry_cfg_node(#20027,#20001) #20028=@"loc,{#10000},1,1,1,0" diff --git a/javascript/extractor/tests/flow/output/trap/anonIndexer.js.trap b/javascript/extractor/tests/flow/output/trap/anonIndexer.js.trap index 11b67847df3..4b144a97faa 100644 --- a/javascript/extractor/tests/flow/output/trap/anonIndexer.js.trap +++ b/javascript/extractor/tests/flow/output/trap/anonIndexer.js.trap @@ -90,6 +90,7 @@ scopes(#20031,3) scopenodes(#20001,#20031) scopenesting(#20031,#20000) isModule(#20001) +isES2015Module(#20001) #20032=* entry_cfg_node(#20032,#20001) #20033=@"loc,{#10000},1,1,1,0" diff --git a/javascript/extractor/tests/flow/output/trap/declared-module-imports.js.trap b/javascript/extractor/tests/flow/output/trap/declared-module-imports.js.trap index d8b9dbbcd55..18c6aad67d3 100644 --- a/javascript/extractor/tests/flow/output/trap/declared-module-imports.js.trap +++ b/javascript/extractor/tests/flow/output/trap/declared-module-imports.js.trap @@ -133,6 +133,7 @@ scopes(#20046,3) scopenodes(#20001,#20046) scopenesting(#20046,#20000) isModule(#20001) +isES2015Module(#20001) #20047=* entry_cfg_node(#20047,#20001) #20048=@"loc,{#10000},1,1,1,0" diff --git a/javascript/extractor/tests/flow/output/trap/export.js.trap b/javascript/extractor/tests/flow/output/trap/export.js.trap index eafcd7d1e33..6e68301a275 100644 --- a/javascript/extractor/tests/flow/output/trap/export.js.trap +++ b/javascript/extractor/tests/flow/output/trap/export.js.trap @@ -223,6 +223,7 @@ scopes(#20083,3) scopenodes(#20001,#20083) scopenesting(#20083,#20000) isModule(#20001) +isES2015Module(#20001) #20084=* stmts(#20084,30,#20001,0,"export ... om ""m"";") hasLocation(#20084,#20003) diff --git a/javascript/extractor/tests/flow/output/trap/exportOpaqueType.js.trap b/javascript/extractor/tests/flow/output/trap/exportOpaqueType.js.trap index 9eb9d7a7a64..2b23e617e92 100644 --- a/javascript/extractor/tests/flow/output/trap/exportOpaqueType.js.trap +++ b/javascript/extractor/tests/flow/output/trap/exportOpaqueType.js.trap @@ -75,6 +75,7 @@ scopes(#20025,3) scopenodes(#20001,#20025) scopenesting(#20025,#20000) isModule(#20001) +isES2015Module(#20001) #20026=* entry_cfg_node(#20026,#20001) #20027=@"loc,{#10000},1,1,1,0" diff --git a/javascript/extractor/tests/flow/output/trap/importType.js.trap b/javascript/extractor/tests/flow/output/trap/importType.js.trap index bdae34bb5c5..51f24931866 100644 --- a/javascript/extractor/tests/flow/output/trap/importType.js.trap +++ b/javascript/extractor/tests/flow/output/trap/importType.js.trap @@ -186,6 +186,7 @@ scopes(#20069,3) scopenodes(#20001,#20069) scopenesting(#20069,#20000) isModule(#20001) +isES2015Module(#20001) #20070=@"var;{type};{#20069}" variables(#20070,"type",#20069) #20071=@"local_type_name;{type};{#20069}" diff --git a/javascript/extractor/tests/flow/output/trap/importTypeInDeclaredModule.js.trap b/javascript/extractor/tests/flow/output/trap/importTypeInDeclaredModule.js.trap index b5b081fa745..a4feff6195c 100644 --- a/javascript/extractor/tests/flow/output/trap/importTypeInDeclaredModule.js.trap +++ b/javascript/extractor/tests/flow/output/trap/importTypeInDeclaredModule.js.trap @@ -210,6 +210,7 @@ scopes(#20072,3) scopenodes(#20001,#20072) scopenesting(#20072,#20000) isModule(#20001) +isES2015Module(#20001) #20073=* entry_cfg_node(#20073,#20001) #20074=@"loc,{#10000},1,1,1,0" diff --git a/javascript/extractor/tests/flow/output/trap/variance.js.trap b/javascript/extractor/tests/flow/output/trap/variance.js.trap index 1524741d813..e032b8259d1 100644 --- a/javascript/extractor/tests/flow/output/trap/variance.js.trap +++ b/javascript/extractor/tests/flow/output/trap/variance.js.trap @@ -165,6 +165,7 @@ scopes(#20058,3) scopenodes(#20001,#20058) scopenesting(#20058,#20000) isModule(#20001) +isES2015Module(#20001) #20059=@"var;{Foo};{#20058}" variables(#20059,"Foo",#20058) #20060=@"local_type_name;{Foo};{#20058}" diff --git a/javascript/extractor/tests/html/output/trap/module.html.trap b/javascript/extractor/tests/html/output/trap/module.html.trap index 94d2e930ba2..a198c52ef7a 100644 --- a/javascript/extractor/tests/html/output/trap/module.html.trap +++ b/javascript/extractor/tests/html/output/trap/module.html.trap @@ -104,6 +104,7 @@ scopes(#20035,3) scopenodes(#20001,#20035) scopenesting(#20035,#20000) isModule(#20001) +isES2015Module(#20001) #20036=@"var;{foo};{#20035}" variables(#20036,"foo",#20035) #20037=@"local_type_name;{foo};{#20035}" diff --git a/javascript/extractor/tests/html/output/trap/tst2.html.trap b/javascript/extractor/tests/html/output/trap/tst2.html.trap index 15a80be2b9e..03904b418b9 100644 --- a/javascript/extractor/tests/html/output/trap/tst2.html.trap +++ b/javascript/extractor/tests/html/output/trap/tst2.html.trap @@ -175,6 +175,7 @@ scopes(#20053,3) scopenodes(#20037,#20053) scopenesting(#20053,#20000) isModule(#20037) +isES2015Module(#20037) #20054=@"var;{inAModule};{#20053}" variables(#20054,"inAModule",#20053) #20055=* @@ -259,6 +260,7 @@ scopes(#20079,3) scopenodes(#20061,#20079) scopenesting(#20079,#20000) isModule(#20061) +isES2015Module(#20061) #20080=@"var;{f};{#20079}" variables(#20080,"f",#20079) #20081=@"local_type_name;{f};{#20079}" diff --git a/javascript/extractor/tests/node/output/trap/tst.mjs.trap b/javascript/extractor/tests/node/output/trap/tst.mjs.trap index 45d0f723864..dce8cdeaebe 100644 --- a/javascript/extractor/tests/node/output/trap/tst.mjs.trap +++ b/javascript/extractor/tests/node/output/trap/tst.mjs.trap @@ -50,6 +50,7 @@ scopes(#20016,3) scopenodes(#20001,#20016) scopenesting(#20016,#20000) isModule(#20001) +isES2015Module(#20001) #20017=@"var;{x};{#20016}" variables(#20017,"x",#20016) #20018=* diff --git a/javascript/extractor/tests/ts/output/trap/decorators.ts.trap b/javascript/extractor/tests/ts/output/trap/decorators.ts.trap index e8c22cfaa84..1c3b9822670 100644 --- a/javascript/extractor/tests/ts/output/trap/decorators.ts.trap +++ b/javascript/extractor/tests/ts/output/trap/decorators.ts.trap @@ -310,6 +310,7 @@ scopes(#20112,3) scopenodes(#20001,#20112) scopenesting(#20112,#20000) isModule(#20001) +isES2015Module(#20001) #20113=@"var;{fun};{#20112}" variables(#20113,"fun",#20112) #20114=@"var;{Class};{#20112}" diff --git a/javascript/extractor/tests/ts/output/trap/export.ts.trap b/javascript/extractor/tests/ts/output/trap/export.ts.trap index d98a2306d0b..fe88825fc6a 100644 --- a/javascript/extractor/tests/ts/output/trap/export.ts.trap +++ b/javascript/extractor/tests/ts/output/trap/export.ts.trap @@ -154,6 +154,7 @@ scopes(#20055,3) scopenodes(#20001,#20055) scopenesting(#20055,#20000) isModule(#20001) +isES2015Module(#20001) #20056=@"var;{f};{#20055}" variables(#20056,"f",#20055) #20057=@"var;{foo};{#20055}" diff --git a/javascript/extractor/tests/ts/output/trap/export2.ts.trap b/javascript/extractor/tests/ts/output/trap/export2.ts.trap index e45d6e63669..8511f3732ac 100644 --- a/javascript/extractor/tests/ts/output/trap/export2.ts.trap +++ b/javascript/extractor/tests/ts/output/trap/export2.ts.trap @@ -50,6 +50,7 @@ scopes(#20015,3) scopenodes(#20001,#20015) scopenesting(#20015,#20000) isModule(#20001) +isES2015Module(#20001) #20016=* stmts(#20016,29,#20001,0,"export default 42;") hasLocation(#20016,#20003) diff --git a/javascript/extractor/tests/ts/output/trap/exportasnamespace.d.ts.trap b/javascript/extractor/tests/ts/output/trap/exportasnamespace.d.ts.trap index eea4d7e1b4d..8b16ab7bef6 100644 --- a/javascript/extractor/tests/ts/output/trap/exportasnamespace.d.ts.trap +++ b/javascript/extractor/tests/ts/output/trap/exportasnamespace.d.ts.trap @@ -96,6 +96,7 @@ scopes(#20033,3) scopenodes(#20001,#20033) scopenesting(#20033,#20000) isModule(#20001) +isES2015Module(#20001) #20034=* stmts(#20034,30,#20001,0,"export ... foo();") hasLocation(#20034,#20003) diff --git a/javascript/extractor/tests/ts/output/trap/exportassign.ts.trap b/javascript/extractor/tests/ts/output/trap/exportassign.ts.trap index 092b9fcb8eb..035f5346697 100644 --- a/javascript/extractor/tests/ts/output/trap/exportassign.ts.trap +++ b/javascript/extractor/tests/ts/output/trap/exportassign.ts.trap @@ -50,6 +50,7 @@ scopes(#20015,3) scopenodes(#20001,#20015) scopenesting(#20015,#20000) isModule(#20001) +isES2015Module(#20001) #20016=* stmts(#20016,33,#20001,0,"export = 42;") hasLocation(#20016,#20003) diff --git a/javascript/extractor/tests/ts/output/trap/importExport.ts.trap b/javascript/extractor/tests/ts/output/trap/importExport.ts.trap index cf72ba04d5f..4b9267996c0 100644 --- a/javascript/extractor/tests/ts/output/trap/importExport.ts.trap +++ b/javascript/extractor/tests/ts/output/trap/importExport.ts.trap @@ -117,6 +117,7 @@ scopes(#20041,3) scopenodes(#20001,#20041) scopenesting(#20041,#20000) isModule(#20001) +isES2015Module(#20001) #20042=@"var;{Something};{#20041}" variables(#20042,"Something",#20041) #20043=@"var;{importExport};{#20041}" diff --git a/javascript/extractor/tests/ts/output/trap/importassign.ts.trap b/javascript/extractor/tests/ts/output/trap/importassign.ts.trap index 04788c62f71..10f8c933632 100644 --- a/javascript/extractor/tests/ts/output/trap/importassign.ts.trap +++ b/javascript/extractor/tests/ts/output/trap/importassign.ts.trap @@ -70,6 +70,7 @@ scopes(#20023,3) scopenodes(#20001,#20023) scopenesting(#20023,#20000) isModule(#20001) +isES2015Module(#20001) #20024=@"var;{x};{#20023}" variables(#20024,"x",#20023) #20025=@"local_type_name;{x};{#20023}" diff --git a/javascript/ql/src/Declarations/DeadStoreOfLocal.ql b/javascript/ql/src/Declarations/DeadStoreOfLocal.ql index c996c74a723..d738227d1a5 100644 --- a/javascript/ql/src/Declarations/DeadStoreOfLocal.ql +++ b/javascript/ql/src/Declarations/DeadStoreOfLocal.ql @@ -49,5 +49,7 @@ where // don't flag assignments in externs not dead.(ASTNode).inExternsFile() and // don't flag exported variables - not any(ES2015Module m).exportsAs(v, _) + not any(ES2015Module m).exportsAs(v, _) and + // don't flag 'exports' assignments in closure modules + not any(Closure::ClosureModule mod).getExportsVariable() = v select dead, "This definition of " + v.getName() + " is useless, since its value is never read." diff --git a/javascript/ql/src/NodeJS/InvalidExport.ql b/javascript/ql/src/NodeJS/InvalidExport.ql index df9a9b3387e..08925ab07d2 100644 --- a/javascript/ql/src/NodeJS/InvalidExport.ql +++ b/javascript/ql/src/NodeJS/InvalidExport.ql @@ -46,5 +46,7 @@ where moduleExportsAssign(_, exportsVal) and // however, if there are no further uses of `exports` the assignment is useless anyway strictcount(exportsVar.getAnAccess()) > 1 - ) + ) and + // export assignments do work in closure modules + not assgn.getTopLevel() instanceof Closure::ClosureModule select assgn, "Assigning to 'exports' does not export anything." diff --git a/javascript/ql/src/javascript.qll b/javascript/ql/src/javascript.qll index bbda30954e5..376d6fb3a7b 100644 --- a/javascript/ql/src/javascript.qll +++ b/javascript/ql/src/javascript.qll @@ -8,6 +8,7 @@ import semmle.javascript.AST import semmle.javascript.BasicBlocks import semmle.javascript.CFG import semmle.javascript.Classes +import semmle.javascript.Closure import semmle.javascript.Comments import semmle.javascript.Concepts import semmle.javascript.Constants diff --git a/javascript/ql/src/semmle/javascript/Closure.qll b/javascript/ql/src/semmle/javascript/Closure.qll index 616e7746d7c..bea775c29ff 100644 --- a/javascript/ql/src/semmle/javascript/Closure.qll +++ b/javascript/ql/src/semmle/javascript/Closure.qll @@ -1,75 +1,209 @@ /** - * Provides classes for working with Google Closure code. + * Provides classes for working with the Closure-Library module system. */ import javascript -/** - * A call to a function in the `goog` namespace such as `goog.provide` or `goog.load`. - */ -class GoogFunctionCall extends CallExpr { - GoogFunctionCall() { - exists(GlobalVariable gv | gv.getName() = "goog" | - this.getCallee().(DotExpr).getBase() = gv.getAnAccess() +module Closure { + /** + * A call to a function in the `goog` namespace such as `goog.provide` or `goog.load`. + */ + class GoogFunctionCall extends CallExpr { + GoogFunctionCall() { + exists(GlobalVariable gv | gv.getName() = "goog" | + this.getCallee().(DotExpr).getBase() = gv.getAnAccess() + ) + } + + /** Gets the name of the invoked function. */ + string getFunctionName() { result = getCallee().(DotExpr).getPropertyName() } + } + + /** + * An expression statement consisting of a call to a function + * in the `goog` namespace. + */ + class GoogFunctionCallStmt extends ExprStmt { + GoogFunctionCallStmt() { super.getExpr() instanceof GoogFunctionCall } + + override GoogFunctionCall getExpr() { result = super.getExpr() } + + /** Gets the name of the invoked function. */ + string getFunctionName() { result = getExpr().getFunctionName() } + + /** Gets the `i`th argument to the invoked function. */ + Expr getArgument(int i) { result = getExpr().getArgument(i) } + + /** Gets an argument to the invoked function. */ + Expr getAnArgument() { result = getArgument(_) } + } + + abstract private class GoogNamespaceRef extends ExprOrStmt { abstract string getNamespaceId(); } + + /** + * A call to `goog.provide`. + */ + class GoogProvide extends GoogFunctionCallStmt, GoogNamespaceRef { + GoogProvide() { getFunctionName() = "provide" } + + /** Gets the identifier of the namespace created by this call. */ + override string getNamespaceId() { result = getArgument(0).getStringValue() } + } + + /** + * A call to `goog.require`. + */ + class GoogRequire extends GoogFunctionCall, GoogNamespaceRef { + GoogRequire() { getFunctionName() = "require" } + + /** Gets the identifier of the namespace imported by this call. */ + override string getNamespaceId() { result = getArgument(0).getStringValue() } + } + + private class GoogRequireImport extends GoogRequire, Import { + /** Gets the module in which this import appears. */ + override Module getEnclosingModule() { result = getTopLevel() } + + /** Gets the (unresolved) path that this import refers to. */ + override PathExpr getImportedPath() { result = getArgument(0) } + } + + /** + * A call to `goog.module` or `goog.declareModuleId`. + */ + class GoogModuleDeclaration extends GoogFunctionCallStmt, GoogNamespaceRef { + GoogModuleDeclaration() { + getFunctionName() = "module" or + getFunctionName() = "declareModuleId" + } + + /** Gets the identifier of the namespace imported by this call. */ + override string getNamespaceId() { result = getArgument(0).getStringValue() } + } + + /** + * A module using the Closure module system, declared using `goog.module()` or `goog.declareModuleId()`. + */ + class ClosureModule extends Module { + ClosureModule() { getAChildStmt() instanceof GoogModuleDeclaration } + + /** + * Gets the call to `goog.module` or `goog.declareModuleId` in this module. + */ + GoogModuleDeclaration getModuleDeclaration() { result = getAChildStmt() } + + /** + * Gets the namespace of this module. + */ + string getNamespaceId() { result = getModuleDeclaration().getNamespaceId() } + + override Module getAnImportedModule() { + exists(GoogRequireImport imprt | + imprt.getEnclosingModule() = this and + result.(ClosureModule).getNamespaceId() = imprt.getNamespaceId() + ) + } + + /** + * Gets the top-level `exports` variable in this module, if this module is defined by + * a `good.module` call. + * + * This variable denotes the object exported from this module. + * + * Has no result for ES6 modules using `goog.declareModuleId`. + */ + Variable getExportsVariable() { + getModuleDeclaration().getFunctionName() = "module" and + result = getScope().getVariable("exports") + } + + override predicate exports(string name, ASTNode export) { + exists(DataFlow::PropWrite write, Expr base | + write.getAstNode() = export and + write.writes(base.flow(), name, _) and + ( + base = getExportsVariable().getAReference() + or + base = getExportsVariable().getAnAssignedExpr() + ) + ) + } + } + + /** + * A global Closure script, that is, a toplevel that is executed in the global scope and + * contains a toplevel call to `goog.provide` or `goog.require`. + */ + class ClosureScript extends TopLevel { + ClosureScript() { + not this instanceof ClosureModule and + getAChildStmt() instanceof GoogProvide + or + getAChildStmt().(ExprStmt).getExpr() instanceof GoogRequire + } + + /** Gets the identifier of a namespace required by this module. */ + string getARequiredNamespace() { + result = getAChildStmt().(ExprStmt).getExpr().(GoogRequire).getNamespaceId() + } + + /** Gets the identifer of a namespace provided by this module. */ + string getAProvidedNamespace() { result = getAChildStmt().(GoogProvide).getNamespaceId() } + } + + /** + * Holds if `name` is a closure namespace, including proper namespace prefixes. + */ + pragma[noinline] + predicate isLibraryNamespacePath(string name) { + exists(string namespace | namespace = any(GoogNamespaceRef provide).getNamespaceId() | + name = namespace.substring(0, namespace.indexOf(".")) + or + name = namespace ) } - /** Gets the name of the invoked function. */ - string getFunctionName() { result = getCallee().(DotExpr).getPropertyName() } -} - -/** - * An expression statement consisting of a call to a function - * in the `goog` namespace. - */ -class GoogFunctionCallStmt extends ExprStmt { - GoogFunctionCallStmt() { super.getExpr() instanceof GoogFunctionCall } - - override GoogFunctionCall getExpr() { result = super.getExpr() } - - /** Gets the name of the invoked function. */ - string getFunctionName() { result = getExpr().getFunctionName() } - - /** Gets the `i`th argument to the invoked function. */ - Expr getArgument(int i) { result = getExpr().getArgument(i) } - - /** Gets an argument to the invoked function. */ - Expr getAnArgument() { result = getArgument(_) } -} - -/** - * A call to `goog.provide`. - */ -class GoogProvide extends GoogFunctionCallStmt { - GoogProvide() { getFunctionName() = "provide" } - - /** Gets the identifier of the namespace created by this call. */ - string getNamespaceId() { result = getArgument(0).(ConstantString).getStringValue() } -} - -/** - * A call to `goog.require`. - */ -class GoogRequire extends GoogFunctionCallStmt { - GoogRequire() { getFunctionName() = "require" } - - /** Gets the identifier of the namespace imported by this call. */ - string getNamespaceId() { result = getArgument(0).(ConstantString).getStringValue() } -} - -/** - * A Closure module, that is, a toplevel that contains a call to `goog.provide` or - * `goog.require`. - */ -class ClosureModule extends TopLevel { - ClosureModule() { - getAChildStmt() instanceof GoogProvide or - getAChildStmt() instanceof GoogRequire + /** + * Gets the closure namespace path addressed by the given dataflow node, if any. + */ + string getLibraryAccessPath(DataFlow::SourceNode node) { + isLibraryNamespacePath(result) and + node = DataFlow::globalVarRef(result) + or + isLibraryNamespacePath(result) and + exists(DataFlow::PropRead read | node = read | + result = getLibraryAccessPath(read.getBase().getALocalSource()) + "." + read.getPropertyName() + ) + or + // Associate an access path with the immediate RHS of a store on a closure namespace. + // This is to support patterns like: + // foo.bar = { baz() {} } + exists(DataFlow::PropWrite write | + node = write.getRhs() and + result = getWrittenLibraryAccessPath(write) + ) + or + result = node.asExpr().(GoogRequire).getNamespaceId() } - /** Gets the identifier of a namespace required by this module. */ - string getARequiredNamespace() { result = getAChildStmt().(GoogRequire).getNamespaceId() } + /** + * Gets the closure namespace path written to by the given property write, if any. + */ + string getWrittenLibraryAccessPath(DataFlow::PropWrite node) { + result = getLibraryAccessPath(node.getBase().getALocalSource()) + "." + node.getPropertyName() + } - /** Gets the identifer of a namespace provided by this module. */ - string getAProvidedNamespace() { result = getAChildStmt().(GoogProvide).getNamespaceId() } + /** + * Gets a dataflow node that refers to the given Closure module. + */ + DataFlow::SourceNode moduleImport(string moduleName) { + getLibraryAccessPath(result) = moduleName + } + + /** + * Gets a dataflow node that refers to the given member of a Closure module. + */ + DataFlow::SourceNode moduleMember(string moduleName, string memberName) { + result = moduleImport(moduleName).getAPropertyRead(memberName) + } } diff --git a/javascript/ql/src/semmle/javascript/ES2015Modules.qll b/javascript/ql/src/semmle/javascript/ES2015Modules.qll index 55e820f77ee..fa3d82c7720 100644 --- a/javascript/ql/src/semmle/javascript/ES2015Modules.qll +++ b/javascript/ql/src/semmle/javascript/ES2015Modules.qll @@ -7,8 +7,7 @@ import javascript */ class ES2015Module extends Module { ES2015Module() { - isModule(this) and - not isNodejs(this) + isES2015Module(this) } override ModuleScope getScope() { result.getScopeElement() = this } diff --git a/javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll b/javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll index 05a102d1c38..5aa7b6a56a3 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll @@ -1039,7 +1039,7 @@ module DataFlow { or exists(GlobalVarAccess va | nd = valueNode(va.(VarUse)) and - cause = "global" + if Closure::isLibraryNamespacePath(va.getName()) then cause = "heap" else cause = "global" ) or exists(Expr e | e = nd.asExpr() and cause = "call" | diff --git a/javascript/ql/src/semmle/javascript/dataflow/internal/InterModuleTypeInference.qll b/javascript/ql/src/semmle/javascript/dataflow/internal/InterModuleTypeInference.qll index 7f8a2325ee2..da2b54fc14f 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/internal/InterModuleTypeInference.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/internal/InterModuleTypeInference.qll @@ -332,3 +332,48 @@ private class AnalyzedExportAssign extends AnalyzedPropertyWrite, DataFlow::Valu source = this } } + +/** + * Flow analysis for assignments to the `exports` variable in a Closure module. + */ +private class AnalyzedClosureExportAssign extends AnalyzedPropertyWrite, DataFlow::ValueNode { + override AssignExpr astNode; + + Closure::ClosureModule mod; + + AnalyzedClosureExportAssign() { astNode.getLhs() = mod.getExportsVariable().getAReference() } + + override predicate writes(AbstractValue baseVal, string propName, DataFlow::AnalyzedNode source) { + baseVal = TAbstractModuleObject(astNode.getTopLevel()) and + propName = "exports" and + source = astNode.getRhs().flow() + } +} + +/** + * Read of a global access path exported by a Closure library. + * + * This adds a direct flow edge to the assigned value. + */ +private class AnalyzedClosureGlobalAccessPath extends AnalyzedNode, AnalyzedPropertyRead { + string accessPath; + + AnalyzedClosureGlobalAccessPath() { accessPath = Closure::getLibraryAccessPath(this) } + + override AnalyzedNode localFlowPred() { + exists(DataFlow::PropWrite write | + Closure::getWrittenLibraryAccessPath(write) = accessPath and + result = write.getRhs() + ) + or + result = AnalyzedNode.super.localFlowPred() + } + + override predicate reads(AbstractValue base, string propName) { + exists(Closure::ClosureModule mod | + mod.getNamespaceId() = accessPath and + base = TAbstractModuleObject(mod) and + propName = "exports" + ) + } +} diff --git a/javascript/ql/src/semmlecode.javascript.dbscheme b/javascript/ql/src/semmlecode.javascript.dbscheme index 7eead88269b..eee08932d52 100644 --- a/javascript/ql/src/semmlecode.javascript.dbscheme +++ b/javascript/ql/src/semmlecode.javascript.dbscheme @@ -128,6 +128,8 @@ case @toplevel.kind of isModule (int tl: @toplevel ref); isNodejs (int tl: @toplevel ref); +isES2015Module (int tl: @toplevel ref); +isClosureModule (int tl: @toplevel ref); // statements #keyset[parent, idx] diff --git a/javascript/ql/test/library-tests/Closure/CallGraph.expected b/javascript/ql/test/library-tests/Closure/CallGraph.expected new file mode 100644 index 00000000000..24fc3e293ea --- /dev/null +++ b/javascript/ql/test/library-tests/Closure/CallGraph.expected @@ -0,0 +1,22 @@ +| tests/importFromEs6.js:9:1:9:15 | es6Module.fun() | tests/es6Module.js:3:8:3:24 | function fun() {} | 0 | +| tests/importFromEs6.js:10:1:10:18 | es6ModuleDefault() | tests/es6ModuleDefault.js:3:16:3:28 | function() {} | 0 | +| tests/importFromEs6.js:12:1:12:16 | googModule.fun() | tests/googModule.js:4:6:4:10 | () {} | 0 | +| tests/importFromEs6.js:13:1:13:19 | googModuleDefault() | tests/googModuleDefault.js:3:11:3:27 | function fun() {} | 0 | +| tests/requireFromEs6.js:12:1:12:18 | globalModule.fun() | tests/globalModule.js:4:6:4:10 | () {} | 0 | +| tests/requireFromEs6.js:13:1:13:21 | globalM ... fault() | tests/globalModuleDefault.js:3:23:3:39 | function fun() {} | 0 | +| tests/requireFromEs6.js:15:1:15:15 | es6Module.fun() | tests/es6Module.js:3:8:3:24 | function fun() {} | 0 | +| tests/requireFromEs6.js:16:1:16:18 | es6ModuleDefault() | tests/es6ModuleDefault.js:3:16:3:28 | function() {} | 0 | +| tests/requireFromEs6.js:18:1:18:16 | googModule.fun() | tests/googModule.js:4:6:4:10 | () {} | 0 | +| tests/requireFromEs6.js:19:1:19:19 | googModuleDefault() | tests/googModuleDefault.js:3:11:3:27 | function fun() {} | 0 | +| tests/requireFromGlobalModule.js:10:1:10:18 | x.y.z.global.fun() | tests/globalModule.js:4:6:4:10 | () {} | 0 | +| tests/requireFromGlobalModule.js:11:1:11:21 | x.y.z.g ... fault() | tests/globalModuleDefault.js:3:23:3:39 | function fun() {} | 0 | +| tests/requireFromGlobalModule.js:13:1:13:16 | x.y.z.goog.fun() | tests/googModule.js:4:6:4:10 | () {} | 0 | +| tests/requireFromGlobalModule.js:14:1:14:19 | x.y.z.googdefault() | tests/googModuleDefault.js:3:11:3:27 | function fun() {} | 0 | +| tests/requireFromGlobalModule.js:16:1:16:15 | x.y.z.es6.fun() | tests/es6Module.js:3:8:3:24 | function fun() {} | 0 | +| tests/requireFromGlobalModule.js:17:1:17:18 | x.y.z.es6default() | tests/es6ModuleDefault.js:3:16:3:28 | function() {} | 0 | +| tests/requireFromGoogModule.js:12:1:12:18 | globalModule.fun() | tests/globalModule.js:4:6:4:10 | () {} | 0 | +| tests/requireFromGoogModule.js:13:1:13:21 | globalM ... fault() | tests/globalModuleDefault.js:3:23:3:39 | function fun() {} | 0 | +| tests/requireFromGoogModule.js:15:1:15:15 | es6Module.fun() | tests/es6Module.js:3:8:3:24 | function fun() {} | 0 | +| tests/requireFromGoogModule.js:16:1:16:18 | es6ModuleDefault() | tests/es6ModuleDefault.js:3:16:3:28 | function() {} | 0 | +| tests/requireFromGoogModule.js:18:1:18:16 | googModule.fun() | tests/googModule.js:4:6:4:10 | () {} | 0 | +| tests/requireFromGoogModule.js:19:1:19:19 | googModuleDefault() | tests/googModuleDefault.js:3:11:3:27 | function fun() {} | 0 | diff --git a/javascript/ql/test/library-tests/Closure/CallGraph.ql b/javascript/ql/test/library-tests/Closure/CallGraph.ql new file mode 100644 index 00000000000..b18c29d2bfe --- /dev/null +++ b/javascript/ql/test/library-tests/Closure/CallGraph.ql @@ -0,0 +1,4 @@ +import javascript + +from DataFlow::InvokeNode node, int imprecision +select node, node.getACallee(imprecision), imprecision diff --git a/javascript/ql/test/library-tests/Closure/ClosureModule.expected b/javascript/ql/test/library-tests/Closure/ClosureModule.expected deleted file mode 100644 index bcef7c4b344..00000000000 --- a/javascript/ql/test/library-tests/Closure/ClosureModule.expected +++ /dev/null @@ -1,2 +0,0 @@ -| a.js:1:1:5:1 | | -| b.js:1:1:3:21 | | diff --git a/javascript/ql/test/library-tests/Closure/ClosureModule.ql b/javascript/ql/test/library-tests/Closure/ClosureModule.ql deleted file mode 100644 index c13884e72eb..00000000000 --- a/javascript/ql/test/library-tests/Closure/ClosureModule.ql +++ /dev/null @@ -1,4 +0,0 @@ -import semmle.javascript.Closure - -from ClosureModule cm -select cm diff --git a/javascript/ql/test/library-tests/Closure/ClosureModule_getAProvidedNamespace.expected b/javascript/ql/test/library-tests/Closure/ClosureModule_getAProvidedNamespace.expected deleted file mode 100644 index b00fa691b2a..00000000000 --- a/javascript/ql/test/library-tests/Closure/ClosureModule_getAProvidedNamespace.expected +++ /dev/null @@ -1 +0,0 @@ -| a.js:1:1:5:1 | | a | diff --git a/javascript/ql/test/library-tests/Closure/ClosureModule_getAProvidedNamespace.ql b/javascript/ql/test/library-tests/Closure/ClosureModule_getAProvidedNamespace.ql deleted file mode 100644 index 23e7b3831b3..00000000000 --- a/javascript/ql/test/library-tests/Closure/ClosureModule_getAProvidedNamespace.ql +++ /dev/null @@ -1,4 +0,0 @@ -import semmle.javascript.Closure - -from ClosureModule cm -select cm, cm.getAProvidedNamespace() diff --git a/javascript/ql/test/library-tests/Closure/ClosureModule_getARequiredNamespace.expected b/javascript/ql/test/library-tests/Closure/ClosureModule_getARequiredNamespace.expected deleted file mode 100644 index 3346345796b..00000000000 --- a/javascript/ql/test/library-tests/Closure/ClosureModule_getARequiredNamespace.expected +++ /dev/null @@ -1 +0,0 @@ -| b.js:1:1:3:21 | | a | diff --git a/javascript/ql/test/library-tests/Closure/ClosureModule_getARequiredNamespace.ql b/javascript/ql/test/library-tests/Closure/ClosureModule_getARequiredNamespace.ql deleted file mode 100644 index c187bf2ce65..00000000000 --- a/javascript/ql/test/library-tests/Closure/ClosureModule_getARequiredNamespace.ql +++ /dev/null @@ -1,4 +0,0 @@ -import semmle.javascript.Closure - -from ClosureModule cm -select cm, cm.getARequiredNamespace() diff --git a/javascript/ql/test/library-tests/Closure/GoogFunctionCall.expected b/javascript/ql/test/library-tests/Closure/GoogFunctionCall.expected deleted file mode 100644 index 315105fa6fc..00000000000 --- a/javascript/ql/test/library-tests/Closure/GoogFunctionCall.expected +++ /dev/null @@ -1,3 +0,0 @@ -| a.js:1:1:1:17 | goog.provide('a') | provide | -| b.js:1:1:1:17 | goog.require('a') | require | -| c.js:2:1:2:14 | goog.leyness() | leyness | diff --git a/javascript/ql/test/library-tests/Closure/GoogFunctionCall.ql b/javascript/ql/test/library-tests/Closure/GoogFunctionCall.ql deleted file mode 100644 index 93b44a4a144..00000000000 --- a/javascript/ql/test/library-tests/Closure/GoogFunctionCall.ql +++ /dev/null @@ -1,4 +0,0 @@ -import semmle.javascript.Closure - -from GoogFunctionCall gfc -select gfc, gfc.getFunctionName() diff --git a/javascript/ql/test/library-tests/Closure/GoogProvide.expected b/javascript/ql/test/library-tests/Closure/GoogProvide.expected deleted file mode 100644 index 818436b0e0a..00000000000 --- a/javascript/ql/test/library-tests/Closure/GoogProvide.expected +++ /dev/null @@ -1 +0,0 @@ -| a.js:1:1:1:18 | goog.provide('a'); | a | diff --git a/javascript/ql/test/library-tests/Closure/GoogProvide.ql b/javascript/ql/test/library-tests/Closure/GoogProvide.ql deleted file mode 100644 index c02842bf060..00000000000 --- a/javascript/ql/test/library-tests/Closure/GoogProvide.ql +++ /dev/null @@ -1,4 +0,0 @@ -import semmle.javascript.Closure - -from GoogProvide gp -select gp, gp.getNamespaceId() diff --git a/javascript/ql/test/library-tests/Closure/GoogRequire.expected b/javascript/ql/test/library-tests/Closure/GoogRequire.expected deleted file mode 100644 index 753c733b30d..00000000000 --- a/javascript/ql/test/library-tests/Closure/GoogRequire.expected +++ /dev/null @@ -1 +0,0 @@ -| b.js:1:1:1:18 | goog.require('a'); | a | diff --git a/javascript/ql/test/library-tests/Closure/GoogRequire.ql b/javascript/ql/test/library-tests/Closure/GoogRequire.ql deleted file mode 100644 index 17c19b361e5..00000000000 --- a/javascript/ql/test/library-tests/Closure/GoogRequire.ql +++ /dev/null @@ -1,4 +0,0 @@ -import semmle.javascript.Closure - -from GoogRequire gr -select gr, gr.getNamespaceId() diff --git a/javascript/ql/test/library-tests/Closure/StrictMode.expected b/javascript/ql/test/library-tests/Closure/StrictMode.expected new file mode 100644 index 00000000000..a968cd35cae --- /dev/null +++ b/javascript/ql/test/library-tests/Closure/StrictMode.expected @@ -0,0 +1,4 @@ +| tests/es6Module.js:0:0:0:0 | tests/es6Module.js | +| tests/es6ModuleDefault.js:0:0:0:0 | tests/es6ModuleDefault.js | +| tests/importFromEs6.js:0:0:0:0 | tests/importFromEs6.js | +| tests/requireFromEs6.js:0:0:0:0 | tests/requireFromEs6.js | diff --git a/javascript/ql/test/library-tests/Closure/StrictMode.ql b/javascript/ql/test/library-tests/Closure/StrictMode.ql new file mode 100644 index 00000000000..211c306a3a5 --- /dev/null +++ b/javascript/ql/test/library-tests/Closure/StrictMode.ql @@ -0,0 +1,5 @@ +import javascript + +from TopLevel tl +where tl.isStrict() +select tl.getFile() diff --git a/javascript/ql/test/library-tests/Closure/a.js b/javascript/ql/test/library-tests/Closure/a.js deleted file mode 100644 index fe9a093506f..00000000000 --- a/javascript/ql/test/library-tests/Closure/a.js +++ /dev/null @@ -1,5 +0,0 @@ -goog.provide('a'); - -a.foo = function() { - return 42; -} \ No newline at end of file diff --git a/javascript/ql/test/library-tests/Closure/b.js b/javascript/ql/test/library-tests/Closure/b.js deleted file mode 100644 index ce45c3aa444..00000000000 --- a/javascript/ql/test/library-tests/Closure/b.js +++ /dev/null @@ -1,3 +0,0 @@ -goog.require('a'); - -console.log(a.foo()); \ No newline at end of file diff --git a/javascript/ql/test/library-tests/Closure/c.js b/javascript/ql/test/library-tests/Closure/c.js deleted file mode 100644 index f46c1963e52..00000000000 --- a/javascript/ql/test/library-tests/Closure/c.js +++ /dev/null @@ -1,2 +0,0 @@ -// not a Closure module -goog.leyness(); \ No newline at end of file diff --git a/javascript/ql/test/library-tests/Closure/moduleImport.expected b/javascript/ql/test/library-tests/Closure/moduleImport.expected new file mode 100644 index 00000000000..f143eea96aa --- /dev/null +++ b/javascript/ql/test/library-tests/Closure/moduleImport.expected @@ -0,0 +1,51 @@ +| x | tests/globalModule.js:3:1:3:1 | x | +| x | tests/globalModuleDefault.js:3:1:3:1 | x | +| x | tests/requireFromGlobalModule.js:10:1:10:1 | x | +| x | tests/requireFromGlobalModule.js:11:1:11:1 | x | +| x | tests/requireFromGlobalModule.js:13:1:13:1 | x | +| x | tests/requireFromGlobalModule.js:14:1:14:1 | x | +| x | tests/requireFromGlobalModule.js:16:1:16:1 | x | +| x | tests/requireFromGlobalModule.js:17:1:17:1 | x | +| x.y | tests/globalModule.js:3:1:3:3 | x.y | +| x.y | tests/globalModuleDefault.js:3:1:3:3 | x.y | +| x.y | tests/requireFromGlobalModule.js:10:1:10:3 | x.y | +| x.y | tests/requireFromGlobalModule.js:11:1:11:3 | x.y | +| x.y | tests/requireFromGlobalModule.js:13:1:13:3 | x.y | +| x.y | tests/requireFromGlobalModule.js:14:1:14:3 | x.y | +| x.y | tests/requireFromGlobalModule.js:16:1:16:3 | x.y | +| x.y | tests/requireFromGlobalModule.js:17:1:17:3 | x.y | +| x.y.z | tests/globalModule.js:3:1:3:5 | x.y.z | +| x.y.z | tests/globalModuleDefault.js:3:1:3:5 | x.y.z | +| x.y.z | tests/requireFromGlobalModule.js:10:1:10:5 | x.y.z | +| x.y.z | tests/requireFromGlobalModule.js:11:1:11:5 | x.y.z | +| x.y.z | tests/requireFromGlobalModule.js:13:1:13:5 | x.y.z | +| x.y.z | tests/requireFromGlobalModule.js:14:1:14:5 | x.y.z | +| x.y.z | tests/requireFromGlobalModule.js:16:1:16:5 | x.y.z | +| x.y.z | tests/requireFromGlobalModule.js:17:1:17:5 | x.y.z | +| x.y.z.es6 | tests/requireFromEs6.js:6:17:6:41 | goog.re ... z.es6') | +| x.y.z.es6 | tests/requireFromGlobalModule.js:7:1:7:25 | goog.re ... z.es6') | +| x.y.z.es6 | tests/requireFromGlobalModule.js:16:1:16:9 | x.y.z.es6 | +| x.y.z.es6 | tests/requireFromGoogModule.js:6:17:6:41 | goog.re ... z.es6') | +| x.y.z.es6default | tests/requireFromEs6.js:7:24:7:55 | goog.re ... fault') | +| x.y.z.es6default | tests/requireFromGlobalModule.js:8:1:8:32 | goog.re ... fault') | +| x.y.z.es6default | tests/requireFromGlobalModule.js:17:1:17:16 | x.y.z.es6default | +| x.y.z.es6default | tests/requireFromGoogModule.js:7:24:7:55 | goog.re ... fault') | +| x.y.z.global | tests/globalModule.js:3:16:5:1 | {\\n fun() {}\\n} | +| x.y.z.global | tests/requireFromEs6.js:3:20:3:47 | goog.re ... lobal') | +| x.y.z.global | tests/requireFromGlobalModule.js:1:1:1:28 | goog.re ... lobal') | +| x.y.z.global | tests/requireFromGlobalModule.js:10:1:10:12 | x.y.z.global | +| x.y.z.global | tests/requireFromGoogModule.js:3:20:3:47 | goog.re ... lobal') | +| x.y.z.global.fun | tests/globalModule.js:4:6:4:10 | () {} | +| x.y.z.globaldefault | tests/globalModuleDefault.js:3:23:3:39 | function fun() {} | +| x.y.z.globaldefault | tests/requireFromEs6.js:4:27:4:61 | goog.re ... fault') | +| x.y.z.globaldefault | tests/requireFromGlobalModule.js:2:1:2:35 | goog.re ... fault') | +| x.y.z.globaldefault | tests/requireFromGlobalModule.js:11:1:11:19 | x.y.z.globaldefault | +| x.y.z.globaldefault | tests/requireFromGoogModule.js:4:27:4:61 | goog.re ... fault') | +| x.y.z.goog | tests/requireFromEs6.js:9:18:9:43 | goog.re ... .goog') | +| x.y.z.goog | tests/requireFromGlobalModule.js:4:1:4:26 | goog.re ... .goog') | +| x.y.z.goog | tests/requireFromGlobalModule.js:13:1:13:10 | x.y.z.goog | +| x.y.z.goog | tests/requireFromGoogModule.js:9:18:9:43 | goog.re ... .goog') | +| x.y.z.googdefault | tests/requireFromEs6.js:10:25:10:57 | goog.re ... fault') | +| x.y.z.googdefault | tests/requireFromGlobalModule.js:5:1:5:33 | goog.re ... fault') | +| x.y.z.googdefault | tests/requireFromGlobalModule.js:14:1:14:17 | x.y.z.googdefault | +| x.y.z.googdefault | tests/requireFromGoogModule.js:10:25:10:57 | goog.re ... fault') | diff --git a/javascript/ql/test/library-tests/Closure/moduleImport.ql b/javascript/ql/test/library-tests/Closure/moduleImport.ql new file mode 100644 index 00000000000..ac6cf21ccca --- /dev/null +++ b/javascript/ql/test/library-tests/Closure/moduleImport.ql @@ -0,0 +1,4 @@ +import javascript + +from string name +select name, Closure::moduleImport(name) diff --git a/javascript/ql/test/library-tests/Closure/moduleMember.expected b/javascript/ql/test/library-tests/Closure/moduleMember.expected new file mode 100644 index 00000000000..281e8176555 --- /dev/null +++ b/javascript/ql/test/library-tests/Closure/moduleMember.expected @@ -0,0 +1,31 @@ +| x | y | tests/globalModule.js:3:1:3:3 | x.y | +| x | y | tests/globalModuleDefault.js:3:1:3:3 | x.y | +| x | y | tests/requireFromGlobalModule.js:10:1:10:3 | x.y | +| x | y | tests/requireFromGlobalModule.js:11:1:11:3 | x.y | +| x | y | tests/requireFromGlobalModule.js:13:1:13:3 | x.y | +| x | y | tests/requireFromGlobalModule.js:14:1:14:3 | x.y | +| x | y | tests/requireFromGlobalModule.js:16:1:16:3 | x.y | +| x | y | tests/requireFromGlobalModule.js:17:1:17:3 | x.y | +| x.y | z | tests/globalModule.js:3:1:3:5 | x.y.z | +| x.y | z | tests/globalModuleDefault.js:3:1:3:5 | x.y.z | +| x.y | z | tests/requireFromGlobalModule.js:10:1:10:5 | x.y.z | +| x.y | z | tests/requireFromGlobalModule.js:11:1:11:5 | x.y.z | +| x.y | z | tests/requireFromGlobalModule.js:13:1:13:5 | x.y.z | +| x.y | z | tests/requireFromGlobalModule.js:14:1:14:5 | x.y.z | +| x.y | z | tests/requireFromGlobalModule.js:16:1:16:5 | x.y.z | +| x.y | z | tests/requireFromGlobalModule.js:17:1:17:5 | x.y.z | +| x.y.z | es6 | tests/requireFromGlobalModule.js:16:1:16:9 | x.y.z.es6 | +| x.y.z | es6default | tests/requireFromGlobalModule.js:17:1:17:16 | x.y.z.es6default | +| x.y.z | global | tests/requireFromGlobalModule.js:10:1:10:12 | x.y.z.global | +| x.y.z | globaldefault | tests/requireFromGlobalModule.js:11:1:11:19 | x.y.z.globaldefault | +| x.y.z | goog | tests/requireFromGlobalModule.js:13:1:13:10 | x.y.z.goog | +| x.y.z | googdefault | tests/requireFromGlobalModule.js:14:1:14:17 | x.y.z.googdefault | +| x.y.z.es6 | fun | tests/requireFromEs6.js:15:1:15:13 | es6Module.fun | +| x.y.z.es6 | fun | tests/requireFromGlobalModule.js:16:1:16:13 | x.y.z.es6.fun | +| x.y.z.es6 | fun | tests/requireFromGoogModule.js:15:1:15:13 | es6Module.fun | +| x.y.z.global | fun | tests/requireFromEs6.js:12:1:12:16 | globalModule.fun | +| x.y.z.global | fun | tests/requireFromGlobalModule.js:10:1:10:16 | x.y.z.global.fun | +| x.y.z.global | fun | tests/requireFromGoogModule.js:12:1:12:16 | globalModule.fun | +| x.y.z.goog | fun | tests/requireFromEs6.js:18:1:18:14 | googModule.fun | +| x.y.z.goog | fun | tests/requireFromGlobalModule.js:13:1:13:14 | x.y.z.goog.fun | +| x.y.z.goog | fun | tests/requireFromGoogModule.js:18:1:18:14 | googModule.fun | diff --git a/javascript/ql/test/library-tests/Closure/moduleMember.ql b/javascript/ql/test/library-tests/Closure/moduleMember.ql new file mode 100644 index 00000000000..8e4cf30998d --- /dev/null +++ b/javascript/ql/test/library-tests/Closure/moduleMember.ql @@ -0,0 +1,4 @@ +import javascript + +from string mod, string name +select mod, name, Closure::moduleMember(mod, name) \ No newline at end of file diff --git a/javascript/ql/test/library-tests/Closure/tests/es6Module.js b/javascript/ql/test/library-tests/Closure/tests/es6Module.js new file mode 100644 index 00000000000..c1a709d4037 --- /dev/null +++ b/javascript/ql/test/library-tests/Closure/tests/es6Module.js @@ -0,0 +1,3 @@ +goog.declareModuleId('x.y.z.es6'); + +export function fun() {} diff --git a/javascript/ql/test/library-tests/Closure/tests/es6ModuleDefault.js b/javascript/ql/test/library-tests/Closure/tests/es6ModuleDefault.js new file mode 100644 index 00000000000..72f98436c2e --- /dev/null +++ b/javascript/ql/test/library-tests/Closure/tests/es6ModuleDefault.js @@ -0,0 +1,3 @@ +goog.declareModuleId('x.y.z.es6default'); + +export default function() {} diff --git a/javascript/ql/test/library-tests/Closure/tests/globalModule.js b/javascript/ql/test/library-tests/Closure/tests/globalModule.js new file mode 100644 index 00000000000..129a313d937 --- /dev/null +++ b/javascript/ql/test/library-tests/Closure/tests/globalModule.js @@ -0,0 +1,5 @@ +goog.provide('x.y.z.global'); + +x.y.z.global = { + fun() {} +}; diff --git a/javascript/ql/test/library-tests/Closure/tests/globalModuleDefault.js b/javascript/ql/test/library-tests/Closure/tests/globalModuleDefault.js new file mode 100644 index 00000000000..d5044e56dbb --- /dev/null +++ b/javascript/ql/test/library-tests/Closure/tests/globalModuleDefault.js @@ -0,0 +1,3 @@ +goog.provide('x.y.z.globaldefault'); + +x.y.z.globaldefault = function fun() {} diff --git a/javascript/ql/test/library-tests/Closure/tests/googModule.js b/javascript/ql/test/library-tests/Closure/tests/googModule.js new file mode 100644 index 00000000000..c986078db3e --- /dev/null +++ b/javascript/ql/test/library-tests/Closure/tests/googModule.js @@ -0,0 +1,5 @@ +goog.module('x.y.z.goog'); + +exports = { + fun() {} +}; diff --git a/javascript/ql/test/library-tests/Closure/tests/googModuleDefault.js b/javascript/ql/test/library-tests/Closure/tests/googModuleDefault.js new file mode 100644 index 00000000000..a4bf3384848 --- /dev/null +++ b/javascript/ql/test/library-tests/Closure/tests/googModuleDefault.js @@ -0,0 +1,3 @@ +goog.module('x.y.z.googdefault'); + +exports = function fun() {}; diff --git a/javascript/ql/test/library-tests/Closure/tests/importFromEs6.js b/javascript/ql/test/library-tests/Closure/tests/importFromEs6.js new file mode 100644 index 00000000000..5e380984817 --- /dev/null +++ b/javascript/ql/test/library-tests/Closure/tests/importFromEs6.js @@ -0,0 +1,13 @@ +// ES6 imports can import files by name, as long as they are modules + +import * as googModule from './googModule'; +import * as googModuleDefault from './googModuleDefault'; + +import * as es6Module from './es6Module'; +import * as es6ModuleDefault from './es6ModuleDefault'; + +es6Module.fun(); +es6ModuleDefault(); + +googModule.fun(); +googModuleDefault(); diff --git a/javascript/ql/test/library-tests/Closure/tests/requireFromEs6.js b/javascript/ql/test/library-tests/Closure/tests/requireFromEs6.js new file mode 100644 index 00000000000..9078bb91c40 --- /dev/null +++ b/javascript/ql/test/library-tests/Closure/tests/requireFromEs6.js @@ -0,0 +1,19 @@ +import * as dummy from 'dummy'; // treat as ES6 module + +let globalModule = goog.require('x.y.z.global'); +let globalModuleDefault = goog.require('x.y.z.globaldefault'); + +let es6Module = goog.require('x.y.z.es6'); +let es6ModuleDefault = goog.require('x.y.z.es6default'); + +let googModule = goog.require('x.y.z.goog'); +let googModuleDefault = goog.require('x.y.z.googdefault'); + +globalModule.fun(); +globalModuleDefault(); + +es6Module.fun(); +es6ModuleDefault(); + +googModule.fun(); +googModuleDefault(); diff --git a/javascript/ql/test/library-tests/Closure/tests/requireFromGlobalModule.js b/javascript/ql/test/library-tests/Closure/tests/requireFromGlobalModule.js new file mode 100644 index 00000000000..0542e6fd356 --- /dev/null +++ b/javascript/ql/test/library-tests/Closure/tests/requireFromGlobalModule.js @@ -0,0 +1,17 @@ +goog.require('x.y.z.global'); +goog.require('x.y.z.globaldefault'); + +goog.require('x.y.z.goog'); +goog.require('x.y.z.googdefault'); + +goog.require('x.y.z.es6'); +goog.require('x.y.z.es6default'); + +x.y.z.global.fun(); +x.y.z.globaldefault(); + +x.y.z.goog.fun(); +x.y.z.googdefault(); + +x.y.z.es6.fun(); +x.y.z.es6default(); diff --git a/javascript/ql/test/library-tests/Closure/tests/requireFromGoogModule.js b/javascript/ql/test/library-tests/Closure/tests/requireFromGoogModule.js new file mode 100644 index 00000000000..00df1fb3ae1 --- /dev/null +++ b/javascript/ql/test/library-tests/Closure/tests/requireFromGoogModule.js @@ -0,0 +1,19 @@ +goog.module('test.importer'); + +let globalModule = goog.require('x.y.z.global'); +let globalModuleDefault = goog.require('x.y.z.globaldefault'); + +let es6Module = goog.require('x.y.z.es6'); +let es6ModuleDefault = goog.require('x.y.z.es6default'); + +let googModule = goog.require('x.y.z.goog'); +let googModuleDefault = goog.require('x.y.z.googdefault'); + +globalModule.fun(); +globalModuleDefault(); + +es6Module.fun(); +es6ModuleDefault(); + +googModule.fun(); +googModuleDefault(); diff --git a/javascript/ql/test/library-tests/Modules/GlobalVariableRef.expected b/javascript/ql/test/library-tests/Modules/GlobalVariableRef.expected new file mode 100644 index 00000000000..90cf72071ae --- /dev/null +++ b/javascript/ql/test/library-tests/Modules/GlobalVariableRef.expected @@ -0,0 +1,3 @@ +| a.js:5:31:5:31 | o | +| exports.js:3:9:3:15 | exports | +| tst.html:6:3:6:7 | alert | diff --git a/javascript/ql/test/library-tests/Modules/GlobalVariableRef.ql b/javascript/ql/test/library-tests/Modules/GlobalVariableRef.ql new file mode 100644 index 00000000000..5b06a07a226 --- /dev/null +++ b/javascript/ql/test/library-tests/Modules/GlobalVariableRef.ql @@ -0,0 +1,5 @@ +import javascript + +from VarAccess access +where access.getVariable() instanceof GlobalVariable +select access diff --git a/javascript/ql/test/library-tests/Modules/ImportNamespaceSpecifier.expected b/javascript/ql/test/library-tests/Modules/ImportNamespaceSpecifier.expected index d7305160b05..61093b96451 100644 --- a/javascript/ql/test/library-tests/Modules/ImportNamespaceSpecifier.expected +++ b/javascript/ql/test/library-tests/Modules/ImportNamespaceSpecifier.expected @@ -1 +1,2 @@ +| exports.js:1:8:1:17 | * as dummy | | m/c.js:1:8:1:13 | * as b | diff --git a/javascript/ql/test/library-tests/Modules/ImportSpecifiers.expected b/javascript/ql/test/library-tests/Modules/ImportSpecifiers.expected index 7abe8cb7244..73763be7450 100644 --- a/javascript/ql/test/library-tests/Modules/ImportSpecifiers.expected +++ b/javascript/ql/test/library-tests/Modules/ImportSpecifiers.expected @@ -1,6 +1,7 @@ | b.js:1:8:1:8 | f | b.js:1:8:1:8 | f | | d.js:1:10:1:21 | default as g | d.js:1:21:1:21 | g | | d.js:1:24:1:29 | x as y | d.js:1:29:1:29 | y | +| exports.js:1:8:1:17 | * as dummy | exports.js:1:13:1:17 | dummy | | f.ts:1:8:1:8 | g | f.ts:1:8:1:8 | g | | g.ts:1:9:1:11 | foo | g.ts:1:9:1:11 | foo | | import-in-mjs.mjs:1:8:1:24 | exported_from_mjs | import-in-mjs.mjs:1:8:1:24 | exported_from_mjs | diff --git a/javascript/ql/test/library-tests/Modules/Imports.expected b/javascript/ql/test/library-tests/Modules/Imports.expected index d3ef73bcc5b..723f3542039 100644 --- a/javascript/ql/test/library-tests/Modules/Imports.expected +++ b/javascript/ql/test/library-tests/Modules/Imports.expected @@ -1,6 +1,7 @@ | b.js:1:1:1:20 | import f from './a'; | b.js:1:15:1:19 | './a' | 1 | | d.js:1:1:1:43 | import ... './a'; | d.js:1:38:1:42 | './a' | 2 | | d.js:2:1:2:13 | import './b'; | d.js:2:8:2:12 | './b' | 0 | +| exports.js:1:1:1:31 | import ... dummy'; | exports.js:1:24:1:30 | 'dummy' | 1 | | f.ts:1:1:1:19 | import g from './e' | f.ts:1:15:1:19 | './e' | 1 | | g.ts:1:1:1:23 | import ... m './f' | g.ts:1:19:1:23 | './f' | 1 | | import-in-mjs.mjs:1:1:1:46 | import ... n-mjs'; | import-in-mjs.mjs:1:31:1:45 | 'export-in-mjs' | 1 | diff --git a/javascript/ql/test/library-tests/Modules/exports.js b/javascript/ql/test/library-tests/Modules/exports.js new file mode 100644 index 00000000000..c4a90d9da65 --- /dev/null +++ b/javascript/ql/test/library-tests/Modules/exports.js @@ -0,0 +1,4 @@ +import * as dummy from 'dummy'; // Recognize as ES6 module + +let x = exports; // global variable +x.foo(); diff --git a/javascript/upgrades/7eead88269b20e9d61f61cd2291affb737dc831e/isES2015Module.ql b/javascript/upgrades/7eead88269b20e9d61f61cd2291affb737dc831e/isES2015Module.ql new file mode 100644 index 00000000000..38971a50710 --- /dev/null +++ b/javascript/upgrades/7eead88269b20e9d61f61cd2291affb737dc831e/isES2015Module.ql @@ -0,0 +1,7 @@ +class TopLevel extends @toplevel { + string toString() { none() } +} + +from TopLevel tl +where isModule(tl) and not isNodejs(tl) +select tl diff --git a/javascript/upgrades/7eead88269b20e9d61f61cd2291affb737dc831e/old.dbscheme b/javascript/upgrades/7eead88269b20e9d61f61cd2291affb737dc831e/old.dbscheme new file mode 100644 index 00000000000..7eead88269b --- /dev/null +++ b/javascript/upgrades/7eead88269b20e9d61f61cd2291affb737dc831e/old.dbscheme @@ -0,0 +1,1088 @@ +/*** Standard fragments ***/ + +/** Files and folders **/ + +@location = @location_default; + +locations_default(unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref + ); + +@sourceline = @locatable; + +numlines(int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref + ); + + +/* + fromSource(0) = unknown, + fromSource(1) = from source, + fromSource(2) = from library +*/ +files(unique int id: @file, + varchar(900) name: string ref, + varchar(900) simple: string ref, + varchar(900) ext: string ref, + int fromSource: int ref); + +folders(unique int id: @folder, + varchar(900) name: string ref, + varchar(900) simple: string ref); + + +@container = @folder | @file ; + + +containerparent(int parent: @container ref, + unique int child: @container ref); + +/** Duplicate code **/ + +duplicateCode( + unique int id : @duplication, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +similarCode( + unique int id : @similarity, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +@duplication_or_similarity = @duplication | @similarity; + +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref); + +/** External data **/ + +externalData( + int id : @externalDataElement, + varchar(900) path : string ref, + int column: int ref, + varchar(900) value : string ref +); + +snapshotDate(unique date snapshotDate : date ref); + +sourceLocationPrefix(varchar(900) prefix : string ref); + +/** Version control data **/ + +svnentries( + int id : @svnentry, + varchar(500) revision : string ref, + varchar(500) author : string ref, + date revisionDate : date ref, + int changeSize : int ref +); + +svnaffectedfiles( + int id : @svnentry ref, + int file : @file ref, + varchar(500) action : string ref +); + +svnentrymsg( + int id : @svnentry ref, + varchar(500) message : string ref +); + +svnchurn( + int commit : @svnentry ref, + int file : @file ref, + int addedLines : int ref, + int deletedLines : int ref +); + + +/*** JavaScript-specific part ***/ + +filetype( + int file: @file ref, + string filetype: string ref +) + +// top-level code fragments +toplevels (unique int id: @toplevel, + int kind: int ref); + +isExterns (int toplevel: @toplevel ref); + +case @toplevel.kind of + 0 = @script +| 1 = @inline_script +| 2 = @event_handler +| 3 = @javascript_url; + +isModule (int tl: @toplevel ref); +isNodejs (int tl: @toplevel ref); + +// statements +#keyset[parent, idx] +stmts (unique int id: @stmt, + int kind: int ref, + int parent: @stmtparent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +stmtContainers (unique int stmt: @stmt ref, + int container: @stmt_container ref); + +jumpTargets (unique int jump: @stmt ref, + int target: @stmt ref); + +@stmtparent = @stmt | @toplevel | @functionexpr | @arrowfunctionexpr; +@stmt_container = @toplevel | @function | @namespacedeclaration | @externalmoduledeclaration | @globalaugmentationdeclaration; + +case @stmt.kind of + 0 = @emptystmt +| 1 = @blockstmt +| 2 = @exprstmt +| 3 = @ifstmt +| 4 = @labeledstmt +| 5 = @breakstmt +| 6 = @continuestmt +| 7 = @withstmt +| 8 = @switchstmt +| 9 = @returnstmt +| 10 = @throwstmt +| 11 = @trystmt +| 12 = @whilestmt +| 13 = @dowhilestmt +| 14 = @forstmt +| 15 = @forinstmt +| 16 = @debuggerstmt +| 17 = @functiondeclstmt +| 18 = @vardeclstmt +| 19 = @case +| 20 = @catchclause +| 21 = @forofstmt +| 22 = @constdeclstmt +| 23 = @letstmt +| 24 = @legacy_letstmt +| 25 = @foreachstmt +| 26 = @classdeclstmt +| 27 = @importdeclaration +| 28 = @exportalldeclaration +| 29 = @exportdefaultdeclaration +| 30 = @exportnameddeclaration +| 31 = @namespacedeclaration +| 32 = @importequalsdeclaration +| 33 = @exportassigndeclaration +| 34 = @interfacedeclaration +| 35 = @typealiasdeclaration +| 36 = @enumdeclaration +| 37 = @externalmoduledeclaration +| 38 = @exportasnamespacedeclaration +| 39 = @globalaugmentationdeclaration +; + +@declstmt = @vardeclstmt | @constdeclstmt | @letstmt | @legacy_letstmt; + +@exportdeclaration = @exportalldeclaration | @exportdefaultdeclaration | @exportnameddeclaration; + +@namespacedefinition = @namespacedeclaration | @enumdeclaration; +@typedefinition = @classdefinition | @interfacedeclaration | @enumdeclaration | @typealiasdeclaration | @enum_member; + +isInstantiated(unique int decl: @namespacedeclaration ref); + +@declarablestmt = @declstmt | @namespacedeclaration | @classdeclstmt | @functiondeclstmt | @enumdeclaration | @externalmoduledeclaration | @globalaugmentationdeclaration; +hasDeclareKeyword(unique int stmt: @declarablestmt ref); + +isForAwaitOf(unique int forof: @forofstmt ref); + +// expressions +#keyset[parent, idx] +exprs (unique int id: @expr, + int kind: int ref, + int parent: @exprparent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +literals (varchar(900) value: string ref, + varchar(900) raw: string ref, + unique int expr: @exprortype ref); + +enclosingStmt (unique int expr: @exprortype ref, + int stmt: @stmt ref); + +exprContainers (unique int expr: @exprortype ref, + int container: @stmt_container ref); + +arraySize (unique int ae: @arraylike ref, + int sz: int ref); + +isDelegating (int yield: @yieldexpr ref); + +@exprorstmt = @expr | @stmt; +@exprortype = @expr | @typeexpr; +@exprparent = @exprorstmt | @property | @functiontypeexpr; +@arraylike = @arrayexpr | @arraypattern; + +case @expr.kind of + 0 = @label +| 1 = @nullliteral +| 2 = @booleanliteral +| 3 = @numberliteral +| 4 = @stringliteral +| 5 = @regexpliteral +| 6 = @thisexpr +| 7 = @arrayexpr +| 8 = @objexpr +| 9 = @functionexpr +| 10 = @seqexpr +| 11 = @conditionalexpr +| 12 = @newexpr +| 13 = @callexpr +| 14 = @dotexpr +| 15 = @indexexpr +| 16 = @negexpr +| 17 = @plusexpr +| 18 = @lognotexpr +| 19 = @bitnotexpr +| 20 = @typeofexpr +| 21 = @voidexpr +| 22 = @deleteexpr +| 23 = @eqexpr +| 24 = @neqexpr +| 25 = @eqqexpr +| 26 = @neqqexpr +| 27 = @ltexpr +| 28 = @leexpr +| 29 = @gtexpr +| 30 = @geexpr +| 31 = @lshiftexpr +| 32 = @rshiftexpr +| 33 = @urshiftexpr +| 34 = @addexpr +| 35 = @subexpr +| 36 = @mulexpr +| 37 = @divexpr +| 38 = @modexpr +| 39 = @bitorexpr +| 40 = @xorexpr +| 41 = @bitandexpr +| 42 = @inexpr +| 43 = @instanceofexpr +| 44 = @logandexpr +| 45 = @logorexpr +| 47 = @assignexpr +| 48 = @assignaddexpr +| 49 = @assignsubexpr +| 50 = @assignmulexpr +| 51 = @assigndivexpr +| 52 = @assignmodexpr +| 53 = @assignlshiftexpr +| 54 = @assignrshiftexpr +| 55 = @assignurshiftexpr +| 56 = @assignorexpr +| 57 = @assignxorexpr +| 58 = @assignandexpr +| 59 = @preincexpr +| 60 = @postincexpr +| 61 = @predecexpr +| 62 = @postdecexpr +| 63 = @parexpr +| 64 = @vardeclarator +| 65 = @arrowfunctionexpr +| 66 = @spreadelement +| 67 = @arraypattern +| 68 = @objectpattern +| 69 = @yieldexpr +| 70 = @taggedtemplateexpr +| 71 = @templateliteral +| 72 = @templateelement +| 73 = @arraycomprehensionexpr +| 74 = @generatorexpr +| 75 = @forincomprehensionblock +| 76 = @forofcomprehensionblock +| 77 = @legacy_letexpr +| 78 = @vardecl +| 79 = @proper_varaccess +| 80 = @classexpr +| 81 = @superexpr +| 82 = @newtargetexpr +| 83 = @namedimportspecifier +| 84 = @importdefaultspecifier +| 85 = @importnamespacespecifier +| 86 = @namedexportspecifier +| 87 = @expexpr +| 88 = @assignexpexpr +| 89 = @jsxelement +| 90 = @jsxqualifiedname +| 91 = @jsxemptyexpr +| 92 = @awaitexpr +| 93 = @functionsentexpr +| 94 = @decorator +| 95 = @exportdefaultspecifier +| 96 = @exportnamespacespecifier +| 97 = @bindexpr +| 98 = @externalmodulereference +| 99 = @dynamicimport +| 100 = @expressionwithtypearguments +| 101 = @prefixtypeassertion +| 102 = @astypeassertion +| 103 = @export_varaccess +| 104 = @decorator_list +| 105 = @non_null_assertion +| 106 = @bigintliteral +| 107 = @nullishcoalescingexpr +; + +@varaccess = @proper_varaccess | @export_varaccess; +@varref = @vardecl | @varaccess; + +@identifier = @label | @varref | @typeidentifier; + +@literal = @nullliteral | @booleanliteral | @numberliteral | @stringliteral | @regexpliteral | @bigintliteral; + +@propaccess = @dotexpr | @indexexpr; + +@invokeexpr = @newexpr | @callexpr; + +@unaryexpr = @negexpr | @plusexpr | @lognotexpr | @bitnotexpr | @typeofexpr | @voidexpr | @deleteexpr | @spreadelement; + +@equalitytest = @eqexpr | @neqexpr | @eqqexpr | @neqqexpr; + +@comparison = @equalitytest | @ltexpr | @leexpr | @gtexpr | @geexpr; + +@binaryexpr = @comparison | @lshiftexpr | @rshiftexpr | @urshiftexpr | @addexpr | @subexpr | @mulexpr | @divexpr | @modexpr | @expexpr | @bitorexpr | @xorexpr | @bitandexpr | @inexpr | @instanceofexpr | @logandexpr | @logorexpr | @nullishcoalescingexpr; + +@assignment = @assignexpr | @assignaddexpr | @assignsubexpr | @assignmulexpr | @assigndivexpr | @assignmodexpr | @assignexpexpr | @assignlshiftexpr | @assignrshiftexpr | @assignurshiftexpr | @assignorexpr | @assignxorexpr | @assignandexpr; + +@updateexpr = @preincexpr | @postincexpr | @predecexpr | @postdecexpr; + +@pattern = @varref | @arraypattern | @objectpattern; + +@comprehensionexpr = @arraycomprehensionexpr | @generatorexpr; + +@comprehensionblock = @forincomprehensionblock | @forofcomprehensionblock; + +@importspecifier = @namedimportspecifier | @importdefaultspecifier | @importnamespacespecifier; + +@exportspecifier = @namedexportspecifier | @exportdefaultspecifier | @exportnamespacespecifier; + +@typeassertion = @astypeassertion | @prefixtypeassertion; + +@classdefinition = @classdeclstmt | @classexpr; +@interfacedefinition = @interfacedeclaration | @interfacetypeexpr; +@classorinterface = @classdefinition | @interfacedefinition; + +@lexical_decl = @vardecl | @typedecl; +@lexical_access = @varaccess | @localtypeaccess | @localvartypeaccess | @localnamespaceaccess; +@lexical_ref = @lexical_decl | @lexical_access; + +// scopes +scopes (unique int id: @scope, + int kind: int ref); + +case @scope.kind of + 0 = @globalscope +| 1 = @functionscope +| 2 = @catchscope +| 3 = @modulescope +| 4 = @blockscope +| 5 = @forscope +| 6 = @forinscope // for-of scopes work the same as for-in scopes +| 7 = @comprehensionblockscope +| 8 = @classexprscope +| 9 = @namespacescope +| 10 = @classdeclscope +| 11 = @interfacescope +| 12 = @typealiasscope +| 13 = @mappedtypescope +| 14 = @enumscope +| 15 = @externalmodulescope +| 16 = @conditionaltypescope; + +scopenodes (unique int node: @ast_node ref, + int scope: @scope ref); + +scopenesting (unique int inner: @scope ref, + int outer: @scope ref); + +// functions +@function = @functiondeclstmt | @functionexpr | @arrowfunctionexpr; + +@parameterized = @function | @catchclause; +@type_parameterized = @function | @classorinterface | @typealiasdeclaration | @mappedtypeexpr | @infertypeexpr; + +isGenerator (int fun: @function ref); +hasRestParameter (int fun: @function ref); +isAsync (int fun: @function ref); + +// variables and lexically scoped type names +#keyset[scope, name] +variables (unique int id: @variable, + varchar(900) name: string ref, + int scope: @scope ref); + +#keyset[scope, name] +local_type_names (unique int id: @local_type_name, + varchar(900) name: string ref, + int scope: @scope ref); + +#keyset[scope, name] +local_namespace_names (unique int id: @local_namespace_name, + varchar(900) name: string ref, + int scope: @scope ref); + +isArgumentsObject (int id: @variable ref); + +@lexical_name = @variable | @local_type_name | @local_namespace_name; + +@bind_id = @varaccess | @localvartypeaccess; +bind (unique int id: @bind_id ref, + int decl: @variable ref); + +decl (unique int id: @vardecl ref, + int decl: @variable ref); + +@typebind_id = @localtypeaccess | @export_varaccess; +typebind (unique int id: @typebind_id ref, + int decl: @local_type_name ref); + +@typedecl_id = @typedecl | @vardecl; +typedecl (unique int id: @typedecl_id ref, + int decl: @local_type_name ref); + +namespacedecl (unique int id: @vardecl ref, + int decl: @local_namespace_name ref); + +@namespacebind_id = @localnamespaceaccess | @export_varaccess; +namespacebind (unique int id: @namespacebind_id ref, + int decl: @local_namespace_name ref); + + +// properties in object literals, property patterns in object patterns, and method declarations in classes +#keyset[parent, index] +properties (unique int id: @property, + int parent: @property_parent ref, + int index: int ref, + int kind: int ref, + varchar(900) tostring: string ref); + +case @property.kind of + 0 = @value_property +| 1 = @property_getter +| 2 = @property_setter +| 3 = @jsx_attribute +| 4 = @function_call_signature +| 5 = @constructor_call_signature +| 6 = @index_signature +| 7 = @enum_member +| 8 = @proper_field +| 9 = @parameter_field +; + +@property_parent = @objexpr | @objectpattern | @classdefinition | @jsxelement | @interfacedefinition | @enumdeclaration; +@property_accessor = @property_getter | @property_setter; +@call_signature = @function_call_signature | @constructor_call_signature; +@field = @proper_field | @parameter_field; +@field_or_vardeclarator = @field | @vardeclarator; + +isComputed (int id: @property ref); +isMethod (int id: @property ref); +isStatic (int id: @property ref); +isAbstractMember (int id: @property ref); +isConstEnum (int id: @enumdeclaration ref); +isAbstractClass (int id: @classdeclstmt ref); + +hasPublicKeyword (int id: @property ref); +hasPrivateKeyword (int id: @property ref); +hasProtectedKeyword (int id: @property ref); +hasReadonlyKeyword (int id: @property ref); +isOptionalMember (int id: @property ref); +hasDefiniteAssignmentAssertion (int id: @field_or_vardeclarator ref); + +#keyset[constructor, param_index] +parameter_fields( + unique int field: @parameter_field ref, + int constructor: @functionexpr ref, + int param_index: int ref +); + +// types +#keyset[parent, idx] +typeexprs ( + unique int id: @typeexpr, + int kind: int ref, + int parent: @typeexpr_parent ref, + int idx: int ref, + varchar(900) tostring: string ref +); + +case @typeexpr.kind of + 0 = @localtypeaccess +| 1 = @typedecl +| 2 = @keywordtypeexpr +| 3 = @stringliteraltypeexpr +| 4 = @numberliteraltypeexpr +| 5 = @booleanliteraltypeexpr +| 6 = @arraytypeexpr +| 7 = @uniontypeexpr +| 8 = @indexedaccesstypeexpr +| 9 = @intersectiontypeexpr +| 10 = @parenthesizedtypeexpr +| 11 = @tupletypeexpr +| 12 = @keyoftypeexpr +| 13 = @qualifiedtypeaccess +| 14 = @generictypeexpr +| 15 = @typelabel +| 16 = @typeoftypeexpr +| 17 = @localvartypeaccess +| 18 = @qualifiedvartypeaccess +| 19 = @thisvartypeaccess +| 20 = @istypeexpr +| 21 = @interfacetypeexpr +| 22 = @typeparameter +| 23 = @plainfunctiontypeexpr +| 24 = @constructortypeexpr +| 25 = @localnamespaceaccess +| 26 = @qualifiednamespaceaccess +| 27 = @mappedtypeexpr +| 28 = @conditionaltypeexpr +| 29 = @infertypeexpr +| 30 = @importtypeaccess +| 31 = @importnamespaceaccess +| 32 = @importvartypeaccess +| 33 = @optionaltypeexpr +| 34 = @resttypeexpr +| 35 = @bigintliteraltypeexpr +; + +@typeref = @typeaccess | @typedecl; +@typeidentifier = @typedecl | @localtypeaccess | @typelabel | @localvartypeaccess | @localnamespaceaccess; +@typeexpr_parent = @expr | @stmt | @property | @typeexpr; +@literaltypeexpr = @stringliteraltypeexpr | @numberliteraltypeexpr | @booleanliteraltypeexpr | @bigintliteraltypeexpr; +@typeaccess = @localtypeaccess | @qualifiedtypeaccess | @importtypeaccess; +@vartypeaccess = @localvartypeaccess | @qualifiedvartypeaccess | @thisvartypeaccess | @importvartypeaccess; +@namespaceaccess = @localnamespaceaccess | @qualifiednamespaceaccess | @importnamespaceaccess; +@importtypeexpr = @importtypeaccess | @importnamespaceaccess | @importvartypeaccess; + +@functiontypeexpr = @plainfunctiontypeexpr | @constructortypeexpr; + +// types +types ( + unique int id: @type, + int kind: int ref, + varchar(900) tostring: string ref +); + +#keyset[parent, idx] +type_child ( + int child: @type ref, + int parent: @type ref, + int idx: int ref +); + +case @type.kind of + 0 = @anytype +| 1 = @stringtype +| 2 = @numbertype +| 3 = @uniontype +| 4 = @truetype +| 5 = @falsetype +| 6 = @typereference +| 7 = @objecttype +| 8 = @canonicaltypevariabletype +| 9 = @typeoftype +| 10 = @voidtype +| 11 = @undefinedtype +| 12 = @nulltype +| 13 = @nevertype +| 14 = @plainsymboltype +| 15 = @uniquesymboltype +| 16 = @objectkeywordtype +| 17 = @intersectiontype +| 18 = @tupletype +| 19 = @lexicaltypevariabletype +| 20 = @thistype +| 21 = @numberliteraltype +| 22 = @stringliteraltype +| 23 = @unknowntype +| 24 = @biginttype +| 25 = @bigintliteraltype +; + +@booleanliteraltype = @truetype | @falsetype; +@symboltype = @plainsymboltype | @uniquesymboltype; +@unionorintersectiontype = @uniontype | @intersectiontype; +@typevariabletype = @canonicaltypevariabletype | @lexicaltypevariabletype; + +@typed_ast_node = @expr | @typeexpr | @function; +ast_node_type( + unique int node: @typed_ast_node ref, + int typ: @type ref); + +invoke_expr_signature( + unique int node: @invokeexpr ref, + int sig: @signature_type ref +); + +invoke_expr_overload_index( + unique int node: @invokeexpr ref, + int index: int ref +); + +symbols ( + unique int id: @symbol, + int kind: int ref, + varchar(900) name: string ref +); + +symbol_parent ( + unique int symbol: @symbol ref, + int parent: @symbol ref +); + +symbol_module ( + int symbol: @symbol ref, + varchar(900) moduleName: string ref +); + +symbol_global ( + int symbol: @symbol ref, + varchar(900) globalName: string ref +); + +case @symbol.kind of + 0 = @root_symbol +| 1 = @member_symbol +| 2 = @other_symbol +; + +@type_with_symbol = @typereference | @typevariabletype | @typeoftype | @uniquesymboltype; +@ast_node_with_symbol = @typedefinition | @namespacedefinition | @toplevel | @typeaccess | @namespaceaccess | @vardecl | @function | @invokeexpr; + +ast_node_symbol( + unique int node: @ast_node_with_symbol ref, + int symbol: @symbol ref); + +type_symbol( + unique int typ: @type_with_symbol ref, + int symbol: @symbol ref); + +#keyset[typ, name] +type_property( + int typ: @type ref, + varchar(900) name: string ref, + int propertyType: @type ref); + +@literaltype = @stringliteraltype | @numberliteraltype | @booleanliteraltype | @bigintliteraltype; +@type_with_literal_value = @stringliteraltype | @numberliteraltype | @bigintliteraltype; +type_literal_value( + unique int typ: @type_with_literal_value ref, + varchar(900) value: string ref); + +signature_types ( + unique int id: @signature_type, + int kind: int ref, + varchar(900) tostring: string ref, + int type_parameters: int ref, + int required_params: int ref +); + +case @signature_type.kind of + 0 = @function_signature_type +| 1 = @constructor_signature_type +; + +#keyset[typ, kind, index] +type_contains_signature ( + int typ: @type ref, + int kind: int ref, // constructor/call/index + int index: int ref, // ordering of overloaded signatures + int sig: @signature_type ref +); + +#keyset[parent, index] +signature_contains_type ( + int child: @type ref, + int parent: @signature_type ref, + int index: int ref +); + +#keyset[sig, index] +signature_parameter_name ( + int sig: @signature_type ref, + int index: int ref, + varchar(900) name: string ref +); + +number_index_type ( + unique int baseType: @type ref, + int propertyType: @type ref +); + +string_index_type ( + unique int baseType: @type ref, + int propertyType: @type ref +); + +base_type_names( + int typeName: @symbol ref, + int baseTypeName: @symbol ref +); + +self_types( + int typeName: @symbol ref, + int selfType: @typereference ref +); + +tuple_type_min_length( + unique int typ: @type ref, + int minLength: int ref +); + +tuple_type_rest( + unique int typ: @type ref +); + +// comments +comments (unique int id: @comment, + int kind: int ref, + int toplevel: @toplevel ref, + varchar(900) text: string ref, + varchar(900) tostring: string ref); + +case @comment.kind of + 0 = @slashslashcomment +| 1 = @slashstarcomment +| 2 = @doccomment +| 3 = @htmlcommentstart +| 4 = @htmlcommentend; + +@htmlcomment = @htmlcommentstart | @htmlcommentend; +@linecomment = @slashslashcomment | @htmlcomment; +@blockcomment = @slashstarcomment | @doccomment; + +// source lines +lines (unique int id: @line, + int toplevel: @toplevel ref, + varchar(900) text: string ref, + varchar(2) terminator: string ref); +indentation (int file: @file ref, + int lineno: int ref, + varchar(1) indentChar: string ref, + int indentDepth: int ref); + +// JavaScript parse errors +jsParseErrors (unique int id: @js_parse_error, + int toplevel: @toplevel ref, + varchar(900) message: string ref, + varchar(900) line: string ref); + +// regular expressions +#keyset[parent, idx] +regexpterm (unique int id: @regexpterm, + int kind: int ref, + int parent: @regexpparent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +@regexpparent = @regexpterm | @regexpliteral; + +case @regexpterm.kind of + 0 = @regexp_alt +| 1 = @regexp_seq +| 2 = @regexp_caret +| 3 = @regexp_dollar +| 4 = @regexp_wordboundary +| 5 = @regexp_nonwordboundary +| 6 = @regexp_positive_lookahead +| 7 = @regexp_negative_lookahead +| 8 = @regexp_star +| 9 = @regexp_plus +| 10 = @regexp_opt +| 11 = @regexp_range +| 12 = @regexp_dot +| 13 = @regexp_group +| 14 = @regexp_normal_char +| 15 = @regexp_hex_escape +| 16 = @regexp_unicode_escape +| 17 = @regexp_dec_escape +| 18 = @regexp_oct_escape +| 19 = @regexp_ctrl_escape +| 20 = @regexp_char_class_escape +| 21 = @regexp_id_escape +| 22 = @regexp_backref +| 23 = @regexp_char_class +| 24 = @regexp_char_range +| 25 = @regexp_positive_lookbehind +| 26 = @regexp_negative_lookbehind +| 27 = @regexp_unicode_property_escape; + +regexpParseErrors (unique int id: @regexp_parse_error, + int regexp: @regexpterm ref, + varchar(900) message: string ref); + +@regexp_quantifier = @regexp_star | @regexp_plus | @regexp_opt | @regexp_range; +@regexp_escape = @regexp_char_escape | @regexp_char_class_escape | @regexp_unicode_property_escape; +@regexp_char_escape = @regexp_hex_escape | @regexp_unicode_escape | @regexp_dec_escape | @regexp_oct_escape | @regexp_ctrl_escape | @regexp_id_escape; +@regexp_constant = @regexp_normal_char | @regexp_char_escape; +@regexp_lookahead = @regexp_positive_lookahead | @regexp_negative_lookahead; +@regexp_lookbehind = @regexp_positive_lookbehind | @regexp_negative_lookbehind; +@regexp_subpattern = @regexp_lookahead | @regexp_lookbehind; + +isGreedy (int id: @regexp_quantifier ref); +rangeQuantifierLowerBound (unique int id: @regexp_range ref, int lo: int ref); +rangeQuantifierUpperBound (unique int id: @regexp_range ref, int hi: int ref); +isCapture (unique int id: @regexp_group ref, int number: int ref); +isNamedCapture (unique int id: @regexp_group ref, string name: string ref); +isInverted (int id: @regexp_char_class ref); +regexpConstValue (unique int id: @regexp_constant ref, varchar(1) value: string ref); +charClassEscape (unique int id: @regexp_char_class_escape ref, varchar(1) value: string ref); +backref (unique int id: @regexp_backref ref, int value: int ref); +namedBackref (unique int id: @regexp_backref ref, string name: string ref); +unicodePropertyEscapeName (unique int id: @regexp_unicode_property_escape ref, string name: string ref); +unicodePropertyEscapeValue (unique int id: @regexp_unicode_property_escape ref, string value: string ref); + +// tokens +#keyset[toplevel, idx] +tokeninfo (unique int id: @token, + int kind: int ref, + int toplevel: @toplevel ref, + int idx: int ref, + varchar(900) value: string ref); + +case @token.kind of + 0 = @token_eof +| 1 = @token_null_literal +| 2 = @token_boolean_literal +| 3 = @token_numeric_literal +| 4 = @token_string_literal +| 5 = @token_regular_expression +| 6 = @token_identifier +| 7 = @token_keyword +| 8 = @token_punctuator; + +// associate comments with the token immediately following them (which may be EOF) +next_token (int comment: @comment ref, int token: @token ref); + +// JSON +#keyset[parent, idx] +json (unique int id: @json_value, + int kind: int ref, + int parent: @json_parent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +json_literals (varchar(900) value: string ref, + varchar(900) raw: string ref, + unique int expr: @json_value ref); + +json_properties (int obj: @json_object ref, + varchar(900) property: string ref, + int value: @json_value ref); + +json_errors (unique int id: @json_parse_error, + varchar(900) message: string ref); + +case @json_value.kind of + 0 = @json_null +| 1 = @json_boolean +| 2 = @json_number +| 3 = @json_string +| 4 = @json_array +| 5 = @json_object; + +@json_parent = @json_object | @json_array | @file; + +// locations +@ast_node = @toplevel | @stmt | @expr | @property | @typeexpr; + +@locatable = @file + | @ast_node + | @comment + | @line + | @js_parse_error | @regexp_parse_error | @json_parse_error + | @regexpterm + | @json_value + | @token + | @cfg_node + | @jsdoc | @jsdoc_type_expr | @jsdoc_tag + | @yaml_node | @yaml_error + | @xmllocatable; + +hasLocation (unique int locatable: @locatable ref, + int location: @location ref); + +// CFG +entry_cfg_node (unique int id: @entry_node, int container: @stmt_container ref); +exit_cfg_node (unique int id: @exit_node, int container: @stmt_container ref); +guard_node (unique int id: @guard_node, int kind: int ref, int test: @expr ref); +case @guard_node.kind of + 0 = @falsy_guard +| 1 = @truthy_guard; +@condition_guard = @falsy_guard | @truthy_guard; + +@synthetic_cfg_node = @entry_node | @exit_node | @guard_node; +@cfg_node = @synthetic_cfg_node | @exprparent; + +successor (int pred: @cfg_node ref, int succ: @cfg_node ref); + +// JSDoc comments +jsdoc (unique int id: @jsdoc, varchar(900) description: string ref, int comment: @comment ref); +#keyset[parent, idx] +jsdoc_tags (unique int id: @jsdoc_tag, varchar(900) title: string ref, + int parent: @jsdoc ref, int idx: int ref, varchar(900) tostring: string ref); +jsdoc_tag_descriptions (unique int tag: @jsdoc_tag ref, varchar(900) text: string ref); +jsdoc_tag_names (unique int tag: @jsdoc_tag ref, varchar(900) text: string ref); + +#keyset[parent, idx] +jsdoc_type_exprs (unique int id: @jsdoc_type_expr, + int kind: int ref, + int parent: @jsdoc_type_expr_parent ref, + int idx: int ref, + varchar(900) tostring: string ref); +case @jsdoc_type_expr.kind of + 0 = @jsdoc_any_type_expr +| 1 = @jsdoc_null_type_expr +| 2 = @jsdoc_undefined_type_expr +| 3 = @jsdoc_unknown_type_expr +| 4 = @jsdoc_void_type_expr +| 5 = @jsdoc_named_type_expr +| 6 = @jsdoc_applied_type_expr +| 7 = @jsdoc_nullable_type_expr +| 8 = @jsdoc_non_nullable_type_expr +| 9 = @jsdoc_record_type_expr +| 10 = @jsdoc_array_type_expr +| 11 = @jsdoc_union_type_expr +| 12 = @jsdoc_function_type_expr +| 13 = @jsdoc_optional_type_expr +| 14 = @jsdoc_rest_type_expr +; + +#keyset[id, idx] +jsdoc_record_field_name (int id: @jsdoc_record_type_expr ref, int idx: int ref, varchar(900) name: string ref); +jsdoc_prefix_qualifier (int id: @jsdoc_type_expr ref); +jsdoc_has_new_parameter (int fn: @jsdoc_function_type_expr ref); + +@jsdoc_type_expr_parent = @jsdoc_type_expr | @jsdoc_tag; + +jsdoc_errors (unique int id: @jsdoc_error, int tag: @jsdoc_tag ref, varchar(900) message: string ref, varchar(900) tostring: string ref); + +// YAML +#keyset[parent, idx] +yaml (unique int id: @yaml_node, + int kind: int ref, + int parent: @yaml_node_parent ref, + int idx: int ref, + varchar(900) tag: string ref, + varchar(900) tostring: string ref); + +case @yaml_node.kind of + 0 = @yaml_scalar_node +| 1 = @yaml_mapping_node +| 2 = @yaml_sequence_node +| 3 = @yaml_alias_node +; + +@yaml_collection_node = @yaml_mapping_node | @yaml_sequence_node; + +@yaml_node_parent = @yaml_collection_node | @file; + +yaml_anchors (unique int node: @yaml_node ref, + varchar(900) anchor: string ref); + +yaml_aliases (unique int alias: @yaml_alias_node ref, + varchar(900) target: string ref); + +yaml_scalars (unique int scalar: @yaml_scalar_node ref, + int style: int ref, + varchar(900) value: string ref); + +yaml_errors (unique int id: @yaml_error, + varchar(900) message: string ref); + +/* XML Files */ + +xmlEncoding( + unique int id: @file ref, + varchar(900) encoding: string ref +); + +xmlDTDs( + unique int id: @xmldtd, + varchar(900) root: string ref, + varchar(900) publicId: string ref, + varchar(900) systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + varchar(900) name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + varchar(900) name: string ref, + varchar(3600) value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + varchar(900) prefixName: string ref, + varchar(900) URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + varchar(3600) text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + varchar(3600) text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +@dataflownode = @expr | @functiondeclstmt | @classdeclstmt | @namespacedeclaration | @enumdeclaration | @property; + +@optionalchainable = @callexpr | @propaccess; + +isOptionalChaining(int id: @optionalchainable ref); + +/* Last updated 2018/10/23. */ diff --git a/javascript/upgrades/7eead88269b20e9d61f61cd2291affb737dc831e/semmlecode.javascript.dbscheme b/javascript/upgrades/7eead88269b20e9d61f61cd2291affb737dc831e/semmlecode.javascript.dbscheme new file mode 100644 index 00000000000..eee08932d52 --- /dev/null +++ b/javascript/upgrades/7eead88269b20e9d61f61cd2291affb737dc831e/semmlecode.javascript.dbscheme @@ -0,0 +1,1090 @@ +/*** Standard fragments ***/ + +/** Files and folders **/ + +@location = @location_default; + +locations_default(unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref + ); + +@sourceline = @locatable; + +numlines(int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref + ); + + +/* + fromSource(0) = unknown, + fromSource(1) = from source, + fromSource(2) = from library +*/ +files(unique int id: @file, + varchar(900) name: string ref, + varchar(900) simple: string ref, + varchar(900) ext: string ref, + int fromSource: int ref); + +folders(unique int id: @folder, + varchar(900) name: string ref, + varchar(900) simple: string ref); + + +@container = @folder | @file ; + + +containerparent(int parent: @container ref, + unique int child: @container ref); + +/** Duplicate code **/ + +duplicateCode( + unique int id : @duplication, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +similarCode( + unique int id : @similarity, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +@duplication_or_similarity = @duplication | @similarity; + +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref); + +/** External data **/ + +externalData( + int id : @externalDataElement, + varchar(900) path : string ref, + int column: int ref, + varchar(900) value : string ref +); + +snapshotDate(unique date snapshotDate : date ref); + +sourceLocationPrefix(varchar(900) prefix : string ref); + +/** Version control data **/ + +svnentries( + int id : @svnentry, + varchar(500) revision : string ref, + varchar(500) author : string ref, + date revisionDate : date ref, + int changeSize : int ref +); + +svnaffectedfiles( + int id : @svnentry ref, + int file : @file ref, + varchar(500) action : string ref +); + +svnentrymsg( + int id : @svnentry ref, + varchar(500) message : string ref +); + +svnchurn( + int commit : @svnentry ref, + int file : @file ref, + int addedLines : int ref, + int deletedLines : int ref +); + + +/*** JavaScript-specific part ***/ + +filetype( + int file: @file ref, + string filetype: string ref +) + +// top-level code fragments +toplevels (unique int id: @toplevel, + int kind: int ref); + +isExterns (int toplevel: @toplevel ref); + +case @toplevel.kind of + 0 = @script +| 1 = @inline_script +| 2 = @event_handler +| 3 = @javascript_url; + +isModule (int tl: @toplevel ref); +isNodejs (int tl: @toplevel ref); +isES2015Module (int tl: @toplevel ref); +isClosureModule (int tl: @toplevel ref); + +// statements +#keyset[parent, idx] +stmts (unique int id: @stmt, + int kind: int ref, + int parent: @stmtparent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +stmtContainers (unique int stmt: @stmt ref, + int container: @stmt_container ref); + +jumpTargets (unique int jump: @stmt ref, + int target: @stmt ref); + +@stmtparent = @stmt | @toplevel | @functionexpr | @arrowfunctionexpr; +@stmt_container = @toplevel | @function | @namespacedeclaration | @externalmoduledeclaration | @globalaugmentationdeclaration; + +case @stmt.kind of + 0 = @emptystmt +| 1 = @blockstmt +| 2 = @exprstmt +| 3 = @ifstmt +| 4 = @labeledstmt +| 5 = @breakstmt +| 6 = @continuestmt +| 7 = @withstmt +| 8 = @switchstmt +| 9 = @returnstmt +| 10 = @throwstmt +| 11 = @trystmt +| 12 = @whilestmt +| 13 = @dowhilestmt +| 14 = @forstmt +| 15 = @forinstmt +| 16 = @debuggerstmt +| 17 = @functiondeclstmt +| 18 = @vardeclstmt +| 19 = @case +| 20 = @catchclause +| 21 = @forofstmt +| 22 = @constdeclstmt +| 23 = @letstmt +| 24 = @legacy_letstmt +| 25 = @foreachstmt +| 26 = @classdeclstmt +| 27 = @importdeclaration +| 28 = @exportalldeclaration +| 29 = @exportdefaultdeclaration +| 30 = @exportnameddeclaration +| 31 = @namespacedeclaration +| 32 = @importequalsdeclaration +| 33 = @exportassigndeclaration +| 34 = @interfacedeclaration +| 35 = @typealiasdeclaration +| 36 = @enumdeclaration +| 37 = @externalmoduledeclaration +| 38 = @exportasnamespacedeclaration +| 39 = @globalaugmentationdeclaration +; + +@declstmt = @vardeclstmt | @constdeclstmt | @letstmt | @legacy_letstmt; + +@exportdeclaration = @exportalldeclaration | @exportdefaultdeclaration | @exportnameddeclaration; + +@namespacedefinition = @namespacedeclaration | @enumdeclaration; +@typedefinition = @classdefinition | @interfacedeclaration | @enumdeclaration | @typealiasdeclaration | @enum_member; + +isInstantiated(unique int decl: @namespacedeclaration ref); + +@declarablestmt = @declstmt | @namespacedeclaration | @classdeclstmt | @functiondeclstmt | @enumdeclaration | @externalmoduledeclaration | @globalaugmentationdeclaration; +hasDeclareKeyword(unique int stmt: @declarablestmt ref); + +isForAwaitOf(unique int forof: @forofstmt ref); + +// expressions +#keyset[parent, idx] +exprs (unique int id: @expr, + int kind: int ref, + int parent: @exprparent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +literals (varchar(900) value: string ref, + varchar(900) raw: string ref, + unique int expr: @exprortype ref); + +enclosingStmt (unique int expr: @exprortype ref, + int stmt: @stmt ref); + +exprContainers (unique int expr: @exprortype ref, + int container: @stmt_container ref); + +arraySize (unique int ae: @arraylike ref, + int sz: int ref); + +isDelegating (int yield: @yieldexpr ref); + +@exprorstmt = @expr | @stmt; +@exprortype = @expr | @typeexpr; +@exprparent = @exprorstmt | @property | @functiontypeexpr; +@arraylike = @arrayexpr | @arraypattern; + +case @expr.kind of + 0 = @label +| 1 = @nullliteral +| 2 = @booleanliteral +| 3 = @numberliteral +| 4 = @stringliteral +| 5 = @regexpliteral +| 6 = @thisexpr +| 7 = @arrayexpr +| 8 = @objexpr +| 9 = @functionexpr +| 10 = @seqexpr +| 11 = @conditionalexpr +| 12 = @newexpr +| 13 = @callexpr +| 14 = @dotexpr +| 15 = @indexexpr +| 16 = @negexpr +| 17 = @plusexpr +| 18 = @lognotexpr +| 19 = @bitnotexpr +| 20 = @typeofexpr +| 21 = @voidexpr +| 22 = @deleteexpr +| 23 = @eqexpr +| 24 = @neqexpr +| 25 = @eqqexpr +| 26 = @neqqexpr +| 27 = @ltexpr +| 28 = @leexpr +| 29 = @gtexpr +| 30 = @geexpr +| 31 = @lshiftexpr +| 32 = @rshiftexpr +| 33 = @urshiftexpr +| 34 = @addexpr +| 35 = @subexpr +| 36 = @mulexpr +| 37 = @divexpr +| 38 = @modexpr +| 39 = @bitorexpr +| 40 = @xorexpr +| 41 = @bitandexpr +| 42 = @inexpr +| 43 = @instanceofexpr +| 44 = @logandexpr +| 45 = @logorexpr +| 47 = @assignexpr +| 48 = @assignaddexpr +| 49 = @assignsubexpr +| 50 = @assignmulexpr +| 51 = @assigndivexpr +| 52 = @assignmodexpr +| 53 = @assignlshiftexpr +| 54 = @assignrshiftexpr +| 55 = @assignurshiftexpr +| 56 = @assignorexpr +| 57 = @assignxorexpr +| 58 = @assignandexpr +| 59 = @preincexpr +| 60 = @postincexpr +| 61 = @predecexpr +| 62 = @postdecexpr +| 63 = @parexpr +| 64 = @vardeclarator +| 65 = @arrowfunctionexpr +| 66 = @spreadelement +| 67 = @arraypattern +| 68 = @objectpattern +| 69 = @yieldexpr +| 70 = @taggedtemplateexpr +| 71 = @templateliteral +| 72 = @templateelement +| 73 = @arraycomprehensionexpr +| 74 = @generatorexpr +| 75 = @forincomprehensionblock +| 76 = @forofcomprehensionblock +| 77 = @legacy_letexpr +| 78 = @vardecl +| 79 = @proper_varaccess +| 80 = @classexpr +| 81 = @superexpr +| 82 = @newtargetexpr +| 83 = @namedimportspecifier +| 84 = @importdefaultspecifier +| 85 = @importnamespacespecifier +| 86 = @namedexportspecifier +| 87 = @expexpr +| 88 = @assignexpexpr +| 89 = @jsxelement +| 90 = @jsxqualifiedname +| 91 = @jsxemptyexpr +| 92 = @awaitexpr +| 93 = @functionsentexpr +| 94 = @decorator +| 95 = @exportdefaultspecifier +| 96 = @exportnamespacespecifier +| 97 = @bindexpr +| 98 = @externalmodulereference +| 99 = @dynamicimport +| 100 = @expressionwithtypearguments +| 101 = @prefixtypeassertion +| 102 = @astypeassertion +| 103 = @export_varaccess +| 104 = @decorator_list +| 105 = @non_null_assertion +| 106 = @bigintliteral +| 107 = @nullishcoalescingexpr +; + +@varaccess = @proper_varaccess | @export_varaccess; +@varref = @vardecl | @varaccess; + +@identifier = @label | @varref | @typeidentifier; + +@literal = @nullliteral | @booleanliteral | @numberliteral | @stringliteral | @regexpliteral | @bigintliteral; + +@propaccess = @dotexpr | @indexexpr; + +@invokeexpr = @newexpr | @callexpr; + +@unaryexpr = @negexpr | @plusexpr | @lognotexpr | @bitnotexpr | @typeofexpr | @voidexpr | @deleteexpr | @spreadelement; + +@equalitytest = @eqexpr | @neqexpr | @eqqexpr | @neqqexpr; + +@comparison = @equalitytest | @ltexpr | @leexpr | @gtexpr | @geexpr; + +@binaryexpr = @comparison | @lshiftexpr | @rshiftexpr | @urshiftexpr | @addexpr | @subexpr | @mulexpr | @divexpr | @modexpr | @expexpr | @bitorexpr | @xorexpr | @bitandexpr | @inexpr | @instanceofexpr | @logandexpr | @logorexpr | @nullishcoalescingexpr; + +@assignment = @assignexpr | @assignaddexpr | @assignsubexpr | @assignmulexpr | @assigndivexpr | @assignmodexpr | @assignexpexpr | @assignlshiftexpr | @assignrshiftexpr | @assignurshiftexpr | @assignorexpr | @assignxorexpr | @assignandexpr; + +@updateexpr = @preincexpr | @postincexpr | @predecexpr | @postdecexpr; + +@pattern = @varref | @arraypattern | @objectpattern; + +@comprehensionexpr = @arraycomprehensionexpr | @generatorexpr; + +@comprehensionblock = @forincomprehensionblock | @forofcomprehensionblock; + +@importspecifier = @namedimportspecifier | @importdefaultspecifier | @importnamespacespecifier; + +@exportspecifier = @namedexportspecifier | @exportdefaultspecifier | @exportnamespacespecifier; + +@typeassertion = @astypeassertion | @prefixtypeassertion; + +@classdefinition = @classdeclstmt | @classexpr; +@interfacedefinition = @interfacedeclaration | @interfacetypeexpr; +@classorinterface = @classdefinition | @interfacedefinition; + +@lexical_decl = @vardecl | @typedecl; +@lexical_access = @varaccess | @localtypeaccess | @localvartypeaccess | @localnamespaceaccess; +@lexical_ref = @lexical_decl | @lexical_access; + +// scopes +scopes (unique int id: @scope, + int kind: int ref); + +case @scope.kind of + 0 = @globalscope +| 1 = @functionscope +| 2 = @catchscope +| 3 = @modulescope +| 4 = @blockscope +| 5 = @forscope +| 6 = @forinscope // for-of scopes work the same as for-in scopes +| 7 = @comprehensionblockscope +| 8 = @classexprscope +| 9 = @namespacescope +| 10 = @classdeclscope +| 11 = @interfacescope +| 12 = @typealiasscope +| 13 = @mappedtypescope +| 14 = @enumscope +| 15 = @externalmodulescope +| 16 = @conditionaltypescope; + +scopenodes (unique int node: @ast_node ref, + int scope: @scope ref); + +scopenesting (unique int inner: @scope ref, + int outer: @scope ref); + +// functions +@function = @functiondeclstmt | @functionexpr | @arrowfunctionexpr; + +@parameterized = @function | @catchclause; +@type_parameterized = @function | @classorinterface | @typealiasdeclaration | @mappedtypeexpr | @infertypeexpr; + +isGenerator (int fun: @function ref); +hasRestParameter (int fun: @function ref); +isAsync (int fun: @function ref); + +// variables and lexically scoped type names +#keyset[scope, name] +variables (unique int id: @variable, + varchar(900) name: string ref, + int scope: @scope ref); + +#keyset[scope, name] +local_type_names (unique int id: @local_type_name, + varchar(900) name: string ref, + int scope: @scope ref); + +#keyset[scope, name] +local_namespace_names (unique int id: @local_namespace_name, + varchar(900) name: string ref, + int scope: @scope ref); + +isArgumentsObject (int id: @variable ref); + +@lexical_name = @variable | @local_type_name | @local_namespace_name; + +@bind_id = @varaccess | @localvartypeaccess; +bind (unique int id: @bind_id ref, + int decl: @variable ref); + +decl (unique int id: @vardecl ref, + int decl: @variable ref); + +@typebind_id = @localtypeaccess | @export_varaccess; +typebind (unique int id: @typebind_id ref, + int decl: @local_type_name ref); + +@typedecl_id = @typedecl | @vardecl; +typedecl (unique int id: @typedecl_id ref, + int decl: @local_type_name ref); + +namespacedecl (unique int id: @vardecl ref, + int decl: @local_namespace_name ref); + +@namespacebind_id = @localnamespaceaccess | @export_varaccess; +namespacebind (unique int id: @namespacebind_id ref, + int decl: @local_namespace_name ref); + + +// properties in object literals, property patterns in object patterns, and method declarations in classes +#keyset[parent, index] +properties (unique int id: @property, + int parent: @property_parent ref, + int index: int ref, + int kind: int ref, + varchar(900) tostring: string ref); + +case @property.kind of + 0 = @value_property +| 1 = @property_getter +| 2 = @property_setter +| 3 = @jsx_attribute +| 4 = @function_call_signature +| 5 = @constructor_call_signature +| 6 = @index_signature +| 7 = @enum_member +| 8 = @proper_field +| 9 = @parameter_field +; + +@property_parent = @objexpr | @objectpattern | @classdefinition | @jsxelement | @interfacedefinition | @enumdeclaration; +@property_accessor = @property_getter | @property_setter; +@call_signature = @function_call_signature | @constructor_call_signature; +@field = @proper_field | @parameter_field; +@field_or_vardeclarator = @field | @vardeclarator; + +isComputed (int id: @property ref); +isMethod (int id: @property ref); +isStatic (int id: @property ref); +isAbstractMember (int id: @property ref); +isConstEnum (int id: @enumdeclaration ref); +isAbstractClass (int id: @classdeclstmt ref); + +hasPublicKeyword (int id: @property ref); +hasPrivateKeyword (int id: @property ref); +hasProtectedKeyword (int id: @property ref); +hasReadonlyKeyword (int id: @property ref); +isOptionalMember (int id: @property ref); +hasDefiniteAssignmentAssertion (int id: @field_or_vardeclarator ref); + +#keyset[constructor, param_index] +parameter_fields( + unique int field: @parameter_field ref, + int constructor: @functionexpr ref, + int param_index: int ref +); + +// types +#keyset[parent, idx] +typeexprs ( + unique int id: @typeexpr, + int kind: int ref, + int parent: @typeexpr_parent ref, + int idx: int ref, + varchar(900) tostring: string ref +); + +case @typeexpr.kind of + 0 = @localtypeaccess +| 1 = @typedecl +| 2 = @keywordtypeexpr +| 3 = @stringliteraltypeexpr +| 4 = @numberliteraltypeexpr +| 5 = @booleanliteraltypeexpr +| 6 = @arraytypeexpr +| 7 = @uniontypeexpr +| 8 = @indexedaccesstypeexpr +| 9 = @intersectiontypeexpr +| 10 = @parenthesizedtypeexpr +| 11 = @tupletypeexpr +| 12 = @keyoftypeexpr +| 13 = @qualifiedtypeaccess +| 14 = @generictypeexpr +| 15 = @typelabel +| 16 = @typeoftypeexpr +| 17 = @localvartypeaccess +| 18 = @qualifiedvartypeaccess +| 19 = @thisvartypeaccess +| 20 = @istypeexpr +| 21 = @interfacetypeexpr +| 22 = @typeparameter +| 23 = @plainfunctiontypeexpr +| 24 = @constructortypeexpr +| 25 = @localnamespaceaccess +| 26 = @qualifiednamespaceaccess +| 27 = @mappedtypeexpr +| 28 = @conditionaltypeexpr +| 29 = @infertypeexpr +| 30 = @importtypeaccess +| 31 = @importnamespaceaccess +| 32 = @importvartypeaccess +| 33 = @optionaltypeexpr +| 34 = @resttypeexpr +| 35 = @bigintliteraltypeexpr +; + +@typeref = @typeaccess | @typedecl; +@typeidentifier = @typedecl | @localtypeaccess | @typelabel | @localvartypeaccess | @localnamespaceaccess; +@typeexpr_parent = @expr | @stmt | @property | @typeexpr; +@literaltypeexpr = @stringliteraltypeexpr | @numberliteraltypeexpr | @booleanliteraltypeexpr | @bigintliteraltypeexpr; +@typeaccess = @localtypeaccess | @qualifiedtypeaccess | @importtypeaccess; +@vartypeaccess = @localvartypeaccess | @qualifiedvartypeaccess | @thisvartypeaccess | @importvartypeaccess; +@namespaceaccess = @localnamespaceaccess | @qualifiednamespaceaccess | @importnamespaceaccess; +@importtypeexpr = @importtypeaccess | @importnamespaceaccess | @importvartypeaccess; + +@functiontypeexpr = @plainfunctiontypeexpr | @constructortypeexpr; + +// types +types ( + unique int id: @type, + int kind: int ref, + varchar(900) tostring: string ref +); + +#keyset[parent, idx] +type_child ( + int child: @type ref, + int parent: @type ref, + int idx: int ref +); + +case @type.kind of + 0 = @anytype +| 1 = @stringtype +| 2 = @numbertype +| 3 = @uniontype +| 4 = @truetype +| 5 = @falsetype +| 6 = @typereference +| 7 = @objecttype +| 8 = @canonicaltypevariabletype +| 9 = @typeoftype +| 10 = @voidtype +| 11 = @undefinedtype +| 12 = @nulltype +| 13 = @nevertype +| 14 = @plainsymboltype +| 15 = @uniquesymboltype +| 16 = @objectkeywordtype +| 17 = @intersectiontype +| 18 = @tupletype +| 19 = @lexicaltypevariabletype +| 20 = @thistype +| 21 = @numberliteraltype +| 22 = @stringliteraltype +| 23 = @unknowntype +| 24 = @biginttype +| 25 = @bigintliteraltype +; + +@booleanliteraltype = @truetype | @falsetype; +@symboltype = @plainsymboltype | @uniquesymboltype; +@unionorintersectiontype = @uniontype | @intersectiontype; +@typevariabletype = @canonicaltypevariabletype | @lexicaltypevariabletype; + +@typed_ast_node = @expr | @typeexpr | @function; +ast_node_type( + unique int node: @typed_ast_node ref, + int typ: @type ref); + +invoke_expr_signature( + unique int node: @invokeexpr ref, + int sig: @signature_type ref +); + +invoke_expr_overload_index( + unique int node: @invokeexpr ref, + int index: int ref +); + +symbols ( + unique int id: @symbol, + int kind: int ref, + varchar(900) name: string ref +); + +symbol_parent ( + unique int symbol: @symbol ref, + int parent: @symbol ref +); + +symbol_module ( + int symbol: @symbol ref, + varchar(900) moduleName: string ref +); + +symbol_global ( + int symbol: @symbol ref, + varchar(900) globalName: string ref +); + +case @symbol.kind of + 0 = @root_symbol +| 1 = @member_symbol +| 2 = @other_symbol +; + +@type_with_symbol = @typereference | @typevariabletype | @typeoftype | @uniquesymboltype; +@ast_node_with_symbol = @typedefinition | @namespacedefinition | @toplevel | @typeaccess | @namespaceaccess | @vardecl | @function | @invokeexpr; + +ast_node_symbol( + unique int node: @ast_node_with_symbol ref, + int symbol: @symbol ref); + +type_symbol( + unique int typ: @type_with_symbol ref, + int symbol: @symbol ref); + +#keyset[typ, name] +type_property( + int typ: @type ref, + varchar(900) name: string ref, + int propertyType: @type ref); + +@literaltype = @stringliteraltype | @numberliteraltype | @booleanliteraltype | @bigintliteraltype; +@type_with_literal_value = @stringliteraltype | @numberliteraltype | @bigintliteraltype; +type_literal_value( + unique int typ: @type_with_literal_value ref, + varchar(900) value: string ref); + +signature_types ( + unique int id: @signature_type, + int kind: int ref, + varchar(900) tostring: string ref, + int type_parameters: int ref, + int required_params: int ref +); + +case @signature_type.kind of + 0 = @function_signature_type +| 1 = @constructor_signature_type +; + +#keyset[typ, kind, index] +type_contains_signature ( + int typ: @type ref, + int kind: int ref, // constructor/call/index + int index: int ref, // ordering of overloaded signatures + int sig: @signature_type ref +); + +#keyset[parent, index] +signature_contains_type ( + int child: @type ref, + int parent: @signature_type ref, + int index: int ref +); + +#keyset[sig, index] +signature_parameter_name ( + int sig: @signature_type ref, + int index: int ref, + varchar(900) name: string ref +); + +number_index_type ( + unique int baseType: @type ref, + int propertyType: @type ref +); + +string_index_type ( + unique int baseType: @type ref, + int propertyType: @type ref +); + +base_type_names( + int typeName: @symbol ref, + int baseTypeName: @symbol ref +); + +self_types( + int typeName: @symbol ref, + int selfType: @typereference ref +); + +tuple_type_min_length( + unique int typ: @type ref, + int minLength: int ref +); + +tuple_type_rest( + unique int typ: @type ref +); + +// comments +comments (unique int id: @comment, + int kind: int ref, + int toplevel: @toplevel ref, + varchar(900) text: string ref, + varchar(900) tostring: string ref); + +case @comment.kind of + 0 = @slashslashcomment +| 1 = @slashstarcomment +| 2 = @doccomment +| 3 = @htmlcommentstart +| 4 = @htmlcommentend; + +@htmlcomment = @htmlcommentstart | @htmlcommentend; +@linecomment = @slashslashcomment | @htmlcomment; +@blockcomment = @slashstarcomment | @doccomment; + +// source lines +lines (unique int id: @line, + int toplevel: @toplevel ref, + varchar(900) text: string ref, + varchar(2) terminator: string ref); +indentation (int file: @file ref, + int lineno: int ref, + varchar(1) indentChar: string ref, + int indentDepth: int ref); + +// JavaScript parse errors +jsParseErrors (unique int id: @js_parse_error, + int toplevel: @toplevel ref, + varchar(900) message: string ref, + varchar(900) line: string ref); + +// regular expressions +#keyset[parent, idx] +regexpterm (unique int id: @regexpterm, + int kind: int ref, + int parent: @regexpparent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +@regexpparent = @regexpterm | @regexpliteral; + +case @regexpterm.kind of + 0 = @regexp_alt +| 1 = @regexp_seq +| 2 = @regexp_caret +| 3 = @regexp_dollar +| 4 = @regexp_wordboundary +| 5 = @regexp_nonwordboundary +| 6 = @regexp_positive_lookahead +| 7 = @regexp_negative_lookahead +| 8 = @regexp_star +| 9 = @regexp_plus +| 10 = @regexp_opt +| 11 = @regexp_range +| 12 = @regexp_dot +| 13 = @regexp_group +| 14 = @regexp_normal_char +| 15 = @regexp_hex_escape +| 16 = @regexp_unicode_escape +| 17 = @regexp_dec_escape +| 18 = @regexp_oct_escape +| 19 = @regexp_ctrl_escape +| 20 = @regexp_char_class_escape +| 21 = @regexp_id_escape +| 22 = @regexp_backref +| 23 = @regexp_char_class +| 24 = @regexp_char_range +| 25 = @regexp_positive_lookbehind +| 26 = @regexp_negative_lookbehind +| 27 = @regexp_unicode_property_escape; + +regexpParseErrors (unique int id: @regexp_parse_error, + int regexp: @regexpterm ref, + varchar(900) message: string ref); + +@regexp_quantifier = @regexp_star | @regexp_plus | @regexp_opt | @regexp_range; +@regexp_escape = @regexp_char_escape | @regexp_char_class_escape | @regexp_unicode_property_escape; +@regexp_char_escape = @regexp_hex_escape | @regexp_unicode_escape | @regexp_dec_escape | @regexp_oct_escape | @regexp_ctrl_escape | @regexp_id_escape; +@regexp_constant = @regexp_normal_char | @regexp_char_escape; +@regexp_lookahead = @regexp_positive_lookahead | @regexp_negative_lookahead; +@regexp_lookbehind = @regexp_positive_lookbehind | @regexp_negative_lookbehind; +@regexp_subpattern = @regexp_lookahead | @regexp_lookbehind; + +isGreedy (int id: @regexp_quantifier ref); +rangeQuantifierLowerBound (unique int id: @regexp_range ref, int lo: int ref); +rangeQuantifierUpperBound (unique int id: @regexp_range ref, int hi: int ref); +isCapture (unique int id: @regexp_group ref, int number: int ref); +isNamedCapture (unique int id: @regexp_group ref, string name: string ref); +isInverted (int id: @regexp_char_class ref); +regexpConstValue (unique int id: @regexp_constant ref, varchar(1) value: string ref); +charClassEscape (unique int id: @regexp_char_class_escape ref, varchar(1) value: string ref); +backref (unique int id: @regexp_backref ref, int value: int ref); +namedBackref (unique int id: @regexp_backref ref, string name: string ref); +unicodePropertyEscapeName (unique int id: @regexp_unicode_property_escape ref, string name: string ref); +unicodePropertyEscapeValue (unique int id: @regexp_unicode_property_escape ref, string value: string ref); + +// tokens +#keyset[toplevel, idx] +tokeninfo (unique int id: @token, + int kind: int ref, + int toplevel: @toplevel ref, + int idx: int ref, + varchar(900) value: string ref); + +case @token.kind of + 0 = @token_eof +| 1 = @token_null_literal +| 2 = @token_boolean_literal +| 3 = @token_numeric_literal +| 4 = @token_string_literal +| 5 = @token_regular_expression +| 6 = @token_identifier +| 7 = @token_keyword +| 8 = @token_punctuator; + +// associate comments with the token immediately following them (which may be EOF) +next_token (int comment: @comment ref, int token: @token ref); + +// JSON +#keyset[parent, idx] +json (unique int id: @json_value, + int kind: int ref, + int parent: @json_parent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +json_literals (varchar(900) value: string ref, + varchar(900) raw: string ref, + unique int expr: @json_value ref); + +json_properties (int obj: @json_object ref, + varchar(900) property: string ref, + int value: @json_value ref); + +json_errors (unique int id: @json_parse_error, + varchar(900) message: string ref); + +case @json_value.kind of + 0 = @json_null +| 1 = @json_boolean +| 2 = @json_number +| 3 = @json_string +| 4 = @json_array +| 5 = @json_object; + +@json_parent = @json_object | @json_array | @file; + +// locations +@ast_node = @toplevel | @stmt | @expr | @property | @typeexpr; + +@locatable = @file + | @ast_node + | @comment + | @line + | @js_parse_error | @regexp_parse_error | @json_parse_error + | @regexpterm + | @json_value + | @token + | @cfg_node + | @jsdoc | @jsdoc_type_expr | @jsdoc_tag + | @yaml_node | @yaml_error + | @xmllocatable; + +hasLocation (unique int locatable: @locatable ref, + int location: @location ref); + +// CFG +entry_cfg_node (unique int id: @entry_node, int container: @stmt_container ref); +exit_cfg_node (unique int id: @exit_node, int container: @stmt_container ref); +guard_node (unique int id: @guard_node, int kind: int ref, int test: @expr ref); +case @guard_node.kind of + 0 = @falsy_guard +| 1 = @truthy_guard; +@condition_guard = @falsy_guard | @truthy_guard; + +@synthetic_cfg_node = @entry_node | @exit_node | @guard_node; +@cfg_node = @synthetic_cfg_node | @exprparent; + +successor (int pred: @cfg_node ref, int succ: @cfg_node ref); + +// JSDoc comments +jsdoc (unique int id: @jsdoc, varchar(900) description: string ref, int comment: @comment ref); +#keyset[parent, idx] +jsdoc_tags (unique int id: @jsdoc_tag, varchar(900) title: string ref, + int parent: @jsdoc ref, int idx: int ref, varchar(900) tostring: string ref); +jsdoc_tag_descriptions (unique int tag: @jsdoc_tag ref, varchar(900) text: string ref); +jsdoc_tag_names (unique int tag: @jsdoc_tag ref, varchar(900) text: string ref); + +#keyset[parent, idx] +jsdoc_type_exprs (unique int id: @jsdoc_type_expr, + int kind: int ref, + int parent: @jsdoc_type_expr_parent ref, + int idx: int ref, + varchar(900) tostring: string ref); +case @jsdoc_type_expr.kind of + 0 = @jsdoc_any_type_expr +| 1 = @jsdoc_null_type_expr +| 2 = @jsdoc_undefined_type_expr +| 3 = @jsdoc_unknown_type_expr +| 4 = @jsdoc_void_type_expr +| 5 = @jsdoc_named_type_expr +| 6 = @jsdoc_applied_type_expr +| 7 = @jsdoc_nullable_type_expr +| 8 = @jsdoc_non_nullable_type_expr +| 9 = @jsdoc_record_type_expr +| 10 = @jsdoc_array_type_expr +| 11 = @jsdoc_union_type_expr +| 12 = @jsdoc_function_type_expr +| 13 = @jsdoc_optional_type_expr +| 14 = @jsdoc_rest_type_expr +; + +#keyset[id, idx] +jsdoc_record_field_name (int id: @jsdoc_record_type_expr ref, int idx: int ref, varchar(900) name: string ref); +jsdoc_prefix_qualifier (int id: @jsdoc_type_expr ref); +jsdoc_has_new_parameter (int fn: @jsdoc_function_type_expr ref); + +@jsdoc_type_expr_parent = @jsdoc_type_expr | @jsdoc_tag; + +jsdoc_errors (unique int id: @jsdoc_error, int tag: @jsdoc_tag ref, varchar(900) message: string ref, varchar(900) tostring: string ref); + +// YAML +#keyset[parent, idx] +yaml (unique int id: @yaml_node, + int kind: int ref, + int parent: @yaml_node_parent ref, + int idx: int ref, + varchar(900) tag: string ref, + varchar(900) tostring: string ref); + +case @yaml_node.kind of + 0 = @yaml_scalar_node +| 1 = @yaml_mapping_node +| 2 = @yaml_sequence_node +| 3 = @yaml_alias_node +; + +@yaml_collection_node = @yaml_mapping_node | @yaml_sequence_node; + +@yaml_node_parent = @yaml_collection_node | @file; + +yaml_anchors (unique int node: @yaml_node ref, + varchar(900) anchor: string ref); + +yaml_aliases (unique int alias: @yaml_alias_node ref, + varchar(900) target: string ref); + +yaml_scalars (unique int scalar: @yaml_scalar_node ref, + int style: int ref, + varchar(900) value: string ref); + +yaml_errors (unique int id: @yaml_error, + varchar(900) message: string ref); + +/* XML Files */ + +xmlEncoding( + unique int id: @file ref, + varchar(900) encoding: string ref +); + +xmlDTDs( + unique int id: @xmldtd, + varchar(900) root: string ref, + varchar(900) publicId: string ref, + varchar(900) systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + varchar(900) name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + varchar(900) name: string ref, + varchar(3600) value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + varchar(900) prefixName: string ref, + varchar(900) URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + varchar(3600) text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + varchar(3600) text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +@dataflownode = @expr | @functiondeclstmt | @classdeclstmt | @namespacedeclaration | @enumdeclaration | @property; + +@optionalchainable = @callexpr | @propaccess; + +isOptionalChaining(int id: @optionalchainable ref); + +/* Last updated 2018/10/23. */ diff --git a/javascript/upgrades/7eead88269b20e9d61f61cd2291affb737dc831e/upgrade.properties b/javascript/upgrades/7eead88269b20e9d61f61cd2291affb737dc831e/upgrade.properties new file mode 100644 index 00000000000..22cc684935f --- /dev/null +++ b/javascript/upgrades/7eead88269b20e9d61f61cd2291affb737dc831e/upgrade.properties @@ -0,0 +1,4 @@ +description: add support for Closure modules +compatibility: backwards + +isES2015Module.rel: run isES2015Module.qlo