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