• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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}