JS: add metadata for generated continuation classes to properly implement RTTI

This commit is contained in:
Alexey Andreev
2016-11-10 18:41:30 +03:00
parent 6860b2c057
commit d41d09ffc4
6 changed files with 74 additions and 7 deletions
@@ -0,0 +1,30 @@
// WITH_RUNTIME
// WITH_REFLECT
class Controller {
suspend fun runInstanceOf(x: Continuation<Boolean>) {
val y: Any = x
x.resume(x is Continuation<*>)
}
suspend fun runCast(x: Continuation<Boolean>) {
val y: Any = x
x.resume(Continuation::class.isInstance(y as Continuation<*>))
}
}
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
c(Controller()).resume(Unit)
}
fun box(): String {
var result = ""
builder {
result = runInstanceOf().toString() + "," + runCast().toString()
}
if (result != "true,true") return "fail: $result"
return "OK"
}
@@ -4520,6 +4520,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
doTest(fileName); 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") @TestMetadata("iterateOverArray.kt")
public void testIterateOverArray() throws Exception { public void testIterateOverArray() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/iterateOverArray.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/iterateOverArray.kt");
@@ -4520,6 +4520,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName); 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") @TestMetadata("iterateOverArray.kt")
public void testIterateOverArray() throws Exception { public void testIterateOverArray() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/iterateOverArray.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/iterateOverArray.kt");
@@ -97,6 +97,8 @@ var JsNameRef.coroutineResult by MetadataProperty(default = false)
*/ */
var JsNameRef.coroutineController by MetadataProperty(default = false) var JsNameRef.coroutineController by MetadataProperty(default = false)
var JsFunction.continuationInterfaceRef: JsExpression? by MetadataProperty(default = null)
enum class TypeCheck { enum class TypeCheck {
TYPEOF, TYPEOF,
INSTANCEOF, INSTANCEOF,
@@ -17,10 +17,7 @@
package org.jetbrains.kotlin.js.coroutine package org.jetbrains.kotlin.js.coroutine
import com.google.dart.compiler.backend.js.ast.* 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.*
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 org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.js.inline.clean.FunctionPostProcessor import org.jetbrains.kotlin.js.inline.clean.FunctionPostProcessor
@@ -70,7 +67,7 @@ class CoroutineFunctionTransformer(
return additionalStatements return additionalStatements
} }
private fun generateContinuationConstructor(bodyTransformer: CoroutineBodyTransformer,statements: MutableList<JsStatement>) { private fun generateContinuationConstructor(bodyTransformer: CoroutineBodyTransformer, statements: MutableList<JsStatement>) {
val constructor = JsFunction(function.scope.parent, JsBlock(), "Continuation") val constructor = JsFunction(function.scope.parent, JsBlock(), "Continuation")
constructor.name = className constructor.name = className
constructor.parameters += function.parameters.map { JsParameter(it.name) } 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<JsStatement>) { private fun generateContinuationMethods(doResumeName: JsName, statements: MutableList<JsStatement>) {
@@ -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.*
import com.google.dart.compiler.backend.js.ast.metadata.* 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.*
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
import org.jetbrains.kotlin.js.descriptorUtils.isCoroutineLambda import org.jetbrains.kotlin.js.descriptorUtils.isCoroutineLambda
import org.jetbrains.kotlin.js.inline.util.getInnerFunction 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.hasCapturedExceptContaining
import org.jetbrains.kotlin.js.translate.context.isCaptured import org.jetbrains.kotlin.js.translate.context.isCaptured
import org.jetbrains.kotlin.js.translate.general.AbstractTranslator 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.BindingUtils.getFunctionDescriptor
import org.jetbrains.kotlin.js.translate.utils.FunctionBodyTranslator.translateFunctionBody import org.jetbrains.kotlin.js.translate.utils.FunctionBodyTranslator.translateFunctionBody
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils.simpleReturnFunction 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.KtDeclarationWithBody
import org.jetbrains.kotlin.psi.KtParameter import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.inline.InlineUtil import org.jetbrains.kotlin.resolve.inline.InlineUtil
import org.jetbrains.kotlin.serialization.deserialization.findClassAcrossModuleDependencies
class LiteralFunctionTranslator(context: TranslationContext) : AbstractTranslator(context) { class LiteralFunctionTranslator(context: TranslationContext) : AbstractTranslator(context) {
fun translate( fun translate(
@@ -81,6 +85,7 @@ class LiteralFunctionTranslator(context: TranslationContext) : AbstractTranslato
lambda.name = null lambda.name = null
} }
lambdaCreator.name.staticRef = lambdaCreator lambdaCreator.name.staticRef = lambdaCreator
lambdaCreator.continuationInterfaceRef = invokingContext.getContinuationInterfaceReference()
return lambdaCreator.withCapturedParameters(descriptor, descriptor.wrapContextForCoroutineIfNecessary(functionContext), return lambdaCreator.withCapturedParameters(descriptor, descriptor.wrapContextForCoroutineIfNecessary(functionContext),
invokingContext) invokingContext)
} }
@@ -91,6 +96,7 @@ class LiteralFunctionTranslator(context: TranslationContext) : AbstractTranslato
invokingContext.addDeclarationStatement(lambda.makeStmt()) invokingContext.addDeclarationStatement(lambda.makeStmt())
lambda.name.staticRef = lambda lambda.name.staticRef = lambda
lambda.continuationInterfaceRef = invokingContext.getContinuationInterfaceReference()
return getReferenceToLambda(invokingContext, descriptor, lambda.name) 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 { private fun CallableMemberDescriptor.wrapContextForCoroutineIfNecessary(context: TranslationContext): TranslationContext {
return if (isCoroutineLambda) context.innerContextWithDescriptorsAliased(mapOf(this to JsLiteral.THIS)) else context return if (isCoroutineLambda) context.innerContextWithDescriptorsAliased(mapOf(this to JsLiteral.THIS)) else context
} }