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 { SourceTextModuleRecord } from "./ecmaModule"; 18import { LOGD } from "./log"; 19import { 20 GlobalVariable, 21 LocalVariable, 22 MandatoryFuncObj, 23 ModuleVariable, 24 VarDeclarationKind, 25 Variable 26} from "./variable"; 27 28 29export enum InitStatus { 30 INITIALIZED, UNINITIALIZED 31} 32 33export enum ModuleVarKind { 34 IMPORTED, EXPORTED, NOT 35} 36 37export abstract class Decl { 38 name: string; 39 node: ts.Node; 40 isModule: ModuleVarKind; 41 42 constructor(name: string, node: ts.Node, isModule: ModuleVarKind) { 43 this.name = name; 44 this.node = node; 45 this.isModule = isModule; 46 } 47} 48 49export class VarDecl extends Decl { 50 constructor(varName: string, node: ts.Node, isModule: ModuleVarKind) { 51 super(varName, node, isModule); 52 } 53} 54 55export class LetDecl extends Decl { 56 constructor(letName: string, node: ts.Node, isModule: ModuleVarKind) { 57 super(letName, node, isModule); 58 } 59} 60 61export class ConstDecl extends Decl { 62 constructor(constName: string, node: ts.Node, isModule: ModuleVarKind) { 63 super(constName, node, isModule); 64 } 65} 66 67export class FuncDecl extends Decl { 68 constructor(funcName: string, node: ts.Node, isModule: ModuleVarKind) { 69 super(funcName, node, isModule); 70 } 71} 72 73export class ClassDecl extends Decl { 74 constructor(className: string, node: ts.Node, isModule: ModuleVarKind) { 75 super(className, node, isModule); 76 } 77} 78 79export class CatchParameter extends Decl { 80 constructor(cpName: string, node: ts.Node, isModule: ModuleVarKind = ModuleVarKind.NOT) { 81 super(cpName, node, isModule); 82 } 83} 84 85export class FunctionParameter extends Decl { 86 constructor(fpName: string, node: ts.Node, isModule: ModuleVarKind = ModuleVarKind.NOT) { 87 super(fpName, node, isModule); 88 } 89} 90 91export abstract class Scope { 92 protected debugTag = "scope"; 93 protected name2variable: Map<string, Variable> = new Map<string, Variable>(); 94 protected decls: Decl[] = []; 95 protected parent: Scope | undefined = undefined; 96 // for debuginfo 97 protected startInsIdx: number | undefined; 98 protected endInsIdx: number | undefined; 99 private isArgumentsOrRestargs: boolean = false; 100 101 constructor() { } 102 103 abstract add(decl: Decl | string, declKind: VarDeclarationKind, status?: InitStatus): Variable | undefined; 104 105 getName2variable(): Map<string, Variable> { 106 return this.name2variable; 107 } 108 109 getScopeStartInsIdx(): number { 110 return this.startInsIdx; 111 } 112 113 setScopeStartInsIdx(startInsIdx: number) { 114 this.startInsIdx = startInsIdx; 115 } 116 117 setScopeEndInsIdx(endInsIdx: number) { 118 this.endInsIdx = endInsIdx; 119 } 120 121 getScopeEndInsIdx(): number { 122 return this.endInsIdx; 123 } 124 125 setParent(parentScope: Scope | undefined): void { 126 this.parent = parentScope; 127 } 128 129 getParent(): Scope | undefined { 130 return this.parent; 131 } 132 133 getRootScope(): Scope { 134 let sp: Scope | undefined = this; 135 let pp = this.getParent(); 136 while (pp != undefined) { 137 sp = pp; 138 pp = pp.getParent(); 139 } 140 141 return sp; 142 } 143 144 getNearestVariableScope(): VariableScope | undefined { 145 let sp: Scope | undefined = this; 146 147 while (sp) { 148 if (sp instanceof VariableScope) { 149 return <VariableScope>sp; 150 } 151 sp = sp.parent; 152 } 153 154 return undefined; 155 } 156 157 getNearestLexicalScope(): VariableScope | LoopScope | undefined { 158 let curScope: Scope | undefined = this; 159 160 while (curScope) { 161 if (curScope instanceof VariableScope || curScope instanceof LoopScope) { 162 return <VariableScope | LoopScope>curScope; 163 } 164 curScope = curScope.parent; 165 } 166 167 return undefined; 168 } 169 170 getNthVariableScope(level: number): VariableScope | undefined { 171 let sp: Scope | undefined = this; 172 let tempLevel = level; 173 174 while (sp) { 175 if (sp instanceof VariableScope) { 176 if (tempLevel === 0) { 177 return <VariableScope>sp; 178 } else { 179 tempLevel--; 180 } 181 } 182 sp = sp.parent; 183 } 184 185 return undefined; 186 } 187 188 findLocal(name: string): Variable | undefined { 189 return this.name2variable.get(name); 190 } 191 192 find(name: string): { scope: Scope | undefined, level: number, v: Variable | undefined } { 193 let curLevel = 0; 194 let curScope: Scope | undefined = this; 195 196 while (curScope) { 197 let resolve = null; 198 let tmpLevel = curLevel; // to store current level, not impact by ++ 199 if (curScope.isLexicalScope() && (<VariableScope | LoopScope>curScope).need2CreateLexEnv()) { 200 curLevel++; 201 } 202 resolve = curScope.findLocal(name); 203 if (resolve) { 204 LOGD(this.debugTag, "scope.find (" + name + ") :"); 205 LOGD(undefined, resolve); 206 return { scope: curScope, level: tmpLevel, v: resolve }; 207 } 208 209 curScope = curScope.getParent(); 210 } 211 212 LOGD(this.debugTag, "scope.find (" + name + ") : undefined"); 213 return { scope: undefined, level: 0, v: undefined }; 214 } 215 216 findDeclPos(name: string): Scope | undefined { 217 let declPos: Scope | undefined = undefined; 218 let curScope: Scope | undefined = this; 219 while (curScope) { 220 if (curScope.hasDecl(name)) { 221 declPos = curScope; 222 break; 223 } 224 225 curScope = curScope.getParent(); 226 } 227 228 return declPos; 229 } 230 231 resolveDeclPos(name: string) { 232 let crossFunc: boolean = false; 233 let curScope: Scope | undefined = this; 234 let enclosingVariableScope: VariableScope = this.getNearestVariableScope(); 235 236 while (curScope) { 237 if (curScope === enclosingVariableScope.parent) { 238 crossFunc = true; 239 } 240 241 let result = curScope.findLocal(name); 242 if (result) { 243 if (!crossFunc) { 244 return {isLexical: false, scope: curScope, defLexicalScope: undefined, v: result}; 245 } 246 247 let enclosingDefLexicalScope = curScope.getNearestLexicalScope(); 248 return {isLexical: true, scope: curScope, defLexicalScope: enclosingDefLexicalScope, v: result}; 249 } 250 251 curScope = curScope.parent; 252 } 253 254 return {isLexical: false, scope: undefined, defLexicalScope: undefined, v: undefined}; 255 } 256 257 setDecls(decl: Decl): void { 258 this.decls.push(decl); 259 } 260 261 hasDecl(name: string): boolean { 262 let decls = this.decls; 263 for (let i = 0; i < decls.length; i++) { 264 if (decls[i].name === name) { 265 return true; 266 } 267 } 268 269 return false; 270 } 271 272 getDecl(name: string): Decl | undefined { 273 let decls = this.decls; 274 for (let i = 0; i < decls.length; i++) { 275 if (decls[i].name === name) { 276 return decls[i]; 277 } 278 } 279 280 return undefined; 281 } 282 283 getDecls(): Decl[] { 284 return this.decls; 285 } 286 287 public setArgumentsOrRestargs(): void { 288 this.isArgumentsOrRestargs = true; 289 } 290 291 public getArgumentsOrRestargs() { 292 return this.isArgumentsOrRestargs; 293 } 294 295 isLexicalScope(): boolean { 296 let scope = this; 297 return ((scope instanceof VariableScope) || (scope instanceof LoopScope)); 298 } 299} 300 301export abstract class VariableScope extends Scope { 302 protected startLexIdx: number = 0; 303 protected needCreateLexEnv: boolean = false; 304 protected parameters: LocalVariable[] = []; 305 protected useArgs = false; 306 protected node: ts.Node | undefined = undefined; 307 protected parentVariableScope: VariableScope | null = null; 308 protected childVariableScope: VariableScope[] = []; 309 protected lexVarInfo: Map<string, number> = new Map<string, number>(); 310 311 getLexVarInfo(): Map<string, number> { 312 return this.lexVarInfo; 313 } 314 315 addLexVarInfo(name: string, slot: number): void { 316 this.lexVarInfo.set(name, slot); 317 } 318 319 getBindingNode(): ts.Node { 320 return this.node; 321 } 322 323 setParentVariableScope(scope: VariableScope): void { 324 this.parentVariableScope = scope; 325 } 326 327 getParentVariableScope(): VariableScope { 328 return this.parentVariableScope; 329 } 330 331 getChildVariableScope(): VariableScope[] { 332 return this.childVariableScope; 333 } 334 335 addChildVariableScope(scope: VariableScope): void { 336 this.childVariableScope.push(scope); 337 } 338 339 addParameter(name: string, declKind: VarDeclarationKind, argIdx: number): Variable | undefined { 340 LOGD(this.debugTag, "VariableScope.addArg(" + name + "), kind(" + declKind + ")", "argIdx(" + argIdx + ")"); 341 let v = this.add(name, declKind, InitStatus.INITIALIZED); 342 if (!(v instanceof LocalVariable)) { 343 throw new Error("Error: argument must be local variable!"); 344 } 345 this.parameters.push(v); 346 return v; 347 } 348 349 addFuncName(funcName: string): void { 350 let funcObj = this.name2variable.get(MandatoryFuncObj); 351 this.name2variable.set(funcName, funcObj!); 352 } 353 354 need2CreateLexEnv(): boolean { 355 return this.needCreateLexEnv; 356 } 357 358 pendingCreateEnv(): void { 359 this.needCreateLexEnv = true; 360 } 361 362 getNumLexEnv(): number { 363 return this.startLexIdx; 364 } 365 366 getParametersCount(): number { 367 return this.parameters.length; 368 } 369 370 getParameters(): LocalVariable[] { 371 return this.parameters; 372 } 373 374 getLexVarIdx(): number { 375 this.needCreateLexEnv = true; 376 return this.startLexIdx++; 377 } 378 379 setUseArgs(value: boolean) { 380 this.useArgs = value; 381 } 382 383 getUseArgs(): boolean { 384 return this.useArgs; 385 } 386 387 hasAfChild(): boolean { 388 let childVariableScopes = this.getChildVariableScope(); 389 for (let child of childVariableScopes) { 390 if (ts.isArrowFunction(child.getBindingNode())) { 391 return true; 392 } 393 } 394 395 return false; 396 } 397} 398 399export class GlobalScope extends VariableScope { 400 constructor(node?: ts.SourceFile) { 401 super(); 402 this.node = node ? node : undefined; 403 } 404 405 add(decl: Decl | string, declKind: VarDeclarationKind, status?: InitStatus): Variable | undefined { 406 let name = decl instanceof Decl ? decl.name : decl; 407 LOGD(this.debugTag, "globalscope.add (" + name + "), kind:" + declKind); 408 let v: Variable | undefined; 409 if (declKind === VarDeclarationKind.NONE || declKind === VarDeclarationKind.VAR || declKind === VarDeclarationKind.FUNCTION) { 410 v = new GlobalVariable(declKind, name); 411 } else { 412 v = new LocalVariable(declKind, name, status); 413 } 414 this.name2variable.set(name, v); 415 return v; 416 } 417} 418 419export class ModuleScope extends VariableScope { 420 private moduleRecord: SourceTextModuleRecord; 421 422 constructor(node: ts.SourceFile) { 423 super(); 424 this.node = node; 425 this.moduleRecord = new SourceTextModuleRecord(node.fileName); 426 } 427 428 setExportDecl(exportedLocalName: string): void { 429 let decl = this.getDecl(exportedLocalName); 430 if (decl && decl.isModule != ModuleVarKind.IMPORTED) { 431 decl.isModule = ModuleVarKind.EXPORTED; 432 } 433 } 434 435 module(): SourceTextModuleRecord { 436 return this.moduleRecord; 437 } 438 439 add(decl: Decl | string, declKind: VarDeclarationKind, status?: InitStatus): Variable | undefined { 440 let [name, isModule] = decl instanceof Decl ? [decl.name, decl.isModule] : [decl, ModuleVarKind.NOT]; 441 LOGD(this.debugTag, "modulescope.add (" + name + "), kind:" + declKind); 442 let v: Variable | undefined; 443 444 if (isModule !== ModuleVarKind.NOT) { 445 v = new ModuleVariable(declKind, name, InitStatus.UNINITIALIZED); 446 if (isModule === ModuleVarKind.EXPORTED) { 447 (<ModuleVariable>v).setExport(); 448 } 449 } else { 450 if (declKind === VarDeclarationKind.NONE) { 451 v = new GlobalVariable(declKind, name); 452 } else if (declKind === VarDeclarationKind.VAR || declKind === VarDeclarationKind.FUNCTION) { 453 v = new LocalVariable(declKind, name); 454 } else { 455 v = new LocalVariable(declKind, name, status); 456 } 457 } 458 459 this.name2variable.set(name, v); 460 return v; 461 } 462} 463 464export class FunctionScope extends VariableScope { 465 private parameterLength: number = 0; 466 private funcName: string = ""; 467 constructor(parent?: Scope, node?: ts.FunctionLikeDeclaration) { 468 super(); 469 this.parent = parent ? parent : undefined; 470 this.node = node ? node : undefined; 471 } 472 473 setParameterLength(length: number): void { 474 this.parameterLength = length; 475 } 476 477 getParameterLength(): number { 478 return this.parameterLength; 479 } 480 481 setFuncName(name: string): void { 482 this.funcName = name; 483 } 484 485 getFuncName(): string { 486 return this.funcName; 487 } 488 489 getParent(): Scope | undefined { 490 return this.parent; 491 } 492 493 add(decl: Decl | string, declKind: VarDeclarationKind, status?: InitStatus): Variable | undefined { 494 let name = decl instanceof Decl ? decl.name : decl; 495 let v: Variable | undefined; 496 LOGD(this.debugTag, "functionscope.add (" + name + "), kind:" + declKind); 497 498 if (declKind === VarDeclarationKind.NONE) { 499 // the variable declared without anything should be global 500 // See EcmaStandard: 13.3.2 Variable Statement 501 let topLevelScope = this.getRootScope(); 502 v = topLevelScope.add(name, declKind); 503 } else if (declKind === VarDeclarationKind.VAR || declKind === VarDeclarationKind.FUNCTION) { 504 v = new LocalVariable(declKind, name); 505 this.name2variable.set(name, v); 506 } else { 507 v = new LocalVariable(declKind, name, status); 508 this.name2variable.set(name, v); 509 } 510 return v; 511 } 512} 513 514export class LocalScope extends Scope { 515 constructor(parent: Scope) { 516 super(); 517 this.parent = parent; 518 } 519 520 add(decl: Decl | string, declKind: VarDeclarationKind, status?: InitStatus): Variable | undefined { 521 let name = decl instanceof Decl ? decl.name : decl; 522 let v: Variable | undefined; 523 524 LOGD(this.debugTag, "localscope.add (" + name + "), kind:" + declKind); 525 if (declKind === VarDeclarationKind.NONE) { 526 let topLevelScope = this.getRootScope(); 527 v = topLevelScope.add(name, declKind); 528 } else if (declKind === VarDeclarationKind.VAR) { 529 /** 530 * the variable declared without anything should be accessible 531 * in all parent scopes so delegate creation to the parent 532 * See EcmaStandard: 13.3.2 Variable Statement 533 */ 534 let functionScope = this.getNearestVariableScope(); 535 v = functionScope!.add(name, declKind); 536 } else { 537 v = new LocalVariable(declKind, name, status); 538 this.name2variable.set(name, v); 539 } 540 541 return v; 542 } 543} 544 545export class LoopScope extends LocalScope { 546 protected startLexIdx: number = 0; 547 protected needCreateLexEnv: boolean = false; 548 protected lexVarInfo: Map<string, number> = new Map<string, number>(); 549 constructor(parent: Scope) { 550 super(parent); 551 } 552 553 getLexVarInfo(): Map<string, number> { 554 return this.lexVarInfo; 555 } 556 557 addLexVarInfo(name: string, slot: number): void { 558 this.lexVarInfo.set(name, slot); 559 } 560 561 need2CreateLexEnv(): boolean { 562 return this.needCreateLexEnv; 563 } 564 565 pendingCreateEnv(): void { 566 this.needCreateLexEnv = true; 567 } 568 569 getLexVarIdx(): number { 570 this.needCreateLexEnv = true; 571 return this.startLexIdx++; 572 } 573 574 getNumLexEnv(): number { 575 return this.startLexIdx; 576 } 577} 578 579