• 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 { 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