Support async function serialization. (#1311)

This commit is contained in:
CyrusNajmabadi 2018-05-03 12:25:52 -07:00 committed by GitHub
parent cc8b87ce3d
commit 5387e78cfa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 112 additions and 6 deletions

View file

@ -27,6 +27,7 @@ lint::
build::
go install -ldflags "-X github.com/pulumi/pulumi/pkg/version.Version=${VERSION}" ${LANGUAGE_HOST}
tsc
cp tests/runtime/jsClosureCases.js bin/tests/runtime
cp README.md ../../LICENSE package.json ./dist/* bin/
node ../../scripts/reversion.js bin/package.json ${VERSION}
node ../../scripts/reversion.js bin/version.js ${VERSION}

View file

@ -369,11 +369,19 @@ function createFunctionInfo(
func.toString().startsWith("class ") &&
proto !== Function.prototype(func);
// Note, i can't think of a better way to determine this. This is particularly hard because
// we can't even necessary refer to async function objects here as this code is rewritten by
// TS, converting all async functions to non async functions.
const isAsyncFunction = func.constructor && func.constructor.name === "AsyncFunction";
// Ensure that the prototype of this function is properly serialized as well. We only need to do
// this for functions with a custom prototype (like a derived class constructor, or a functoin
// this for functions with a custom prototype (like a derived class constructor, or a function
// that a user has explicit set the prototype for). Normal functions will pick up
// Function.prototype by default, so we don't need to do anything for them.
if (proto !== Function.prototype && !isDerivedNoCaptureConstructor(func)) {
if (proto !== Function.prototype &&
!isAsyncFunction &&
!isDerivedNoCaptureConstructor(func)) {
const protoEntry = getOrCreateEntry(proto, undefined, context, serialize);
functionInfo.proto = protoEntry;

View file

@ -140,6 +140,12 @@ function parseFunctionCode(funcString: string): [string, ParsedFunctionCode] {
return ["", { funcExprWithoutName: funcString, isArrowFunction: true }];
}
let isAsync = false;
if (funcString.startsWith("async ")) {
isAsync = true;
funcString = funcString.substr("async".length).trimLeft();
}
if (funcString.startsWith("function get ") || funcString.startsWith("function set ")) {
const trimmed = funcString.substr("function get".length);
return makeFunctionDeclaration(trimmed, /*isFunctionDeclaration: */ false);
@ -171,8 +177,8 @@ function parseFunctionCode(funcString: string): [string, ParsedFunctionCode] {
const isSubClass = classDecl.heritageClauses && classDecl.heritageClauses.some(
c => c.token === ts.SyntaxKind.ExtendsKeyword);
return isSubClass
? makeFunctionDeclaration("constructor() { super(); }", /*isFunctionDeclaration: */ false)
: makeFunctionDeclaration("constructor() { }", /*isFunctionDeclaration: */ false);
? makeFunctionDeclaration("constructor() { super(); }", /*isFunctionDeclaration:*/ false)
: makeFunctionDeclaration("constructor() { }", /*isFunctionDeclaration:*/ false);
}
const constructorCode = funcString.substring(constructor.pos, constructor.end).trim();
@ -185,7 +191,9 @@ function parseFunctionCode(funcString: string): [string, ParsedFunctionCode] {
return makeFunctionDeclaration(funcString, /*isFunctionDeclaration: */ false);
function makeFunctionDeclaration(v: string, isFunctionDeclaration: boolean): [string, ParsedFunctionCode] {
let prefix = "function ";
let prefix = isAsync ? "async " : "";
prefix += "function ";
v = v.trimLeft();
if (v.startsWith("*")) {

View file

@ -3780,6 +3780,16 @@ return function /*f3*/() {
});
}
// Run a bunch of direct checks on async js functions if we're in node 8 or above.
// We can't do this inline as node6 doesn't understand 'async functions'. And we
// can't do this in TS as TS will convert the async-function to be a normal non-async
// function.
const version = Number(process.version.match(/^v(\d+)\.\d+/)![1]);
if (version >= 8) {
const jsCases = require("./jsClosureCases");
cases.push(...jsCases.cases);
}
// Make a callback to keep running tests.
let remaining = cases;
while (true) {

View file

@ -0,0 +1,79 @@
"use strict";
// Copyright 2016-2018, Pulumi Corporation. All rights reserved.
const cases = [];
cases.push({
title: "Async anonymous function closure (js)",
// tslint:disable-next-line
func: async function (a) { await a; },
expectText: `exports.handler = __f0;
function __f0() {
return (function() {
with({ }) {
return async function (a) { await a; };
}
}).apply(undefined, undefined).apply(this, arguments);
}
`,
});
cases.push({
title: "Async anonymous function closure - extra space (js)",
// tslint:disable-next-line
func: async function (a) { await a; },
expectText: `exports.handler = __f0;
function __f0() {
return (function() {
with({ }) {
return async function (a) { await a; };
}
}).apply(undefined, undefined).apply(this, arguments);
}
`,
});
cases.push({
title: "Async named function closure (js)",
// tslint:disable-next-line
func: async function foo(a) { await a; },
expectText: `exports.handler = __foo;
function __foo() {
return (function() {
with({ foo: __foo }) {
return async function /*foo*/(a) { await a; };
}
}).apply(undefined, undefined).apply(this, arguments);
}
`,
});
cases.push({
title: "Async arrow function closure (js)",
// tslint:disable-next-line
func: async (a) => { await a; },
expectText: `exports.handler = __f0;
function __f0() {
return (function() {
with({ }) {
return async (a) => { await a; };
}
}).apply(undefined, undefined).apply(this, arguments);
}
`,
});
module.exports.cases = cases;

View file

@ -13,7 +13,7 @@
"noImplicitAny": true,
"noImplicitReturns": true,
"forceConsistentCasingInFileNames": true,
"strictNullChecks": true
"strictNullChecks": true,
},
"files": [
"index.ts",