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