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 { IteratorType } from "../statement/forOfStatement"; 17import * as ts from "typescript"; 18import { 19 Label, 20 VReg 21} from "../irnodes"; 22import { PandaGen } from "../pandagen"; 23import { CatchTable, LabelPair } from "../statement/tryStatement"; 24import { FunctionBuilder } from "../function/functionBuilder"; 25import { AsyncGeneratorFunctionBuilder } from "../function/asyncGeneratorFunctionBuilder"; 26import { CacheList, getVregisterCache } from "./vregisterCache"; 27 28export class Iterator { 29 private iterRecord: { iterator: VReg, nextMethod: VReg }; 30 private iterDone: VReg; 31 private iterValue: VReg; 32 private pandaGen: PandaGen; 33 private node: ts.Node; 34 private kind: IteratorType = IteratorType.Normal; 35 private funcBuilder: FunctionBuilder | undefined = undefined; 36 37 constructor(iterRecord: {iterator: VReg, nextMethod: VReg}, iterDone: VReg, iterValue: VReg, 38 pandaGen: PandaGen, node: ts.Node, kind ? : IteratorType, funcBuilder ? : FunctionBuilder) { 39 this.iterRecord = iterRecord; 40 this.iterDone = iterDone; 41 this.iterValue = iterValue; 42 this.pandaGen = pandaGen; 43 this.node = node; 44 if (kind) { 45 this.kind = kind; 46 } 47 48 if (funcBuilder) { 49 this.funcBuilder = funcBuilder; 50 } 51 } 52 53 getIterator() { 54 let pandaGen = this.pandaGen; 55 let iterator = this.iterRecord.iterator; 56 57 // get iterator 58 this.kind == IteratorType.Normal ? pandaGen.getIterator(this.node) : pandaGen.getAsyncIterator(this.node); 59 pandaGen.storeAccumulator(this.node, iterator); 60 61 // get the next method 62 pandaGen.loadObjProperty(this.node, iterator, "next"); 63 pandaGen.storeAccumulator(this.node, this.iterRecord.nextMethod); 64 } 65 66 method(): VReg { 67 return this.iterRecord.nextMethod; 68 } 69 70 getMethod(id: string) { 71 this.pandaGen.loadObjProperty(this.node, this.iterRecord.iterator, id); 72 this.pandaGen.storeAccumulator(this.node, this.iterRecord.nextMethod); 73 } 74 75 /** 76 * iterResult = nextMethod.call(iterator); 77 * if (!isObject(iterResult)) { 78 * throw TypeError 79 * } 80 */ 81 callNext(iterResult: VReg) { 82 this.pandaGen.call(this.node, [this.iterRecord.nextMethod, this.iterRecord.iterator], true); 83 this.pandaGen.storeAccumulator(this.node, iterResult); 84 } 85 86 callMethodwithValue(value: VReg) { 87 this.pandaGen.call(this.node, [this.iterRecord.nextMethod, this.iterRecord.iterator, value], true); 88 } 89 90 iteratorComplete(iterResult: VReg) { 91 this.pandaGen.loadObjProperty(this.node, iterResult, "done"); 92 this.pandaGen.storeAccumulator(this.node, this.iterDone); 93 } 94 95 iteratorValue(iterResult: VReg) { 96 this.pandaGen.loadObjProperty(this.node, iterResult, "value"); 97 this.pandaGen.storeAccumulator(this.node, this.iterValue); 98 } 99 100 close() { 101 let pg = this.pandaGen; 102 if (this.kind == IteratorType.Normal) { 103 pg.closeIterator(this.node, this.iterRecord.iterator); 104 return; 105 } 106 107 let completion = pg.getTemp(); 108 let res = pg.getTemp(); 109 let exception = pg.getTemp(); 110 let noReturn = new Label(); 111 112 let tryBeginLabel = new Label(); 113 let tryEndLabel = new Label(); 114 let catchBeginLabel = new Label(); 115 let catchEndLabel = new Label(); 116 new CatchTable( 117 pg, 118 catchBeginLabel, 119 new LabelPair(tryBeginLabel, tryEndLabel) 120 ); 121 122 pg.storeAccumulator(this.node, completion); 123 pg.storeConst(this.node, exception, CacheList.HOLE); 124 125 126 pg.label(this.node, tryBeginLabel); 127 128 // 4. Let innerResult be GetMethod(iterator, "return"). 129 this.getMethod("return"); 130 131 // 5. If innerResult.[[Type]] is normal, then 132 // a. Let return be innerResult.[[Value]]. 133 // b. If return is undefined, return Completion(completion). 134 pg.branchIfUndefined(this.node, noReturn); 135 this.callNext(res); 136 137 // if (this.kind == IteratorType.Async) { 138 // if (!this.funcBuilder) { 139 // throw new Error("function builder are not supposed to be undefined"); 140 // } 141 142 // (<AsyncGeneratorFunctionBuilder>this.funcBuilder).await(this.node); 143 // } 144 (<AsyncGeneratorFunctionBuilder>this.funcBuilder).await(this.node); 145 pg.storeAccumulator(this.node, res); 146 147 pg.label(this.node, tryEndLabel); 148 pg.branch(this.node, catchEndLabel); 149 150 pg.label(this.node, catchBeginLabel); 151 pg.storeAccumulator(this.node, exception); 152 pg.label(this.node, catchEndLabel); 153 154 let skipThrow = new Label(); 155 let doThrow = new Label(); 156 pg.loadAccumulator(this.node, getVregisterCache(pg, CacheList.HOLE)); 157 pg.condition(this.node, ts.SyntaxKind.ExclamationEqualsToken, exception, skipThrow); 158 159 pg.label(this.node, doThrow); 160 pg.loadAccumulator(this.node, exception); 161 pg.throw(this.node); 162 163 pg.label(this.node, skipThrow); 164 165 pg.loadAccumulator(this.node, res); 166 pg.throwIfNotObject(this.node, res); 167 168 pg.label(this.node, noReturn); 169 pg.loadAccumulator(this.node, completion); 170 171 pg.freeTemps(completion, res, exception) 172 } 173 174 getCurrentValue() { 175 return this.iterValue; 176 } 177 178 getCurrrentDone() { 179 return this.iterDone; 180 } 181}