[JS] Support typeOf

This commit is contained in:
Svyatoslav Kuzmich
2019-09-11 19:22:54 +03:00
parent c1e1dbd83e
commit 7592048437
28 changed files with 670 additions and 12 deletions
@@ -129,6 +129,8 @@ var JsExpression.range: Pair<RangeType, RangeKind>? by MetadataProperty(default
var JsExpression.primitiveKClass: JsExpression? by MetadataProperty(default = null)
var JsExpression.kType: JsExpression? by MetadataProperty(default = null)
data class CoroutineMetadata(
val doResumeName: JsName,
val stateName: JsName,
@@ -166,7 +168,8 @@ enum class SpecialFunction(val suggestedName: String) {
COROUTINE_CONTROLLER("coroutineController"),
COROUTINE_RECEIVER("coroutineReceiver"),
SET_COROUTINE_RESULT("setCoroutineResult"),
GET_KCLASS("getKClass")
GET_KCLASS("getKClass"),
GET_REIFIED_TYPE_PARAMETER_KTYPE("getReifiedTypeParameterKType")
}
enum class BoxingKind {
@@ -23,19 +23,27 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.NotFoundClasses
import org.jetbrains.kotlin.diagnostics.Errors.UNSUPPORTED
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.calls.checkers.AbstractReflectionApiCallChecker
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.storage.getValue
private val ALLOWED_KCLASS_MEMBERS = setOf("simpleName", "isInstance")
private val ALLOWED_CLASSES = setOf(
FqName("kotlin.reflect.KType"),
FqName("kotlin.reflect.KTypeProjection"),
FqName("kotlin.reflect.KTypeProjection.Companion"),
FqName("kotlin.reflect.KVariance")
)
class JsReflectionAPICallChecker(
module: ModuleDescriptor,
private val reflectionTypes: ReflectionTypes,
notFoundClasses: NotFoundClasses,
storageManager: StorageManager
module: ModuleDescriptor,
private val reflectionTypes: ReflectionTypes,
notFoundClasses: NotFoundClasses,
storageManager: StorageManager
) : AbstractReflectionApiCallChecker(module, notFoundClasses, storageManager) {
override val isWholeReflectionApiAvailable: Boolean
get() = false
@@ -47,6 +55,7 @@ class JsReflectionAPICallChecker(
private val kClass by storageManager.createLazyValue { reflectionTypes.kClass }
override fun isAllowedReflectionApi(descriptor: CallableDescriptor, containingClass: ClassDescriptor): Boolean =
super.isAllowedReflectionApi(descriptor, containingClass) ||
DescriptorUtils.isSubclass(containingClass, kClass) && descriptor.name.asString() in ALLOWED_KCLASS_MEMBERS
super.isAllowedReflectionApi(descriptor, containingClass) ||
DescriptorUtils.isSubclass(containingClass, kClass) && descriptor.name.asString() in ALLOWED_KCLASS_MEMBERS ||
containingClass.fqNameSafe in ALLOWED_CLASSES
}
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.js.backend.ast.metadata.inlineStrategy
import org.jetbrains.kotlin.js.backend.ast.metadata.psiElement
import org.jetbrains.kotlin.js.inline.clean.FunctionPostProcessor
import org.jetbrains.kotlin.js.inline.clean.removeUnusedLocalFunctionDeclarations
import org.jetbrains.kotlin.js.inline.clean.substituteKTypes
import org.jetbrains.kotlin.js.inline.util.extractFunction
import org.jetbrains.kotlin.js.inline.util.refreshLabelNames
import org.jetbrains.kotlin.js.translate.expression.InlineMetadata
@@ -47,11 +48,11 @@ class InlineAstVisitor(
}
override fun endVisit(function: JsFunction, ctx: JsContext<*>) {
// Cleanup
patchReturnsFromSecondaryConstructor(function)
refreshLabelNames(function.body, function.scope)
removeUnusedLocalFunctionDeclarations(function)
FunctionPostProcessor(function).apply()
substituteKTypes(function)
}
@@ -0,0 +1,26 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.js.inline.clean
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.backend.ast.metadata.SpecialFunction
import org.jetbrains.kotlin.js.backend.ast.metadata.kType
import org.jetbrains.kotlin.js.backend.ast.metadata.specialFunction
// Replaces getReifiedTypeParameterKType(<Class Constructor>) with its KType
fun substituteKTypes(root: JsNode) {
val visitor = object : JsVisitorWithContextImpl() {
override fun endVisit(invocation: JsInvocation, ctx: JsContext<in JsNode>) {
val qualifier = invocation.qualifier as? JsNameRef ?: return
if (qualifier.name?.specialFunction != SpecialFunction.GET_REIFIED_TYPE_PARAMETER_KTYPE) return
val firstArg = invocation.arguments.first()
firstArg.kType?.let {
ctx.replaceMe(it)
}
}
}
visitor.accept(root)
}
+1
View File
@@ -465,6 +465,7 @@ enum SpecialFunction {
COROUTINE_RECEIVER = 8;
SET_COROUTINE_RESULT = 9;
GET_KCLASS = 10;
GET_REIFIED_TYPE_PARAMETER_KTYPE = 11;
}
@@ -558,6 +558,7 @@ class JsAstDeserializer(program: JsProgram, private val sourceRoots: Iterable<Fi
JsAstProtoBuf.SpecialFunction.COROUTINE_RECEIVER -> SpecialFunction.COROUTINE_RECEIVER
JsAstProtoBuf.SpecialFunction.SET_COROUTINE_RESULT -> SpecialFunction.SET_COROUTINE_RESULT
JsAstProtoBuf.SpecialFunction.GET_KCLASS -> SpecialFunction.GET_KCLASS
JsAstProtoBuf.SpecialFunction.GET_REIFIED_TYPE_PARAMETER_KTYPE -> SpecialFunction.GET_REIFIED_TYPE_PARAMETER_KTYPE
}
private fun <T : JsNode> withLocation(fileId: Int?, location: Location?, action: () -> T): T {
@@ -183,6 +183,10 @@ public final class JsAstProtoBuf {
* <code>GET_KCLASS = 10;</code>
*/
GET_KCLASS(9, 10),
/**
* <code>GET_REIFIED_TYPE_PARAMETER_KTYPE = 11;</code>
*/
GET_REIFIED_TYPE_PARAMETER_KTYPE(10, 11),
;
/**
@@ -225,6 +229,10 @@ public final class JsAstProtoBuf {
* <code>GET_KCLASS = 10;</code>
*/
public static final int GET_KCLASS_VALUE = 10;
/**
* <code>GET_REIFIED_TYPE_PARAMETER_KTYPE = 11;</code>
*/
public static final int GET_REIFIED_TYPE_PARAMETER_KTYPE_VALUE = 11;
public final int getNumber() { return value; }
@@ -241,6 +249,7 @@ public final class JsAstProtoBuf {
case 8: return COROUTINE_RECEIVER;
case 9: return SET_COROUTINE_RESULT;
case 10: return GET_KCLASS;
case 11: return GET_REIFIED_TYPE_PARAMETER_KTYPE;
default: return null;
}
}
@@ -601,6 +601,7 @@ class JsAstSerializer(private val jsAstValidator: ((JsProgramFragment, Set<JsNam
SpecialFunction.COROUTINE_RECEIVER -> JsAstProtoBuf.SpecialFunction.COROUTINE_RECEIVER
SpecialFunction.SET_COROUTINE_RESULT -> JsAstProtoBuf.SpecialFunction.SET_COROUTINE_RESULT
SpecialFunction.GET_KCLASS -> JsAstProtoBuf.SpecialFunction.GET_KCLASS
SpecialFunction.GET_REIFIED_TYPE_PARAMETER_KTYPE -> JsAstProtoBuf.SpecialFunction.GET_REIFIED_TYPE_PARAMETER_KTYPE
}
private fun serialize(name: JsName): Int = nameMap.getOrPut(name) {
@@ -17310,6 +17310,49 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/reflection/typeOf/multipleLayers.kt");
}
@TestMetadata("compiler/testData/codegen/box/reflection/typeOf/js")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Js extends AbstractIrJsCodegenBoxTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR, testDataFilePath);
}
public void testAllFilesPresentInJs() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/reflection/typeOf/js"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS_IR, true);
}
@TestMetadata("classes.kt")
public void testClasses() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/js/classes.kt");
}
@TestMetadata("inlineClasses.kt")
public void testInlineClasses() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/js/inlineClasses.kt");
}
@TestMetadata("kType.kt")
public void testKType() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/js/kType.kt");
}
@TestMetadata("manyTypeArguments.kt")
public void testManyTypeArguments() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/js/manyTypeArguments.kt");
}
@TestMetadata("multipleLayers.kt")
public void testMultipleLayers() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/js/multipleLayers.kt");
}
@TestMetadata("multipleModules.kt")
public void testMultipleModules() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/js/multipleModules.kt");
}
}
@TestMetadata("compiler/testData/codegen/box/reflection/typeOf/noReflect")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -18465,6 +18465,49 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/reflection/typeOf/multipleLayers.kt");
}
@TestMetadata("compiler/testData/codegen/box/reflection/typeOf/js")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Js extends AbstractJsCodegenBoxTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS, testDataFilePath);
}
public void testAllFilesPresentInJs() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/reflection/typeOf/js"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true);
}
@TestMetadata("classes.kt")
public void testClasses() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/js/classes.kt");
}
@TestMetadata("inlineClasses.kt")
public void testInlineClasses() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/js/inlineClasses.kt");
}
@TestMetadata("kType.kt")
public void testKType() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/js/kType.kt");
}
@TestMetadata("manyTypeArguments.kt")
public void testManyTypeArguments() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/js/manyTypeArguments.kt");
}
@TestMetadata("multipleLayers.kt")
public void testMultipleLayers() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/js/multipleLayers.kt");
}
@TestMetadata("multipleModules.kt")
public void testMultipleModules() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/js/multipleModules.kt");
}
}
@TestMetadata("compiler/testData/codegen/box/reflection/typeOf/noReflect")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -75,6 +75,15 @@ public final class Namer {
public static final String GET_KCLASS = "getKClass";
public static final String GET_KCLASS_FROM_EXPRESSION = "getKClassFromExpression";
public static final String CREATE_KTYPE = "createKType";
public static final String MARK_KTYPE_NULLABLE = "markKTypeNullable";
public static final String CREATE_KTYPE_PARAMETER = "createKTypeParameter";
public static final String CREATE_REIFIED_KTYPE_PARAMETER = "getReifiedTypeParameterKType";
public static final String GET_START_KTYPE_PROJECTION = "getStarKTypeProjection";
public static final String CREATE_COVARIANT_KTYPE_PROJECTION = "createCovariantKTypeProjection";
public static final String CREATE_INVARIANT_KTYPE_PROJECTION = "createInvariantKTypeProjection";
public static final String CREATE_CONTRAVARIANT_KTYPE_PROJECTION = "createContravariantKTypeProjection";
public static final String CALLEE_NAME = "$fun";
public static final String CALL_FUNCTION = "call";
@@ -39,7 +39,8 @@ class FunctionIntrinsics {
AsDynamicFIF,
CoroutineContextFIF,
SuspendCoroutineUninterceptedOrReturnFIF,
InterceptedFIF
InterceptedFIF,
TypeOfFIF
)
fun getIntrinsic(descriptor: FunctionDescriptor, context: TranslationContext): FunctionIntrinsic? {
@@ -0,0 +1,111 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.js.translate.intrinsic.functions.factories
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.backend.ast.metadata.SpecialFunction
import org.jetbrains.kotlin.js.backend.ast.metadata.specialFunction
import org.jetbrains.kotlin.js.translate.callTranslator.CallInfo
import org.jetbrains.kotlin.js.translate.context.Namer
import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.expression.ExpressionVisitor
import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsic
import org.jetbrains.kotlin.js.translate.utils.getReferenceToJsClass
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.types.SimpleType
import org.jetbrains.kotlin.types.TypeProjection
import org.jetbrains.kotlin.types.Variance
object TypeOfFIF : FunctionIntrinsicFactory {
override fun getIntrinsic(descriptor: FunctionDescriptor, context: TranslationContext): FunctionIntrinsic? =
Intrinsic.takeIf { descriptor.fqNameUnsafe.asString() == "kotlin.reflect.typeOf" }
object Intrinsic : FunctionIntrinsic() {
override fun apply(callInfo: CallInfo, arguments: List<JsExpression>, context: TranslationContext): JsExpression {
val type = callInfo.resolvedCall.typeArguments.values.single() as SimpleType
return KTypeConstructor(context).createKType(type)
}
}
}
class KTypeConstructor(val context: TranslationContext) {
fun callHelperFunction(name: String, vararg arguments: JsExpression) =
JsInvocation(context.getReferenceToIntrinsic(name), *arguments)
fun createKType(type: SimpleType): JsExpression {
val classifier: ClassifierDescriptor = type.constructor.declarationDescriptor!!
if (classifier is TypeParameterDescriptor && classifier.isReified) {
val kClassName = context.getNameForIntrinsic(SpecialFunction.GET_REIFIED_TYPE_PARAMETER_KTYPE.suggestedName)
kClassName.specialFunction = SpecialFunction.GET_REIFIED_TYPE_PARAMETER_KTYPE
val reifiedTypeParameterType = JsInvocation(kClassName.makeRef(), getReferenceToJsClass(classifier, context))
if (type.isMarkedNullable) {
return callHelperFunction(Namer.MARK_KTYPE_NULLABLE, reifiedTypeParameterType)
}
return reifiedTypeParameterType
}
val kClassifier = createKClassifier(classifier)
val arguments = JsArrayLiteral(type.arguments.map { createKTypeProjection(it) })
val isMarkedNullable = JsBooleanLiteral(type.isMarkedNullable)
return callHelperFunction(
Namer.CREATE_KTYPE,
kClassifier,
arguments,
isMarkedNullable
)
}
fun createKTypeProjection(tp: TypeProjection): JsExpression {
if (tp.isStarProjection) {
return callHelperFunction(Namer.GET_START_KTYPE_PROJECTION)
}
val factoryName = when (tp.projectionKind) {
Variance.INVARIANT -> Namer.CREATE_INVARIANT_KTYPE_PROJECTION
Variance.IN_VARIANCE -> Namer.CREATE_CONTRAVARIANT_KTYPE_PROJECTION
Variance.OUT_VARIANCE -> Namer.CREATE_COVARIANT_KTYPE_PROJECTION
}
val kType = createKType(tp.type as SimpleType)
return callHelperFunction(factoryName, kType)
}
fun createKClassifier(classifier: ClassifierDescriptor): JsExpression =
when (classifier) {
is TypeParameterDescriptor -> createKTypeParameter(classifier)
else -> ExpressionVisitor.getObjectKClass(context, classifier)
}
fun createKTypeParameter(typeParameter: TypeParameterDescriptor): JsExpression {
val name = JsStringLiteral(typeParameter.name.asString())
val upperBounds = JsArrayLiteral(typeParameter.upperBounds.map { createKType(it as SimpleType) })
val variance = when (typeParameter.variance) {
Variance.INVARIANT -> JsStringLiteral("invariant")
Variance.IN_VARIANCE -> JsStringLiteral("in")
Variance.OUT_VARIANCE -> JsStringLiteral("out")
}
if (typeParameter.isReified) {
val kClassName = context.getNameForIntrinsic(SpecialFunction.GET_REIFIED_TYPE_PARAMETER_KTYPE.suggestedName)
kClassName.specialFunction = SpecialFunction.GET_REIFIED_TYPE_PARAMETER_KTYPE
return JsInvocation(kClassName.makeRef(), getReferenceToJsClass(typeParameter, context))
}
return callHelperFunction(
Namer.CREATE_KTYPE_PARAMETER,
name,
upperBounds,
variance
)
}
}
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.js.translate.context.Namer
import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.expression.ExpressionVisitor
import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsicWithReceiverComputed
import org.jetbrains.kotlin.js.translate.intrinsic.functions.factories.KTypeConstructor
import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils.simpleReturnFunction
import org.jetbrains.kotlin.psi.*
@@ -39,6 +40,7 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.Receiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.SimpleType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.utils.DFS
@@ -126,7 +128,9 @@ fun <T, S> List<T>.splitToRanges(classifier: (T) -> S): List<Pair<List<T>, S>> {
}
fun getReferenceToJsClass(type: KotlinType, context: TranslationContext): JsExpression =
getReferenceToJsClass(type.constructor.declarationDescriptor, context)
getReferenceToJsClass(type.constructor.declarationDescriptor, context).also {
it.kType = KTypeConstructor(context).createKType(type as SimpleType)
}
fun getReferenceToJsClass(classifierDescriptor: ClassifierDescriptor?, context: TranslationContext): JsExpression {
return when (classifierDescriptor) {