1/* 2 * Copyright (c) 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 16 17import * as ts from "typescript"; 18import { CmdOptions } from "./cmdOptions"; 19import * as jshelpers from "./jshelpers"; 20import { Recorder } from "./recorder"; 21import { 22 GlobalScope, 23 Scope, 24 VariableScope 25} from "./scope"; 26import { 27 isMandatoryParam, 28 MandatoryArguments, 29 MandatoryFuncObj, 30 MandatoryNewTarget, 31 MandatoryThis, 32 VarDeclarationKind 33} from "./variable"; 34 35 36export class LexicalBinder { 37 private srcFile: ts.SourceFile; 38 private recorder: Recorder; 39 constructor(src: ts.SourceFile, recorder: Recorder) { 40 this.srcFile = src; 41 this.recorder = recorder; 42 } 43 44 resolve(): void { 45 this.resolveIdentReference(this.srcFile, this.recorder.getScopeOfNode(this.srcFile)); 46 } 47 48 resolveIdentReference(node: ts.Node, scope: Scope): void { 49 node.forEachChild((child) => { 50 let tmp = this.recorder.getScopeOfNode(child); 51 let newScope = tmp ? tmp : scope; 52 switch (child.kind) { 53 case ts.SyntaxKind.Identifier: { // 79 54 if (this.hasDeclarationParent(<ts.Identifier>child) || this.isPropertyName(<ts.Identifier>child)) { 55 break; 56 } 57 58 this.lookUpLexicalReference(jshelpers.getTextOfIdentifierOrLiteral(<ts.Identifier>child), newScope); 59 break; 60 } 61 case ts.SyntaxKind.SuperKeyword: { 62 let enclosingVariableScope = newScope.getNearestVariableScope(); 63 if (!ts.isArrowFunction(enclosingVariableScope.getBindingNode())) { 64 this.resolveIdentReference(child, newScope); 65 break; 66 } 67 enclosingVariableScope = enclosingVariableScope.getParentVariableScope(); 68 while (enclosingVariableScope) { 69 if (!ts.isArrowFunction(enclosingVariableScope.getBindingNode())) { 70 break; 71 } 72 73 enclosingVariableScope = enclosingVariableScope.getParentVariableScope(); 74 } 75 76 this.setMandatoryParamLexical(MandatoryFuncObj, enclosingVariableScope); 77 this.setMandatoryParamLexical(MandatoryThis, enclosingVariableScope); 78 this.resolveIdentReference(child, newScope); 79 break; 80 } 81 case ts.SyntaxKind.ThisKeyword: { // 108 82 let enclosingVariableScope = newScope.getNearestVariableScope(); 83 if (!ts.isArrowFunction(enclosingVariableScope.getBindingNode())) { 84 this.resolveIdentReference(child, newScope); 85 break; 86 } 87 88 this.lookUpLexicalReference(MandatoryThis, enclosingVariableScope); 89 this.resolveIdentReference(child, newScope); 90 break; 91 } 92 case ts.SyntaxKind.Constructor: { // 169 93 if (!(<ts.ConstructorDeclaration>child).parent.heritageClauses) { 94 this.resolveIdentReference(child, newScope); 95 break; 96 } 97 98 if (!(<VariableScope>newScope).hasAfChild() && 99 ((<VariableScope>newScope).getChildVariableScope().length > 0)) { 100 this.resolveIdentReference(child, newScope); 101 break; 102 } 103 104 this.setMandatoryParamLexical(MandatoryFuncObj, <VariableScope>newScope); 105 this.setMandatoryParamLexical(MandatoryThis, <VariableScope>newScope); 106 this.resolveIdentReference(child, newScope); 107 break; 108 } 109 case ts.SyntaxKind.ArrowFunction: { //212 110 this.setMandatoryParamLexicalForNCFuncInDebug(<VariableScope>newScope); 111 this.resolveIdentReference(child, newScope); 112 break; 113 } 114 case ts.SyntaxKind.MetaProperty: { // 229 115 let id: string = jshelpers.getTextOfIdentifierOrLiteral((<ts.MetaProperty>child).name); 116 if (id != "target") { 117 this.resolveIdentReference(child, newScope); 118 break; 119 } 120 121 let enclosingVariableScope = newScope.getNearestVariableScope(); 122 if (!ts.isArrowFunction(enclosingVariableScope.getBindingNode())) { 123 this.resolveIdentReference(child, newScope); 124 break; 125 } 126 127 this.lookUpLexicalReference(MandatoryNewTarget, enclosingVariableScope); 128 this.resolveIdentReference(child, newScope); 129 break; 130 } 131 default: { 132 this.resolveIdentReference(child, newScope); 133 } 134 } 135 }); 136 } 137 138 lookUpLexicalReference(name: string, scope: Scope): void { 139 let declPosInfo = scope.resolveDeclPos(name); 140 if (!declPosInfo.isLexical) { // if find declaration position in the current function 141 return; 142 } 143 144 if (isMandatoryParam(name)) { 145 declPosInfo.v.setLexVar(declPosInfo.defLexicalScope); 146 return; 147 } 148 149 // Gloabl declaration should not be set lexical, otherwise redundant lexical env will be created in main 150 if (declPosInfo.scope instanceof GlobalScope) { 151 return; 152 } 153 154 declPosInfo.v.setLexVar(declPosInfo.defLexicalScope); 155 } 156 157 setMandatoryParamLexical(name: string, scope: VariableScope): void { 158 if (ts.isArrowFunction(scope.getBindingNode())) { 159 throw new Error("Arrow function should not be processed"); 160 } 161 162 let v = scope.findLocal(name); 163 v.setLexVar(scope); 164 } 165 166 setMandatoryParamLexicalForNCFuncInDebug(scope: VariableScope): void { 167 if (!ts.isArrowFunction(scope.getBindingNode())) { 168 throw new Error("Non-ArrowFunction should not be processed"); 169 } 170 171 if (!CmdOptions.isDebugMode()) { 172 return; 173 } 174 175 let newTargetPosInfo = scope.resolveDeclPos(MandatoryNewTarget); 176 if (!newTargetPosInfo.isLexical) { 177 throw new Error("4newTarget must be lexical"); 178 } 179 newTargetPosInfo.v.setLexVar(newTargetPosInfo.defLexicalScope); 180 181 let thisPosInfo = scope.resolveDeclPos(MandatoryThis); 182 if (!thisPosInfo.isLexical) { 183 throw new Error("This must be lexical"); 184 } 185 thisPosInfo.v.setLexVar(thisPosInfo.defLexicalScope); 186 187 let curScope: VariableScope = scope; 188 while (curScope) { 189 if (!ts.isArrowFunction(curScope.getBindingNode())) { 190 break; 191 } 192 193 curScope = curScope.getParentVariableScope(); 194 } 195 196 let v = undefined; 197 if (curScope.getUseArgs()) { 198 v = curScope.findLocal(MandatoryArguments); 199 } else { 200 v = curScope.add(MandatoryArguments, VarDeclarationKind.CONST); 201 } 202 v.setLexVar(curScope); 203 } 204 205 hasDeclarationParent(id: ts.Identifier): boolean { 206 let parent = id.parent; 207 if (ts.isBindingElement(parent) && 208 parent.name === id) { 209 while (parent && !ts.isVariableDeclaration(parent)) { 210 parent = parent.parent; 211 } 212 213 return parent ? true : false; 214 } 215 216 if ((ts.isVariableDeclaration(parent) || ts.isClassDeclaration(parent) || 217 ts.isClassExpression(parent) || ts.isFunctionLike(parent)) 218 && parent.name === id) { 219 return true; 220 } 221 222 return false; 223 } 224 225 isPropertyName(id: ts.Identifier): boolean { 226 let parent = id.parent; 227 if (ts.isPropertyAccessExpression(parent) && (parent.name === id)) { // eg. a.b -> b is the property name 228 return true; 229 } 230 231 return false; 232 } 233}