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