• 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 {
18    loadAccumulator,
19    loadAccumulatorString,
20    loadLexicalVar,
21    storeAccumulator,
22    storeLexicalVar,
23    storeModuleVariable,
24    throwConstAssignment,
25    throwUndefinedIfHole
26} from "./base/bcGenUtil";
27import { CacheList, getVregisterCache } from "./base/vregisterCache";
28import { Compiler } from "./compiler";
29import { NodeKind } from "./debuginfo";
30import {
31    IRNode,
32    VReg
33} from "./irnodes";
34import { PandaGen } from "./pandagen";
35import { Scope } from "./scope";
36import {
37    LocalVariable,
38    Variable
39} from "./variable";
40import * as jshelpers from "./jshelpers";
41
42abstract class VariableAccessBase {
43    variable: Variable;
44    scope: Scope;
45    level: number;
46
47    constructor(scope: Scope, level: number, variable: Variable) {
48        this.variable = variable;
49        this.scope = scope;
50        this.level = level;
51    }
52
53    isLexVar() {
54        return this.variable.isLexVar;
55    }
56
57    getEnvSlotOfVar(): number | undefined {
58        if (this.isLexVar()) {
59            return this.variable.idxLex;
60        }
61
62        return undefined;
63    }
64
65    abstract expand(pandaGen: PandaGen, compiler: Compiler): Array<IRNode>;
66}
67
68export class VariableAccessLoad extends VariableAccessBase {
69    constructor(scope: Scope, level: number, variable: Variable) {
70        super(scope, level, variable);
71    }
72
73    expand(pandaGen: PandaGen): Array<IRNode> {
74        if (this.isLexVar()) {
75            return this.loadLexEnvVar(pandaGen);
76        } else {
77            return this.loadLocalVar(pandaGen);
78        }
79    }
80
81    private loadLocalVar(pandaGen: PandaGen): Array<IRNode> {
82        let insns: Array<IRNode> = new Array<IRNode>();
83        let v = this.variable;
84        let bindVreg = pandaGen.getVregForVariable(v);
85
86        // check TDZ first
87        if (!(<LocalVariable>v).isInitialized()) {
88            let holeReg = pandaGen.getTemp();
89            insns.push(loadAccumulator(getVregisterCache(pandaGen, CacheList.HOLE)));
90            insns.push(storeAccumulator(holeReg));
91            checkTDZ(pandaGen, holeReg, v.getName(), insns);
92            pandaGen.freeTemps(holeReg);
93            return insns;
94        }
95        if (v.getName() === "4funcObj") {
96            this.scope.setCallOpt("4funcObj")
97        }
98        insns.push(loadAccumulator(bindVreg));
99
100        return insns;
101    }
102
103    private loadLexEnvVar(pandaGen: PandaGen): Array<IRNode> {
104        let insns: Array<IRNode> = new Array<IRNode>();
105        let v = this.variable;
106
107        let slot = v.idxLex;
108        insns.push(loadLexicalVar(this.level, slot));
109
110        // check TDZ
111        if (v.isLetOrConst()) {
112            let tempReg = pandaGen.getTemp();
113
114            insns.push(storeAccumulator(tempReg));
115            checkTDZ(pandaGen, tempReg, v.getName(), insns);
116            insns.push(loadAccumulator(tempReg));
117            pandaGen.freeTemps(tempReg);
118        }
119
120        return insns;
121    }
122}
123
124export class VariableAcessStore extends VariableAccessBase {
125    node: ts.Node | NodeKind;
126    isDeclaration: boolean;
127    constructor(scope: Scope, level: number, variable: Variable, isDeclaration: boolean, node: ts.Node | NodeKind) {
128        super(scope, level, variable);
129        this.isDeclaration = isDeclaration;
130        this.node = node;
131    }
132
133    expand(pandaGen: PandaGen): Array<IRNode> {
134        if (this.isLexVar()) {
135            return this.storeLexEnvVar(pandaGen);
136        } else {
137            return this.storeLocalVar(pandaGen);
138        }
139    }
140
141    private storeLocalVar(pandaGen: PandaGen): Array<IRNode> {
142        let insns: Array<IRNode> = new Array<IRNode>();
143        let v = <LocalVariable>this.variable;
144        let bindVreg = pandaGen.getVregForVariable(v);
145
146        if (!this.isDeclaration) {
147            // check TDZ first
148            if (!v.isInitialized()) {
149                let nameReg = pandaGen.getTemp();
150                let tempReg = pandaGen.getTemp();
151                let holeReg = pandaGen.getTemp();
152                insns.push(storeAccumulator(tempReg));
153                insns.push(loadAccumulator(getVregisterCache(pandaGen, CacheList.HOLE)));
154                insns.push(storeAccumulator(holeReg));
155                checkTDZ(pandaGen, holeReg, v.getName(), insns);
156                insns.push(loadAccumulator(tempReg));
157                pandaGen.freeTemps(nameReg, tempReg, holeReg);
158            }
159
160            // check const assignment
161            checkConstAssignment(pandaGen, v, insns, this.node);
162        }
163
164        if (v.getName() === "4funcObj") {
165            this.scope.setCallOpt("4funcObj")
166        }
167        insns.push(storeAccumulator(bindVreg));
168
169        if (v.isExportVar()) {
170            insns.push(storeModuleVariable(v.getExportedName()));
171        }
172
173        return insns;
174    }
175
176    private storeLexEnvVar(pandaGen: PandaGen): Array<IRNode> {
177        let insns: Array<IRNode> = new Array<IRNode>();
178        let v = <LocalVariable>this.variable;
179
180        // save the value first
181        let valueReg: VReg = pandaGen.getTemp();
182        insns.push(storeAccumulator(valueReg));
183
184        let slot = v.idxLex;
185        if (v.isLetOrConst() || v.isClass()) {
186            if (!this.isDeclaration) {
187                let holeReg = pandaGen.getTemp();
188                /**
189                 * check TDZ first
190                 * If acc == hole -> throw reference error
191                 * else -> excute the next insn
192                */
193                insns.push(loadLexicalVar(this.level, slot));
194                insns.push(storeAccumulator(holeReg));
195                checkTDZ(pandaGen, holeReg, v.getName(), insns);
196
197                // const assignment check need to be down after TDZ check
198                checkConstAssignment(pandaGen, v, insns, this.node);
199                pandaGen.freeTemps(holeReg);
200            }
201        }
202
203        insns.push(storeLexicalVar(this.level, slot, valueReg));
204        insns.push(loadAccumulator(valueReg));
205        if (v.isExportVar()) {
206            insns.push(storeModuleVariable(v.getExportedName()));
207        }
208        pandaGen.freeTemps(valueReg);
209
210        return insns;
211    }
212}
213
214function checkTDZ(pg: PandaGen, holeReg: VReg, name: string, expansion: IRNode[]) {
215    let nameReg = pg.getTemp();
216    expansion.push(loadAccumulatorString(name));
217    expansion.push(storeAccumulator(nameReg));
218    expansion.push(throwUndefinedIfHole(holeReg, nameReg));
219    pg.freeTemps(nameReg);
220}
221
222function checkConstAssignment(pg: PandaGen, v: Variable, expansion: IRNode[], node: ts.Node | NodeKind) {
223    let nameReg = pg.getTemp();
224    if (v.isConst()) {
225        expansion.push(loadAccumulatorString(v.getName()));
226        expansion.push(storeAccumulator(nameReg));
227        expansion.push(throwConstAssignment(nameReg));
228    }
229
230    if (v.isClass() && node != NodeKind.FirstNodeOfFunction &&
231        node != NodeKind.Invalid && node != NodeKind.Normal) {
232        let className = v.getName();
233        while (node) {
234            if (ts.isClassLike(node) && node.name &&
235                jshelpers.getTextOfIdentifierOrLiteral(node.name) == className) {
236                break;
237            }
238
239            node = node.parent;
240        }
241
242        // class name binding inside class is immutable
243        if (node) {
244            expansion.push(loadAccumulatorString(className));
245            expansion.push(storeAccumulator(nameReg));
246            expansion.push(throwConstAssignment(nameReg));
247        }
248    }
249
250    pg.freeTemps(nameReg);
251}