js-ast-parser
Version:
把javascript代码字符串解析为AST对象
474 lines • 20.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.JavascriptScopeAnalyzer = void 0;
const Block_1 = require("../../Block");
const constants_1 = require("../../constants");
const Definition_1 = require("./Definition");
const constance_1 = require("./constance");
const Scope_1 = require("./Scope");
const definitionType_1 = require("./definitionType");
class JavascriptScopeAnalyzer {
constructor() {
this.scheduledAssinments = [];
this.scheduledVarDefCheck = [];
this.scheduledNewExpression = [];
this.definitionMap = {};
this.depth = 0;
this.applyingScheduled = false;
}
reset() {
this.scheduledAssinments = [];
this.scheduledVarDefCheck = [];
this.scheduledNewExpression = [];
this.definitionMap = {};
this.depth = 0;
this.currentScope = null;
}
analyze(block, parnetScope) {
if (!block)
return;
this.depth++;
let currentScope = new Scope_1.Scope();
currentScope.parent = parnetScope || this.currentScope;
this.currentScope = currentScope;
block.scope = currentScope;
block.body.forEach((node) => {
if (node instanceof Block_1.Block) {
return this.analyze(node);
}
else if ((node.code & constants_1.NodeCode.Expression) === node.code) {
this.analyzeExpression(node);
}
else {
this.analyzeStatement(node);
}
});
this.currentScope = this.currentScope.parent;
this.depth--;
if (this.depth === 0 && !this.applyingScheduled) {
this.applyingScheduled = true;
this.scheduledAssinments.forEach(fn => fn());
this.scheduledNewExpression.forEach(fn => fn());
this.scheduledVarDefCheck.forEach(fn => fn());
this.applyingScheduled = false;
}
return block.scope;
}
analyzeStatement(node) {
switch (node.code) {
case constants_1.NodeCode.VariableDeclarationStatement:
this.analyzeVariable(node);
break;
case constants_1.NodeCode.ImportStatement:
this.analyzeImport(node);
break;
case constants_1.NodeCode.FunctionDeclarationStatement:
this.analyzeFunctionDeclaration(node);
break;
case constants_1.NodeCode.ClassDeclarationStatement:
this.analyzeClassDeclaration(node);
break;
case constants_1.NodeCode.DeleteStatement:
this.analyzeDeleteStatement(node);
break;
case constants_1.NodeCode.DoWhileStatement:
this.analyzeDoWhileStatement(node);
break;
case constants_1.NodeCode.ExportDeclarationStatement:
this.analyzeExported(node);
break;
case constants_1.NodeCode.ForStatement:
this.analyzeForStatement(node);
break;
case constants_1.NodeCode.IfStatement:
this.analyzeIfStatement(node);
break;
case constants_1.NodeCode.ReturnStatement:
this.analyzeReturn(node);
break;
case constants_1.NodeCode.SwitchStatement:
node.cases.forEach(item => this.analyzeSwitchCase(item));
break;
case constants_1.NodeCode.ThrowStatement:
this.analyzeThrowStatement(node);
break;
case constants_1.NodeCode.TryCathchStatement:
this.analyzeTryCatch(node);
break;
case constants_1.NodeCode.WhileStatement:
this.analyzeWhileStatement(node);
break;
}
this.analyzeUnexpect(node);
}
analyzeWhileStatement(statement) {
this.analyzeExpression(statement.test);
this.analyze(statement.consequence);
}
analyzeTryCatch(statement) {
this.analyze(statement.body);
if (statement.catchHandler) {
this.analyzeFunctionDeclaration(statement.catchHandler);
}
if (statement.finalizer) {
this.analyze(statement.finalizer);
}
}
analyzeThrowStatement(statement) {
this.analyzeExpression(statement);
}
analyzeSwitchCase(statement) {
this.analyzeExpression(statement.test);
this.analyze(statement.consequent);
}
analyzeReturn(statement) {
this.analyzeExpression(statement.argument);
}
analyzeIfStatement(statement) {
this.analyzeExpression(statement.test);
this.analyze(statement.consequent);
if (statement.alternate) {
if (statement.alternate.code) {
this.analyzeIfStatement(statement.alternate);
}
else {
this.analyze(statement.alternate);
}
}
}
analyzeForStatement(statement) {
var _a;
let currentScope = new Scope_1.Scope();
currentScope.parent = this.currentScope;
this.currentScope = currentScope;
if (statement.forIn || statement.forOf) {
this.analyzeExpression(statement.right);
if (statement.left.code === constants_1.NodeCode.VariableDeclarationStatement) {
this.analyzeVariable(statement.left);
}
else {
this.analyzeExpression(statement.left);
}
}
else {
if (((_a = statement.init) === null || _a === void 0 ? void 0 : _a.code) === constants_1.NodeCode.VariableDeclarationStatement) {
this.analyzeVariable(statement.init);
}
else {
this.analyzeExpression(statement.init);
}
this.analyzeExpression(statement.test);
if (statement.update) {
statement.update.forEach(expr => this.analyzeExpression(expr));
}
}
this.analyze(statement.body);
this.currentScope = this.currentScope.parent;
}
analyzeExported(statement) {
}
analyzeDoWhileStatement(statement) {
this.analyze(statement.body);
this.analyzeExpression(statement.test);
}
analyzeDeleteStatement(statement) {
this.analyzeExpression(statement.argument);
}
setDefinition(newDefinition = null) {
let prev = this.currentDefinition;
this.currentDefinition = newDefinition;
return () => {
this.currentDefinition = prev;
};
}
setScope(newScope = null) {
let prev = this.currentScope;
this.currentScope = newScope;
return () => {
this.currentScope = prev;
};
}
setState({ newDefinition, newScope }) {
let resetDefinition = this.setDefinition(newDefinition);
let resetScope = this.setScope(newScope);
return () => {
resetDefinition();
resetScope();
};
}
analyzeImport(statement) {
[{
imported: statement.imported,
local: statement.local
}].concat(statement.specifiers).forEach(specifier => {
let definition;
if (specifier.local) {
definition = new Definition_1.Definition(specifier.local, definitionType_1.DefinitionType.IMPORT, constance_1.DefinitionCode.ImportSpecifier, this.currentScope);
}
else if (statement.imported) {
definition = new Definition_1.Definition(specifier.imported, definitionType_1.DefinitionType.IMPORT, constance_1.DefinitionCode.ImportSpecifier, this.currentScope);
}
if (definition) {
this.currentScope.addDefinition(definition);
this.currentScope.addUnuse(definition);
}
});
}
analyzeVariable(statement) {
let declarationKeyWord = statement.declarationKeyWord, type, code;
if (declarationKeyWord === 'const') {
type = definitionType_1.DefinitionType.CONST;
code = constance_1.DefinitionCode.ConstVariable;
}
else {
type = definitionType_1.DefinitionType.VARIABLE;
code = constance_1.DefinitionCode.Variable;
}
statement.declarations.forEach(declaration => {
let definition = new Definition_1.Definition(declaration, type, code, this.currentScope);
this.currentScope.addUnuse(definition);
if (declaration.code === constants_1.NodeCode.AssignmentExpression) {
let prevScope = this.currentScope, prevMap = this.definitionMap, prevDefinition = this.currentDefinition;
this.currentDefinition = definition;
this.analyzeExpression(declaration.right);
this.currentScope = prevScope;
this.definitionMap = prevMap;
this.currentDefinition = prevDefinition;
}
this.currentScope.addDefinition(definition);
});
}
analyzeExpression(expression) {
if (!expression)
return;
let currentDefinition = this.currentDefinition, currentScope = this.currentScope;
switch (expression.code) {
case constants_1.NodeCode.ArrayLiteralExpression:
this.analyzeArray(expression);
break;
case constants_1.NodeCode.ObjectLiteralExpression:
this.analyzeObject(expression);
break;
case constants_1.NodeCode.IdentifierLiteralExpression:
this.scheduledVarDefCheck.push(() => {
let definition = currentScope.getDefinition(expression.identifier);
if (definition) {
currentScope.deleteUnuse(definition);
if (currentDefinition)
currentDefinition.definitions = definition.definitions;
}
else {
currentScope.UndeclaredVariables.add(expression);
}
});
break;
case constants_1.NodeCode.FunctionExpression:
this.analyzeFunExpression(expression);
if (this.currentDefinition)
this.currentDefinition.definitions = (0, constance_1.getFunctionDefinitions)();
break;
case constants_1.NodeCode.RegExpression:
if (this.currentDefinition)
this.currentDefinition.definitions = (0, constance_1.getRegExpDefinitions)();
break;
case constants_1.NodeCode.StringLiteralExpression:
if (this.currentDefinition)
this.currentDefinition.definitions = (0, constance_1.getStringDefinitions)();
break;
case constants_1.NodeCode.TemplateLiteralExpression:
this.analyzeTemplate(expression);
break;
case constants_1.NodeCode.AssignmentExpression:
this.analyzeAssigment(expression);
break;
case constants_1.NodeCode.BinaryExpression:
this.analyzeBinary(expression);
break;
case constants_1.NodeCode.CallExpression:
this.analyzeCallExpression(expression);
break;
case constants_1.NodeCode.MemberExpression:
this.analyzeMemberExpression(expression);
break;
case constants_1.NodeCode.BracketEnwrapedExpressioin:
this.analyzeBracketEnwraped(expression);
break;
case constants_1.NodeCode.NewExpression:
this.scheduledNewExpression.push(() => {
let reset = this.setState({
newDefinition: currentDefinition,
newScope: currentScope
});
this.analyzeNewExpression(expression);
reset();
});
case constants_1.NodeCode.TernaryExpression:
this.analyzeTernaryExression(expression);
break;
case constants_1.NodeCode.TypeOfExpression:
this.analyzeTypeOf(expression);
break;
case constants_1.NodeCode.VoidExpression:
this.analyzeVoid(expression);
break;
case constants_1.NodeCode.UnaryExpression:
this.analyzeUnary(expression);
break;
default:
break;
}
this.analyzeUnexpect(expression);
}
analyzeUnexpect(node) {
if (node.unexpectedNodes.length) {
node.unexpectedNodes.forEach((item) => {
if (!item.value.code)
return;
if ((item.value.code & constants_1.NodeCode.Expression) === node.code) {
this.analyzeExpression(item.value);
}
else {
this.analyzeStatement(item.value);
}
});
}
}
analyzeBracketEnwraped(expression) {
this.analyzeExpression(expression.expression);
}
analyzeUnary(expression) {
this.analyzeExpression(expression.argument);
}
analyzeTernaryExression(expression) {
let reset = this.setDefinition(null);
this.analyzeExpression(expression.test);
this.analyzeExpression(expression.consequent);
this.analyzeExpression(expression.alternate);
reset();
}
analyzeTypeOf(expression) {
let reset = this.setDefinition(null);
this.analyzeExpression(expression.argument);
reset();
}
analyzeVoid(expression) {
let reset = this.setDefinition(null);
this.analyzeExpression(expression.argument);
reset();
}
analyzeNewExpression(expression) {
let key = expression.callee.code === constants_1.NodeCode.IdentifierLiteralExpression ?
expression.callee.identifier : expression.callee.callee.identifier;
if (expression.callee.arguments) {
expression.callee.arguments.forEach((expression) => this.analyzeExpression(expression));
}
let definition = this.currentScope.getDefinition(key);
if (definition) {
this.currentScope.deleteUnuse(definition);
if (definition.astNode.code !== constants_1.NodeCode.ClassDeclarationStatement)
return;
let classDeclaration = definition.astNode;
if (this.currentDefinition) {
this.currentDefinition.definitions = classDeclaration.methods.map(item => {
return new Definition_1.Definition(item, definitionType_1.DefinitionType.METHOD, constance_1.DefinitionCode.Method, this.currentScope);
});
}
}
}
analyzeTemplate(expression) {
expression.content.forEach(item => {
if (item.code !== constants_1.NodeCode.StringLiteralExpression) {
let reset = this.setDefinition(null);
this.analyzeExpression(item);
reset;
}
});
if (this.currentDefinition)
this.currentDefinition.definitions = (0, constance_1.getStringDefinitions)();
}
analyzeMemberExpression(expression) {
var _a;
if (((_a = expression.owner) === null || _a === void 0 ? void 0 : _a.code) === constants_1.NodeCode.IdentifierLiteralExpression && expression.owner.identifier === 'this')
return;
let reset = this.setDefinition(null);
this.analyzeExpression(expression.owner);
reset();
}
analyzeCallExpression(expression) {
let reset = this.setDefinition(null);
this.analyzeExpression(expression.callee);
expression.arguments.forEach(item => this.analyzeExpression(item));
reset();
}
analyzeBinary(expression) {
let reset = this.setDefinition(null);
this.analyzeExpression(expression.left);
this.analyzeExpression(expression.right);
reset();
}
analyzeArray(express) {
let reset = this.setDefinition(null);
express.items.forEach(item => {
this.analyzeExpression(item);
});
reset();
if (this.currentDefinition)
this.currentDefinition.definitions = (0, constance_1.getArrayDefininitons)();
}
analyzeAssigment(expression) {
this.analyzeExpression(expression.left);
this.analyzeExpression(expression.right);
}
analyzeObject(expression) {
let definitions = expression.properties.map(prop => {
let definition = new Definition_1.Definition(prop.key, definitionType_1.DefinitionType.PROPERTY, constance_1.DefinitionCode.Property, this.currentScope);
let reset = this.setDefinition(definition);
this.analyzeExpression(prop.value);
reset();
return definition;
});
if (this.currentDefinition)
this.currentDefinition.definitions = definitions.concat((0, constance_1.getObjectDefinitions)());
}
analyzeFunExpression(expression) {
let definition = new Definition_1.Definition(expression, definitionType_1.DefinitionType.FUNCTION, constance_1.DefinitionCode.FunctionDeclaration, this.currentScope);
let curScope = new Scope_1.Scope(), prevScope = this.currentScope;
curScope.parent = this.currentScope;
this.currentScope = curScope;
expression.params.forEach((identifier) => {
let definition = new Definition_1.Definition(identifier, definitionType_1.DefinitionType.VARIABLE, constance_1.DefinitionCode.Variable, this.currentScope);
this.currentScope.addUnuse(definition);
this.currentScope.addDefinition(definition);
});
this.analyze(expression.body);
this.currentScope = prevScope;
return definition;
}
analyzeFunctionDeclaration(statement) {
let definition = new Definition_1.Definition(statement, definitionType_1.DefinitionType.FUNCTION, constance_1.DefinitionCode.FunctionDeclaration, this.currentScope);
this.currentScope.addUnuse(definition);
this.currentScope.addDefinition(definition);
let curScope = new Scope_1.Scope(), prevScope = this.currentScope;
curScope.parent = this.currentScope;
this.currentScope = curScope;
statement.params.forEach((identifier) => {
let definition = new Definition_1.Definition(identifier, definitionType_1.DefinitionType.VARIABLE, constance_1.DefinitionCode.Variable, this.currentScope);
this.currentScope.addUnuse(definition);
this.currentScope.addDefinition(definition);
});
this.analyze(statement.body);
this.currentScope = prevScope;
return definition;
}
analyzeClassDeclaration(statement) {
let definition = new Definition_1.Definition(statement, definitionType_1.DefinitionType.CLASS, constance_1.DefinitionCode.ClassDeclaration, this.currentScope);
this.currentScope.addUnuse(definition);
this.currentScope.addDefinition(definition);
if (statement.super) {
this.analyzeExpression(statement.super);
}
return definition;
}
}
exports.JavascriptScopeAnalyzer = JavascriptScopeAnalyzer;
//# sourceMappingURL=scope-analyzer.js.map