From 97b6c3013a6779c63bcf9481b3a71c86ab40bd09 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Tue, 31 Jan 2017 15:22:43 +0300 Subject: [PATCH] JS: fix destructuring declaration and increment in coroutines. See KT-16058 --- .../augmentedAssignment.kt | 31 +++++++++++++++++++ .../suspendFunctionAsCoroutine/operators.kt | 12 +++++-- .../suspendFunctionAsCoroutine/operators.txt | 1 + .../semantics/JsCodegenBoxTestGenerated.java | 14 ++++----- .../DestructuringDeclarationTranslator.java | 10 +++--- .../js/translate/expression/LoopTranslator.kt | 16 +++++----- .../operation/DynamicIncrementTranslator.java | 2 +- .../operation/IncrementTranslator.java | 10 +++--- .../IntrinsicIncrementTranslator.java | 8 ++--- .../OverloadedIncrementTranslator.java | 6 ++-- 10 files changed, 74 insertions(+), 36 deletions(-) create mode 100644 compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/augmentedAssignment.kt diff --git a/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/augmentedAssignment.kt b/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/augmentedAssignment.kt new file mode 100644 index 00000000000..f6f00fb6a93 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/augmentedAssignment.kt @@ -0,0 +1,31 @@ +// WITH_RUNTIME +// WITH_COROUTINES + +// TARGET_BACKEND: JS +// TODO: looks like this is a bug in JVM backend +import kotlin.coroutines.experimental.* +import kotlin.coroutines.experimental.intrinsics.* + +suspend fun suspendThere(v: A): A = suspendCoroutineOrReturn { x -> + x.resume(v) + COROUTINE_SUSPENDED +} + +class A(val value: String) { + operator suspend fun plus(other: A) = suspendThere(A(value + other.value)) +} + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + + +fun box(): String { + var a = A("O") + + builder { + a += A("K") + } + + return a.value +} diff --git a/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/operators.kt b/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/operators.kt index 3b1ef94c20f..75130f4da20 100644 --- a/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/operators.kt +++ b/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/operators.kt @@ -1,6 +1,5 @@ // WITH_RUNTIME // WITH_COROUTINES -// IGNORE_BACKEND: JS import kotlin.coroutines.experimental.* import kotlin.coroutines.experimental.intrinsics.* import kotlin.reflect.KProperty @@ -34,7 +33,7 @@ class A(val x: String) { operator suspend fun unaryPlus() = suspendThere(x + "K") operator suspend fun inc(): A = suspendCoroutineOrReturn { x -> - isProvideDelegateCalled = true + isIncCalled = true x.resume(this) COROUTINE_SUSPENDED } @@ -86,7 +85,13 @@ suspend fun foo4() { suspend fun foo6() { var y = a++ - if (y.isIncCalled) throw RuntimeException("fail 7") + if (!y.isIncCalled) throw RuntimeException("fail 7") +} + +suspend fun foo7() { + a.isIncCalled = false + val y = ++a + if (!y.isIncCalled) throw RuntimeException("fail 8") } fun box(): String { @@ -98,6 +103,7 @@ fun box(): String { foo4() //foo5() foo6() + foo7() } return "OK" diff --git a/compiler/testData/codegen/light-analysis/coroutines/suspendFunctionAsCoroutine/operators.txt b/compiler/testData/codegen/light-analysis/coroutines/suspendFunctionAsCoroutine/operators.txt index ba1c3abe24f..d321bbe16b2 100644 --- a/compiler/testData/codegen/light-analysis/coroutines/suspendFunctionAsCoroutine/operators.txt +++ b/compiler/testData/codegen/light-analysis/coroutines/suspendFunctionAsCoroutine/operators.txt @@ -60,6 +60,7 @@ public final class OperatorsKt { public final static @org.jetbrains.annotations.Nullable method foo3(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object public final static @org.jetbrains.annotations.Nullable method foo4(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object public final static @org.jetbrains.annotations.Nullable method foo6(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object + public final static @org.jetbrains.annotations.Nullable method foo7(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object public final static @org.jetbrains.annotations.NotNull method getA(): A public final static method setA(@org.jetbrains.annotations.NotNull p0: A): void public final static @org.jetbrains.annotations.Nullable method suspendThere(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index b9f067573d1..cc229590112 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -5891,6 +5891,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); } + @TestMetadata("augmentedAssignment.kt") + public void testAugmentedAssignment() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/augmentedAssignment.kt"); + doTest(fileName); + } + @TestMetadata("dispatchResume.kt") public void testDispatchResume() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/dispatchResume.kt"); @@ -5930,13 +5936,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { @TestMetadata("operators.kt") public void testOperators() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/operators.kt"); - try { - doTest(fileName); - } - catch (Throwable ignore) { - return; - } - throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + doTest(fileName); } @TestMetadata("privateFunctions.kt") diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/DestructuringDeclarationTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/DestructuringDeclarationTranslator.java index 39f6ab2b806..5adb8cdc8fc 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/DestructuringDeclarationTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/DestructuringDeclarationTranslator.java @@ -77,18 +77,18 @@ public class DestructuringDeclarationTranslator extends AbstractTranslator { } private JsVars translate() { - List jsVars = new ArrayList(); if (initializer != null) { - jsVars.add(new JsVars.JsVar(multiObjectName, initializer)); + context().getCurrentBlock().getStatements().add(JsAstUtils.newVar(multiObjectName, initializer)); } + List jsVars = new ArrayList(); JsNameRef multiObjNameRef = multiObjectName.makeRef(); for (KtDestructuringDeclarationEntry entry : multiDeclaration.getEntries()) { - VariableDescriptor descriptor = BindingContextUtils.getNotNull( context().bindingContext(), BindingContext.VARIABLE, entry); + VariableDescriptor descriptor = BindingContextUtils.getNotNull(context().bindingContext(), BindingContext.VARIABLE, entry); // Do not call `componentX` for destructuring entry called _ if (descriptor.getName().isSpecial()) continue; - ResolvedCall entryInitCall = context().bindingContext().get(BindingContext.COMPONENT_RESOLVED_CALL, entry); + ResolvedCall entryInitCall = context().bindingContext().get(BindingContext.COMPONENT_RESOLVED_CALL, entry); assert entryInitCall != null : "Entry init call must be not null"; JsExpression entryInitializer = CallTranslator.translate(context(), entryInitCall, multiObjNameRef); FunctionDescriptor candidateDescriptor = entryInitCall.getCandidateDescriptor(); @@ -101,7 +101,7 @@ public class DestructuringDeclarationTranslator extends AbstractTranslator { entryInitializer = JsAstUtils.charToBoxedChar(entryInitializer); } - JsName name = context().getNameForDescriptor(descriptor); + JsName name = context().getNameForDescriptor(descriptor); if (isVarCapturedInClosure(context().bindingContext(), descriptor)) { JsNameRef alias = getCapturedVarAccessor(name.makeRef()); entryInitializer = JsAstUtils.wrapValue(alias, entryInitializer); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LoopTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LoopTranslator.kt index a89bcdba5d6..1e87f41dc95 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LoopTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LoopTranslator.kt @@ -36,6 +36,7 @@ import org.jetbrains.kotlin.psi.KtForExpression import org.jetbrains.kotlin.psi.KtWhileExpressionBase import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.utils.singletonOrEmptyList fun createWhile(doWhile: Boolean, expression: KtWhileExpressionBase, context: TranslationContext): JsNode { val conditionExpression = expression.condition ?: @@ -89,13 +90,13 @@ fun translateForExpression(expression: KtForExpression, context: TranslationCont fun isForOverRangeLiteral(): Boolean = loopRange is KtBinaryExpression && loopRange.operationToken == KtTokens.RANGE && isForOverRange() - fun isForOverArray(): Boolean { + fun isForOverArray(): Boolean { return KotlinBuiltIns.isArray(rangeType) || KotlinBuiltIns.isPrimitiveArray(rangeType) } val loopParameter = expression.loopParameter!! - val destructuringParameter: KtDestructuringDeclaration? = loopParameter?.destructuringDeclaration + val destructuringParameter: KtDestructuringDeclaration? = loopParameter.destructuringDeclaration val parameterName = if (destructuringParameter == null) { context.getNameForElement(loopParameter) } @@ -109,18 +110,19 @@ fun translateForExpression(expression: KtForExpression, context: TranslationCont return realBody } else { + val block = JsBlock() + val currentVarInit = if (destructuringParameter == null) { newVar(parameterName, itemValue) } else { - DestructuringDeclarationTranslator.translate(destructuringParameter, parameterName, itemValue, context) + DestructuringDeclarationTranslator.translate( + destructuringParameter, parameterName, itemValue, context.innerBlock(block)) } + block.statements += currentVarInit + block.statements += if (realBody is JsBlock) realBody.statements else realBody.singletonOrEmptyList() - if (realBody == null) return JsBlock(currentVarInit) - - val block = convertToBlock(realBody) - block.statements.add(0, currentVarInit) return block } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/operation/DynamicIncrementTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/operation/DynamicIncrementTranslator.java index 84aa5ac3096..7b1887df103 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/operation/DynamicIncrementTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/operation/DynamicIncrementTranslator.java @@ -63,7 +63,7 @@ public class DynamicIncrementTranslator extends IncrementTranslator { @Override @NotNull - protected JsExpression operationExpression(@NotNull JsExpression receiver) { + protected JsExpression operationExpression(@NotNull TranslationContext context, @NotNull JsExpression receiver) { return unaryAsBinary(receiver); } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/operation/IncrementTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/operation/IncrementTranslator.java index d806093c89c..db9a759da05 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/operation/IncrementTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/operation/IncrementTranslator.java @@ -91,7 +91,7 @@ public abstract class IncrementTranslator extends AbstractTranslator { // code fragment: expr(a++) // generate: expr(a = a.inc(), a) JsExpression getExpression = accessTranslator.translateAsGet(); - JsExpression reassignment = variableReassignment(getExpression); + JsExpression reassignment = variableReassignment(context().innerBlock(accessBlock), getExpression); accessBlock.getStatements().add(JsAstUtils.asSyntheticStatement(reassignment)); JsExpression getNewValue = accessTranslator.translateAsGet(); @@ -114,7 +114,7 @@ public abstract class IncrementTranslator extends AbstractTranslator { // generate: expr( (t1 = a, t2 = t1, a = t1.inc(), t2) ) TemporaryVariable t1 = context().declareTemporary(accessTranslator.translateAsGet()); accessBlock.getStatements().add(t1.assignmentStatement()); - JsExpression variableReassignment = variableReassignment(t1.reference()); + JsExpression variableReassignment = variableReassignment(context().innerBlock(accessBlock), t1.reference()); accessBlock.getStatements().add(JsAstUtils.asSyntheticStatement(variableReassignment)); JsExpression result; @@ -131,13 +131,13 @@ public abstract class IncrementTranslator extends AbstractTranslator { } @NotNull - private JsExpression variableReassignment(@NotNull JsExpression toCallMethodUpon) { - JsExpression overloadedMethodCallOnPropertyGetter = operationExpression(toCallMethodUpon); + private JsExpression variableReassignment(@NotNull TranslationContext context, @NotNull JsExpression toCallMethodUpon) { + JsExpression overloadedMethodCallOnPropertyGetter = operationExpression(context, toCallMethodUpon); return accessTranslator.translateAsSet(overloadedMethodCallOnPropertyGetter); } @NotNull - abstract JsExpression operationExpression(@NotNull JsExpression receiver); + abstract JsExpression operationExpression(@NotNull TranslationContext context, @NotNull JsExpression receiver); private static boolean isDynamic(TranslationContext context, KtUnaryExpression expression) { CallableDescriptor operationDescriptor = getCallableDescriptorForOperationExpression(context.bindingContext(), expression); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/operation/IntrinsicIncrementTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/operation/IntrinsicIncrementTranslator.java index 5eb02303b16..028a5c34039 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/operation/IntrinsicIncrementTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/operation/IntrinsicIncrementTranslator.java @@ -41,9 +41,9 @@ public final class IntrinsicIncrementTranslator extends IncrementTranslator { @Override @NotNull - protected JsExpression operationExpression(@NotNull JsExpression receiver) { - FunctionIntrinsic intrinsic = context().intrinsics().getFunctionIntrinsic(resolvedCall.getResultingDescriptor()); - CallInfo callInfo = CallInfoKt.getCallInfo(context(), resolvedCall, receiver); - return intrinsic.apply(callInfo, Collections.emptyList(), context()); + protected JsExpression operationExpression(@NotNull TranslationContext context, @NotNull JsExpression receiver) { + FunctionIntrinsic intrinsic = context.intrinsics().getFunctionIntrinsic(resolvedCall.getResultingDescriptor()); + CallInfo callInfo = CallInfoKt.getCallInfo(context, resolvedCall, receiver); + return intrinsic.apply(callInfo, Collections.emptyList(), context); } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/operation/OverloadedIncrementTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/operation/OverloadedIncrementTranslator.java index f965546b432..d8af21e1aed 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/operation/OverloadedIncrementTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/operation/OverloadedIncrementTranslator.java @@ -38,11 +38,9 @@ public final class OverloadedIncrementTranslator extends IncrementTranslator { this.resolvedCall = CallUtilKt.getFunctionResolvedCallWithAssert(expression, context.bindingContext()); } - @Override @NotNull - protected JsExpression operationExpression(@NotNull JsExpression receiver) { - return CallTranslator.translate(context(), resolvedCall, receiver); + protected JsExpression operationExpression(@NotNull TranslationContext context, @NotNull JsExpression receiver) { + return CallTranslator.translate(context, resolvedCall, receiver); } - }