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