• 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 { PrimitiveType } from "./base/typeSystem";
18import { isBindingPattern } from "./base/util";
19import { CmdOptions } from "./cmdOptions";
20import * as jshelpers from "./jshelpers";
21import { Recorder } from "./recorder";
22import {
23    CatchParameter,
24    ClassDecl,
25    ConstDecl,
26    Decl,
27    FuncDecl,
28    InitStatus,
29    LetDecl,
30    Scope,
31    VarDecl,
32    VariableScope
33} from "./scope";
34import { isGlobalIdentifier } from "./syntaxCheckHelper";
35import { TypeRecorder } from "./typeRecorder";
36import {
37    MandatoryArguments,
38    MandatoryFuncObj,
39    MandatoryNewTarget,
40    MandatoryThis,
41    VarDeclarationKind,
42    Variable
43} from "./variable";
44
45function setVariableOrParameterType(node: ts.Node, v: Variable | undefined) {
46    if (v) {
47        let typeIndex = TypeRecorder.getInstance().tryGetVariable2Type(ts.getOriginalNode(node));
48        if (typeIndex === PrimitiveType.ANY) {
49            typeIndex = TypeRecorder.getInstance().tryGetTypeIndex(ts.getOriginalNode(node));
50        }
51        v.setTypeIndex(typeIndex);
52    }
53}
54
55function setClassOrFunctionType(node: ts.Node, v: Variable | undefined) {
56    if (v) {
57        let typeIndex = TypeRecorder.getInstance().tryGetTypeIndex(ts.getOriginalNode(node));
58        v.setTypeIndex(typeIndex);
59    }
60}
61
62function setTypeIndex(node: ts.Node, v: Variable | undefined, isClassOrFunction: boolean) {
63    if (isClassOrFunction) {
64        setClassOrFunctionType(node, v);
65    } else {
66        setVariableOrParameterType(node, v);
67    }
68}
69
70function addInnerArgs(node: ts.Node, scope: VariableScope, enableTypeRecord: boolean): void {
71    // the first argument for js function is func_obj
72    scope.addParameter(MandatoryFuncObj, VarDeclarationKind.CONST, -1);
73    // the second argument for newTarget
74
75    if (node.kind == ts.SyntaxKind.ArrowFunction) {
76        scope.addParameter("0newTarget", VarDeclarationKind.CONST, -1);
77        scope.addParameter("0this", VarDeclarationKind.CONST, 0);
78    } else {
79        scope.addParameter(MandatoryNewTarget, VarDeclarationKind.CONST, -1);
80        scope.addParameter(MandatoryThis, VarDeclarationKind.CONST, 0);
81    }
82
83    if (CmdOptions.isCommonJs() && node.kind === ts.SyntaxKind.SourceFile) {
84        scope.addParameter("exports", VarDeclarationKind.LET, 1);
85        scope.addParameter("require", VarDeclarationKind.LET, 2);
86        scope.addParameter("module", VarDeclarationKind.LET, 3);
87        scope.addParameter("__filename", VarDeclarationKind.LET, 4);
88        scope.addParameter("__dirname", VarDeclarationKind.LET, 5);
89    }
90
91    if (node.kind != ts.SyntaxKind.SourceFile) {
92        let funcNode = <ts.FunctionLikeDeclaration>node;
93        addParameters(funcNode, scope, enableTypeRecord);
94    }
95
96    if (scope.getUseArgs() || CmdOptions.isDebugMode()) {
97        if (ts.isArrowFunction(node)) {
98            let parentVariableScope = <VariableScope>scope.getParentVariableScope();
99            parentVariableScope.add(MandatoryArguments, VarDeclarationKind.CONST, InitStatus.INITIALIZED);
100            parentVariableScope.setUseArgs(true);
101            scope.setUseArgs(false);
102        } else if (scope.getUseArgs()){
103            if (!scope.findLocal(MandatoryArguments)) {
104                scope.add(MandatoryArguments, VarDeclarationKind.CONST, InitStatus.INITIALIZED);
105            }
106        }
107    }
108}
109
110export function addVariableToScope(recorder: Recorder, enableTypeRecord: boolean) {
111    let scopeMap = recorder.getScopeMap();
112    let hoistMap = recorder.getHoistMap();
113
114    scopeMap.forEach((scope, node) => {
115        let hoistDecls = [];
116        if (scope instanceof VariableScope) {
117            addInnerArgs(node, scope, enableTypeRecord);
118
119            hoistDecls = <Decl[]>hoistMap.get(scope);
120            if (hoistDecls) {
121                hoistDecls.forEach(hoistDecl => {
122                    let v: Variable | undefined;
123                    if (hoistDecl instanceof VarDecl) {
124                        v = scope.add(hoistDecl, VarDeclarationKind.VAR);
125                    } else if (hoistDecl instanceof FuncDecl) {
126                        v = scope.add(hoistDecl, VarDeclarationKind.FUNCTION);
127                    } else {
128                        throw new Error("Wrong type of declaration to be hoisted")
129                    }
130
131                    if (enableTypeRecord) {
132                        setTypeIndex(hoistDecl.node, v, hoistDecl instanceof FuncDecl);
133                    }
134                })
135            }
136        }
137
138
139        let decls = scope.getDecls();
140        let nearestVariableScope = <VariableScope>scope.getNearestVariableScope();
141        hoistDecls = <Decl[]>hoistMap.get(nearestVariableScope);
142        for (let j = 0; j < decls.length; j++) {
143            let decl = decls[j];
144            // @ts-ignore
145            if (hoistDecls && hoistDecls.includes(decl)) {
146                continue;
147            }
148            let v: Variable | undefined;
149            if (decl instanceof LetDecl) {
150                v = scope.add(decl, VarDeclarationKind.LET, InitStatus.UNINITIALIZED);
151            } else if (decl instanceof ConstDecl) {
152                v = scope.add(decl, VarDeclarationKind.CONST, InitStatus.UNINITIALIZED);
153            } else if (decl instanceof FuncDecl) {
154                let funcNode = decl.node;
155                if (ts.isFunctionDeclaration(funcNode)) {
156                    v = scope.add(decl, VarDeclarationKind.FUNCTION);
157                } else if (ts.isFunctionExpression(funcNode)) {
158                    let functionScope = <Scope>recorder.getScopeOfNode(funcNode);
159                    v = functionScope.add(decl, VarDeclarationKind.FUNCTION);
160                }
161            } else if (decl instanceof CatchParameter) {
162                v = scope.add(decl, VarDeclarationKind.LET);
163            } else if (decl instanceof ClassDecl) {
164                let classNode = decl.node;
165                if (ts.isClassDeclaration(classNode)) {
166                    v = scope.add(decl, VarDeclarationKind.CLASS, InitStatus.UNINITIALIZED);
167                } else {
168                    let classScope = <Scope>recorder.getScopeOfNode(classNode);
169                    v = classScope.add(decl, VarDeclarationKind.CLASS, InitStatus.UNINITIALIZED);
170                }
171            } else {
172                /**
173                 * Case 1: var declaration share a same name with function declaration, then
174                 * function declaration will be hoisted and the var declaration will be left be.
175                 * Case 2: "var undefined" in global scope is not added to hoistDecls,
176                 * but it should be added to scope
177                 */
178                if (isGlobalIdentifier(decls[j].name)) {
179                    v = scope.add(decls[j].name, VarDeclarationKind.VAR);
180                }
181            }
182            if (enableTypeRecord) {
183                setTypeIndex(decl.node, v, decl instanceof ClassDecl || decl instanceof FuncDecl);
184            }
185        }
186    })
187}
188
189function addParameters(node: ts.FunctionLikeDeclaration, scope: VariableScope, enableTypeRecord: boolean): void {
190    let patternParams: Array<ts.BindingPattern> = new Array<ts.BindingPattern>();
191    for (let i = 0; i < node.parameters.length; ++i) {
192        let param = node.parameters[i];
193        let name: string = '';
194        if (isBindingPattern(param.name)) {
195            patternParams.push(<ts.BindingPattern>param.name);
196            name = i.toString() + "pattern";
197        } else if (ts.isIdentifier(param.name)) {
198            name = jshelpers.getTextOfIdentifierOrLiteral(<ts.Identifier>param.name);
199        }
200
201        let v = scope.addParameter(name, VarDeclarationKind.VAR, i + 1);
202
203        if (enableTypeRecord) {
204            setTypeIndex(param.name, v, false);
205        }
206    }
207
208    for (let i = 0; i < patternParams.length; i++) {
209        addPatternParamterElements(patternParams[i], scope);
210    }
211}
212
213function addPatternParamterElements(pattern: ts.BindingPattern, scope: VariableScope) {
214    let name: string = '';
215    pattern.elements.forEach(bindingElement => {
216        if (ts.isOmittedExpression(bindingElement)) {
217            return;
218        }
219
220        bindingElement = <ts.BindingElement>bindingElement;
221        if (ts.isIdentifier(bindingElement.name)) {
222            name = jshelpers.getTextOfIdentifierOrLiteral(bindingElement.name);
223            scope.add(name, VarDeclarationKind.VAR);
224        } else if (isBindingPattern(bindingElement.name)) {
225            let innerPattern = <ts.BindingPattern>bindingElement.name;
226            addPatternParamterElements(innerPattern, scope);
227        }
228    });
229}
230