diff --git a/core/descriptors/src/org/jetbrains/kotlin/builtins/ReflectionTypes.kt b/core/descriptors/src/org/jetbrains/kotlin/builtins/ReflectionTypes.kt index b7ed08cf6ae..9eed1e040dc 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/builtins/ReflectionTypes.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/builtins/ReflectionTypes.kt @@ -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) } diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/CastTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/CastTestGenerated.java index 77920944d5d..dc39ef5000d 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/CastTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/CastTestGenerated.java @@ -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"); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/FunctionCallCases.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/FunctionCallCases.kt index 2f1113d63f9..3925cd55fbc 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/FunctionCallCases.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/FunctionCallCases.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) } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt index 57156bfa0b1..269d752cdda 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt @@ -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 { val supertypes = getSupertypesWithoutFakes(descriptor) + .filter { it.constructor.declarationDescriptor !is FunctionClassDescriptor } if (supertypes.isEmpty()) { return emptyList() } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/PatternTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/PatternTranslator.java index f9426451bf0..6629a582ece 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/PatternTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/PatternTranslator.java @@ -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; diff --git a/js/js.translator/testData/expression/cast/cases/castExtensionToKMutableProperty.kt b/js/js.translator/testData/expression/cast/cases/castExtensionToKMutableProperty.kt new file mode 100644 index 00000000000..fabf25f6b8a --- /dev/null +++ b/js/js.translator/testData/expression/cast/cases/castExtensionToKMutableProperty.kt @@ -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())) + assertEquals(23, ((A::x as Any) as KMutableProperty1)(A())) + assertEquals(false, (23 as Any) is KMutableProperty1<*, *>) + assertEquals(false, ({ A().x } as Any) is KMutableProperty1<*, *>) + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/expression/cast/cases/castExtensionToKProperty1.kt b/js/js.translator/testData/expression/cast/cases/castExtensionToKProperty1.kt new file mode 100644 index 00000000000..dad564abf45 --- /dev/null +++ b/js/js.translator/testData/expression/cast/cases/castExtensionToKProperty1.kt @@ -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())) + assertEquals(false, (23 as Any) is KProperty1<*, *>) + assertEquals(false, ({ A().x } as Any) is KProperty1<*, *>) + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/expression/cast/cases/castToKMutableProperty0.kt b/js/js.translator/testData/expression/cast/cases/castToKMutableProperty0.kt new file mode 100644 index 00000000000..7f38de2bb62 --- /dev/null +++ b/js/js.translator/testData/expression/cast/cases/castToKMutableProperty0.kt @@ -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)()) + assertEquals(23, ((::x as Any) as KMutableProperty0)()) + assertEquals(false, (23 as Any) is KMutableProperty0<*>) + assertEquals(false, ({ x } as Any) is KMutableProperty0<*>) + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/expression/cast/cases/castToKMutableProperty1.kt b/js/js.translator/testData/expression/cast/cases/castToKMutableProperty1.kt new file mode 100644 index 00000000000..09bc2c9fbd2 --- /dev/null +++ b/js/js.translator/testData/expression/cast/cases/castToKMutableProperty1.kt @@ -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())) + assertEquals(23, ((A::x as Any) as KMutableProperty1)(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" +} \ No newline at end of file diff --git a/js/js.translator/testData/expression/cast/cases/castToKProperty0.kt b/js/js.translator/testData/expression/cast/cases/castToKProperty0.kt new file mode 100644 index 00000000000..fffc28bfa51 --- /dev/null +++ b/js/js.translator/testData/expression/cast/cases/castToKProperty0.kt @@ -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)()) + assertEquals(false, (23 as Any) is KProperty0<*>) + assertEquals(false, ({ x } as Any) is KProperty0<*>) + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/expression/cast/cases/castToKProperty1.kt b/js/js.translator/testData/expression/cast/cases/castToKProperty1.kt new file mode 100644 index 00000000000..9cf20a2c0e6 --- /dev/null +++ b/js/js.translator/testData/expression/cast/cases/castToKProperty1.kt @@ -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())) + assertEquals(false, (23 as Any) is KProperty1<*, *>) + assertEquals(false, ({ A().x } as Any) is KProperty1<*, *>) + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/expression/cast/cases/primitiveToClass.kt b/js/js.translator/testData/expression/cast/cases/primitiveToClass.kt new file mode 100644 index 00000000000..3b87dc2faaf --- /dev/null +++ b/js/js.translator/testData/expression/cast/cases/primitiveToClass.kt @@ -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" +} \ No newline at end of file diff --git a/js/js.translator/testData/kotlin_lib_ecma5.js b/js/js.translator/testData/kotlin_lib_ecma5.js index 43cc8c68b73..e2761daa0fb 100644 --- a/js/js.translator/testData/kotlin_lib_ecma5.js +++ b/js/js.translator/testData/kotlin_lib_ecma5.js @@ -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 = {};