From d41d09ffc4f627922d9b1982426fb1b5d005db78 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Thu, 10 Nov 2016 18:41:30 +0300 Subject: [PATCH] JS: add metadata for generated continuation classes to properly implement RTTI --- .../box/coroutines/instanceOfContinuation.kt | 30 +++++++++++++++++++ .../ir/IrBlackBoxCodegenTestGenerated.java | 6 ++++ .../codegen/BlackBoxCodegenTestGenerated.java | 6 ++++ .../js/ast/metadata/metadataProperties.kt | 2 ++ .../coroutine/CoroutineFunctionTransformer.kt | 23 ++++++++++---- .../expression/LiteralFunctionTranslator.kt | 14 ++++++++- 6 files changed, 74 insertions(+), 7 deletions(-) create mode 100644 compiler/testData/codegen/box/coroutines/instanceOfContinuation.kt diff --git a/compiler/testData/codegen/box/coroutines/instanceOfContinuation.kt b/compiler/testData/codegen/box/coroutines/instanceOfContinuation.kt new file mode 100644 index 00000000000..88d43908380 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/instanceOfContinuation.kt @@ -0,0 +1,30 @@ +// WITH_RUNTIME +// WITH_REFLECT + +class Controller { + suspend fun runInstanceOf(x: Continuation) { + val y: Any = x + x.resume(x is Continuation<*>) + } + + suspend fun runCast(x: Continuation) { + val y: Any = x + x.resume(Continuation::class.isInstance(y as Continuation<*>)) + } +} + +fun builder(coroutine c: Controller.() -> Continuation) { + c(Controller()).resume(Unit) +} + +fun box(): String { + var result = "" + + builder { + result = runInstanceOf().toString() + "," + runCast().toString() + } + + if (result != "true,true") return "fail: $result" + + return "OK" +} diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 90fb2883520..e581c7c4f74 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -4520,6 +4520,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } + @TestMetadata("instanceOfContinuation.kt") + public void testInstanceOfContinuation() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/instanceOfContinuation.kt"); + doTest(fileName); + } + @TestMetadata("iterateOverArray.kt") public void testIterateOverArray() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/iterateOverArray.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index d077fd90ce4..ffc713e9199 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -4520,6 +4520,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("instanceOfContinuation.kt") + public void testInstanceOfContinuation() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/instanceOfContinuation.kt"); + doTest(fileName); + } + @TestMetadata("iterateOverArray.kt") public void testIterateOverArray() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/iterateOverArray.kt"); diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/metadata/metadataProperties.kt b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/metadata/metadataProperties.kt index 9f1cf098564..c23038f341d 100644 --- a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/metadata/metadataProperties.kt +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/metadata/metadataProperties.kt @@ -97,6 +97,8 @@ var JsNameRef.coroutineResult by MetadataProperty(default = false) */ var JsNameRef.coroutineController by MetadataProperty(default = false) +var JsFunction.continuationInterfaceRef: JsExpression? by MetadataProperty(default = null) + enum class TypeCheck { TYPEOF, INSTANCEOF, diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/coroutine/CoroutineFunctionTransformer.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/coroutine/CoroutineFunctionTransformer.kt index 99f72de606e..8103e114286 100644 --- a/js/js.inliner/src/org/jetbrains/kotlin/js/coroutine/CoroutineFunctionTransformer.kt +++ b/js/js.inliner/src/org/jetbrains/kotlin/js/coroutine/CoroutineFunctionTransformer.kt @@ -17,10 +17,7 @@ package org.jetbrains.kotlin.js.coroutine import com.google.dart.compiler.backend.js.ast.* -import com.google.dart.compiler.backend.js.ast.metadata.SideEffectKind -import com.google.dart.compiler.backend.js.ast.metadata.coroutineController -import com.google.dart.compiler.backend.js.ast.metadata.coroutineResult -import com.google.dart.compiler.backend.js.ast.metadata.sideEffects +import com.google.dart.compiler.backend.js.ast.metadata.* import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.js.inline.clean.FunctionPostProcessor @@ -70,7 +67,7 @@ class CoroutineFunctionTransformer( return additionalStatements } - private fun generateContinuationConstructor(bodyTransformer: CoroutineBodyTransformer,statements: MutableList) { + private fun generateContinuationConstructor(bodyTransformer: CoroutineBodyTransformer, statements: MutableList) { val constructor = JsFunction(function.scope.parent, JsBlock(), "Continuation") constructor.name = className constructor.parameters += function.parameters.map { JsParameter(it.name) } @@ -96,7 +93,21 @@ class CoroutineFunctionTransformer( } } - statements.add(0, constructor.makeStmt()) + statements.addAll(0, listOf(constructor.makeStmt(), generateCoroutineMetadata(constructor.name))) + } + + private fun generateCoroutineMetadata(constructorName: JsName): JsStatement { + val interfaceRef = function.continuationInterfaceRef!!.deepCopy() + + val metadataObject = JsObjectLiteral(true) + metadataObject.propertyInitializers += JsPropertyInitializer( + JsNameRef("type"), JsNameRef("CLASS", JsNameRef("TYPE", Namer.KOTLIN_NAME))) + metadataObject.propertyInitializers += JsPropertyInitializer( + JsNameRef("classIndex"), JsInvocation(JsNameRef("newClassIndex", Namer.KOTLIN_NAME))) + metadataObject.propertyInitializers += JsPropertyInitializer(JsNameRef("simpleName"), JsLiteral.NULL) + metadataObject.propertyInitializers += JsPropertyInitializer(JsNameRef("baseClasses"), JsArrayLiteral(listOf(interfaceRef))) + + return JsAstUtils.assignment(JsNameRef(Namer.METADATA, constructorName.makeRef()), metadataObject).makeStmt() } private fun generateContinuationMethods(doResumeName: JsName, statements: MutableList) { diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LiteralFunctionTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LiteralFunctionTranslator.kt index fa3a2637fba..7759e836e7c 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LiteralFunctionTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LiteralFunctionTranslator.kt @@ -18,8 +18,8 @@ package org.jetbrains.kotlin.js.translate.expression import com.google.dart.compiler.backend.js.ast.* import com.google.dart.compiler.backend.js.ast.metadata.* +import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl import org.jetbrains.kotlin.js.descriptorUtils.isCoroutineLambda import org.jetbrains.kotlin.js.inline.util.getInnerFunction @@ -28,14 +28,18 @@ import org.jetbrains.kotlin.js.translate.context.getNameForCapturedDescriptor import org.jetbrains.kotlin.js.translate.context.hasCapturedExceptContaining import org.jetbrains.kotlin.js.translate.context.isCaptured import org.jetbrains.kotlin.js.translate.general.AbstractTranslator +import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator import org.jetbrains.kotlin.js.translate.utils.BindingUtils.getFunctionDescriptor import org.jetbrains.kotlin.js.translate.utils.FunctionBodyTranslator.translateFunctionBody import org.jetbrains.kotlin.js.translate.utils.JsAstUtils import org.jetbrains.kotlin.js.translate.utils.TranslationUtils.simpleReturnFunction +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.KtDeclarationWithBody import org.jetbrains.kotlin.psi.KtParameter import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.inline.InlineUtil +import org.jetbrains.kotlin.serialization.deserialization.findClassAcrossModuleDependencies class LiteralFunctionTranslator(context: TranslationContext) : AbstractTranslator(context) { fun translate( @@ -81,6 +85,7 @@ class LiteralFunctionTranslator(context: TranslationContext) : AbstractTranslato lambda.name = null } lambdaCreator.name.staticRef = lambdaCreator + lambdaCreator.continuationInterfaceRef = invokingContext.getContinuationInterfaceReference() return lambdaCreator.withCapturedParameters(descriptor, descriptor.wrapContextForCoroutineIfNecessary(functionContext), invokingContext) } @@ -91,6 +96,7 @@ class LiteralFunctionTranslator(context: TranslationContext) : AbstractTranslato invokingContext.addDeclarationStatement(lambda.makeStmt()) lambda.name.staticRef = lambda + lambda.continuationInterfaceRef = invokingContext.getContinuationInterfaceReference() return getReferenceToLambda(invokingContext, descriptor, lambda.name) } @@ -104,6 +110,12 @@ class LiteralFunctionTranslator(context: TranslationContext) : AbstractTranslato } } +private fun TranslationContext.getContinuationInterfaceReference(): JsExpression { + val coroutineClassId = ClassId.topLevel(KotlinBuiltIns.COROUTINES_PACKAGE_FQ_NAME.child(Name.identifier("Continuation"))) + val classDescriptor = currentModule.findClassAcrossModuleDependencies(coroutineClassId)!! + return ReferenceTranslator.translateAsTypeReference(classDescriptor, this) +} + private fun CallableMemberDescriptor.wrapContextForCoroutineIfNecessary(context: TranslationContext): TranslationContext { return if (isCoroutineLambda) context.innerContextWithDescriptorsAliased(mapOf(this to JsLiteral.THIS)) else context }