1/* 2 * Copyright (c) 2021 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 16/** 17 * The loopStatement implementation. 18 * This file implements Compilation process of loop statement 19 * and uses Pandagen to generate bytecode. 20 * 21 */ 22 23import { Variable } from "src/variable"; 24import * as ts from "typescript"; 25import { LReference } from "../base/lreference"; 26import { 27 CacheList, 28 getVregisterCache 29} from "../base/vregisterCache"; 30import { Compiler } from "../compiler"; 31import { Label, VReg } from "../irnodes"; 32import { LoopScope, Scope } from "../scope"; 33import { LabelTarget } from "./labelTarget"; 34 35export function compileDoStatement(stmt: ts.DoStatement, compiler: Compiler): void { 36 compiler.pushScope(stmt); 37 let pandaGen = compiler.getPandaGen(); 38 39 let loopScope = <LoopScope>compiler.getRecorder().getScopeOfNode(stmt); 40 let needCreateLoopEnv: boolean = loopScope.need2CreateLexEnv() ? true : false; 41 42 let loopStartLabel = new Label(); 43 let loopEndLabel = new Label(); 44 let conditionLabel = new Label(); 45 let labelTarget = new LabelTarget(stmt, loopEndLabel, conditionLabel, needCreateLoopEnv); 46 LabelTarget.pushLabelTarget(labelTarget); 47 LabelTarget.updateName2LabelTarget(stmt.parent, labelTarget); 48 49 50 pandaGen.label(stmt, loopStartLabel); 51 if (needCreateLoopEnv) { 52 pandaGen.createLexEnv(stmt, loopScope); 53 } 54 55 compiler.compileStatement(stmt.statement); 56 pandaGen.label(stmt, conditionLabel); 57 compiler.compileCondition(stmt.expression, loopEndLabel); 58 59 if (needCreateLoopEnv) { 60 pandaGen.popLexicalEnv(stmt); 61 } 62 63 pandaGen.branch(stmt, loopStartLabel); 64 pandaGen.label(stmt, loopEndLabel); 65 66 if (needCreateLoopEnv) { 67 pandaGen.popLexicalEnv(stmt); 68 compiler.popEnv(); 69 } 70 71 LabelTarget.popLabelTarget(); 72 compiler.popScope(); 73} 74 75export function compileWhileStatement(stmt: ts.WhileStatement, compiler: Compiler): void { 76 compiler.pushScope(stmt); 77 let pandaGen = compiler.getPandaGen(); 78 79 let loopScope = <LoopScope>compiler.getRecorder().getScopeOfNode(stmt); 80 let needCreateLoopEnv: boolean = loopScope.need2CreateLexEnv() ? true : false; 81 82 let loopStartLabel = new Label(); 83 let loopEndLabel = new Label(); 84 let labelTarget = new LabelTarget(stmt, loopEndLabel, loopStartLabel, needCreateLoopEnv); 85 LabelTarget.pushLabelTarget(labelTarget); 86 LabelTarget.updateName2LabelTarget(stmt.parent, labelTarget); 87 88 pandaGen.label(stmt, loopStartLabel); 89 if (needCreateLoopEnv) { 90 pandaGen.createLexEnv(stmt, loopScope); 91 } 92 compiler.compileCondition(stmt.expression, loopEndLabel); 93 94 compiler.compileStatement(stmt.statement); 95 96 if (needCreateLoopEnv) { 97 pandaGen.popLexicalEnv(stmt); 98 } 99 100 pandaGen.branch(stmt, loopStartLabel); 101 pandaGen.label(stmt, loopEndLabel); 102 103 if (needCreateLoopEnv) { 104 pandaGen.popLexicalEnv(stmt); 105 compiler.popEnv(); 106 } 107 108 LabelTarget.popLabelTarget(); 109 compiler.popScope(); 110} 111 112export function compileForStatement(stmt: ts.ForStatement, compiler: Compiler): void { 113 compiler.pushScope(stmt); 114 let pandaGen = compiler.getPandaGen(); 115 116 // determine if loopenv need to be created 117 let loopScope = <LoopScope>compiler.getRecorder().getScopeOfNode(stmt); 118 let needCreateLoopEnv: boolean = loopScope.need2CreateLexEnv(); 119 let createEnvAtBegining: boolean = false; 120 if (needCreateLoopEnv) { 121 // determine the location where loopenv should be created 122 if (stmt.initializer && ts.isVariableDeclarationList(stmt.initializer)) { 123 loopScope.getName2variable().forEach(v => { 124 if (v.isLetOrConst() && v.isLexVar) { 125 createEnvAtBegining = true; 126 } 127 }); 128 } 129 } 130 131 let loopStartLabel = new Label(); 132 let loopEndLabel = new Label(); 133 let incLabel = new Label(); 134 let labelTarget = new LabelTarget(stmt, loopEndLabel, incLabel, needCreateLoopEnv); 135 LabelTarget.pushLabelTarget(labelTarget); 136 LabelTarget.updateName2LabelTarget(stmt.parent, labelTarget); 137 138 if (stmt.initializer && ts.isVariableDeclarationList(stmt.initializer) && createEnvAtBegining && needCreateLoopEnv) { 139 pandaGen.createLexEnv(stmt, loopScope); 140 141 let declList = <ts.VariableDeclarationList>stmt.initializer; 142 declList.declarations.forEach(decl => compiler.compileVariableDeclaration(decl)); 143 144 // loopCondition 145 pandaGen.label(stmt, loopStartLabel); 146 147 if (stmt.condition) { 148 compiler.compileCondition(stmt.condition, loopEndLabel); 149 } 150 151 // loopBody 152 compiler.compileStatement(stmt.statement); 153 154 // loopIncrementor 155 pandaGen.label(stmt, incLabel); 156 157 // load init from current env for the use of the next iteration 158 type variableInfo = { scope: Scope | undefined, level: number, v: Variable | undefined }; 159 let variables: Map<variableInfo, VReg> = new Map<variableInfo, VReg>(); 160 let tmpVregs: Array<VReg> = new Array<VReg>(); 161 loopScope.getName2variable().forEach((v, name) => { 162 if (v.isLexVar && v.isLetOrConst()) { 163 let tmp = pandaGen.getTemp(); 164 tmpVregs.push(tmp); 165 let varInfo = loopScope.find(name); 166 variables.set(varInfo, tmp); 167 compiler.loadTarget(stmt, varInfo); 168 pandaGen.storeAccumulator(stmt, tmp); 169 } 170 }); 171 172 // pop the current loopenv and create a new loopenv before the next iteration 173 pandaGen.popLexicalEnv(stmt); 174 pandaGen.createLexEnv(stmt, loopScope); 175 variables.forEach((reg, varInfo) => { 176 let slot: number = (<Variable>varInfo.v).idxLex; 177 // emitStore is not used here to avoid dead-zone check within it, just use storeLexcialVar 178 pandaGen.storeLexicalVar(stmt, varInfo.level, slot, reg); 179 }) 180 181 // must compile incrementor after store the previous value into the corresponding slot, otherwise will fall into a dead loop 182 if (stmt.incrementor) { 183 compiler.compileExpression(stmt.incrementor); 184 } 185 186 pandaGen.branch(stmt, loopStartLabel); 187 pandaGen.label(stmt, loopEndLabel); 188 189 pandaGen.popLexicalEnv(stmt); 190 compiler.popEnv(); 191 pandaGen.freeTemps(...tmpVregs); 192 } else { // compile for in fast mode 193 if (needCreateLoopEnv) { 194 pandaGen.createLexEnv(stmt, loopScope); 195 } 196 197 if (stmt.initializer) { 198 if (ts.isVariableDeclarationList(stmt.initializer)) { 199 let declList = <ts.VariableDeclarationList>stmt.initializer; 200 declList.declarations.forEach((decl) => compiler.compileVariableDeclaration(decl)); 201 } else { 202 compiler.compileExpression(stmt.initializer); 203 } 204 } 205 206 if (needCreateLoopEnv) { 207 pandaGen.popLexicalEnv(stmt); 208 compiler.popEnv(); 209 } 210 211 // loopCondition 212 pandaGen.label(stmt, loopStartLabel); 213 214 // createLoopEnv if needed 215 if (needCreateLoopEnv) { 216 pandaGen.createLexEnv(stmt, loopScope); 217 } 218 219 if (stmt.condition) { 220 compiler.compileCondition(stmt.condition, loopEndLabel); 221 } 222 223 // loopBody 224 compiler.compileStatement(stmt.statement); 225 226 // loopIncrementor 227 pandaGen.label(stmt, incLabel); 228 if (stmt.incrementor) { 229 compiler.compileExpression(stmt.incrementor); 230 } 231 232 // pop the current loopenv before next iteration 233 if (needCreateLoopEnv) { 234 pandaGen.popLexicalEnv(stmt); 235 } 236 237 pandaGen.branch(stmt, loopStartLabel); 238 pandaGen.label(stmt, loopEndLabel); 239 240 if (needCreateLoopEnv) { 241 pandaGen.popLexicalEnv(stmt); 242 compiler.popEnv(); 243 } 244 } 245 246 LabelTarget.popLabelTarget(); 247 compiler.popScope(); 248} 249 250export function compileForInStatement(stmt: ts.ForInStatement, compiler: Compiler): void { 251 compiler.pushScope(stmt); 252 let pandaGen = compiler.getPandaGen(); 253 254 // determine the location where env should be created 255 let loopScope = <LoopScope>compiler.getRecorder().getScopeOfNode(stmt); 256 let needCreateLexEnv: boolean = loopScope.need2CreateLexEnv() ? true : false; 257 258 // init label info; 259 let loopStartLabel = new Label(); 260 let loopEndLabel = new Label(); 261 let labelTarget = new LabelTarget(stmt, loopEndLabel, loopStartLabel, needCreateLexEnv); 262 LabelTarget.pushLabelTarget(labelTarget); 263 LabelTarget.updateName2LabelTarget(stmt.parent, labelTarget); 264 265 let iterReg = pandaGen.getTemp(); 266 let propName = pandaGen.getTemp(); 267 268 if (needCreateLexEnv) { 269 pandaGen.createLexEnv(stmt, loopScope); 270 } 271 272 // create enumerator 273 compiler.compileExpression(stmt.expression); 274 pandaGen.getPropIterator(stmt); 275 pandaGen.storeAccumulator(stmt, iterReg); 276 277 if (needCreateLexEnv) { 278 pandaGen.popLexicalEnv(stmt); 279 compiler.popEnv(); 280 } 281 282 pandaGen.label(stmt, loopStartLabel); 283 if (needCreateLexEnv) { 284 pandaGen.createLexEnv(stmt, loopScope); 285 } 286 287 // get next prop of enumerator 288 pandaGen.getNextPropName(stmt, iterReg); 289 pandaGen.storeAccumulator(stmt, propName); 290 pandaGen.condition(stmt, ts.SyntaxKind.ExclamationEqualsEqualsToken, getVregisterCache(pandaGen, CacheList.UNDEFINED), loopEndLabel); 291 292 let lref = LReference.generateLReference(compiler, stmt.initializer, false); 293 pandaGen.loadAccumulator(stmt, propName); 294 lref.setValue(); 295 296 compiler.compileStatement(stmt.statement); 297 298 if (needCreateLexEnv) { 299 pandaGen.popLexicalEnv(stmt); 300 } 301 pandaGen.branch(stmt, loopStartLabel); 302 pandaGen.label(stmt, loopEndLabel); 303 304 if (needCreateLexEnv) { 305 pandaGen.popLexicalEnv(stmt); 306 compiler.popEnv(); 307 } 308 309 pandaGen.freeTemps(iterReg, propName); 310 LabelTarget.popLabelTarget(); 311 compiler.popScope(); 312}