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}