From faf631b7014335deb3d742c11a9c63f9c703838a Mon Sep 17 00:00:00 2001 From: bi-hu Date: Mon, 13 Jun 2022 11:52:22 +0800 Subject: [PATCH] AsyncGenerator adds front-end Signed-off-by: bi-hu https://gitee.com/bi-hu-01/ark_ts2abc/issues/I5BYSB --- ts2panda/src/base/bcGenUtil.ts | 5 + ts2panda/src/compiler.ts | 6 +- ts2panda/src/expression/yieldExpression.ts | 5 +- .../function/asyncGeneratorFunctionBuilder.ts | 171 ++++++++++++++++++ ts2panda/src/function/functionBuilder.ts | 3 +- ts2panda/src/pandagen.ts | 16 ++ 6 files changed, 201 insertions(+), 5 deletions(-) create mode 100644 ts2panda/src/function/asyncGeneratorFunctionBuilder.ts diff --git a/ts2panda/src/base/bcGenUtil.ts b/ts2panda/src/base/bcGenUtil.ts index ebde0fb137..a4bdb192d2 100644 --- a/ts2panda/src/base/bcGenUtil.ts +++ b/ts2panda/src/base/bcGenUtil.ts @@ -34,6 +34,7 @@ import { EcmaDefineclasswithbuffer, EcmaDefinefuncdyn, EcmaDefinegeneratorfunc, + EcmaDefineasyncgeneratorfunc, EcmaDefinegettersetterbyvalue, EcmaDefinemethod, EcmaDefinencfuncdyn, @@ -398,6 +399,10 @@ export function defineGeneratorFunc(name: string, env: VReg, paramLength: number return new EcmaDefinegeneratorfunc(name, new Imm(paramLength), env); } +export function defineAsyncGeneratorFunc(name: string, env: VReg, paramLength: number) { + return new EcmaDefineasyncgeneratorfunc(name, new Imm(paramLength), env); +} + export function defineNCFunc(name: string, env: VReg, paramLength: number) { return new EcmaDefinencfuncdyn(name, new Imm(paramLength), env); } diff --git a/ts2panda/src/compiler.ts b/ts2panda/src/compiler.ts index 59cd1d776e..c7621e8980 100644 --- a/ts2panda/src/compiler.ts +++ b/ts2panda/src/compiler.ts @@ -60,6 +60,7 @@ import { compileYieldExpression } from "./expression/yieldExpression"; import { AsyncFunctionBuilder } from "./function/asyncFunctionBuilder"; import { FunctionBuilder, FunctionBuilderType } from "./function/functionBuilder"; import { GeneratorFunctionBuilder } from "./function/generatorFunctionBuilder"; +import { AsyncGeneratorFunctionBuilder } from "./function/asyncGeneratorFunctionBuilder"; import { hoistFunctionInBlock } from "./hoisting"; @@ -414,7 +415,8 @@ export class Compiler { if (decl.modifiers[i].kind == ts.SyntaxKind.AsyncKeyword) { // async generator if (decl.asteriskToken) { - throw new Error("Async generator is not supported"); + return new AsyncGeneratorFunctionBuilder(pandaGen, this); + //throw new Error("Async generator is not supported"); } else { // async return new AsyncFunctionBuilder(pandaGen); } @@ -1142,7 +1144,7 @@ export class Compiler { private compileAwaitExpression(expr: ts.AwaitExpression) { let pandaGen = this.pandaGen; - if (!(this.funcBuilder instanceof AsyncFunctionBuilder)) { + if (!(this.funcBuilder instanceof AsyncFunctionBuilder || this.funcBuilder instanceof AsyncGeneratorFunctionBuilder)) { //new add throw new DiagnosticError(expr.parent, DiagnosticCode.await_expressions_are_only_allowed_within_async_functions_and_at_the_top_levels_of_modules); } diff --git a/ts2panda/src/expression/yieldExpression.ts b/ts2panda/src/expression/yieldExpression.ts index 99fc5f856b..f064ebd5f1 100644 --- a/ts2panda/src/expression/yieldExpression.ts +++ b/ts2panda/src/expression/yieldExpression.ts @@ -17,10 +17,11 @@ import * as ts from "typescript"; import { GeneratorFunctionBuilder } from "../function/generatorFunctionBuilder"; import { DiagnosticCode, DiagnosticError } from "../diagnostic"; import { CacheList, getVregisterCache } from "../base/vregisterCache"; +import { AsyncGeneratorFunctionBuilder } from "../function/asyncGeneratorFunctionBuilder"; import { Compiler } from "../compiler"; export function compileYieldExpression(compiler: Compiler, expr: ts.YieldExpression) { - if (!(compiler.getFuncBuilder() instanceof GeneratorFunctionBuilder)) { + if (!(compiler.getFuncBuilder() instanceof GeneratorFunctionBuilder || compiler.getFuncBuilder() instanceof AsyncGeneratorFunctionBuilder)) { throw new DiagnosticError(expr.parent, DiagnosticCode.A_yield_expression_is_only_allowed_in_a_generator_body); } @@ -29,7 +30,7 @@ export function compileYieldExpression(compiler: Compiler, expr: ts.YieldExpress function genYieldExpr(compiler: Compiler, expr: ts.YieldExpression) { let pandaGen = compiler.getPandaGen(); - let funcBuilder = compiler.getFuncBuilder(); + let funcBuilder = compiler.getFuncBuilder(); if (expr.expression) { let retValue = pandaGen.getTemp(); diff --git a/ts2panda/src/function/asyncGeneratorFunctionBuilder.ts b/ts2panda/src/function/asyncGeneratorFunctionBuilder.ts new file mode 100644 index 0000000000..429873b33e --- /dev/null +++ b/ts2panda/src/function/asyncGeneratorFunctionBuilder.ts @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Scope } from "src/scope"; +import ts from "typescript"; +import { CacheList, getVregisterCache } from "../base/vregisterCache"; +import { Compiler, ControlFlowChange } from "../compiler"; +import { + Label, + VReg +} from "../irnodes"; +import { PandaGen } from "../pandagen"; +import { Recorder } from "../recorder"; +import { IteratorRecord, IteratorType, getIteratorRecord } from "../statement/forOfStatement"; +import { AsyncFunctionBuilder } from "./asyncFunctionBuilder"; +import { NodeKind } from "../debuginfo"; +enum ResumeMode { Return = 0, Throw, Next }; + +/** + * async function *foo() { + * yield 'a' + * } +*/ +export class AsyncGeneratorFunctionBuilder { + private asyncPandaGen: PandaGen; + private compiler: Compiler; + private asyncGenObj: VReg; + private retValue: VReg; + + constructor(pandaGen: PandaGen, compiler: Compiler) { + this.asyncPandaGen = pandaGen; + this.compiler = compiler; + this.asyncGenObj = pandaGen.getTemp(); + this.retValue = pandaGen.getTemp(); + } + + prepare(node: ts.Node, recorder: Recorder) { + let pandaGen = this.asyncPandaGen; + let scope = recorder.getScopeOfNode(node); + + // backend handle funcobj, frontend set undefined + pandaGen.createAsyncGeneratorObj(node, getVregisterCache(pandaGen, CacheList.FUNC)); + pandaGen.storeAccumulator(node, this.asyncGenObj); + pandaGen.suspendGenerator(node, this.asyncGenObj, getVregisterCache(pandaGen, CacheList.undefined)); + pandaGen.resumeGenerator(node, this.asyncGenObj); + pandaGen.storeAccumulator(node, this.retValue); + + this.handleMode(node); + } + + await(node: ts.Node, value: VReg): void { + let pandaGen = this.asyncPandaGen; + let promise = this.asyncPandaGen.getTemp(); + + pandaGen.asyncFunctionAwaitUncaught(node, this.asyncGenObj, value); + pandaGen.storeAccumulator(node, promise); + + pandaGen.suspendGenerator(node, this.asyncGenObj, promise); + + pandaGen.freeTemps(promise); + + pandaGen.resumeGenerator(node, this.asyncGenObj); + pandaGen.storeAccumulator(node, this.retValue); + + this.handleMode(node); + } + + yield(node: ts.Node, value: VReg) { + let pandaGen = this.asyncPandaGen; + let promise = this.asyncPandaGen.getTemp(); + pandaGen.asyncFunctionAwaitUncaught(node, this.asyncGenObj, value); + + pandaGen.storeAccumulator(node, promise); + pandaGen.suspendGenerator(node, this.asyncGenObj, promise); + pandaGen.freeTemps(promise); + pandaGen.resumeGenerator(node, this.asyncGenObj); + pandaGen.storeAccumulator(node, this.retValue); + this.asyncHandleMode(node, value); + } + +private asyncHandleMode(node: ts.Node, value: VReg) { + let pandaGen = this.asyncPandaGen; + + let modeType = pandaGen.getTemp(); + + pandaGen.getResumeMode(node, this.asyncGenObj); + pandaGen.storeAccumulator(node, modeType); + + // .next(value) + pandaGen.loadAccumulatorInt(node, ResumeMode.Next); + + let notNextLabel = new Label(); + pandaGen.condition(node, ts.SyntaxKind.EqualsEqualsToken, modeType, notNextLabel); + pandaGen.EcmaAsyncgeneratorresolve(node, this.asyncGenObj, value); + pandaGen.loadAccumulator(node, this.retValue); + + // .throw(value) + pandaGen.label(node, notNextLabel); + pandaGen.loadAccumulatorInt(node, ResumeMode.Throw); + let notThrowLabel = new Label(); + pandaGen.condition(node, ts.SyntaxKind.EqualsEqualsToken, modeType, notThrowLabel); + pandaGen.loadAccumulator(node, this.retValue); + pandaGen.throw(node); + + pandaGen.freeTemps(modeType); + + // .return(value) + pandaGen.label(node, notThrowLabel); + pandaGen.loadAccumulator(node, this.retValue); + pandaGen.return(node); +} + + private handleMode(node: ts.Node) { + let pandaGen = this.asyncPandaGen; + + let modeType = pandaGen.getTemp(); + + pandaGen.getResumeMode(node, this.asyncGenObj); + pandaGen.storeAccumulator(node, modeType); + + // .return(value) + pandaGen.loadAccumulatorInt(node, ResumeMode.Return); + + let notRetLabel = new Label(); + + pandaGen.condition(node, ts.SyntaxKind.EqualsEqualsToken, modeType, notRetLabel); + + // if there are finally blocks, should implement these at first. + this.compiler.compileFinallyBeforeCFC( + undefined, + ControlFlowChange.Break, + undefined + ); + + pandaGen.loadAccumulator(node, this.retValue); + pandaGen.return(node); + + // .throw(value) + pandaGen.label(node, notRetLabel); + + pandaGen.loadAccumulatorInt(node, ResumeMode.Throw); + + let notThrowLabel = new Label(); + + pandaGen.condition(node, ts.SyntaxKind.EqualsEqualsToken, modeType, notThrowLabel); + pandaGen.loadAccumulator(node, this.retValue); + pandaGen.throw(node); + + pandaGen.freeTemps(modeType); + + // .next(value) + pandaGen.label(node, notThrowLabel); + pandaGen.loadAccumulator(node, this.retValue); + } + + cleanUp() { + this.asyncPandaGen.freeTemps(this.asyncGenObj, this.retValue); + } +} diff --git a/ts2panda/src/function/functionBuilder.ts b/ts2panda/src/function/functionBuilder.ts index f05185a8e1..075da8b6f8 100644 --- a/ts2panda/src/function/functionBuilder.ts +++ b/ts2panda/src/function/functionBuilder.ts @@ -16,8 +16,9 @@ import * as ts from "typescript"; import { GeneratorFunctionBuilder } from "./generatorFunctionBuilder"; import { AsyncFunctionBuilder } from "./asyncFunctionBuilder"; +import { AsyncGeneratorFunctionBuilder } from "./asyncGeneratorFunctionBuilder"; -export type FunctionBuilderType = AsyncFunctionBuilder | GeneratorFunctionBuilder | FunctionBuilder; +export type FunctionBuilderType = AsyncFunctionBuilder | GeneratorFunctionBuilder | FunctionBuilder | AsyncGeneratorFunctionBuilder; export class FunctionBuilder { // @ts-ignore diff --git a/ts2panda/src/pandagen.ts b/ts2panda/src/pandagen.ts index 0db314b6bd..5a89966657 100644 --- a/ts2panda/src/pandagen.ts +++ b/ts2panda/src/pandagen.ts @@ -42,6 +42,7 @@ import { defineClassWithBuffer, defineFunc, defineGeneratorFunc, + defineAsyncGeneratorFunc, defineGetterSetterByValue, defineMethod, defineNCFunc, @@ -133,7 +134,9 @@ import { EcmaCallspreaddyn, EcmaCopyrestargs, EcmaCreategeneratorobj, + EcmaCreateasyncgeneratorobj, EcmaCreateiterresultobj, + EcmaAsyncgeneratorresolve, EcmaDecdyn, EcmaDiv2dyn, EcmaEqdyn, @@ -948,6 +951,11 @@ export class PandaGen { if (realNode.modifiers[i].kind == ts.SyntaxKind.AsyncKeyword) { if (realNode.asteriskToken) { // support async* further + this.add( + node, + defineAsyncGeneratorFunc(name, env, paramLength) + ); + return; } else { // async this.add( node, @@ -1006,10 +1014,18 @@ export class PandaGen { this.add(node, new EcmaCreategeneratorobj(funcObj)); } + createAsyncGeneratorObj(node: ts.Node, funcObj: VReg) { + this.add(node, new EcmaCreateasyncgeneratorobj(funcObj)); + } + EcmaCreateiterresultobj(node: ts.Node, value: VReg, done: VReg) { this.add(node, new EcmaCreateiterresultobj(value, done)); } + EcmaAsyncgeneratorresolve(node: ts.Node, genObj: VReg, value: VReg) { + this.add(node, new EcmaAsyncgeneratorresolve(genObj, value)); + } + suspendGenerator(node: ts.Node, genObj: VReg, iterRslt: VReg) { this.add(node, new EcmaSuspendgenerator(genObj, iterRslt)); } -- Gitee