From 05a4f5d7c8d6b5a93ee9cf1e7c3f67e196d06fe8 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 24 Nov 2018 20:56:15 -0500 Subject: [PATCH] trace dependencies where possible --- src/compile/Component.ts | 71 ++++++++++++++++++++++++++++++--- src/utils/annotateWithScopes.ts | 12 +++--- 2 files changed, 72 insertions(+), 11 deletions(-) diff --git a/src/compile/Component.ts b/src/compile/Component.ts index e0141e075d..057df7b5e2 100644 --- a/src/compile/Component.ts +++ b/src/compile/Component.ts @@ -16,6 +16,7 @@ import error from '../utils/error'; import getCodeFrame from '../utils/getCodeFrame'; import flattenReference from '../utils/flattenReference'; import addToSet from '../utils/addToSet'; +import isReference from 'is-reference'; type Meta = { namespace?: string; @@ -51,7 +52,8 @@ export default class Component { name: string; options: CompileOptions; fragment: Fragment; - scope: Scope; + module_scope: Scope; + module_scope_map: WeakMap; meta: Meta; @@ -379,8 +381,59 @@ export default class Component { } findDependenciesForFunctionCall(name) { - // TODO - return null; + const declaration = this.node_for_declaration.get(name); + + const dependencies = new Set(); + + if (!declaration) { + // Global or module-scoped function — can't have + // local state as dependency by definition + return dependencies; + } + + let { module_scope, module_scope_map: map } = this; + let scope = module_scope; + + const component = this; + let bail = false; + + walk(declaration, { + enter(node, parent) { + if (map.has(node)) { + scope = map.get(node); + } + + if (isReference(node, parent)) { + const { name } = flattenReference(node); + if (scope.findOwner(name) === module_scope) { + dependencies.add(name); + } + } + + if (node.type === 'CallExpression') { + if (node.callee.type === 'Identifier') { + const call_dependencies = component.findDependenciesForFunctionCall(node.callee.name); + if (!call_dependencies) { + bail = true; + return this.skip(); + } + + addToSet(dependencies, call_dependencies); + } else { + bail = true; + return this.skip(); + } + } + }, + + leave(node) { + if (map.has(node)) { + scope = map.get(node); + } + } + }); + + return bail ? null : dependencies; } extract_imports_and_exports(content, imports, exports) { @@ -448,6 +501,11 @@ export default class Component { this.addSourcemapLocations(script.content); + // const { scope, map, globals } = createScopes(script.content); + // scope.declarations.forEach((node, name) => { + // this.node_for_declaration.set(name, node); + // }); + // TODO unindent this.extract_imports_and_exports(script.content, this.imports, this.module_exports); @@ -471,11 +529,14 @@ export default class Component { }); let { scope, map, globals } = createScopes(script.content); - this.scope = scope; + this.module_scope = scope; + this.module_scope_map = map; - scope.declarations.forEach(name => { + scope.declarations.forEach((node, name) => { this.userVars.add(name); this.declarations.push(name); + + this.node_for_declaration.set(name, node); }); this.writable_declarations = scope.writable_declarations; diff --git a/src/utils/annotateWithScopes.ts b/src/utils/annotateWithScopes.ts index 5d316d0797..e89b3b50d6 100644 --- a/src/utils/annotateWithScopes.ts +++ b/src/utils/annotateWithScopes.ts @@ -12,18 +12,18 @@ export function createScopes(expression: Node) { enter(node: Node, parent: Node) { if (/Function/.test(node.type)) { if (node.type === 'FunctionDeclaration') { - scope.declarations.add(node.id.name); + scope.declarations.set(node.id.name, node); scope = new Scope(scope, false); map.set(node, scope); } else { scope = new Scope(scope, false); map.set(node, scope); - if (node.id) scope.declarations.add(node.id.name); + if (node.id) scope.declarations.set(node.id.name, node); } node.params.forEach((param: Node) => { extractNames(param).forEach(name => { - scope.declarations.add(name); + scope.declarations.set(name, node); }); }); } else if (/For(?:In|Of)Statement/.test(node.type)) { @@ -55,7 +55,7 @@ export class Scope { parent: Scope; block: boolean; - declarations: Set = new Set(); + declarations: Map = new Map(); writable_declarations: Set = new Set(); initialised_declarations: Set = new Set(); @@ -73,13 +73,13 @@ export class Scope { node.declarations.forEach((declarator: Node) => { extractNames(declarator.id).forEach(name => { - this.declarations.add(name); + this.declarations.set(name, node); if (writable) this.writable_declarations.add(name); if (initialised) this.initialised_declarations.add(name); }); }); } else { - this.declarations.add(node.id.name); + this.declarations.set(node.id.name, node); } }