UNPKG

js-ast-parser

Version:
474 lines 20.2 kB
"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