• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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