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