diff --git a/javascript/extractor/src/com/semmle/js/ast/DefaultVisitor.java b/javascript/extractor/src/com/semmle/js/ast/DefaultVisitor.java index e6c6110fd36..d6779fdaa3b 100644 --- a/javascript/extractor/src/com/semmle/js/ast/DefaultVisitor.java +++ b/javascript/extractor/src/com/semmle/js/ast/DefaultVisitor.java @@ -35,7 +35,6 @@ import com.semmle.ts.ast.InterfaceDeclaration; import com.semmle.ts.ast.InterfaceTypeExpr; import com.semmle.ts.ast.IntersectionTypeExpr; import com.semmle.ts.ast.IsTypeExpr; -import com.semmle.ts.ast.UnaryTypeExpr; import com.semmle.ts.ast.KeywordTypeExpr; import com.semmle.ts.ast.MappedTypeExpr; import com.semmle.ts.ast.NamespaceDeclaration; @@ -49,6 +48,7 @@ import com.semmle.ts.ast.TypeAssertion; import com.semmle.ts.ast.TypeExpression; import com.semmle.ts.ast.TypeParameter; import com.semmle.ts.ast.TypeofTypeExpr; +import com.semmle.ts.ast.UnaryTypeExpr; import com.semmle.ts.ast.UnionTypeExpr; import com.semmle.util.exception.CatastrophicError; diff --git a/javascript/extractor/src/com/semmle/js/ast/Visitor.java b/javascript/extractor/src/com/semmle/js/ast/Visitor.java index a1ab0101208..f825d448719 100644 --- a/javascript/extractor/src/com/semmle/js/ast/Visitor.java +++ b/javascript/extractor/src/com/semmle/js/ast/Visitor.java @@ -31,7 +31,6 @@ import com.semmle.ts.ast.InterfaceDeclaration; import com.semmle.ts.ast.InterfaceTypeExpr; import com.semmle.ts.ast.IntersectionTypeExpr; import com.semmle.ts.ast.IsTypeExpr; -import com.semmle.ts.ast.UnaryTypeExpr; import com.semmle.ts.ast.KeywordTypeExpr; import com.semmle.ts.ast.MappedTypeExpr; import com.semmle.ts.ast.NamespaceDeclaration; @@ -44,6 +43,7 @@ import com.semmle.ts.ast.TypeAliasDeclaration; import com.semmle.ts.ast.TypeAssertion; import com.semmle.ts.ast.TypeParameter; import com.semmle.ts.ast.TypeofTypeExpr; +import com.semmle.ts.ast.UnaryTypeExpr; import com.semmle.ts.ast.UnionTypeExpr; /** diff --git a/javascript/extractor/src/com/semmle/js/extractor/ASTExtractor.java b/javascript/extractor/src/com/semmle/js/extractor/ASTExtractor.java index 64bad10273d..52a0adc122b 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/ASTExtractor.java +++ b/javascript/extractor/src/com/semmle/js/extractor/ASTExtractor.java @@ -1,11 +1,5 @@ package com.semmle.js.extractor; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.Stack; - import com.semmle.js.ast.AClass; import com.semmle.js.ast.AFunction; import com.semmle.js.ast.AFunctionExpression; @@ -104,6 +98,7 @@ import com.semmle.js.ast.jsx.JSXMemberExpression; import com.semmle.js.ast.jsx.JSXNamespacedName; import com.semmle.js.ast.jsx.JSXOpeningElement; import com.semmle.js.ast.jsx.JSXSpreadAttribute; +import com.semmle.js.extractor.ExtractionMetrics.ExtractionPhase; import com.semmle.js.extractor.ExtractorConfig.Platform; import com.semmle.js.extractor.ExtractorConfig.SourceType; import com.semmle.js.extractor.ScopeManager.DeclKind; @@ -149,6 +144,11 @@ import com.semmle.ts.ast.UnionTypeExpr; import com.semmle.util.collections.CollectionUtil; import com.semmle.util.trap.TrapWriter; import com.semmle.util.trap.TrapWriter.Label; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.Stack; /** Extractor for AST-based information; invoked by the {@link JSExtractor}. */ public class ASTExtractor { @@ -193,6 +193,10 @@ public class ASTExtractor { return scopeManager; } + public ExtractionMetrics getMetrics() { + return lexicalExtractor.getMetrics(); + } + /** * The binding semantics for an identifier. * @@ -1946,9 +1950,11 @@ public class ASTExtractor { } public void extract(Node root, Platform platform, SourceType sourceType, int toplevelKind) { + lexicalExtractor.getMetrics().startPhase(ExtractionPhase.ASTExtractor_extract); trapwriter.addTuple("toplevels", toplevelLabel, toplevelKind); locationManager.emitNodeLocation(root, toplevelLabel); root.accept(new V(platform, sourceType), null); + lexicalExtractor.getMetrics().stopPhase(ExtractionPhase.ASTExtractor_extract); } } diff --git a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java index 0544290ebd6..5ffa5680cd9 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java +++ b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java @@ -1,5 +1,26 @@ package com.semmle.js.extractor; +import com.semmle.js.extractor.ExtractorConfig.SourceType; +import com.semmle.js.extractor.FileExtractor.FileType; +import com.semmle.js.extractor.trapcache.DefaultTrapCache; +import com.semmle.js.extractor.trapcache.DummyTrapCache; +import com.semmle.js.extractor.trapcache.ITrapCache; +import com.semmle.js.parser.ParsedProject; +import com.semmle.js.parser.TypeScriptParser; +import com.semmle.ts.extractor.TypeExtractor; +import com.semmle.ts.extractor.TypeTable; +import com.semmle.util.data.StringUtil; +import com.semmle.util.exception.CatastrophicError; +import com.semmle.util.exception.Exceptions; +import com.semmle.util.exception.ResourceError; +import com.semmle.util.exception.UserError; +import com.semmle.util.extraction.ExtractorOutputConfig; +import com.semmle.util.files.FileUtil; +import com.semmle.util.io.csv.CSVReader; +import com.semmle.util.language.LegacyLanguage; +import com.semmle.util.process.Env; +import com.semmle.util.projectstructure.ProjectLayout; +import com.semmle.util.trap.TrapWriter; import java.io.File; import java.io.IOException; import java.io.Reader; @@ -27,28 +48,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.stream.Stream; -import com.semmle.js.extractor.ExtractorConfig.SourceType; -import com.semmle.js.extractor.FileExtractor.FileType; -import com.semmle.js.extractor.trapcache.DefaultTrapCache; -import com.semmle.js.extractor.trapcache.DummyTrapCache; -import com.semmle.js.extractor.trapcache.ITrapCache; -import com.semmle.js.parser.ParsedProject; -import com.semmle.js.parser.TypeScriptParser; -import com.semmle.ts.extractor.TypeExtractor; -import com.semmle.ts.extractor.TypeTable; -import com.semmle.util.data.StringUtil; -import com.semmle.util.exception.CatastrophicError; -import com.semmle.util.exception.Exceptions; -import com.semmle.util.exception.ResourceError; -import com.semmle.util.exception.UserError; -import com.semmle.util.extraction.ExtractorOutputConfig; -import com.semmle.util.files.FileUtil; -import com.semmle.util.io.csv.CSVReader; -import com.semmle.util.language.LegacyLanguage; -import com.semmle.util.process.Env; -import com.semmle.util.projectstructure.ProjectLayout; -import com.semmle.util.trap.TrapWriter; - /** * An alternative entry point to the JavaScript extractor. * @@ -71,8 +70,8 @@ import com.semmle.util.trap.TrapWriter; * patterns that can be used to refine the list of files to include and exclude *
  • LGTM_INDEX_TYPESCRIPT: whether to extract TypeScript *
  • LGTM_INDEX_FILETYPES: a newline-separated list of ".extension:filetype" pairs - * specifying which {@link FileType} to use for the given extension; the additional file - * type XML is also supported + * specifying which {@link FileType} to use for the given extension; the additional file type + * XML is also supported *
  • LGTM_INDEX_XML_MODE: whether to extract XML files *
  • LGTM_THREADS: the maximum number of files to extract in parallel *
  • LGTM_TRAP_CACHE: the path of a directory to use for trap caching @@ -166,8 +165,8 @@ import com.semmle.util.trap.TrapWriter; *

    If LGTM_INDEX_XML_MODE is set to ALL, then all files with extension * .xml under LGTM_SRC are extracted as XML (in addition to any files * whose file type is specified to be XML via LGTM_INDEX_SOURCE_TYPE). - * Currently XML extraction does not respect inclusion and exclusion filters, but this is a bug, - * not a feature, and hence will change eventually. + * Currently XML extraction does not respect inclusion and exclusion filters, but this is a bug, not + * a feature, and hence will change eventually. * *

    Note that all these customisations only apply to LGTM_SRC. Extraction of externs * is not customisable. @@ -288,8 +287,7 @@ public class AutoBuild { try { fileType = StringUtil.uc(fileType); if ("XML".equals(fileType)) { - if (extension.length() < 2) - throw new UserError("Invalid extension '" + extension + "'."); + if (extension.length() < 2) throw new UserError("Invalid extension '" + extension + "'."); xmlExtensions.add(extension.substring(1)); } else { fileTypes.put(extension, FileType.valueOf(fileType)); @@ -304,8 +302,7 @@ public class AutoBuild { private void setupXmlMode() { String xmlMode = getEnvVar("LGTM_INDEX_XML_MODE", "DISABLED"); xmlMode = StringUtil.uc(xmlMode.trim()); - if ("ALL".equals(xmlMode)) - xmlExtensions.add("xml"); + if ("ALL".equals(xmlMode)) xmlExtensions.add("xml"); else if (!"DISABLED".equals(xmlMode)) throw new UserError("Invalid XML mode '" + xmlMode + "' (should be either ALL or DISABLED)."); } @@ -744,8 +741,7 @@ public class AutoBuild { try { long start = logBeginProcess("Extracting " + file); Integer loc = extractor.extract(f, state); - if (!extractor.getConfig().isExterns() && (loc == null || loc != 0)) - seenCode = true; + if (!extractor.getConfig().isExterns() && (loc == null || loc != 0)) seenCode = true; logEndProcess(start, "Done extracting " + file); } catch (Throwable t) { System.err.println("Exception while extracting " + file + "."); @@ -776,8 +772,7 @@ public class AutoBuild { } protected void extractXml() throws IOException { - if (xmlExtensions.isEmpty()) - return; + if (xmlExtensions.isEmpty()) return; List cmd = new ArrayList<>(); cmd.add("odasa"); cmd.add("index"); diff --git a/javascript/extractor/src/com/semmle/js/extractor/CFGExtractor.java b/javascript/extractor/src/com/semmle/js/extractor/CFGExtractor.java index c31e3267025..2aeba574bae 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/CFGExtractor.java +++ b/javascript/extractor/src/com/semmle/js/extractor/CFGExtractor.java @@ -93,6 +93,7 @@ import com.semmle.js.ast.jsx.JSXMemberExpression; import com.semmle.js.ast.jsx.JSXNamespacedName; import com.semmle.js.ast.jsx.JSXOpeningElement; import com.semmle.js.ast.jsx.JSXSpreadAttribute; +import com.semmle.js.extractor.ExtractionMetrics.ExtractionPhase; import com.semmle.ts.ast.DecoratorList; import com.semmle.ts.ast.EnumDeclaration; import com.semmle.ts.ast.EnumMember; @@ -171,11 +172,13 @@ public class CFGExtractor { private final TrapWriter trapwriter; private final Label toplevelLabel; private final LocationManager locationManager; + private final ExtractionMetrics metrics; public CFGExtractor(ASTExtractor astExtractor) { this.trapwriter = astExtractor.getTrapwriter(); this.toplevelLabel = astExtractor.getToplevelLabel(); this.locationManager = astExtractor.getLocationManager(); + this.metrics = astExtractor.getMetrics(); } @SuppressWarnings("unchecked") @@ -1955,6 +1958,8 @@ public class CFGExtractor { } public void extract(Node nd) { + metrics.startPhase(ExtractionPhase.CFGExtractor_extract); nd.accept(new V(), new SimpleSuccessorInfo(null)); + metrics.stopPhase(ExtractionPhase.CFGExtractor_extract); } } diff --git a/javascript/extractor/src/com/semmle/js/extractor/ExtractionMetrics.java b/javascript/extractor/src/com/semmle/js/extractor/ExtractionMetrics.java new file mode 100644 index 00000000000..d77f71f04ce --- /dev/null +++ b/javascript/extractor/src/com/semmle/js/extractor/ExtractionMetrics.java @@ -0,0 +1,167 @@ +package com.semmle.js.extractor; + +import com.semmle.util.trap.TrapWriter; +import com.semmle.util.trap.TrapWriter.Label; +import java.io.File; +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadMXBean; +import java.util.Stack; + +/** Metrics for the (single-threaded) extraction of a single file. */ +public class ExtractionMetrics { + /** + * The phase of the extraction that should be measured time for. + * + *

    Convention: the enum names have the format {ClassName}_{MethodName}, and should + * identify the methods they correspond to. + */ + public enum ExtractionPhase { + ASTExtractor_extract(0), + CFGExtractor_extract(1), + FileExtractor_extractContents(2), + JSExtractor_extract(3), + JSParser_parse(4), + LexicalExtractor_extractLines(5), + LexicalExtractor_extractTokens(6), + TypeScriptASTConverter_convertAST(7), + TypeScriptParser_talkToParserWrapper(8); + + /** The id used in the database for the time spent performing this phase of the extraction. */ + final int dbschemeId; + + ExtractionPhase(int dbschemeId) { + this.dbschemeId = dbschemeId; + } + } + + /** The cache file, if any. */ + private File cacheFile; + + /** True iff the extraction of this file reuses an existing trap cache file. */ + private boolean canReuseCacheFile; + + /** The cumulative CPU-time spent in each extraction phase so far. */ + private final long[] cpuTimes = new long[ExtractionPhase.values().length]; + + /** The label for the file that is being extracted. */ + private Label fileLabel; + + /** The number of UTF16 code units in the file that is being extracted. */ + private int length; + + /** The previous time a CPU-time measure was performed. */ + private long previousCpuTime; + + /** The previous time a wallclock-time measure was performed. */ + private long previousWallclockTime; + + /** The extraction phase stack. */ + private final Stack stack = new Stack<>(); + + /** The current thread, used for measuring CPU-time. */ + private final ThreadMXBean thread = ManagementFactory.getThreadMXBean(); + + /** The cumulative wallclock-time spent in each extraction phase so far. */ + private final long[] wallclockTimes = new long[ExtractionPhase.values().length]; + + /** + * True iff extraction metrics could not be obtained for this file (due to an unforeseen error + * that should not prevent the ordinary extraction from succeeding). + */ + private boolean timingsFailed; + + /** + * Writes the data metrics to a trap file. Note that this makes the resulting trap file content + * non-deterministic. + */ + public void writeDataToTrap(TrapWriter trapwriter) { + trapwriter.addTuple( + "extraction_data", + fileLabel, + cacheFile != null ? cacheFile.getAbsolutePath() : "", + canReuseCacheFile, + length); + } + + /** + * Writes the timing metrics to a trap file. Note that this makes the resulting trap file content + * non-deterministic. + */ + public void writeTimingsToTrap(TrapWriter trapwriter) { + if (!stack.isEmpty()) { + failTimings( + String.format( + "Could not properly record extraction times for %s. (stack = %s)%n", + fileLabel, stack.toString())); + } + if (!timingsFailed) { + for (int i = 0; i < ExtractionPhase.values().length; i++) { + trapwriter.addTuple("extraction_time", fileLabel, i, 0, (float) cpuTimes[i]); + trapwriter.addTuple("extraction_time", fileLabel, i, 1, (float) wallclockTimes[i]); + } + } + } + + private void failTimings(String msg) { + System.err.println(msg); + System.err.flush(); + this.timingsFailed = true; + } + + private void incrementCurrentTimer() { + long nowWallclock = System.nanoTime(); + long nowCpu = thread.getCurrentThreadCpuTime(); + + if (!stack.isEmpty()) { + // increment by the time elapsed + wallclockTimes[stack.peek().dbschemeId] += nowWallclock - previousWallclockTime; + cpuTimes[stack.peek().dbschemeId] += nowCpu - previousCpuTime; + } + + // update the running clock + previousWallclockTime = nowWallclock; + previousCpuTime = nowCpu; + } + + public void setCacheFile(File cacheFile) { + this.cacheFile = cacheFile; + } + + public void setCanReuseCacheFile(boolean canReuseCacheFile) { + this.canReuseCacheFile = canReuseCacheFile; + } + + public void setFileLabel(Label fileLabel) { + this.fileLabel = fileLabel; + } + + public void setLength(int length) { + this.length = length; + } + + public void startPhase(ExtractionPhase event) { + incrementCurrentTimer(); + stack.push(event); + } + + public void stopPhase( + ExtractionPhase + event /* technically not needed, but useful for documentation and sanity checking */) { + if (stack.isEmpty()) { + failTimings( + String.format( + "Inconsistent extraction time recording: trying to stop timer %s, but no timer is running", + event)); + return; + } + if (stack.peek() != event) { + failTimings( + String.format( + "Inconsistent extraction time recording: trying to stop timer %s, but current timer is: %s", + event, stack.peek())); + return; + } + incrementCurrentTimer(); + stack.pop(); + } +} diff --git a/javascript/extractor/src/com/semmle/js/extractor/FileExtractor.java b/javascript/extractor/src/com/semmle/js/extractor/FileExtractor.java index f70454c8daa..d80ddeaf303 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/FileExtractor.java +++ b/javascript/extractor/src/com/semmle/js/extractor/FileExtractor.java @@ -1,5 +1,6 @@ package com.semmle.js.extractor; +import com.semmle.js.extractor.ExtractionMetrics.ExtractionPhase; import com.semmle.js.extractor.trapcache.CachingTrapWriter; import com.semmle.js.extractor.trapcache.ITrapCache; import com.semmle.util.data.StringUtil; @@ -384,10 +385,9 @@ public class FileExtractor { return config.hasFileType() || FileType.forFile(f, config) != null; } - /** - * @return the number of lines of code extracted, or {@code null} if the file was cached - */ + /** @return the number of lines of code extracted, or {@code null} if the file was cached */ public Integer extract(File f, ExtractorState state) throws IOException { + // populate source archive String source = new WholeIO(config.getDefaultEncoding()).strictread(f); outputConfig.getSourceArchive().add(f, source); @@ -395,6 +395,7 @@ public class FileExtractor { // extract language-independent bits TrapWriter trapwriter = outputConfig.getTrapWriterFactory().mkTrapWriter(f); Label fileLabel = trapwriter.populateFile(f); + LocationManager locationManager = new LocationManager(f, trapwriter, fileLabel); locationManager.emitFileLocation(fileLabel, 0, 0, 0, 0); @@ -426,22 +427,34 @@ public class FileExtractor { private Integer extractContents( File f, Label fileLabel, String source, LocationManager locationManager, ExtractorState state) throws IOException { + ExtractionMetrics metrics = new ExtractionMetrics(); + metrics.startPhase(ExtractionPhase.FileExtractor_extractContents); + metrics.setLength(source.length()); + metrics.setFileLabel(fileLabel); TrapWriter trapwriter = locationManager.getTrapWriter(); FileType fileType = getFileType(f); File cacheFile = null, // the cache file for this extraction resultFile = null; // the final result TRAP file for this extraction - // check whether we can perform caching - if (bumpIdCounter(trapwriter) && fileType.isTrapCachingAllowed()) { + if (bumpIdCounter(trapwriter)) { resultFile = outputConfig.getTrapWriterFactory().getTrapFileFor(f); - if (resultFile != null) cacheFile = trapCache.lookup(source, config, fileType); + } + // check whether we can perform caching + if (resultFile != null && fileType.isTrapCachingAllowed()) { + cacheFile = trapCache.lookup(source, config, fileType); } - if (cacheFile != null) { + boolean canUseCacheFile = cacheFile != null; + boolean canReuseCacheFile = canUseCacheFile && cacheFile.exists(); + + metrics.setCacheFile(cacheFile); + metrics.setCanReuseCacheFile(canReuseCacheFile); + metrics.writeDataToTrap(trapwriter); + if (canUseCacheFile) { FileUtil.close(trapwriter); - if (cacheFile.exists()) { + if (canReuseCacheFile) { FileUtil.append(cacheFile, resultFile); return null; } @@ -459,18 +472,20 @@ public class FileExtractor { try { IExtractor extractor = fileType.mkExtractor(config, state); TextualExtractor textualExtractor = - new TextualExtractor(trapwriter, locationManager, source, config.getExtractLines()); + new TextualExtractor( + trapwriter, locationManager, source, config.getExtractLines(), metrics); LoCInfo loc = extractor.extract(textualExtractor); int numLines = textualExtractor.getNumLines(); int linesOfCode = loc.getLinesOfCode(), linesOfComments = loc.getLinesOfComments(); trapwriter.addTuple("numlines", fileLabel, numLines, linesOfCode, linesOfComments); trapwriter.addTuple("filetype", fileLabel, fileType.toString()); + metrics.stopPhase(ExtractionPhase.FileExtractor_extractContents); + metrics.writeTimingsToTrap(trapwriter); successful = true; return linesOfCode; } finally { if (!successful && trapwriter instanceof CachingTrapWriter) ((CachingTrapWriter) trapwriter).discard(); - FileUtil.close(trapwriter); } } diff --git a/javascript/extractor/src/com/semmle/js/extractor/HTMLExtractor.java b/javascript/extractor/src/com/semmle/js/extractor/HTMLExtractor.java index 6f8cd0481c7..3274741a970 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/HTMLExtractor.java +++ b/javascript/extractor/src/com/semmle/js/extractor/HTMLExtractor.java @@ -191,7 +191,12 @@ public class HTMLExtractor implements IExtractor { JSExtractor extractor = new JSExtractor(config); try { TextualExtractor tx = - new TextualExtractor(trapwriter, scriptLocationManager, source, config.getExtractLines()); + new TextualExtractor( + trapwriter, + scriptLocationManager, + source, + config.getExtractLines(), + textualExtractor.getMetrics()); return extractor.extract(tx, source, toplevelKind, scopeManager).snd(); } catch (ParseError e) { e.setPosition(scriptLocationManager.translatePosition(e.getPosition())); diff --git a/javascript/extractor/src/com/semmle/js/extractor/JSExtractor.java b/javascript/extractor/src/com/semmle/js/extractor/JSExtractor.java index 6e1bee505ee..45dd4648220 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/JSExtractor.java +++ b/javascript/extractor/src/com/semmle/js/extractor/JSExtractor.java @@ -3,6 +3,7 @@ package com.semmle.js.extractor; import com.semmle.js.ast.Comment; import com.semmle.js.ast.Node; import com.semmle.js.ast.Token; +import com.semmle.js.extractor.ExtractionMetrics.ExtractionPhase; import com.semmle.js.extractor.ExtractorConfig.ECMAVersion; import com.semmle.js.extractor.ExtractorConfig.Platform; import com.semmle.js.extractor.ExtractorConfig.SourceType; @@ -52,7 +53,8 @@ public class JSExtractor { SourceType sourceType = establishSourceType(source, true); - JSParser.Result parserRes = JSParser.parse(config, sourceType, source); + JSParser.Result parserRes = + JSParser.parse(config, sourceType, source, textualExtractor.getMetrics()); return extract(textualExtractor, source, toplevelKind, scopeManager, sourceType, parserRes); } @@ -87,6 +89,7 @@ public class JSExtractor { SourceType sourceType, JSParser.Result parserRes) throws ParseError { + textualExtractor.getMetrics().startPhase(ExtractionPhase.JSExtractor_extract); Label toplevelLabel; TrapWriter trapwriter = textualExtractor.getTrapwriter(); LocationManager locationManager = textualExtractor.getLocationManager(); @@ -104,7 +107,6 @@ public class JSExtractor { new LexicalExtractor(textualExtractor, parserRes.getTokens(), parserRes.getComments()); ASTExtractor scriptExtractor = new ASTExtractor(lexicalExtractor, scopeManager); toplevelLabel = scriptExtractor.getToplevelLabel(); - lexicalExtractor.extractComments(toplevelLabel); loc = lexicalExtractor.extractLines(parserRes.getSource(), toplevelLabel); lexicalExtractor.extractTokens(toplevelLabel); @@ -126,7 +128,6 @@ public class JSExtractor { for (ParseError parseError : parserRes.getErrors()) { if (!config.isTolerateParseErrors()) throw parseError; - Label key = trapwriter.freshLabel(); String errorLine = textualExtractor.getLine(parseError.getPosition().getLine()); trapwriter.addTuple("jsParseErrors", key, toplevelLabel, "Error: " + parseError, errorLine); @@ -139,6 +140,8 @@ public class JSExtractor { if (platform == Platform.NODE && sourceType == SourceType.COMMONJS_MODULE) textualExtractor.getTrapwriter().addTuple("isNodejs", toplevelLabel); + textualExtractor.getMetrics().stopPhase(ExtractionPhase.JSExtractor_extract); + return Pair.make(toplevelLabel, loc); } diff --git a/javascript/extractor/src/com/semmle/js/extractor/LexicalExtractor.java b/javascript/extractor/src/com/semmle/js/extractor/LexicalExtractor.java index 31b94b33c0d..6ca47eb5f6e 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/LexicalExtractor.java +++ b/javascript/extractor/src/com/semmle/js/extractor/LexicalExtractor.java @@ -4,6 +4,7 @@ import com.semmle.js.ast.Comment; import com.semmle.js.ast.Position; import com.semmle.js.ast.SourceElement; import com.semmle.js.ast.Token; +import com.semmle.js.extractor.ExtractionMetrics.ExtractionPhase; import com.semmle.util.trap.TrapWriter; import com.semmle.util.trap.TrapWriter.Label; import java.util.List; @@ -40,9 +41,16 @@ public class LexicalExtractor { return comments; } + public ExtractionMetrics getMetrics() { + return textualExtractor.getMetrics(); + } + public LoCInfo extractLines(String src, Label toplevelKey) { + textualExtractor.getMetrics().startPhase(ExtractionPhase.LexicalExtractor_extractLines); Position end = textualExtractor.extractLines(src, toplevelKey); - return emitNumlines(toplevelKey, new Position(1, 0, 0), end); + LoCInfo info = emitNumlines(toplevelKey, new Position(1, 0, 0), end); + textualExtractor.getMetrics().stopPhase(ExtractionPhase.LexicalExtractor_extractLines); + return info; } /** @@ -112,11 +120,11 @@ public class LexicalExtractor { } public void extractTokens(Label toplevelKey) { + textualExtractor.getMetrics().startPhase(ExtractionPhase.LexicalExtractor_extractTokens); int j = 0; for (int i = 0, n = tokens.size(), idx = 0; i < n; ++i) { Token token = tokens.get(i); if (token == null) continue; - Label key = trapwriter.freshLabel(); int kind = -1; switch (token.getType()) { @@ -164,6 +172,7 @@ public class LexicalExtractor { if (token.getLoc().equals(next.getLoc())) tokens.set(i + 1, null); } } + textualExtractor.getMetrics().stopPhase(ExtractionPhase.LexicalExtractor_extractTokens); } public void extractComments(Label toplevelKey) { diff --git a/javascript/extractor/src/com/semmle/js/extractor/Main.java b/javascript/extractor/src/com/semmle/js/extractor/Main.java index 95dd6c58c22..3f4ba8e5a18 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/Main.java +++ b/javascript/extractor/src/com/semmle/js/extractor/Main.java @@ -37,7 +37,7 @@ public class Main { * A version identifier that should be updated every time the extractor changes in 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-09-02"; + public static final String EXTRACTOR_VERSION = "2019-09-04"; public static final Pattern NEWLINE = Pattern.compile("\n"); diff --git a/javascript/extractor/src/com/semmle/js/extractor/TextualExtractor.java b/javascript/extractor/src/com/semmle/js/extractor/TextualExtractor.java index dd4102b0c10..2cec0f4ab31 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/TextualExtractor.java +++ b/javascript/extractor/src/com/semmle/js/extractor/TextualExtractor.java @@ -20,14 +20,20 @@ public class TextualExtractor { private final LocationManager locationManager; private final Label fileLabel; private final boolean extractLines; + private final ExtractionMetrics metrics; public TextualExtractor( - TrapWriter trapwriter, LocationManager locationManager, String source, boolean extractLines) { + TrapWriter trapwriter, + LocationManager locationManager, + String source, + boolean extractLines, + ExtractionMetrics metrics) { this.trapwriter = trapwriter; this.locationManager = locationManager; this.source = source; this.fileLabel = locationManager.getFileLabel(); this.extractLines = extractLines; + this.metrics = metrics; } public TrapWriter getTrapwriter() { @@ -42,6 +48,10 @@ public class TextualExtractor { return source; } + public ExtractionMetrics getMetrics() { + return metrics; + } + public String mkToString(SourceElement nd) { return sanitiseToString(nd.getLoc().getSource()); } diff --git a/javascript/extractor/src/com/semmle/js/extractor/TypeExprKinds.java b/javascript/extractor/src/com/semmle/js/extractor/TypeExprKinds.java index 7001f9540b4..78b901da475 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/TypeExprKinds.java +++ b/javascript/extractor/src/com/semmle/js/extractor/TypeExprKinds.java @@ -17,7 +17,6 @@ import com.semmle.ts.ast.InferTypeExpr; import com.semmle.ts.ast.InterfaceTypeExpr; import com.semmle.ts.ast.IntersectionTypeExpr; import com.semmle.ts.ast.IsTypeExpr; -import com.semmle.ts.ast.UnaryTypeExpr; import com.semmle.ts.ast.KeywordTypeExpr; import com.semmle.ts.ast.MappedTypeExpr; import com.semmle.ts.ast.OptionalTypeExpr; @@ -26,6 +25,7 @@ import com.semmle.ts.ast.RestTypeExpr; import com.semmle.ts.ast.TupleTypeExpr; import com.semmle.ts.ast.TypeParameter; import com.semmle.ts.ast.TypeofTypeExpr; +import com.semmle.ts.ast.UnaryTypeExpr; import com.semmle.ts.ast.UnionTypeExpr; import com.semmle.util.exception.CatastrophicError; @@ -129,8 +129,10 @@ public class TypeExprKinds { @Override public Integer visit(UnaryTypeExpr nd, Void c) { switch (nd.getKind()) { - case Keyof: return keyofTypeExpr; - case Readonly: return readonlyTypeExpr; + case Keyof: + return keyofTypeExpr; + case Readonly: + return readonlyTypeExpr; } throw new CatastrophicError("Unhandled UnaryTypeExpr kind: " + nd.getKind()); } diff --git a/javascript/extractor/src/com/semmle/js/extractor/TypeScriptExtractor.java b/javascript/extractor/src/com/semmle/js/extractor/TypeScriptExtractor.java index 615dac52877..e1823206808 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/TypeScriptExtractor.java +++ b/javascript/extractor/src/com/semmle/js/extractor/TypeScriptExtractor.java @@ -21,7 +21,7 @@ public class TypeScriptExtractor implements IExtractor { LocationManager locationManager = textualExtractor.getLocationManager(); String source = textualExtractor.getSource(); File sourceFile = locationManager.getSourceFile(); - Result res = parser.parse(sourceFile, source); + Result res = parser.parse(sourceFile, source, textualExtractor.getMetrics()); ScopeManager scopeManager = new ScopeManager(textualExtractor.getTrapwriter(), ECMAVersion.ECMA2017); try { diff --git a/javascript/extractor/src/com/semmle/js/extractor/test/AutoBuildTests.java b/javascript/extractor/src/com/semmle/js/extractor/test/AutoBuildTests.java index ea79971aedc..0d45908885b 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/test/AutoBuildTests.java +++ b/javascript/extractor/src/com/semmle/js/extractor/test/AutoBuildTests.java @@ -1,5 +1,14 @@ package com.semmle.js.extractor.test; +import com.semmle.js.extractor.AutoBuild; +import com.semmle.js.extractor.ExtractorState; +import com.semmle.js.extractor.FileExtractor; +import com.semmle.js.extractor.FileExtractor.FileType; +import com.semmle.util.data.StringUtil; +import com.semmle.util.exception.UserError; +import com.semmle.util.files.FileUtil; +import com.semmle.util.files.FileUtil8; +import com.semmle.util.process.Env; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -16,23 +25,12 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; - import org.junit.After; import org.junit.Assert; import org.junit.Assume; import org.junit.Before; import org.junit.Test; -import com.semmle.js.extractor.AutoBuild; -import com.semmle.js.extractor.ExtractorState; -import com.semmle.js.extractor.FileExtractor; -import com.semmle.js.extractor.FileExtractor.FileType; -import com.semmle.util.data.StringUtil; -import com.semmle.util.exception.UserError; -import com.semmle.util.files.FileUtil; -import com.semmle.util.files.FileUtil8; -import com.semmle.util.process.Env; - public class AutoBuildTests { private Path SEMMLE_DIST, LGTM_SRC; private Set expected; @@ -132,16 +130,18 @@ public class AutoBuildTests { @Override protected void extractXml() throws IOException { - Files.walkFileTree(LGTM_SRC, new SimpleFileVisitor(){ - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) - throws IOException { - String ext = FileUtil.extension(file); - if (!ext.isEmpty() && getXmlExtensions().contains(ext.substring(1))) - actual.add(file.toString()); - return FileVisitResult.CONTINUE; - } - }); + Files.walkFileTree( + LGTM_SRC, + new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) + throws IOException { + String ext = FileUtil.extension(file); + if (!ext.isEmpty() && getXmlExtensions().contains(ext.substring(1))) + actual.add(file.toString()); + return FileVisitResult.CONTINUE; + } + }); } }.run(); String expectedString = StringUtil.glue("\n", expected.stream().sorted().toArray()); diff --git a/javascript/extractor/src/com/semmle/js/extractor/test/NodeJSDetectorTests.java b/javascript/extractor/src/com/semmle/js/extractor/test/NodeJSDetectorTests.java index 0faaf8111f5..92b11666a8f 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/test/NodeJSDetectorTests.java +++ b/javascript/extractor/src/com/semmle/js/extractor/test/NodeJSDetectorTests.java @@ -1,6 +1,7 @@ package com.semmle.js.extractor.test; import com.semmle.js.ast.Node; +import com.semmle.js.extractor.ExtractionMetrics; import com.semmle.js.extractor.ExtractorConfig; import com.semmle.js.extractor.ExtractorConfig.SourceType; import com.semmle.js.extractor.NodeJSDetector; @@ -13,7 +14,7 @@ public class NodeJSDetectorTests { private static final ExtractorConfig CONFIG = new ExtractorConfig(false); private void isNodeJS(String src, boolean expected) { - Result res = JSParser.parse(CONFIG, SourceType.SCRIPT, src); + Result res = JSParser.parse(CONFIG, SourceType.SCRIPT, src, new ExtractionMetrics()); Node ast = res.getAST(); Assert.assertNotNull(ast); Assert.assertTrue(NodeJSDetector.looksLikeNodeJS(ast) == expected); diff --git a/javascript/extractor/src/com/semmle/js/extractor/test/TrapTests.java b/javascript/extractor/src/com/semmle/js/extractor/test/TrapTests.java index 29831a0e369..6f3f7e1e445 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/test/TrapTests.java +++ b/javascript/extractor/src/com/semmle/js/extractor/test/TrapTests.java @@ -166,6 +166,16 @@ public class TrapTests { String expected = new WholeIO().strictreadText(trap); expectedVsActual.add(Pair.make(expected, actual)); } + + @Override + public void addTuple(String tableName, Object... values) { + if ("extraction_data".equals(tableName) + || "extraction_time".equals(tableName)) { + // ignore non-deterministic tables + return; + } + super.addTuple(tableName, values); + } }; } diff --git a/javascript/extractor/src/com/semmle/js/parser/JSDocParser.java b/javascript/extractor/src/com/semmle/js/parser/JSDocParser.java index c20c31b5b00..391f703d2b0 100644 --- a/javascript/extractor/src/com/semmle/js/parser/JSDocParser.java +++ b/javascript/extractor/src/com/semmle/js/parser/JSDocParser.java @@ -47,9 +47,8 @@ public class JSDocParser { // This occurs before the start of 'source', so the lineStart is negative. int firstLineStart = -(startPos.getColumn() + "/**".length() - 1); this.absoluteOffset = startPos.getOffset(); - Pair> r = p.new TagParser(null).parseComment( - startPos.getLine() - 1, - firstLineStart); + Pair> r = + p.new TagParser(null).parseComment(startPos.getLine() - 1, firstLineStart); List tags = new ArrayList<>(); for (JSDocTagParser.Tag tag : r.snd()) { String title = tag.title; @@ -269,21 +268,21 @@ public class JSDocParser { return new SourceLocation(pos()); } - /** - * Returns the absolute position of the start of the current token. - */ + /** Returns the absolute position of the start of the current token. */ private Position pos() { - return new Position(this.lineNumber + 1, startOfCurToken - lineStart, startOfCurToken + absoluteOffset); + return new Position( + this.lineNumber + 1, startOfCurToken - lineStart, startOfCurToken + absoluteOffset); } /** * Returns the absolute position of the end of the previous token. * - * This can differ from the start of the current token in case the two tokens - * are separated by whitespace. + *

    This can differ from the start of the current token in case the two tokens are separated + * by whitespace. */ private Position endPos() { - return new Position(this.lineNumber + 1, endOfPrevToken - lineStart, endOfPrevToken + absoluteOffset); + return new Position( + this.lineNumber + 1, endOfPrevToken - lineStart, endOfPrevToken + absoluteOffset); } private T finishNode(T node) { @@ -306,7 +305,8 @@ public class JSDocParser { if (index >= source.length()) return -1; int ch = source.charAt(index); ++index; - if (isLineTerminator(ch) && !(ch == '\r' && index < endIndex && source.charAt(index) == '\n')) { + if (isLineTerminator(ch) + && !(ch == '\r' && index < endIndex && source.charAt(index) == '\n')) { lineNumber += 1; lineStart = index; index = skipStars(index, endIndex); @@ -950,7 +950,10 @@ public class JSDocParser { consume(Token.COLON); expr = finishNode( - new ParameterType(new SourceLocation(loc), ((NameExpression) expr).getName(), parseTypeExpression())); + new ParameterType( + new SourceLocation(loc), + ((NameExpression) expr).getName(), + parseTypeExpression())); } if (token == Token.EQUAL) { consume(Token.EQUAL); @@ -1125,8 +1128,7 @@ public class JSDocParser { consume(Token.RBRACK, "expected an array-style type declaration (' + value + '[])"); List expressions = new ArrayList<>(); expressions.add(expr); - NameExpression nameExpr = - finishNode(new NameExpression(new SourceLocation(loc), "Array")); + NameExpression nameExpr = finishNode(new NameExpression(new SourceLocation(loc), "Array")); return finishNode(new TypeApplication(loc, nameExpr, expressions)); } @@ -1183,7 +1185,8 @@ public class JSDocParser { return expr; } - private JSDocTypeExpression parseType(int startIndex, int endIndex, int lineStart, int lineNumber) throws ParseError { + private JSDocTypeExpression parseType( + int startIndex, int endIndex, int lineStart, int lineNumber) throws ParseError { JSDocTypeExpression expr; this.lineNumber = lineNumber; @@ -1202,7 +1205,8 @@ public class JSDocParser { return expr; } - private JSDocTypeExpression parseParamType(int startIndex, int endIndex, int lineStart, int lineNumber) throws ParseError { + private JSDocTypeExpression parseParamType( + int startIndex, int endIndex, int lineStart, int lineNumber) throws ParseError { JSDocTypeExpression expr; this.lineNumber = lineNumber; diff --git a/javascript/extractor/src/com/semmle/js/parser/JSParser.java b/javascript/extractor/src/com/semmle/js/parser/JSParser.java index 18147038243..d912886303f 100644 --- a/javascript/extractor/src/com/semmle/js/parser/JSParser.java +++ b/javascript/extractor/src/com/semmle/js/parser/JSParser.java @@ -3,6 +3,8 @@ package com.semmle.js.parser; import com.semmle.js.ast.Comment; import com.semmle.js.ast.Node; import com.semmle.js.ast.Token; +import com.semmle.js.extractor.ExtractionMetrics; +import com.semmle.js.extractor.ExtractionMetrics.ExtractionPhase; import com.semmle.js.extractor.ExtractorConfig; import com.semmle.js.extractor.ExtractorConfig.SourceType; import java.util.List; @@ -65,7 +67,11 @@ public class JSParser { } } - public static Result parse(ExtractorConfig config, SourceType sourceType, String source) { - return JcornWrapper.parse(config, sourceType, source); + public static Result parse( + ExtractorConfig config, SourceType sourceType, String source, ExtractionMetrics metrics) { + metrics.startPhase(ExtractionPhase.JSParser_parse); + Result result = JcornWrapper.parse(config, sourceType, source); + metrics.stopPhase(ExtractionPhase.JSParser_parse); + return result; } } diff --git a/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java b/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java index 9a996627eae..dc36d57bf30 100644 --- a/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java +++ b/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java @@ -1,13 +1,5 @@ package com.semmle.js.parser; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonNull; @@ -149,6 +141,13 @@ import com.semmle.ts.ast.TypeofTypeExpr; import com.semmle.ts.ast.UnaryTypeExpr; import com.semmle.ts.ast.UnionTypeExpr; import com.semmle.util.collections.CollectionUtil; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * Utility class for converting a The Node.js half of the wrapper is expected to live at {@code * $SEMMLE_DIST/tools/typescript-parser-wrapper/main.js}; non-standard locations can be configured * using the property {@value #PARSER_WRAPPER_PATH_ENV_VAR}. - * - *

    The script launches the Node.js wrapper in the Node.js runtime, looking for {@code node} - * on the {@code PATH} by default. Non-standard locations can be configured using the property - * {@value #TYPESCRIPT_NODE_RUNTIME_VAR}, and additional arguments can be configured using the - * property {@value #TYPESCRIPT_NODE_RUNTIME_EXTRA_ARGS_VAR}. + * + *

    The script launches the Node.js wrapper in the Node.js runtime, looking for {@code node} on + * the {@code PATH} by default. Non-standard locations can be configured using the property {@value + * #TYPESCRIPT_NODE_RUNTIME_VAR}, and additional arguments can be configured using the property + * {@value #TYPESCRIPT_NODE_RUNTIME_EXTRA_ARGS_VAR}. * *

    The script is started upon parsing the first TypeScript file and then is kept running in the * background, passing it requests for parsing files and getting JSON-encoded ASTs as responses. @@ -62,8 +61,8 @@ public class TypeScriptParser { public static final String PARSER_WRAPPER_PATH_ENV_VAR = "SEMMLE_TYPESCRIPT_PARSER_WRAPPER"; /** - * An environment variable that can be set to indicate the location of the Node.js runtime, - * as an alternative to adding Node to the PATH. + * An environment variable that can be set to indicate the location of the Node.js runtime, as an + * alternative to adding Node to the PATH. */ public static final String TYPESCRIPT_NODE_RUNTIME_VAR = "SEMMLE_TYPESCRIPT_NODE_RUNTIME"; @@ -71,7 +70,8 @@ public class TypeScriptParser { * An environment variable that can be set to provide additional arguments to the Node.js runtime * each time it is invoked. Arguments should be separated by spaces. */ - public static final String TYPESCRIPT_NODE_RUNTIME_EXTRA_ARGS_VAR = "SEMMLE_TYPESCRIPT_NODE_RUNTIME_EXTRA_ARGS"; + public static final String TYPESCRIPT_NODE_RUNTIME_EXTRA_ARGS_VAR = + "SEMMLE_TYPESCRIPT_NODE_RUNTIME_EXTRA_ARGS"; /** * An environment variable that can be set to specify a timeout to use when verifying the @@ -102,8 +102,8 @@ public class TypeScriptParser { /** * An environment variable with additional VM arguments to pass to the Node process. - *

    - * Only --inspect or --inspect-brk may be used at the moment. + * + *

    Only --inspect or --inspect-brk may be used at the moment. */ public static final String TYPESCRIPT_NODE_FLAGS = "SEMMLE_TYPESCRIPT_NODE_FLAGS"; @@ -123,8 +123,8 @@ public class TypeScriptParser { private String nodeJsRuntime; /** - * Arguments to pass to the Node.js runtime each time it is invoked. - * Initialised by {@link #verifyNodeInstallation}. + * Arguments to pass to the Node.js runtime each time it is invoked. Initialised by {@link + * #verifyNodeInstallation}. */ private List nodeJsRuntimeExtraArgs = Collections.emptyList(); @@ -139,7 +139,8 @@ public class TypeScriptParser { /** * Verifies that Node.js and TypeScript are installed and throws an exception otherwise. * - * @param verbose if true, log the Node.js executable path, version strings, and any additional arguments. + * @param verbose if true, log the Node.js executable path, version strings, and any additional + * arguments. */ public void verifyInstallation(boolean verbose) { verifyNodeInstallation(); @@ -158,7 +159,7 @@ public class TypeScriptParser { ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream err = new ByteArrayOutputStream(); - + // Determine where to find the Node.js runtime. String explicitNodeJsRuntime = Env.systemEnv().get(TYPESCRIPT_NODE_RUNTIME_VAR); if (explicitNodeJsRuntime != null) { @@ -175,7 +176,9 @@ public class TypeScriptParser { nodeJsRuntimeExtraArgs = Arrays.asList(extraArgs.split("\\s+")); } - Builder b = new Builder(getNodeJsRuntimeInvocation("--version"), out, err, getParserWrapper().getParentFile()); + Builder b = + new Builder( + getNodeJsRuntimeInvocation("--version"), out, err, getParserWrapper().getParentFile()); b.expectFailure(); // We want to do our own logging in case of an error. int timeout = Env.systemEnv().getInt(TYPESCRIPT_TIMEOUT_VAR, 10000); @@ -203,15 +206,14 @@ public class TypeScriptParser { } /** - * Gets a command line to invoke the Node.js runtime. - * Any arguments in {@link TypeScriptParser#nodeJsRuntimeExtraArgs} - * are passed first, followed by those in {@code args}. + * Gets a command line to invoke the Node.js runtime. Any arguments in {@link + * TypeScriptParser#nodeJsRuntimeExtraArgs} are passed first, followed by those in {@code args}. */ - private List getNodeJsRuntimeInvocation(String ...args) { + private List getNodeJsRuntimeInvocation(String... args) { List result = new ArrayList<>(); result.add(nodeJsRuntime); result.addAll(nodeJsRuntimeExtraArgs); - for(String arg : args) { + for (String arg : args) { result.add(arg); } return result; @@ -361,17 +363,22 @@ public class TypeScriptParser { * *

    If the file is not part of a project, only syntactic information will be extracted. */ - public Result parse(File sourceFile, String source) { + public Result parse(File sourceFile, String source, ExtractionMetrics metrics) { JsonObject request = new JsonObject(); request.add("command", new JsonPrimitive("parse")); request.add("filename", new JsonPrimitive(sourceFile.getAbsolutePath())); + metrics.startPhase(ExtractionMetrics.ExtractionPhase.TypeScriptParser_talkToParserWrapper); JsonObject response = talkToParserWrapper(request); + metrics.stopPhase(ExtractionMetrics.ExtractionPhase.TypeScriptParser_talkToParserWrapper); try { checkResponseType(response, "ast"); JsonObject nodeFlags = response.get("nodeFlags").getAsJsonObject(); JsonObject syntaxKinds = response.get("syntaxKinds").getAsJsonObject(); JsonObject ast = response.get("ast").getAsJsonObject(); - return new TypeScriptASTConverter(nodeFlags, syntaxKinds).convertAST(ast, source); + metrics.startPhase(ExtractionMetrics.ExtractionPhase.TypeScriptASTConverter_convertAST); + Result converted = new TypeScriptASTConverter(nodeFlags, syntaxKinds).convertAST(ast, source); + metrics.stopPhase(ExtractionMetrics.ExtractionPhase.TypeScriptASTConverter_convertAST); + return converted; } catch (IllegalStateException e) { throw new CatastrophicError( "TypeScript parser wrapper sent unexpected response: " + response, e); diff --git a/javascript/extractor/src/com/semmle/ts/ast/UnaryTypeExpr.java b/javascript/extractor/src/com/semmle/ts/ast/UnaryTypeExpr.java index 2f2619c6ece..67575a4fdca 100644 --- a/javascript/extractor/src/com/semmle/ts/ast/UnaryTypeExpr.java +++ b/javascript/extractor/src/com/semmle/ts/ast/UnaryTypeExpr.java @@ -6,7 +6,7 @@ import com.semmle.js.ast.Visitor; /** * A unary operator applied to a type. * - * This can be keyof T or readonly T. + *

    This can be keyof T or readonly T. */ public class UnaryTypeExpr extends TypeExpression { private final ITypeExpression elementType; diff --git a/javascript/extractor/src/com/semmle/ts/extractor/TypeExtractor.java b/javascript/extractor/src/com/semmle/ts/extractor/TypeExtractor.java index 8d1aae35681..ed9242490e9 100644 --- a/javascript/extractor/src/com/semmle/ts/extractor/TypeExtractor.java +++ b/javascript/extractor/src/com/semmle/ts/extractor/TypeExtractor.java @@ -4,7 +4,6 @@ import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.semmle.util.trap.TrapWriter; import com.semmle.util.trap.TrapWriter.Label; - import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; diff --git a/javascript/ql/src/meta/extraction-metrics/FileData.ql b/javascript/ql/src/meta/extraction-metrics/FileData.ql new file mode 100644 index 00000000000..3d5cbfed59f --- /dev/null +++ b/javascript/ql/src/meta/extraction-metrics/FileData.ql @@ -0,0 +1,25 @@ +/** + * @name Extraction metrics file data + * @description Extraction metrics and related information for profiling the extraction of individual files. + * @kind table + * @id js/meta/extraction/file-data + * @tags meta + */ + +import javascript +import semmle.javascript.meta.ExtractionMetrics::ExtractionMetrics + +FileWithExtractionMetrics getACacheMember(string cacheFile) { cacheFile = result.getCacheFile() } + +FileWithExtractionMetrics getACacheHit(FileWithExtractionMetrics f) { + result = getACacheMember(f.getCacheFile()) and + result.isFromCache() +} + +from FileWithExtractionMetrics file, boolean fromCache +where (if file.isFromCache() then fromCache = true else fromCache = false) +select file.getAbsolutePath() as FILE, file.getCpuTime() as CPU_NANO, + file.getNumberOfLines() as LINES, count(Locatable n | n.getFile() = file) as LOCATABLES, + count(TypeAnnotation n | n.getFile() = file) as TYPES, file.getLength() as LENGTH, + fromCache as FROM_CACHE, count(getACacheMember(file.getCacheFile())) as CACHE_MEMBERS, + count(getACacheHit(file)) as CACHE_HITS, file.getCacheFile() as CACHE_FILE diff --git a/javascript/ql/src/meta/extraction-metrics/MissingMetrics.ql b/javascript/ql/src/meta/extraction-metrics/MissingMetrics.ql new file mode 100644 index 00000000000..76216331f50 --- /dev/null +++ b/javascript/ql/src/meta/extraction-metrics/MissingMetrics.ql @@ -0,0 +1,18 @@ +/** + * @name File with missing extraction metrics + * @description A file missing extraction metrics is indicative of a faulty extractor. + * @kind problem + * @problem.severity warning + * @precision low + * @id js/meta/extraction/missing-metrics + * @tags meta + */ + +import javascript +import semmle.javascript.meta.ExtractionMetrics::ExtractionMetrics + +from File f, string cause +where not extraction_data(f, _, _, _) and cause = "No extraction_data for this file" + or + not extraction_time(f, _,_, _) and cause = "No extraction_time for this file" +select f, cause \ No newline at end of file diff --git a/javascript/ql/src/meta/extraction-metrics/PhaseTimings.ql b/javascript/ql/src/meta/extraction-metrics/PhaseTimings.ql new file mode 100644 index 00000000000..a9fd872eea1 --- /dev/null +++ b/javascript/ql/src/meta/extraction-metrics/PhaseTimings.ql @@ -0,0 +1,12 @@ +/** + * @name Extractor phase timings + * @description An overview of how time was spent during extraction + * @kind table + * @id js/meta/extraction/phase-timings + * @tags meta + */ + +import semmle.javascript.meta.ExtractionMetrics::ExtractionMetrics + +from PhaseName phaseName +select phaseName, Aggregated::getCpuTime(phaseName) as CPU_NANO, Aggregated::getWallclockTime(phaseName) as WALLCLOCK_NANO diff --git a/javascript/ql/src/semmle/javascript/meta/ExtractionMetrics.qll b/javascript/ql/src/semmle/javascript/meta/ExtractionMetrics.qll new file mode 100644 index 00000000000..2e21a3122b3 --- /dev/null +++ b/javascript/ql/src/semmle/javascript/meta/ExtractionMetrics.qll @@ -0,0 +1,130 @@ +import javascript + +/** + * INTERNAL: Do not use in ordinary queries. + * + * Extraction metrics for profiling extraction behaviours. + */ +module ExtractionMetrics { + /** + * A file with extraction metrics. + */ + class FileWithExtractionMetrics extends File { + FileWithExtractionMetrics() { + extraction_data(this, _, _, _) and extraction_time(this, _, _, _) + } + + /** + * Gets the CPU time in nanoseconds it took to extract this file. + */ + float getCpuTime() { result = strictsum(getTime(_, 0)) } + + /** + * Gets the wall-clock time in nanoseconds it took to extract this file. + */ + float getWallclockTime() { result = strictsum(getTime(_, 1)) } + + /** + * Gets the CPU time in nanoseconds it took to process phase `phaseName` during the extraction of this file. + */ + float getCpuTime(PhaseName phaseName) { result = getTime(phaseName, 0) } + + /** + * Gets the wall-clock time in nanoseconds it took to process phase `phaseName` during the extraction of this file. + */ + float getWallclockTime(PhaseName phaseName) { result = getTime(phaseName, 1) } + + /** + * Holds if this file was extracted from the trap cache. + */ + predicate isFromCache() { extraction_data(this, _, true, _) } + + /** + * Gets the path to the cache file used for extraction of this file. + */ + string getCacheFile() { extraction_data(this, result, _, _) } + + /** + * Gets the number of UTF16 code units in this file. + */ + int getLength() { extraction_data(this, _, _, result) } + + private float getTime(PhaseName phaseName, int timerKind) { + exists(float time | + // note that we use strictsum to make it clear if data is missing because it comes from an upgraded database. + strictsum(int phaseId, float r | + phaseName = getExtractionPhaseName(phaseId) and + extraction_time(this, phaseId, timerKind, r) + | + r + ) = time + | + // assume the cache-lookup was for free + if isFromCache() then result = 0 else result = time + ) + } + } + + /** + * Converts database ids to human-readable names. + */ + private string getExtractionPhaseName(int phaseId) { + // these names ought to match the names used in + // `com.semmle.js.extractor.ExtractionTimer.ExtractionPhase` + "ASTExtractor_extract" = result and 0 = phaseId + or + "CFGExtractor_extract" = result and 1 = phaseId + or + "FileExtractor_extractContents" = result and 2 = phaseId + or + "JSExtractor_extract" = result and 3 = phaseId + or + "JSParser_parse" = result and 4 = phaseId + or + "LexicalExtractor_extractLines" = result and 5 = phaseId + or + "LexicalExtractor_extractTokens" = result and 6 = phaseId + or + "TypeScriptASTConverter_convertAST" = result and 7 = phaseId + or + "TypeScriptParser_talkToParserWrapper" = result and 8 = phaseId + } + + /** + * The name of a phase of the extraction. + */ + class PhaseName extends string { + PhaseName() { this = getExtractionPhaseName(_) } + } + + /** + * Utilities for aggregating metrics for multiple files. + */ + module Aggregated { + /** + * Gets the total CPU time spent on extraction. + */ + float getCpuTime() { result = strictsum(any(FileWithExtractionMetrics f).getCpuTime()) } + + /** + * Gets the total wallclock time spent on extraction. + */ + float getWallclockTime() { + result = strictsum(any(FileWithExtractionMetrics f).getWallclockTime()) + } + + /** + * Gets the total CPU time spent in phase `phaseName` of the extraction. + */ + float getCpuTime(PhaseName phaseName) { + result = strictsum(any(FileWithExtractionMetrics f).getCpuTime(phaseName)) + } + + /** + * Gets the total wallclock time spent in phase `phaseName` of the extraction. + */ + float getWallclockTime(PhaseName phaseName) { + result = strictsum(any(FileWithExtractionMetrics f).getWallclockTime(phaseName)) + } + } +} diff --git a/javascript/ql/src/semmlecode.javascript.dbscheme b/javascript/ql/src/semmlecode.javascript.dbscheme index b9202b62ec8..56b74a6808d 100644 --- a/javascript/ql/src/semmlecode.javascript.dbscheme +++ b/javascript/ql/src/semmlecode.javascript.dbscheme @@ -1136,3 +1136,32 @@ configLocations( ); @configLocatable = @config | @configName | @configValue; + +/** + * The time taken for the extraction of a file. + * This table contains non-deterministic content. + * + * The sum of the `time` column for each (`file`, `timerKind`) pair + * is the total time taken for extraction of `file`. The `extractionPhase` + * column provides a granular view of the extraction time of the file. + */ +extraction_time( + int file : @file ref, + // see `com.semmle.js.extractor.ExtractionMetrics.ExtractionPhase`. + int extractionPhase: int ref, + // 0 for the elapsed CPU time in nanoseconds, 1 for the elapsed wallclock time in nanoseconds + int timerKind: int ref, + float time: float ref +) + +/** + * Non-timing related data for the extraction of a single file. + * This table contains non-deterministic content. + */ +extraction_data( + int file : @file ref, + // the absolute path to the cache file + varchar(900) cacheFile: string ref, + boolean fromCache: boolean ref, + int length: int ref +) \ No newline at end of file diff --git a/javascript/ql/src/semmlecode.javascript.dbscheme.stats b/javascript/ql/src/semmlecode.javascript.dbscheme.stats index 725fa27fd1f..01026d1ec3e 100644 --- a/javascript/ql/src/semmlecode.javascript.dbscheme.stats +++ b/javascript/ql/src/semmlecode.javascript.dbscheme.stats @@ -30782,5 +30782,457 @@ + +extraction_time +378 + + +file +21 + + +extractionPhase +9 + + +timerKind +2 + + +time +43 + + + + +file +extractionPhase + + +12 + + +9 +10 +21 + + + + + + +file +timerKind + + +12 + + +2 +3 +21 + + + + + + +file +time + + +12 + + +3 +4 +21 + + + + + + +extractionPhase +file + + +12 + + +21 +22 +9 + + + + + + +extractionPhase +timerKind + + +12 + + +2 +3 +9 + + + + + + +extractionPhase +time + + +12 + + +1 +2 +8 + + +42 +43 +1 + + + + + + +timerKind +file + + +12 + + +21 +22 +2 + + + + + + +timerKind +extractionPhase + + +12 + + +9 +10 +2 + + + + + + +timerKind +time + + +12 + + +22 +23 +2 + + + + + + +time +file + + +12 + + +1 +2 +42 + + +21 +22 +1 + + + + + + +time +extractionPhase + + +12 + + +1 +2 +42 + + +8 +9 +1 + + + + + + +time +timerKind + + +12 + + +1 +2 +42 + + +2 +3 +1 + + + + + + + + +extraction_data +21 + + +file +21 + + +cacheFile +21 + + +fromCache +1 + + +length +21 + + + + +file +cacheFile + + +12 + + +1 +2 +21 + + + + + + +file +fromCache + + +12 + + +1 +2 +21 + + + + + + +file +length + + +12 + + +1 +2 +21 + + + + + + +cacheFile +file + + +12 + + +1 +2 +21 + + + + + + +cacheFile +fromCache + + +12 + + +1 +2 +21 + + + + + + +cacheFile +length + + +12 + + +1 +2 +21 + + + + + + +fromCache +file + + +12 + + +21 +22 +1 + + + + + + +fromCache +cacheFile + + +12 + + +21 +22 +1 + + + + + + +fromCache +length + + +12 + + +21 +22 +1 + + + + + + +length +file + + +12 + + +1 +2 +21 + + + + + + +length +cacheFile + + +12 + + +1 +2 +21 + + + + + + +length +fromCache + + +12 + + +1 +2 +21 + + + + + + + diff --git a/javascript/ql/test/library-tests/meta/Extraction/FileWithExtractionMetrics.expected b/javascript/ql/test/library-tests/meta/Extraction/FileWithExtractionMetrics.expected new file mode 100644 index 00000000000..6232c852ab6 --- /dev/null +++ b/javascript/ql/test/library-tests/meta/Extraction/FileWithExtractionMetrics.expected @@ -0,0 +1,4 @@ +| tst2.js:0:0:0:0 | tst2.js | +| tst.html:0:0:0:0 | tst.html | +| tst.js:0:0:0:0 | tst.js | +| tst.ts:0:0:0:0 | tst.ts | diff --git a/javascript/ql/test/library-tests/meta/Extraction/FileWithExtractionMetrics.ql b/javascript/ql/test/library-tests/meta/Extraction/FileWithExtractionMetrics.ql new file mode 100644 index 00000000000..92d439fabb2 --- /dev/null +++ b/javascript/ql/test/library-tests/meta/Extraction/FileWithExtractionMetrics.ql @@ -0,0 +1,4 @@ +import semmle.javascript.meta.ExtractionMetrics::ExtractionMetrics + +from FileWithExtractionMetrics f +select f diff --git a/javascript/ql/test/library-tests/meta/Extraction/FileWithExtractionMetrics_length.expected b/javascript/ql/test/library-tests/meta/Extraction/FileWithExtractionMetrics_length.expected new file mode 100644 index 00000000000..afed63c473b --- /dev/null +++ b/javascript/ql/test/library-tests/meta/Extraction/FileWithExtractionMetrics_length.expected @@ -0,0 +1,4 @@ +| tst2.js:0:0:0:0 | tst2.js | 26 | +| tst.html:0:0:0:0 | tst.html | 127 | +| tst.js:0:0:0:0 | tst.js | 26 | +| tst.ts:0:0:0:0 | tst.ts | 31 | diff --git a/javascript/ql/test/library-tests/meta/Extraction/FileWithExtractionMetrics_length.ql b/javascript/ql/test/library-tests/meta/Extraction/FileWithExtractionMetrics_length.ql new file mode 100644 index 00000000000..51b17dae29f --- /dev/null +++ b/javascript/ql/test/library-tests/meta/Extraction/FileWithExtractionMetrics_length.ql @@ -0,0 +1,4 @@ +import semmle.javascript.meta.ExtractionMetrics::ExtractionMetrics + +from FileWithExtractionMetrics f +select f, f.getLength() diff --git a/javascript/ql/test/library-tests/meta/Extraction/FileWithExtractionMetrics_phases.expected b/javascript/ql/test/library-tests/meta/Extraction/FileWithExtractionMetrics_phases.expected new file mode 100644 index 00000000000..53266032907 --- /dev/null +++ b/javascript/ql/test/library-tests/meta/Extraction/FileWithExtractionMetrics_phases.expected @@ -0,0 +1,36 @@ +| tst2.js:0:0:0:0 | tst2.js | ASTExtractor_extract | +| tst2.js:0:0:0:0 | tst2.js | CFGExtractor_extract | +| tst2.js:0:0:0:0 | tst2.js | FileExtractor_extractContents | +| tst2.js:0:0:0:0 | tst2.js | JSExtractor_extract | +| tst2.js:0:0:0:0 | tst2.js | JSParser_parse | +| tst2.js:0:0:0:0 | tst2.js | LexicalExtractor_extractLines | +| tst2.js:0:0:0:0 | tst2.js | LexicalExtractor_extractTokens | +| tst2.js:0:0:0:0 | tst2.js | TypeScriptASTConverter_convertAST | +| tst2.js:0:0:0:0 | tst2.js | TypeScriptParser_talkToParserWrapper | +| tst.html:0:0:0:0 | tst.html | ASTExtractor_extract | +| tst.html:0:0:0:0 | tst.html | CFGExtractor_extract | +| tst.html:0:0:0:0 | tst.html | FileExtractor_extractContents | +| tst.html:0:0:0:0 | tst.html | JSExtractor_extract | +| tst.html:0:0:0:0 | tst.html | JSParser_parse | +| tst.html:0:0:0:0 | tst.html | LexicalExtractor_extractLines | +| tst.html:0:0:0:0 | tst.html | LexicalExtractor_extractTokens | +| tst.html:0:0:0:0 | tst.html | TypeScriptASTConverter_convertAST | +| tst.html:0:0:0:0 | tst.html | TypeScriptParser_talkToParserWrapper | +| tst.js:0:0:0:0 | tst.js | ASTExtractor_extract | +| tst.js:0:0:0:0 | tst.js | CFGExtractor_extract | +| tst.js:0:0:0:0 | tst.js | FileExtractor_extractContents | +| tst.js:0:0:0:0 | tst.js | JSExtractor_extract | +| tst.js:0:0:0:0 | tst.js | JSParser_parse | +| tst.js:0:0:0:0 | tst.js | LexicalExtractor_extractLines | +| tst.js:0:0:0:0 | tst.js | LexicalExtractor_extractTokens | +| tst.js:0:0:0:0 | tst.js | TypeScriptASTConverter_convertAST | +| tst.js:0:0:0:0 | tst.js | TypeScriptParser_talkToParserWrapper | +| tst.ts:0:0:0:0 | tst.ts | ASTExtractor_extract | +| tst.ts:0:0:0:0 | tst.ts | CFGExtractor_extract | +| tst.ts:0:0:0:0 | tst.ts | FileExtractor_extractContents | +| tst.ts:0:0:0:0 | tst.ts | JSExtractor_extract | +| tst.ts:0:0:0:0 | tst.ts | JSParser_parse | +| tst.ts:0:0:0:0 | tst.ts | LexicalExtractor_extractLines | +| tst.ts:0:0:0:0 | tst.ts | LexicalExtractor_extractTokens | +| tst.ts:0:0:0:0 | tst.ts | TypeScriptASTConverter_convertAST | +| tst.ts:0:0:0:0 | tst.ts | TypeScriptParser_talkToParserWrapper | diff --git a/javascript/ql/test/library-tests/meta/Extraction/FileWithExtractionMetrics_phases.ql b/javascript/ql/test/library-tests/meta/Extraction/FileWithExtractionMetrics_phases.ql new file mode 100644 index 00000000000..d528cea5b84 --- /dev/null +++ b/javascript/ql/test/library-tests/meta/Extraction/FileWithExtractionMetrics_phases.ql @@ -0,0 +1,7 @@ +import semmle.javascript.meta.ExtractionMetrics::ExtractionMetrics + +from FileWithExtractionMetrics f, PhaseName phase +where + exists(f.getCpuTime(phase)) and + exists(f.getWallclockTime(phase)) +select f, phase diff --git a/javascript/ql/test/library-tests/meta/Extraction/tst.html b/javascript/ql/test/library-tests/meta/Extraction/tst.html new file mode 100644 index 00000000000..fbba27110e1 --- /dev/null +++ b/javascript/ql/test/library-tests/meta/Extraction/tst.html @@ -0,0 +1,7 @@ + + + diff --git a/javascript/ql/test/library-tests/meta/Extraction/tst.js b/javascript/ql/test/library-tests/meta/Extraction/tst.js new file mode 100644 index 00000000000..2c62e7b5027 --- /dev/null +++ b/javascript/ql/test/library-tests/meta/Extraction/tst.js @@ -0,0 +1 @@ +console.log("extract me") diff --git a/javascript/ql/test/library-tests/meta/Extraction/tst.ts b/javascript/ql/test/library-tests/meta/Extraction/tst.ts new file mode 100644 index 00000000000..7665c35799a --- /dev/null +++ b/javascript/ql/test/library-tests/meta/Extraction/tst.ts @@ -0,0 +1 @@ +console.log("extract me too"); diff --git a/javascript/ql/test/library-tests/meta/Extraction/tst2.js b/javascript/ql/test/library-tests/meta/Extraction/tst2.js new file mode 100644 index 00000000000..2c62e7b5027 --- /dev/null +++ b/javascript/ql/test/library-tests/meta/Extraction/tst2.js @@ -0,0 +1 @@ +console.log("extract me") diff --git a/javascript/upgrades/b9202b62ec8a0304316515941cb45646c6dd6fdd/old.dbscheme b/javascript/upgrades/b9202b62ec8a0304316515941cb45646c6dd6fdd/old.dbscheme new file mode 100644 index 00000000000..b9202b62ec8 --- /dev/null +++ b/javascript/upgrades/b9202b62ec8a0304316515941cb45646c6dd6fdd/old.dbscheme @@ -0,0 +1,1138 @@ +/*** 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; +@type_annotation = @typeexpr | @jsdoc_type_expr; + +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 +| 108 = @e4x_xml_anyname +| 109 = @e4x_xml_static_attribute_selector +| 110 = @e4x_xml_dynamic_attribute_selector +| 111 = @e4x_xml_filter_expression +| 112 = @e4x_xml_static_qualident +| 113 = @e4x_xml_dynamic_qualident +| 114 = @e4x_xml_dotdotexpr +; + +@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; + +@e4x_xml_attribute_selector = @e4x_xml_static_attribute_selector | @e4x_xml_dynamic_attribute_selector; +@e4x_xml_qualident = @e4x_xml_static_qualident | @e4x_xml_dynamic_qualident; + +// 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 +| 36 = @readonlytypeexpr +; + +@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); + +json_locations(unique int locatable: @json_locatable ref, + int location: @location_default 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; + +@json_locatable = @json_value | @json_parse_error; + +// locations +@ast_node = @toplevel | @stmt | @expr | @property | @typeexpr; + +@locatable = @file + | @ast_node + | @comment + | @line + | @js_parse_error | @regexp_parse_error + | @regexpterm + | @json_locatable + | @token + | @cfg_node + | @jsdoc | @jsdoc_type_expr | @jsdoc_tag + | @yaml_locatable + | @xmllocatable + | @configLocatable; + +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); + +yaml_locations(unique int locatable: @yaml_locatable ref, + int location: @location_default ref); + +@yaml_locatable = @yaml_node | @yaml_error; + +/* 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); + +/* + * configuration files with key value pairs + */ + +configs( + unique int id: @config +); + +configNames( + unique int id: @configName, + int config: @config ref, + string name: string ref +); + +configValues( + unique int id: @configValue, + int config: @config ref, + string value: string ref +); + +configLocations( + int locatable: @configLocatable ref, + int location: @location_default ref +); + +@configLocatable = @config | @configName | @configValue; diff --git a/javascript/upgrades/b9202b62ec8a0304316515941cb45646c6dd6fdd/semmlecode.javascript.dbscheme b/javascript/upgrades/b9202b62ec8a0304316515941cb45646c6dd6fdd/semmlecode.javascript.dbscheme new file mode 100644 index 00000000000..56b74a6808d --- /dev/null +++ b/javascript/upgrades/b9202b62ec8a0304316515941cb45646c6dd6fdd/semmlecode.javascript.dbscheme @@ -0,0 +1,1167 @@ +/*** 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; +@type_annotation = @typeexpr | @jsdoc_type_expr; + +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 +| 108 = @e4x_xml_anyname +| 109 = @e4x_xml_static_attribute_selector +| 110 = @e4x_xml_dynamic_attribute_selector +| 111 = @e4x_xml_filter_expression +| 112 = @e4x_xml_static_qualident +| 113 = @e4x_xml_dynamic_qualident +| 114 = @e4x_xml_dotdotexpr +; + +@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; + +@e4x_xml_attribute_selector = @e4x_xml_static_attribute_selector | @e4x_xml_dynamic_attribute_selector; +@e4x_xml_qualident = @e4x_xml_static_qualident | @e4x_xml_dynamic_qualident; + +// 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 +| 36 = @readonlytypeexpr +; + +@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); + +json_locations(unique int locatable: @json_locatable ref, + int location: @location_default 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; + +@json_locatable = @json_value | @json_parse_error; + +// locations +@ast_node = @toplevel | @stmt | @expr | @property | @typeexpr; + +@locatable = @file + | @ast_node + | @comment + | @line + | @js_parse_error | @regexp_parse_error + | @regexpterm + | @json_locatable + | @token + | @cfg_node + | @jsdoc | @jsdoc_type_expr | @jsdoc_tag + | @yaml_locatable + | @xmllocatable + | @configLocatable; + +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); + +yaml_locations(unique int locatable: @yaml_locatable ref, + int location: @location_default ref); + +@yaml_locatable = @yaml_node | @yaml_error; + +/* 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); + +/* + * configuration files with key value pairs + */ + +configs( + unique int id: @config +); + +configNames( + unique int id: @configName, + int config: @config ref, + string name: string ref +); + +configValues( + unique int id: @configValue, + int config: @config ref, + string value: string ref +); + +configLocations( + int locatable: @configLocatable ref, + int location: @location_default ref +); + +@configLocatable = @config | @configName | @configValue; + +/** + * The time taken for the extraction of a file. + * This table contains non-deterministic content. + * + * The sum of the `time` column for each (`file`, `timerKind`) pair + * is the total time taken for extraction of `file`. The `extractionPhase` + * column provides a granular view of the extraction time of the file. + */ +extraction_time( + int file : @file ref, + // see `com.semmle.js.extractor.ExtractionMetrics.ExtractionPhase`. + int extractionPhase: int ref, + // 0 for the elapsed CPU time in nanoseconds, 1 for the elapsed wallclock time in nanoseconds + int timerKind: int ref, + float time: float ref +) + +/** + * Non-timing related data for the extraction of a single file. + * This table contains non-deterministic content. + */ +extraction_data( + int file : @file ref, + // the absolute path to the cache file + varchar(900) cacheFile: string ref, + boolean fromCache: boolean ref, + int length: int ref +) \ No newline at end of file diff --git a/javascript/upgrades/b9202b62ec8a0304316515941cb45646c6dd6fdd/upgrade.properties b/javascript/upgrades/b9202b62ec8a0304316515941cb45646c6dd6fdd/upgrade.properties new file mode 100644 index 00000000000..9f29b948d94 --- /dev/null +++ b/javascript/upgrades/b9202b62ec8a0304316515941cb45646c6dd6fdd/upgrade.properties @@ -0,0 +1,2 @@ +description: adds extraction metrics tables +compatibility: backwards \ No newline at end of file