From ce890c98d14eec0aa909fed2be5ced2692e7463a Mon Sep 17 00:00:00 2001 From: Ben Newman Date: Thu, 4 Feb 2016 12:02:14 -0500 Subject: [PATCH] Share a cache of ASTs between js-analyze.js functions. --- tools/isobuild/import-scanner.js | 6 ++++-- tools/isobuild/js-analyze.js | 32 ++++++++++++++++++++++++++------ tools/isobuild/linker.js | 4 ++-- 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/tools/isobuild/import-scanner.js b/tools/isobuild/import-scanner.js index 3d46f7756a..1d2dac8bdb 100644 --- a/tools/isobuild/import-scanner.js +++ b/tools/isobuild/import-scanner.js @@ -126,8 +126,10 @@ export default class ImportScanner { return IMPORT_SCANNER_CACHE.get(file.hash); } - const result = - keys(findImportedModuleIdentifiers(file.data.toString("utf8"))); + const result = keys(findImportedModuleIdentifiers( + file.data.toString("utf8"), + file.hash, + )); // there should always be file.hash, but better safe than sorry if (file.hash) { diff --git a/tools/isobuild/js-analyze.js b/tools/isobuild/js-analyze.js index 353334e9b2..155be24fa5 100644 --- a/tools/isobuild/js-analyze.js +++ b/tools/isobuild/js-analyze.js @@ -1,10 +1,24 @@ import { parse } from 'meteor-babel'; import { analyze as analyzeScope } from 'escope'; +import LRU from "lru-cache"; + +var AST_CACHE = new LRU({ + max: Math.pow(2, 12), + length(ast) { + return ast.loc.end.line; + } +}); // Like babel.parse, but annotates any thrown error with $ParseError = true. -function tryToParse(source) { +function tryToParse(source, hash) { + if (hash && AST_CACHE.has(hash)) { + return AST_CACHE.get(hash); + } + + let ast; + try { - return parse(source, { + ast = parse(source, { strictMode: false, ecmaVersion: 6, sourceType: "module", @@ -17,6 +31,12 @@ function tryToParse(source) { } throw e; } + + if (hash) { + AST_CACHE.set(hash, ast); + } + + return ast; } var dependencyKeywordPattern = /\b(require|import|export)\b/g; @@ -35,7 +55,7 @@ var dependencyKeywordPattern = /\b(require|import|export)\b/g; * if the tokens were actually what we thought they were (a `require` * function call, or an `import` or `export` statement). */ -export function findImportedModuleIdentifiers(source) { +export function findImportedModuleIdentifiers(source, hash) { const identifiers = {}; const possibleIndexes = []; let match; @@ -49,7 +69,7 @@ export function findImportedModuleIdentifiers(source) { return {}; } - const ast = tryToParse(source); + const ast = tryToParse(source, hash); function walk(node, left, right) { if (left >= right) { @@ -149,8 +169,8 @@ function getImportedModuleId(node) { // // It only cares about assignments to variables; an assignment to a field on an // object (`Foo.Bar = true`) neither causes `Foo` nor `Foo.Bar` to be returned. -export function findAssignedGlobals(source) { - const ast = tryToParse(source); +export function findAssignedGlobals(source, hash) { + const ast = tryToParse(source, hash); // We have to pass ignoreEval; otherwise, the existence of a direct eval call // causes escope to not bother to resolve references in the eval's scope. diff --git a/tools/isobuild/linker.js b/tools/isobuild/linker.js index cbecf3f046..680f1fd981 100644 --- a/tools/isobuild/linker.js +++ b/tools/isobuild/linker.js @@ -456,8 +456,8 @@ _.extend(File.prototype, { } try { - return (ASSIGNED_GLOBALS_CACHE[self.sourceHash] = - _.keys(findAssignedGlobals(self.source))); + return ASSIGNED_GLOBALS_CACHE[self.sourceHash] = + _.keys(findAssignedGlobals(self.source, self.sourceHash)); } catch (e) { if (!e.$ParseError) { throw e;