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