1/* 2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import * as ts from "typescript"; 17import { PrimitiveType } from "./base/typeSystem"; 18import { isBindingPattern } from "./base/util"; 19import { CmdOptions } from "./cmdOptions"; 20import * as jshelpers from "./jshelpers"; 21import { Recorder } from "./recorder"; 22import { 23 CatchParameter, 24 ClassDecl, 25 ConstDecl, 26 Decl, 27 FuncDecl, 28 InitStatus, 29 LetDecl, 30 Scope, 31 VarDecl, 32 VariableScope 33} from "./scope"; 34import { isGlobalIdentifier } from "./syntaxCheckHelper"; 35import { TypeRecorder } from "./typeRecorder"; 36import { 37 MandatoryArguments, 38 MandatoryFuncObj, 39 MandatoryNewTarget, 40 MandatoryThis, 41 VarDeclarationKind, 42 Variable 43} from "./variable"; 44 45function setVariableOrParameterType(node: ts.Node, v: Variable | undefined) { 46 if (v) { 47 let typeIndex = TypeRecorder.getInstance().tryGetVariable2Type(ts.getOriginalNode(node)); 48 if (typeIndex === PrimitiveType.ANY) { 49 typeIndex = TypeRecorder.getInstance().tryGetTypeIndex(ts.getOriginalNode(node)); 50 } 51 v.setTypeIndex(typeIndex); 52 } 53} 54 55function setClassOrFunctionType(node: ts.Node, v: Variable | undefined) { 56 if (v) { 57 let typeIndex = TypeRecorder.getInstance().tryGetTypeIndex(ts.getOriginalNode(node)); 58 v.setTypeIndex(typeIndex); 59 } 60} 61 62function setTypeIndex(node: ts.Node, v: Variable | undefined, isClassOrFunction: boolean) { 63 if (isClassOrFunction) { 64 setClassOrFunctionType(node, v); 65 } else { 66 setVariableOrParameterType(node, v); 67 } 68} 69 70function addInnerArgs(node: ts.Node, scope: VariableScope, enableTypeRecord: boolean): void { 71 // the first argument for js function is func_obj 72 scope.addParameter(MandatoryFuncObj, VarDeclarationKind.CONST, -1); 73 // the second argument for newTarget 74 75 if (node.kind == ts.SyntaxKind.ArrowFunction) { 76 scope.addParameter("0newTarget", VarDeclarationKind.CONST, -1); 77 scope.addParameter("0this", VarDeclarationKind.CONST, 0); 78 } else { 79 scope.addParameter(MandatoryNewTarget, VarDeclarationKind.CONST, -1); 80 scope.addParameter(MandatoryThis, VarDeclarationKind.CONST, 0); 81 } 82 83 if (CmdOptions.isCommonJs() && node.kind === ts.SyntaxKind.SourceFile) { 84 scope.addParameter("exports", VarDeclarationKind.LET, 1); 85 scope.addParameter("require", VarDeclarationKind.LET, 2); 86 scope.addParameter("module", VarDeclarationKind.LET, 3); 87 scope.addParameter("__filename", VarDeclarationKind.LET, 4); 88 scope.addParameter("__dirname", VarDeclarationKind.LET, 5); 89 } 90 91 if (node.kind != ts.SyntaxKind.SourceFile) { 92 let funcNode = <ts.FunctionLikeDeclaration>node; 93 addParameters(funcNode, scope, enableTypeRecord); 94 } 95 96 if (scope.getUseArgs() || CmdOptions.isDebugMode()) { 97 if (ts.isArrowFunction(node)) { 98 let parentVariableScope = <VariableScope>scope.getParentVariableScope(); 99 parentVariableScope.add(MandatoryArguments, VarDeclarationKind.CONST, InitStatus.INITIALIZED); 100 parentVariableScope.setUseArgs(true); 101 scope.setUseArgs(false); 102 } else if (scope.getUseArgs()){ 103 if (!scope.findLocal(MandatoryArguments)) { 104 scope.add(MandatoryArguments, VarDeclarationKind.CONST, InitStatus.INITIALIZED); 105 } 106 } 107 } 108} 109 110export function addVariableToScope(recorder: Recorder, enableTypeRecord: boolean) { 111 let scopeMap = recorder.getScopeMap(); 112 let hoistMap = recorder.getHoistMap(); 113 114 scopeMap.forEach((scope, node) => { 115 let hoistDecls = []; 116 if (scope instanceof VariableScope) { 117 addInnerArgs(node, scope, enableTypeRecord); 118 119 hoistDecls = <Decl[]>hoistMap.get(scope); 120 if (hoistDecls) { 121 hoistDecls.forEach(hoistDecl => { 122 let v: Variable | undefined; 123 if (hoistDecl instanceof VarDecl) { 124 v = scope.add(hoistDecl, VarDeclarationKind.VAR); 125 } else if (hoistDecl instanceof FuncDecl) { 126 v = scope.add(hoistDecl, VarDeclarationKind.FUNCTION); 127 } else { 128 throw new Error("Wrong type of declaration to be hoisted") 129 } 130 131 if (enableTypeRecord) { 132 setTypeIndex(hoistDecl.node, v, hoistDecl instanceof FuncDecl); 133 } 134 }) 135 } 136 } 137 138 139 let decls = scope.getDecls(); 140 let nearestVariableScope = <VariableScope>scope.getNearestVariableScope(); 141 hoistDecls = <Decl[]>hoistMap.get(nearestVariableScope); 142 for (let j = 0; j < decls.length; j++) { 143 let decl = decls[j]; 144 // @ts-ignore 145 if (hoistDecls && hoistDecls.includes(decl)) { 146 continue; 147 } 148 let v: Variable | undefined; 149 if (decl instanceof LetDecl) { 150 v = scope.add(decl, VarDeclarationKind.LET, InitStatus.UNINITIALIZED); 151 } else if (decl instanceof ConstDecl) { 152 v = scope.add(decl, VarDeclarationKind.CONST, InitStatus.UNINITIALIZED); 153 } else if (decl instanceof FuncDecl) { 154 let funcNode = decl.node; 155 if (ts.isFunctionDeclaration(funcNode)) { 156 v = scope.add(decl, VarDeclarationKind.FUNCTION); 157 } else if (ts.isFunctionExpression(funcNode)) { 158 let functionScope = <Scope>recorder.getScopeOfNode(funcNode); 159 v = functionScope.add(decl, VarDeclarationKind.FUNCTION); 160 } 161 } else if (decl instanceof CatchParameter) { 162 v = scope.add(decl, VarDeclarationKind.LET); 163 } else if (decl instanceof ClassDecl) { 164 let classNode = decl.node; 165 if (ts.isClassDeclaration(classNode)) { 166 v = scope.add(decl, VarDeclarationKind.CLASS, InitStatus.UNINITIALIZED); 167 } else { 168 let classScope = <Scope>recorder.getScopeOfNode(classNode); 169 v = classScope.add(decl, VarDeclarationKind.CLASS, InitStatus.UNINITIALIZED); 170 } 171 } else { 172 /** 173 * Case 1: var declaration share a same name with function declaration, then 174 * function declaration will be hoisted and the var declaration will be left be. 175 * Case 2: "var undefined" in global scope is not added to hoistDecls, 176 * but it should be added to scope 177 */ 178 if (isGlobalIdentifier(decls[j].name)) { 179 v = scope.add(decls[j].name, VarDeclarationKind.VAR); 180 } 181 } 182 if (enableTypeRecord) { 183 setTypeIndex(decl.node, v, decl instanceof ClassDecl || decl instanceof FuncDecl); 184 } 185 } 186 }) 187} 188 189function addParameters(node: ts.FunctionLikeDeclaration, scope: VariableScope, enableTypeRecord: boolean): void { 190 let patternParams: Array<ts.BindingPattern> = new Array<ts.BindingPattern>(); 191 for (let i = 0; i < node.parameters.length; ++i) { 192 let param = node.parameters[i]; 193 let name: string = ''; 194 if (isBindingPattern(param.name)) { 195 patternParams.push(<ts.BindingPattern>param.name); 196 name = i.toString() + "pattern"; 197 } else if (ts.isIdentifier(param.name)) { 198 name = jshelpers.getTextOfIdentifierOrLiteral(<ts.Identifier>param.name); 199 } 200 201 let v = scope.addParameter(name, VarDeclarationKind.VAR, i + 1); 202 203 if (enableTypeRecord) { 204 setTypeIndex(param.name, v, false); 205 } 206 } 207 208 for (let i = 0; i < patternParams.length; i++) { 209 addPatternParamterElements(patternParams[i], scope); 210 } 211} 212 213function addPatternParamterElements(pattern: ts.BindingPattern, scope: VariableScope) { 214 let name: string = ''; 215 pattern.elements.forEach(bindingElement => { 216 if (ts.isOmittedExpression(bindingElement)) { 217 return; 218 } 219 220 bindingElement = <ts.BindingElement>bindingElement; 221 if (ts.isIdentifier(bindingElement.name)) { 222 name = jshelpers.getTextOfIdentifierOrLiteral(bindingElement.name); 223 scope.add(name, VarDeclarationKind.VAR); 224 } else if (isBindingPattern(bindingElement.name)) { 225 let innerPattern = <ts.BindingPattern>bindingElement.name; 226 addPatternParamterElements(innerPattern, scope); 227 } 228 }); 229} 230