Skip to content

Commit

Permalink
Correct transpilation of async arrow function in non-async function.
Browse files Browse the repository at this point in the history
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=160341741
  • Loading branch information
brad4d committed Jun 28, 2017
1 parent b93ccd3 commit 3fcc79d
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 12 deletions.
30 changes: 21 additions & 9 deletions src/com/google/javascript/jscomp/RewriteAsyncFunctions.java
Expand Up @@ -58,8 +58,8 @@ public final class RewriteAsyncFunctions implements NodeTraversal.Callback, HotS
private static final class LexicalContext {
final Optional<Node> function; // absent for top level
final LexicalContext thisAndArgumentsContext;
boolean asyncThisReplacementWasDone = false;
boolean asyncArgumentsReplacementWasDone = false;
boolean mustAddAsyncThisVariable = false;
boolean mustAddAsyncArgumentsVariable = false;

/** Creates root-level context. */
LexicalContext() {
Expand All @@ -79,17 +79,29 @@ boolean isAsyncContext() {
}

boolean mustReplaceThisAndArguments() {
return thisAndArgumentsContext.isAsyncContext();
return isAsyncContext() || thisAndArgumentsContext.isAsyncContext();
}

void recordAsyncThisReplacementWasDone() {
checkState(thisAndArgumentsContext.isAsyncContext());
thisAndArgumentsContext.asyncThisReplacementWasDone = true;
if (thisAndArgumentsContext.isAsyncContext()) {
thisAndArgumentsContext.mustAddAsyncThisVariable = true;
} else {
// The current context is an async arrow function within a non-async function,
// so it must define its own replacement variable.
checkState(isAsyncContext());
mustAddAsyncThisVariable = true;
}
}

void recordAsyncArgumentsReplacementWasDone() {
checkState(thisAndArgumentsContext.isAsyncContext());
thisAndArgumentsContext.asyncArgumentsReplacementWasDone = true;
if (thisAndArgumentsContext.isAsyncContext()) {
thisAndArgumentsContext.mustAddAsyncArgumentsVariable = true;
} else {
// The current context is an async arrow function within a non-async function,
// so it must define its own replacement variable.
checkState(isAsyncContext());
mustAddAsyncArgumentsVariable = true;
}
}
}

Expand Down Expand Up @@ -176,10 +188,10 @@ private void convertAsyncFunction(LexicalContext functionContext) {
Node newBody = IR.block().useSourceInfoIfMissingFrom(originalBody);
originalFunction.replaceChild(originalBody, newBody);

if (functionContext.asyncThisReplacementWasDone) {
if (functionContext.mustAddAsyncThisVariable) {
newBody.addChildToBack(IR.constNode(IR.name(ASYNC_THIS), IR.thisNode()));
}
if (functionContext.asyncArgumentsReplacementWasDone) {
if (functionContext.mustAddAsyncArgumentsVariable) {
newBody.addChildToBack(IR.constNode(IR.name(ASYNC_ARGUMENTS), IR.name("arguments")));
}

Expand Down
32 changes: 29 additions & 3 deletions test/com/google/javascript/jscomp/RewriteAsyncFunctionsTest.java
Expand Up @@ -149,23 +149,24 @@ public void testClassMethod() {
"}"));
}

public void testClassMethodWithAsyncArrow() {
public void testAsyncClassMethodWithAsyncArrow() {
test(
LINE_JOINER.join(
"class A {",
" async f() {",
" let g = async () => { console.log(this); };",
" let g = async () => { console.log(this, arguments); };",
" g();",
" }",
"}"),
LINE_JOINER.join(
"class A {",
" f() {",
" const $jscomp$async$this = this;",
" const $jscomp$async$arguments = arguments;",
" function *$jscomp$async$generator() {",
" let g = () => {",
" function *$jscomp$async$generator() {",
" console.log($jscomp$async$this);",
" console.log($jscomp$async$this, $jscomp$async$arguments);",
" }",
" return $jscomp.executeAsyncGenerator($jscomp$async$generator());",
" };",
Expand All @@ -176,6 +177,31 @@ public void testClassMethodWithAsyncArrow() {
"}"));
}

public void testNonAsyncClassMethodWithAsyncArrow() {
test(
LINE_JOINER.join(
"class A {",
" f() {",
" let g = async () => { console.log(this, arguments); };",
" g();",
" }",
"}"),
LINE_JOINER.join(
"class A {",
" f() {",
" let g = () => {",
" const $jscomp$async$this = this;",
" const $jscomp$async$arguments = arguments;",
" function *$jscomp$async$generator() {",
" console.log($jscomp$async$this, $jscomp$async$arguments);",
" }",
" return $jscomp.executeAsyncGenerator($jscomp$async$generator());",
" };",
" g();",
" }",
"}"));
}

public void testArrowFunctionExpressionBody() {
test(
"let f = async () => 1;",
Expand Down
Expand Up @@ -162,6 +162,24 @@ testSuite({
});
},

testNonAsyncMemberFunctionUsingThisInAsyncArrowFunction() {
class C {
constructor() {
this.value = 0;
}

delayedIncrementAndReturnThis() {
const nestedArrow = async () => { this.value++; return this; };
return nestedArrow();
}
}
const c = new C();
return c.delayedIncrementAndReturnThis().then(result => {
assertEquals(c, result);
assertEquals(1, c.value);
});
},

testArgumentsHandledCorrectly() {
const expected1 = {};
const expected2 = 2;
Expand Down

0 comments on commit 3fcc79d

Please sign in to comment.