123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748 |
- "use strict";
- const Syntax = require("estraverse").Syntax;
- const Reference = require("./reference");
- const Variable = require("./variable");
- const Definition = require("./definition").Definition;
- const assert = require("assert");
- function isStrictScope(scope, block, isMethodDefinition, useDirective) {
- let body;
-
- if (scope.upper && scope.upper.isStrict) {
- return true;
- }
- if (isMethodDefinition) {
- return true;
- }
- if (scope.type === "class" || scope.type === "module") {
- return true;
- }
- if (scope.type === "block" || scope.type === "switch") {
- return false;
- }
- if (scope.type === "function") {
- if (block.type === Syntax.ArrowFunctionExpression && block.body.type !== Syntax.BlockStatement) {
- return false;
- }
- if (block.type === Syntax.Program) {
- body = block;
- } else {
- body = block.body;
- }
- if (!body) {
- return false;
- }
- } else if (scope.type === "global") {
- body = block;
- } else {
- return false;
- }
-
- if (useDirective) {
- for (let i = 0, iz = body.body.length; i < iz; ++i) {
- const stmt = body.body[i];
- if (stmt.type !== Syntax.DirectiveStatement) {
- break;
- }
- if (stmt.raw === "\"use strict\"" || stmt.raw === "'use strict'") {
- return true;
- }
- }
- } else {
- for (let i = 0, iz = body.body.length; i < iz; ++i) {
- const stmt = body.body[i];
- if (stmt.type !== Syntax.ExpressionStatement) {
- break;
- }
- const expr = stmt.expression;
- if (expr.type !== Syntax.Literal || typeof expr.value !== "string") {
- break;
- }
- if (expr.raw !== null && expr.raw !== undefined) {
- if (expr.raw === "\"use strict\"" || expr.raw === "'use strict'") {
- return true;
- }
- } else {
- if (expr.value === "use strict") {
- return true;
- }
- }
- }
- }
- return false;
- }
- function registerScope(scopeManager, scope) {
- scopeManager.scopes.push(scope);
- const scopes = scopeManager.__nodeToScope.get(scope.block);
- if (scopes) {
- scopes.push(scope);
- } else {
- scopeManager.__nodeToScope.set(scope.block, [scope]);
- }
- }
- function shouldBeStatically(def) {
- return (
- (def.type === Variable.ClassName) ||
- (def.type === Variable.Variable && def.parent.kind !== "var")
- );
- }
- class Scope {
- constructor(scopeManager, type, upperScope, block, isMethodDefinition) {
-
- this.type = type;
-
- this.set = new Map();
-
- this.taints = new Map();
-
- this.dynamic = this.type === "global" || this.type === "with";
-
- this.block = block;
-
- this.through = [];
-
- this.variables = [];
-
- this.references = [];
-
- this.variableScope =
- (this.type === "global" || this.type === "function" || this.type === "module") ? this : upperScope.variableScope;
-
- this.functionExpressionScope = false;
-
- this.directCallToEvalScope = false;
-
- this.thisFound = false;
- this.__left = [];
-
- this.upper = upperScope;
-
- this.isStrict = isStrictScope(this, block, isMethodDefinition, scopeManager.__useDirective());
-
- this.childScopes = [];
- if (this.upper) {
- this.upper.childScopes.push(this);
- }
- this.__declaredVariables = scopeManager.__declaredVariables;
- registerScope(scopeManager, this);
- }
- __shouldStaticallyClose(scopeManager) {
- return (!this.dynamic || scopeManager.__isOptimistic());
- }
- __shouldStaticallyCloseForGlobal(ref) {
-
- const name = ref.identifier.name;
- if (!this.set.has(name)) {
- return false;
- }
- const variable = this.set.get(name);
- const defs = variable.defs;
- return defs.length > 0 && defs.every(shouldBeStatically);
- }
- __staticCloseRef(ref) {
- if (!this.__resolve(ref)) {
- this.__delegateToUpperScope(ref);
- }
- }
- __dynamicCloseRef(ref) {
-
- let current = this;
- do {
- current.through.push(ref);
- current = current.upper;
- } while (current);
- }
- __globalCloseRef(ref) {
-
-
- if (this.__shouldStaticallyCloseForGlobal(ref)) {
- this.__staticCloseRef(ref);
- } else {
- this.__dynamicCloseRef(ref);
- }
- }
- __close(scopeManager) {
- let closeRef;
- if (this.__shouldStaticallyClose(scopeManager)) {
- closeRef = this.__staticCloseRef;
- } else if (this.type !== "global") {
- closeRef = this.__dynamicCloseRef;
- } else {
- closeRef = this.__globalCloseRef;
- }
-
- for (let i = 0, iz = this.__left.length; i < iz; ++i) {
- const ref = this.__left[i];
- closeRef.call(this, ref);
- }
- this.__left = null;
- return this.upper;
- }
-
-
- __isValidResolution(ref, variable) {
- return true;
- }
- __resolve(ref) {
- const name = ref.identifier.name;
- if (!this.set.has(name)) {
- return false;
- }
- const variable = this.set.get(name);
- if (!this.__isValidResolution(ref, variable)) {
- return false;
- }
- variable.references.push(ref);
- variable.stack = variable.stack && ref.from.variableScope === this.variableScope;
- if (ref.tainted) {
- variable.tainted = true;
- this.taints.set(variable.name, true);
- }
- ref.resolved = variable;
- return true;
- }
- __delegateToUpperScope(ref) {
- if (this.upper) {
- this.upper.__left.push(ref);
- }
- this.through.push(ref);
- }
- __addDeclaredVariablesOfNode(variable, node) {
- if (node === null || node === undefined) {
- return;
- }
- let variables = this.__declaredVariables.get(node);
- if (variables === null || variables === undefined) {
- variables = [];
- this.__declaredVariables.set(node, variables);
- }
- if (variables.indexOf(variable) === -1) {
- variables.push(variable);
- }
- }
- __defineGeneric(name, set, variables, node, def) {
- let variable;
- variable = set.get(name);
- if (!variable) {
- variable = new Variable(name, this);
- set.set(name, variable);
- variables.push(variable);
- }
- if (def) {
- variable.defs.push(def);
- this.__addDeclaredVariablesOfNode(variable, def.node);
- this.__addDeclaredVariablesOfNode(variable, def.parent);
- }
- if (node) {
- variable.identifiers.push(node);
- }
- }
- __define(node, def) {
- if (node && node.type === Syntax.Identifier) {
- this.__defineGeneric(
- node.name,
- this.set,
- this.variables,
- node,
- def
- );
- }
- }
- __referencing(node, assign, writeExpr, maybeImplicitGlobal, partial, init) {
-
- if (!node || node.type !== Syntax.Identifier) {
- return;
- }
-
- if (node.name === "super") {
- return;
- }
- const ref = new Reference(node, this, assign || Reference.READ, writeExpr, maybeImplicitGlobal, !!partial, !!init);
- this.references.push(ref);
- this.__left.push(ref);
- }
- __detectEval() {
- let current = this;
- this.directCallToEvalScope = true;
- do {
- current.dynamic = true;
- current = current.upper;
- } while (current);
- }
- __detectThis() {
- this.thisFound = true;
- }
- __isClosed() {
- return this.__left === null;
- }
-
- resolve(ident) {
- let ref, i, iz;
- assert(this.__isClosed(), "Scope should be closed.");
- assert(ident.type === Syntax.Identifier, "Target should be identifier.");
- for (i = 0, iz = this.references.length; i < iz; ++i) {
- ref = this.references[i];
- if (ref.identifier === ident) {
- return ref;
- }
- }
- return null;
- }
-
- isStatic() {
- return !this.dynamic;
- }
-
- isArgumentsMaterialized() {
- return true;
- }
-
- isThisMaterialized() {
- return true;
- }
- isUsedName(name) {
- if (this.set.has(name)) {
- return true;
- }
- for (let i = 0, iz = this.through.length; i < iz; ++i) {
- if (this.through[i].identifier.name === name) {
- return true;
- }
- }
- return false;
- }
- }
- class GlobalScope extends Scope {
- constructor(scopeManager, block) {
- super(scopeManager, "global", null, block, false);
- this.implicit = {
- set: new Map(),
- variables: [],
-
- left: []
- };
- }
- __close(scopeManager) {
- const implicit = [];
- for (let i = 0, iz = this.__left.length; i < iz; ++i) {
- const ref = this.__left[i];
- if (ref.__maybeImplicitGlobal && !this.set.has(ref.identifier.name)) {
- implicit.push(ref.__maybeImplicitGlobal);
- }
- }
-
- for (let i = 0, iz = implicit.length; i < iz; ++i) {
- const info = implicit[i];
- this.__defineImplicit(info.pattern,
- new Definition(
- Variable.ImplicitGlobalVariable,
- info.pattern,
- info.node,
- null,
- null,
- null
- ));
- }
- this.implicit.left = this.__left;
- return super.__close(scopeManager);
- }
- __defineImplicit(node, def) {
- if (node && node.type === Syntax.Identifier) {
- this.__defineGeneric(
- node.name,
- this.implicit.set,
- this.implicit.variables,
- node,
- def
- );
- }
- }
- }
- class ModuleScope extends Scope {
- constructor(scopeManager, upperScope, block) {
- super(scopeManager, "module", upperScope, block, false);
- }
- }
- class FunctionExpressionNameScope extends Scope {
- constructor(scopeManager, upperScope, block) {
- super(scopeManager, "function-expression-name", upperScope, block, false);
- this.__define(block.id,
- new Definition(
- Variable.FunctionName,
- block.id,
- block,
- null,
- null,
- null
- ));
- this.functionExpressionScope = true;
- }
- }
- class CatchScope extends Scope {
- constructor(scopeManager, upperScope, block) {
- super(scopeManager, "catch", upperScope, block, false);
- }
- }
- class WithScope extends Scope {
- constructor(scopeManager, upperScope, block) {
- super(scopeManager, "with", upperScope, block, false);
- }
- __close(scopeManager) {
- if (this.__shouldStaticallyClose(scopeManager)) {
- return super.__close(scopeManager);
- }
- for (let i = 0, iz = this.__left.length; i < iz; ++i) {
- const ref = this.__left[i];
- ref.tainted = true;
- this.__delegateToUpperScope(ref);
- }
- this.__left = null;
- return this.upper;
- }
- }
- class BlockScope extends Scope {
- constructor(scopeManager, upperScope, block) {
- super(scopeManager, "block", upperScope, block, false);
- }
- }
- class SwitchScope extends Scope {
- constructor(scopeManager, upperScope, block) {
- super(scopeManager, "switch", upperScope, block, false);
- }
- }
- class FunctionScope extends Scope {
- constructor(scopeManager, upperScope, block, isMethodDefinition) {
- super(scopeManager, "function", upperScope, block, isMethodDefinition);
-
-
- if (this.block.type !== Syntax.ArrowFunctionExpression) {
- this.__defineArguments();
- }
- }
- isArgumentsMaterialized() {
-
-
-
-
-
-
-
-
- if (this.block.type === Syntax.ArrowFunctionExpression) {
- return false;
- }
- if (!this.isStatic()) {
- return true;
- }
- const variable = this.set.get("arguments");
- assert(variable, "Always have arguments variable.");
- return variable.tainted || variable.references.length !== 0;
- }
- isThisMaterialized() {
- if (!this.isStatic()) {
- return true;
- }
- return this.thisFound;
- }
- __defineArguments() {
- this.__defineGeneric(
- "arguments",
- this.set,
- this.variables,
- null,
- null
- );
- this.taints.set("arguments", true);
- }
-
-
-
-
-
-
- __isValidResolution(ref, variable) {
-
- if (this.block.type === "Program") {
- return true;
- }
- const bodyStart = this.block.body.range[0];
-
- return !(
- variable.scope === this &&
- ref.identifier.range[0] < bodyStart &&
- variable.defs.every(d => d.name.range[0] >= bodyStart)
- );
- }
- }
- class ForScope extends Scope {
- constructor(scopeManager, upperScope, block) {
- super(scopeManager, "for", upperScope, block, false);
- }
- }
- class ClassScope extends Scope {
- constructor(scopeManager, upperScope, block) {
- super(scopeManager, "class", upperScope, block, false);
- }
- }
- module.exports = {
- Scope,
- GlobalScope,
- ModuleScope,
- FunctionExpressionNameScope,
- CatchScope,
- WithScope,
- BlockScope,
- SwitchScope,
- FunctionScope,
- ForScope,
- ClassScope
- };
|