KT-12417: add support of RTTI against K(Mutable)Property* types

This commit is contained in:
Alexey Andreev
2016-06-14 16:38:17 +03:00
parent 048b6b60e9
commit 2e6a48576d
13 changed files with 256 additions and 36 deletions
@@ -120,6 +120,15 @@ class ReflectionTypes(module: ModuleDescriptor) {
fun isCallableType(type: KotlinType): Boolean =
type.isFunctionTypeOrSubtype || isKCallableType(type)
@JvmStatic fun isFunctionalKPropertyType(type: KotlinType): Boolean {
val descriptor = type.constructor.declarationDescriptor ?: return false
val segments = DescriptorUtils.getFqName(descriptor).pathSegments()
return segments.size == 3 && segments.subList(0, 2) == KOTLIN_REFLECT_FQ_NAME.pathSegments() &&
segments.last().let { it.isProperty("KProperty") || it.isProperty("KMutableProperty") }
}
private fun Name.isProperty(baseName: String) = asString().startsWith(baseName) && asString() != baseName
private fun isKCallableType(type: KotlinType): Boolean =
hasFqName(type.constructor, KotlinBuiltIns.FQ_NAMES.kCallable) ||
type.constructor.supertypes.any { isKCallableType(it) }
@@ -35,6 +35,18 @@ public class CastTestGenerated extends AbstractCastTest {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("js/js.translator/testData/expression/cast/cases"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("castExtensionToKMutableProperty.kt")
public void testCastExtensionToKMutableProperty() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/expression/cast/cases/castExtensionToKMutableProperty.kt");
doTest(fileName);
}
@TestMetadata("castExtensionToKProperty1.kt")
public void testCastExtensionToKProperty1() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/expression/cast/cases/castExtensionToKProperty1.kt");
doTest(fileName);
}
@TestMetadata("castToAny.kt")
public void testCastToAny() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/expression/cast/cases/castToAny.kt");
@@ -71,6 +83,30 @@ public class CastTestGenerated extends AbstractCastTest {
doTest(fileName);
}
@TestMetadata("castToKMutableProperty0.kt")
public void testCastToKMutableProperty0() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/expression/cast/cases/castToKMutableProperty0.kt");
doTest(fileName);
}
@TestMetadata("castToKMutableProperty1.kt")
public void testCastToKMutableProperty1() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/expression/cast/cases/castToKMutableProperty1.kt");
doTest(fileName);
}
@TestMetadata("castToKProperty0.kt")
public void testCastToKProperty0() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/expression/cast/cases/castToKProperty0.kt");
doTest(fileName);
}
@TestMetadata("castToKProperty1.kt")
public void testCastToKProperty1() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/expression/cast/cases/castToKProperty1.kt");
doTest(fileName);
}
@TestMetadata("castToNotNull.kt")
public void testCastToNotNull() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/expression/cast/cases/castToNotNull.kt");
@@ -89,6 +125,12 @@ public class CastTestGenerated extends AbstractCastTest {
doTest(fileName);
}
@TestMetadata("primitiveToClass.kt")
public void testPrimitiveToClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/expression/cast/cases/primitiveToClass.kt");
doTest(fileName);
}
@TestMetadata("reifiedToNotNull.kt")
public void testReifiedToNotNull() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/expression/cast/cases/reifiedToNotNull.kt");
@@ -17,7 +17,10 @@
package org.jetbrains.kotlin.js.translate.callTranslator
import com.google.dart.compiler.backend.js.ast.*
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.js.PredefinedAnnotation
import org.jetbrains.kotlin.js.translate.context.Namer
import org.jetbrains.kotlin.js.translate.context.TranslationContext
@@ -161,14 +164,13 @@ object NativeSetterCallCase : AnnotatedAsNativeXCallCase(PredefinedAnnotation.NA
object InvokeIntrinsic : FunctionCallCase() {
fun canApply(callInfo: FunctionCallInfo): Boolean {
val callableDescriptor = callInfo.callableDescriptor
if (callableDescriptor.name != OperatorNameConventions.INVOKE)
return false
val isExtension = callableDescriptor.extensionReceiverParameter != null
val parameterCount = callableDescriptor.valueParameters.size + (if (isExtension) 1 else 0)
val funDeclaration = callableDescriptor.containingDeclaration
if (callableDescriptor is FunctionInvokeDescriptor) return true
if (!isExtension && funDeclaration == callInfo.context.reflectionTypes.getKFunction(parameterCount))
return true
// Otherwise, it can be extension lambda
if (callableDescriptor.name != OperatorNameConventions.INVOKE || callableDescriptor.extensionReceiverParameter == null)
return false
val parameterCount = callableDescriptor.valueParameters.size + 1
val funDeclaration = callableDescriptor.containingDeclaration
return funDeclaration == callableDescriptor.builtIns.getFunction(parameterCount)
}
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.backend.common.CodegenUtil
import org.jetbrains.kotlin.backend.common.bridges.Bridge
import org.jetbrains.kotlin.backend.common.bridges.generateBridgesForFunctionDescriptor
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.js.descriptorUtils.hasPrimaryConstructor
import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator
@@ -328,6 +329,7 @@ class ClassTranslator private constructor(
private fun getSupertypesNameReferences(): List<JsExpression> {
val supertypes = getSupertypesWithoutFakes(descriptor)
.filter { it.constructor.declarationDescriptor !is FunctionClassDescriptor }
if (supertypes.isEmpty()) {
return emptyList()
}
@@ -22,6 +22,8 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.KtNodeTypes;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.builtins.PrimitiveType;
import org.jetbrains.kotlin.builtins.ReflectionTypes;
import org.jetbrains.kotlin.builtins.ReflectionTypesKt;
import org.jetbrains.kotlin.descriptors.CallableDescriptor;
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
@@ -181,7 +183,9 @@ public final class PatternTranslator extends AbstractTranslator {
private JsExpression getIsTypeCheckCallableForBuiltin(@NotNull KotlinType type) {
if (isAnyOrNullableAny(type)) return namer().isAny();
if (isFunctionTypeOrSubtype(type)) return namer().isTypeOf(program().getStringLiteral("function"));
if (isFunctionTypeOrSubtype(type) && !ReflectionTypes.isFunctionalKPropertyType(type)) {
return namer().isTypeOf(program().getStringLiteral("function"));
}
if (isArray(type)) return Namer.IS_ARRAY_FUN_REF;
@@ -0,0 +1,21 @@
package foo
import kotlin.reflect.KProperty1
import kotlin.reflect.KMutableProperty1
class A
var A.x: Int
get() = 23
set(value) { }
fun box(): String {
assertEquals(true, (A::x as Any) is KProperty1<*, *>)
assertEquals(true, (A::x as Any) is KMutableProperty1<*, *>)
assertEquals(23, ((A::x as Any) as KProperty1<A, Any>)(A()))
assertEquals(23, ((A::x as Any) as KMutableProperty1<A, Any>)(A()))
assertEquals(false, (23 as Any) is KMutableProperty1<*, *>)
assertEquals(false, ({ A().x } as Any) is KMutableProperty1<*, *>)
return "OK"
}
@@ -0,0 +1,17 @@
package foo
import kotlin.reflect.KProperty1
class A
val A.x: Int
get() = 23
fun box(): String {
assertEquals(true, (A::x as Any) is KProperty1<*, *>)
assertEquals(23, ((A::x as Any) as KProperty1<A, Any>)(A()))
assertEquals(false, (23 as Any) is KProperty1<*, *>)
assertEquals(false, ({ A().x } as Any) is KProperty1<*, *>)
return "OK"
}
@@ -0,0 +1,17 @@
package foo
import kotlin.reflect.KProperty0
import kotlin.reflect.KMutableProperty0
var x = 23
fun box(): String {
assertEquals(true, (::x as Any) is KProperty0<*>)
assertEquals(true, (::x as Any) is KMutableProperty0<*>)
assertEquals(23, ((::x as Any) as KProperty0<Any>)())
assertEquals(23, ((::x as Any) as KMutableProperty0<Any>)())
assertEquals(false, (23 as Any) is KMutableProperty0<*>)
assertEquals(false, ({ x } as Any) is KMutableProperty0<*>)
return "OK"
}
@@ -0,0 +1,20 @@
package foo
import kotlin.reflect.KProperty1
import kotlin.reflect.KMutableProperty1
class A {
var x = 23
}
fun box(): String {
assertEquals(true, (A::x as Any) is KProperty1<*, *>)
assertEquals(true, (A::x as Any) is KMutableProperty1<*, *>)
assertEquals(23, ((A::x as Any) as KProperty1<A, Any>)(A()))
assertEquals(23, ((A::x as Any) as KMutableProperty1<A, Any>)(A()))
assertEquals(false, (23 as Any) is KProperty1<*, *>)
assertEquals(false, (23 as Any) is KMutableProperty1<*, *>)
assertEquals(false, ({ A().x } as Any) is KMutableProperty1<*, *>)
return "OK"
}
@@ -0,0 +1,14 @@
package foo
import kotlin.reflect.KProperty0
val x = 23
fun box(): String {
assertEquals(true, (::x as Any) is KProperty0<*>)
assertEquals(23, ((::x as Any) as KProperty0<Any>)())
assertEquals(false, (23 as Any) is KProperty0<*>)
assertEquals(false, ({ x } as Any) is KProperty0<*>)
return "OK"
}
@@ -0,0 +1,16 @@
package foo
import kotlin.reflect.KProperty1
class A {
val x = 23
}
fun box(): String {
assertEquals(true, (A::x as Any) is KProperty1<*, *>)
assertEquals(23, ((A::x as Any) as KProperty1<A, Any>)(A()))
assertEquals(false, (23 as Any) is KProperty1<*, *>)
assertEquals(false, ({ A().x } as Any) is KProperty1<*, *>)
return "OK"
}
@@ -0,0 +1,19 @@
package foo
interface A
class B
fun box(): String {
assertEquals(false, (23 as Any) is A)
assertEquals(false, (23 as Any) is B)
assertEquals(false, (23L as Any) is A)
assertEquals(false, (23L as Any) is B)
assertEquals(false, ("qwe" as Any) is A)
assertEquals(false, ("qwe" as Any) is B)
assertEquals(false, ({ 23 } as Any) is A)
assertEquals(false, ({ 23 } as Any) is B)
assertEquals(false, (true as Any) is A)
assertEquals(false, (true as Any) is B)
return "OK"
}
+64 -27
View File
@@ -348,11 +348,11 @@ var Kotlin = {};
klass.$metadata$.properties[propertyName].set.call(thisObject, value);
};
function isInheritanceFromTrait(objConstructor, trait) {
if (isNativeClass(objConstructor) || objConstructor.$metadata$.classIndex < trait.$metadata$.classIndex) {
function isInheritanceFromTrait(metadata, trait) {
if (metadata == null || metadata.classIndex < trait.$metadata$.classIndex) {
return false;
}
var baseClasses = objConstructor.$metadata$.baseClasses;
var baseClasses = metadata.baseClasses;
var i;
for (i = 0; i < baseClasses.length; i++) {
if (baseClasses[i] === trait) {
@@ -360,7 +360,7 @@ var Kotlin = {};
}
}
for (i = 0; i < baseClasses.length; i++) {
if (isInheritanceFromTrait(baseClasses[i], trait)) {
if (isInheritanceFromTrait(baseClasses[i].$metadata$, trait)) {
return true;
}
}
@@ -368,7 +368,7 @@ var Kotlin = {};
}
Kotlin.isType = function (object, klass) {
if (object == null || klass == null) {
if (object == null || klass == null || (typeof object !== 'object' && typeof object !== 'function')) {
return false;
}
else {
@@ -379,7 +379,8 @@ var Kotlin = {};
return false;
}
else {
return isInheritanceFromTrait(object.constructor, klass);
var metadata = "$metadata$" in object ? object.$metadata$ : object.constructor.$metadata$;
return isInheritanceFromTrait(metadata, klass);
}
}
};
@@ -418,34 +419,70 @@ var Kotlin = {};
};
Kotlin.getCallableRefForTopLevelProperty = function(packageName, name, isVar) {
var obj = {};
obj.name = name;
obj.get = function() { return packageName[name]; };
if (isVar) {
obj.set_za3rmp$ = function(value) { packageName[name] = value; };
}
return obj;
var getFun = function() { return packageName[name]; };
var setFun = isVar ? function(value) { packageName[name] = value; } : null;
return getPropertyRefClass(name, getFun, "get", setFun, "set_za3rmp$", propertyRefClassMetadataCache.zeroArg);
};
Kotlin.getCallableRefForMemberProperty = function(name, isVar) {
var obj = {};
obj.name = name;
obj.get_za3rmp$ = function(receiver) { return receiver[name]; };
if (isVar) {
obj.set_wn2jw4$ = function(receiver, value) { receiver[name] = value; };
}
return obj;
var getFun = function(receiver) { return receiver[name]; };
var setFun = isVar ? function(receiver, value) { receiver[name] = value; } : null;
return getPropertyRefClass(name, getFun, "get_za3rmp$", setFun, "set_wn2jw4$", propertyRefClassMetadataCache.oneArg);
};
Kotlin.getCallableRefForExtensionProperty = function(name, getFun, setFun) {
var obj = {};
obj.name = name;
obj.get_za3rmp$ = getFun;
if (typeof setFun === "function") {
obj.set_wn2jw4$ = setFun;
}
return obj;
var getFunWrapper = function(receiver, extensionReceiver) { return getFun(receiver, extensionReceiver) };
return getPropertyRefClass(name, getFunWrapper, "get_za3rmp$", setFun, "set_wn2jw4$", propertyRefClassMetadataCache.oneArg);
};
function getPropertyRefClass(name, getFun, getName, setFun, setName, cache) {
var obj = getFun;
var isMutable = typeof setFun === "function";
obj.$metadata$ = getPropertyRefMetadata(isMutable ? cache.mutable : cache.immutable);
obj[getName] = getFun;
if (isMutable) {
obj[setName] = setFun;
}
obj.constructor = obj;
Object.defineProperty(obj, "name", { get : function() { return name; } });
return obj;
}
var propertyRefClassMetadataCache = {
zeroArg: {
mutable: { value: null, implementedInterface: function () {
return Kotlin.modules['stdlib'].kotlin.reflect.KMutableProperty0 }
},
immutable: { value: null, implementedInterface: function () {
return Kotlin.modules['stdlib'].kotlin.reflect.KProperty0 }
}
},
oneArg: {
mutable: { value: null, implementedInterface: function () {
return Kotlin.modules['stdlib'].kotlin.reflect.KMutableProperty1 }
},
immutable: { value: null, implementedInterface: function () {
return Kotlin.modules['stdlib'].kotlin.reflect.KProperty1 }
}
}
};
function getPropertyRefMetadata(cache) {
if (cache.value === null) {
cache.value = {
baseClasses: [cache.implementedInterface()],
baseClass: null,
classIndex: Kotlin.newClassIndex(),
functions: {},
properties: {},
types: {},
staticMembers: {}
};
}
return cache.value;
}
////////////////////////////////// packages & modules //////////////////////////////
Kotlin.modules = {};