diff --git a/ts2panda/src/base/bcGenUtil.ts b/ts2panda/src/base/bcGenUtil.ts index ebde0fb13733f9e72f786904550f42c643c57de1..a4bdb192d295424f9652481c77ad2bf3dec2b33f 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 fcc8536e3665dbe5dce8a7feb01cddd381d9a792..5c4a607d524e03d4a27152e4ca640ab2b06e31aa 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"; @@ -324,7 +325,7 @@ export class Compiler { return ; } // exit GlobalScopefunction or Function Block return - if (this.funcBuilder instanceof AsyncFunctionBuilder) { + if (this.funcBuilder instanceof AsyncFunctionBuilder || this.funcBuilder instanceof AsyncGeneratorFunctionBuilder) { this.funcBuilder.resolve(NodeKind.Invalid, getVregisterCache(pandaGen, CacheList.undefined)); pandaGen.return(NodeKind.Invalid); } else { @@ -345,7 +346,7 @@ export class Compiler { let retValue = pandaGen.getTemp(); pandaGen.storeAccumulator(body, retValue); - if (this.funcBuilder instanceof AsyncFunctionBuilder) { + if (this.funcBuilder instanceof AsyncFunctionBuilder || this.funcBuilder instanceof AsyncGeneratorFunctionBuilder) { this.funcBuilder.resolve(body, retValue); pandaGen.return(NodeKind.Invalid); } else { @@ -414,7 +415,7 @@ 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); } else { // async return new AsyncFunctionBuilder(pandaGen); } @@ -1142,7 +1143,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)) { 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 99fc5f856b924926160ff4ecc94c907f7d192ba4..f064ebd5f1365a916fc0e4a67d96d98150c8054e 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 0000000000000000000000000000000000000000..53eec4ec79c7e14c82f2ae57e2578a3a424ce1c6 --- /dev/null +++ b/ts2panda/src/function/asyncGeneratorFunctionBuilder.ts @@ -0,0 +1,172 @@ +/* + * 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); + } + + 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.EcmaAsyncgeneratorresolve(node, this.asyncGenObj, value, getVregisterCache(pandaGen, CacheList.False)); + 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 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); + } + + 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.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); + } + + resolve(node: ts.Node | NodeKind, value: VReg) { + let pandaGen = this.asyncPandaGen; + pandaGen.EcmaAsyncgeneratorresolve(node, this.asyncGenObj, value, getVregisterCache(pandaGen, CacheList.True)); + } + + cleanUp() { + this.asyncPandaGen.freeTemps(this.asyncGenObj, this.retValue); + } +} diff --git a/ts2panda/src/function/functionBuilder.ts b/ts2panda/src/function/functionBuilder.ts index f05185a8e159f2d17fc4a7425d63fb70250bfb7c..075da8b6f8d003d570d3fdfa8ab370afea674323 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/index.ts b/ts2panda/src/index.ts index e1b34e285c1ffe9fa790adb63fb0443608df4e23..acd3fe3de55e5cd92c53b9dae372b48bf472ee00 100644 --- a/ts2panda/src/index.ts +++ b/ts2panda/src/index.ts @@ -278,7 +278,7 @@ namespace Compiler { allowJs: true, noEmitOnError: false, noImplicitAny: true, - target: ts.ScriptTarget.ES2017, + target: ts.ScriptTarget.ES2018, module: ts.ModuleKind.ES2015, strictNullChecks: true, skipLibCheck: true, diff --git a/ts2panda/src/pandagen.ts b/ts2panda/src/pandagen.ts index 65834994a59213dc421946a26277d79324a86a13..208470e4862c54224a452a523b4ff43c09e14a75 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, @@ -949,6 +952,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, @@ -1011,10 +1019,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 | NodeKind, genObj: VReg, value: VReg, done: VReg) { + this.add(node, new EcmaAsyncgeneratorresolve(genObj, value, done)); + } + suspendGenerator(node: ts.Node, genObj: VReg, iterRslt: VReg) { this.add(node, new EcmaSuspendgenerator(genObj, iterRslt)); } diff --git a/ts2panda/src/statement/returnStatement.ts b/ts2panda/src/statement/returnStatement.ts index adbe63fbd0e970830d470b7b71c750ac94b1b56f..6a1b3cc049c70ea4a9576f9f750a28712bf31e32 100644 --- a/ts2panda/src/statement/returnStatement.ts +++ b/ts2panda/src/statement/returnStatement.ts @@ -17,6 +17,7 @@ import * as ts from "typescript"; import { CacheList, getVregisterCache } from "../base/vregisterCache"; import { Compiler, ControlFlowChange } from "../compiler"; import { AsyncFunctionBuilder } from "../function/asyncFunctionBuilder"; +import { AsyncGeneratorFunctionBuilder } from "../function/asyncGeneratorFunctionBuilder"; import { Label, VReg } from "../irnodes"; import * as jshelpers from "../jshelpers"; import { checkValidUseSuperBeforeSuper } from "./classStatement"; @@ -122,7 +123,7 @@ function compileNormalReturn(stmt: ts.ReturnStatement, returnValue: VReg, compil pandaGen.loadAccumulator(stmt, returnValue); let funcBuilder = compiler.getFuncBuilder(); - if (funcBuilder instanceof AsyncFunctionBuilder) { + if (funcBuilder instanceof AsyncFunctionBuilder || funcBuilder instanceof AsyncGeneratorFunctionBuilder) { let resovledVal = pandaGen.getTemp(); pandaGen.storeAccumulator(stmt, resovledVal);