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 16import { Scope } from "src/scope"; 17import * as ts from "typescript"; 18import { CacheList, getVregisterCache } from "../base/vregisterCache"; 19import { Compiler, ControlFlowChange } from "../compiler"; 20import { 21 Label, 22 VReg 23} from "../irnodes"; 24import { PandaGen } from "../pandagen"; 25import { Recorder } from "../recorder"; 26import { IteratorRecord, IteratorType, getIteratorRecord } from "../statement/forOfStatement"; 27 28enum ResumeMode { Return = 0, Throw, Next }; 29 30/** 31 * function *foo() { 32 * yield 'a' 33 * } 34*/ 35export class GeneratorFunctionBuilder { 36 private pandaGen: PandaGen; 37 private compiler: Compiler; 38 private genObj: VReg; 39 private retVal: VReg; 40 41 constructor(pandaGen: PandaGen, compiler: Compiler) { 42 this.pandaGen = pandaGen; 43 this.compiler = compiler; 44 this.genObj = pandaGen.getTemp(); 45 this.retVal = pandaGen.getTemp(); 46 } 47 48 prepare(node: ts.Node, recorder: Recorder) { 49 let pandaGen = this.pandaGen; 50 // @ts-ignore 51 let scope = <Scope>recorder.getScopeOfNode(node); 52 53 // backend handle funcobj, frontend set undefined 54 pandaGen.createGeneratorObj(node, getVregisterCache(pandaGen, CacheList.FUNC)); 55 pandaGen.storeAccumulator(node, this.genObj); 56 pandaGen.suspendGenerator(node, this.genObj, getVregisterCache(pandaGen, CacheList.undefined)); 57 pandaGen.resumeGenerator(node, this.genObj); 58 pandaGen.storeAccumulator(node, this.retVal); 59 60 this.handleMode(node); 61 } 62 63 yield(node: ts.Node, value: VReg) { 64 let pandaGen = this.pandaGen; 65 66 let iterRslt = pandaGen.getTemp(); 67 pandaGen.EcmaCreateiterresultobj(node, value, getVregisterCache(pandaGen, CacheList.False)); 68 pandaGen.storeAccumulator(node, iterRslt); 69 pandaGen.suspendGenerator(node, this.genObj, iterRslt); 70 pandaGen.freeTemps(iterRslt); 71 72 pandaGen.resumeGenerator(node, this.genObj); 73 pandaGen.storeAccumulator(node, this.retVal); 74 75 this.handleMode(node); 76 } 77 78 yieldStar(expr: ts.YieldExpression) { 79 let pandaGen = this.pandaGen; 80 let method = pandaGen.getTemp(); 81 let object = pandaGen.getTemp(); 82 83 let receivedValue = pandaGen.getTemp(); 84 let modeType = pandaGen.getTemp(); 85 86 let loopStartLabel = new Label(); 87 let callreturnLabel = new Label(); 88 let callthrowLabel = new Label(); 89 let iteratorCompletionLabel = new Label(); 90 let exitLabel_return = new Label(); 91 let exitLabel_throw = new Label(); 92 let exitLabel_value = new Label(); 93 let exitLabel_TypeError = new Label(); 94 95 // get innerIterator & iterator.[[Nextmethod]] (spec 4 & 5), support async in the future 96 let type: IteratorType = IteratorType.Normal; 97 let iterator: IteratorRecord = getIteratorRecord(pandaGen, expr, method, object, type); 98 99 // init receivedValue with Undefined (spec 6) 100 pandaGen.moveVreg(expr, receivedValue, getVregisterCache(pandaGen, CacheList.undefined)); 101 102 // init modeType with Next (spec 6) 103 pandaGen.loadAccumulatorInt(expr, ResumeMode.Next); 104 pandaGen.storeAccumulator(expr, modeType); 105 106 // starts executeing iterator.[[method]] (spec 7) 107 pandaGen.label(expr, loopStartLabel); 108 pandaGen.loadAccumulatorInt(expr, ResumeMode.Next); 109 pandaGen.condition(expr, ts.SyntaxKind.EqualsEqualsToken, modeType, callreturnLabel); 110 111 // call next 112 pandaGen.call(expr, [iterator.getNextMethod(), iterator.getObject(), receivedValue], true); 113 pandaGen.branch(expr, iteratorCompletionLabel); 114 115 // call return 116 pandaGen.label(expr, callreturnLabel); 117 pandaGen.loadAccumulatorInt(expr, ResumeMode.Return); 118 pandaGen.condition(expr, ts.SyntaxKind.EqualsEqualsToken, modeType, callthrowLabel); 119 120 pandaGen.loadObjProperty(expr, iterator.getObject(), "return"); 121 pandaGen.storeAccumulator(expr, method); 122 123 // whether iterator.[[return]] exists 124 pandaGen.condition(expr, ts.SyntaxKind.ExclamationEqualsEqualsToken, getVregisterCache(pandaGen, CacheList.undefined), exitLabel_return); 125 pandaGen.call(expr, [method, iterator.getObject(), receivedValue], true); 126 pandaGen.branch(expr, iteratorCompletionLabel); 127 128 // no return method 129 pandaGen.label(expr, exitLabel_return); 130 131 // if there are finally blocks, should implement these at first. 132 this.compiler.compileFinallyBeforeCFC( 133 undefined, 134 ControlFlowChange.Break, 135 undefined 136 ); 137 138 // spec 7.c.iii.2 Return Completion(received). 139 pandaGen.loadAccumulator(expr, receivedValue); 140 pandaGen.return(expr); 141 142 // call throw 143 pandaGen.label(expr, callthrowLabel); 144 pandaGen.loadObjProperty(expr, iterator.getObject(), "throw"); 145 pandaGen.storeAccumulator(expr, method); 146 147 // whether iterator.[[throw]] exists 148 pandaGen.condition(expr, ts.SyntaxKind.ExclamationEqualsEqualsToken, getVregisterCache(pandaGen, CacheList.undefined), exitLabel_throw); 149 pandaGen.call(expr, [method, iterator.getObject(), receivedValue], true); 150 pandaGen.branch(expr, iteratorCompletionLabel); 151 152 // NOTE: If iterator does not have a throw method, this throw is 153 // going to terminate the yield* loop. But first we need to give 154 // iterator a chance to clean up. 155 pandaGen.label(expr, exitLabel_throw); 156 pandaGen.loadObjProperty(expr, iterator.getObject(), "return"); 157 pandaGen.storeAccumulator(expr, method); 158 159 // whether iterator.[[return]] exists 160 pandaGen.condition(expr, ts.SyntaxKind.ExclamationEqualsEqualsToken, getVregisterCache(pandaGen, CacheList.undefined), exitLabel_TypeError); 161 162 // [[return]] exists 163 pandaGen.call(expr, [method, iterator.getObject()], true); 164 let innerResult = pandaGen.getTemp(); 165 pandaGen.storeAccumulator(expr, innerResult); 166 pandaGen.throwIfNotObject(expr, innerResult); 167 pandaGen.freeTemps(innerResult); 168 169 pandaGen.label(expr, exitLabel_TypeError); 170 pandaGen.throwThrowNotExist(expr); 171 172 // iteratorCompletion 173 pandaGen.label(expr, iteratorCompletionLabel); 174 pandaGen.storeAccumulator(expr, this.retVal); 175 pandaGen.throwIfNotObject(expr, this.retVal); 176 177 pandaGen.loadObjProperty(expr, this.retVal, "done"); 178 pandaGen.jumpIfTrue(expr, exitLabel_value); 179 180 pandaGen.suspendGenerator(expr, this.genObj, this.retVal); 181 pandaGen.resumeGenerator(expr, this.genObj); 182 pandaGen.storeAccumulator(expr, receivedValue); 183 pandaGen.getResumeMode(expr, this.genObj); 184 pandaGen.storeAccumulator(expr, modeType); 185 pandaGen.branch(expr, loopStartLabel); 186 187 // spec 7.a.v.1/7.b.ii.6.a/7.c.viii Return ? IteratorValue(innerResult). 188 // Decide if we trigger a return or if the yield* expression should just 189 // produce a value. 190 let outputLabel = new Label(); 191 192 pandaGen.label(expr, exitLabel_value); 193 pandaGen.loadObjProperty(expr, this.retVal, "value"); 194 let outputResult = pandaGen.getTemp(); 195 pandaGen.storeAccumulator(expr, outputResult); 196 pandaGen.loadAccumulatorInt(expr, ResumeMode.Return); 197 pandaGen.condition(expr, ts.SyntaxKind.EqualsEqualsToken, modeType, outputLabel); 198 199 this.compiler.compileFinallyBeforeCFC( 200 undefined, 201 ControlFlowChange.Break, 202 undefined 203 ); 204 pandaGen.loadAccumulator(expr, outputResult); 205 pandaGen.return(expr); 206 207 pandaGen.label(expr, outputLabel); 208 pandaGen.loadAccumulator(expr, outputResult); 209 210 pandaGen.freeTemps(method, object, receivedValue, modeType, outputResult); 211 } 212 213 private handleMode(node: ts.Node) { 214 let pandaGen = this.pandaGen; 215 216 let modeType = pandaGen.getTemp(); 217 218 pandaGen.getResumeMode(node, this.genObj); 219 pandaGen.storeAccumulator(node, modeType); 220 221 // .return(value) 222 pandaGen.loadAccumulatorInt(node, ResumeMode.Return); 223 224 let notRetLabel = new Label(); 225 226 pandaGen.condition(node, ts.SyntaxKind.EqualsEqualsToken, modeType, notRetLabel); 227 228 // if there are finally blocks, should implement these at first. 229 this.compiler.compileFinallyBeforeCFC( 230 undefined, 231 ControlFlowChange.Break, 232 undefined 233 ); 234 235 pandaGen.loadAccumulator(node, this.retVal); 236 pandaGen.return(node); 237 238 // .throw(value) 239 pandaGen.label(node, notRetLabel); 240 241 pandaGen.loadAccumulatorInt(node, ResumeMode.Throw); 242 243 let notThrowLabel = new Label(); 244 245 pandaGen.condition(node, ts.SyntaxKind.EqualsEqualsToken, modeType, notThrowLabel); 246 pandaGen.loadAccumulator(node, this.retVal); 247 pandaGen.throw(node); 248 249 pandaGen.freeTemps(modeType); 250 251 // .next(value) 252 pandaGen.label(node, notThrowLabel); 253 pandaGen.loadAccumulator(node, this.retVal); 254 } 255 256 cleanUp() { 257 this.pandaGen.freeTemps(this.genObj, this.retVal); 258 } 259} 260