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(): void { 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): void { 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): void { 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): void { 87 this.pandaGen.call(this.node, [this.iterRecord.nextMethod, this.iterRecord.iterator, value], true); 88 } 89 90 iteratorComplete(iterResult: VReg): void { 91 this.pandaGen.loadObjProperty(this.node, iterResult, "done"); 92 this.pandaGen.storeAccumulator(this.node, this.iterDone); 93 } 94 95 iteratorValue(iterResult: VReg): void { 96 this.pandaGen.loadObjProperty(this.node, iterResult, "value"); 97 this.pandaGen.storeAccumulator(this.node, this.iterValue); 98 } 99 100 close(): void { 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 (<AsyncGeneratorFunctionBuilder>this.funcBuilder).await(this.node); 138 pg.storeAccumulator(this.node, res); 139 140 pg.label(this.node, tryEndLabel); 141 pg.branch(this.node, catchEndLabel); 142 143 pg.label(this.node, catchBeginLabel); 144 pg.storeAccumulator(this.node, exception); 145 pg.label(this.node, catchEndLabel); 146 147 let skipThrow = new Label(); 148 let doThrow = new Label(); 149 pg.loadAccumulator(this.node, getVregisterCache(pg, CacheList.HOLE)); 150 pg.condition(this.node, ts.SyntaxKind.ExclamationEqualsToken, exception, skipThrow); 151 152 pg.label(this.node, doThrow); 153 pg.loadAccumulator(this.node, exception); 154 pg.throw(this.node); 155 156 pg.label(this.node, skipThrow); 157 158 pg.loadAccumulator(this.node, res); 159 pg.throwIfNotObject(this.node, res); 160 161 pg.label(this.node, noReturn); 162 pg.loadAccumulator(this.node, completion); 163 164 pg.freeTemps(completion, res, exception) 165 } 166 167 getCurrentValue(): VReg { 168 return this.iterValue; 169 } 170 171 getCurrrentDone(): VReg { 172 return this.iterDone; 173 } 174}