diff --git a/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/bridges/bridges.kt b/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/bridges/bridges.kt index 35ee109220c..d81f02fd050 100644 --- a/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/bridges/bridges.kt +++ b/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/bridges/bridges.kt @@ -69,7 +69,7 @@ public fun generateBridges( return bridgesToGenerate.map { Bridge(it, method) }.toSet() } -private fun findAllReachableDeclarations(function: Function): MutableSet { +public fun findAllReachableDeclarations(function: Function): MutableSet { val collector = object : DFS.NodeHandlerWithListResult() { override fun afterChildren(current: Function) { if (current.isDeclaration) { @@ -86,7 +86,7 @@ private fun findAllReachableDeclarations(function: F * Given a concrete function, finds an implementation (a concrete declaration) of this function in the supertypes. * The implementation is guaranteed to exist because if it wouldn't, the given function would've been abstract */ -private fun findConcreteSuperDeclaration(function: Function): Function { +public fun findConcreteSuperDeclaration(function: Function): Function { require(!function.isAbstract, { "Only concrete functions have implementations: $function" }) if (function.isDeclaration) return function diff --git a/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/bridges/impl.kt b/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/bridges/impl.kt index 3d77fe243d3..3e39635757f 100644 --- a/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/bridges/impl.kt +++ b/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/bridges/impl.kt @@ -47,7 +47,7 @@ public fun generateBridgesForFunctionDescriptor( * can generate a bridge near an implementation (of course, in case it has a super-declaration with a different signature). Ultimately this * eases the process of determining what bridges are already generated in our supertypes and need to be inherited, not regenerated. */ -private data class DescriptorBasedFunctionHandle(val descriptor: FunctionDescriptor) : FunctionHandle { +public data class DescriptorBasedFunctionHandle(val descriptor: FunctionDescriptor) : FunctionHandle { private val overridden = descriptor.getOverriddenDescriptors().map { DescriptorBasedFunctionHandle(it.getOriginal()) } override val isDeclaration: Boolean = diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassBodyCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassBodyCodegen.java index 3e2d31814fb..9af494fa4b2 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassBodyCodegen.java @@ -96,7 +96,6 @@ public abstract class ClassBodyCodegen extends MemberCodegen { return !(declaration instanceof JetProperty || declaration instanceof JetNamedFunction); } - protected void generateDeclaration(JetDeclaration declaration) { if (declaration instanceof JetProperty || declaration instanceof JetNamedFunction) { genFunctionOrProperty(declaration); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java index 036e0c85a9f..4a2714b33a1 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java @@ -36,6 +36,7 @@ import org.jetbrains.kotlin.descriptors.annotations.Annotated; import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor; import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget; import org.jetbrains.kotlin.jvm.RuntimeAssertionInfo; +import org.jetbrains.kotlin.load.java.BuiltinsPropertiesUtilKt; import org.jetbrains.kotlin.load.java.JvmAnnotationNames; import org.jetbrains.kotlin.load.kotlin.nativeDeclarations.NativeDeclarationsPackage; import org.jetbrains.kotlin.name.FqName; @@ -513,20 +514,53 @@ public class FunctionCodegen { // If the function doesn't have a physical declaration among super-functions, it's a SAM adapter or alike and doesn't need bridges if (CallResolverUtilPackage.isOrOverridesSynthesized(descriptor)) return; - Set> bridgesToGenerate = BridgesPackage.generateBridgesForFunctionDescriptor( - descriptor, - new Function1() { - @Override - public Method invoke(FunctionDescriptor descriptor) { - return typeMapper.mapSignature(descriptor).getAsmMethod(); - } - } - ); + boolean isSpecial = BuiltinsPropertiesUtilKt.overridesBuiltinSpecialDeclaration(descriptor); - if (!bridgesToGenerate.isEmpty()) { - PsiElement origin = descriptor.getKind() == DECLARATION ? getSourceFromDescriptor(descriptor) : null; - for (Bridge bridge : bridgesToGenerate) { - generateBridge(origin, descriptor, bridge.getFrom(), bridge.getTo()); + Set> bridgesToGenerate; + if (!isSpecial) { + bridgesToGenerate = BridgesPackage.generateBridgesForFunctionDescriptor( + descriptor, + new Function1() { + @Override + public Method invoke(FunctionDescriptor descriptor) { + return typeMapper.mapSignature(descriptor).getAsmMethod(); + } + } + ); + if (!bridgesToGenerate.isEmpty()) { + PsiElement origin = descriptor.getKind() == DECLARATION ? getSourceFromDescriptor(descriptor) : null; + for (Bridge bridge : bridgesToGenerate) { + generateBridge(origin, descriptor, bridge.getFrom(), bridge.getTo(), false, false); + } + } + } + else { + Set> specials = BuiltinSpecialBridgesUtil.generateBridgesForBuiltinSpecial( + descriptor, + new Function1() { + @Override + public Method invoke(FunctionDescriptor descriptor) { + return typeMapper.mapSignature(descriptor).getAsmMethod(); + } + } + ); + + if (!specials.isEmpty()) { + PsiElement origin = descriptor.getKind() == DECLARATION ? getSourceFromDescriptor(descriptor) : null; + for (BridgeForBuiltinSpecial bridge : specials) { + generateBridge( + origin, descriptor, bridge.getFrom(), bridge.getTo(), + bridge.isSpecial(), bridge.isDelegateToSuper()); + } + } + + if (!descriptor.getKind().isReal() && isAbstractMethod(descriptor, OwnerKind.IMPLEMENTATION)) { + CallableDescriptor overridden = BuiltinsPropertiesUtilKt.getBuiltinSpecialOverridden(descriptor); + assert overridden != null; + + Method method = typeMapper.mapSignature(descriptor).getAsmMethod(); + int flags = ACC_ABSTRACT | getVisibilityAccessFlag(descriptor); + v.newMethod(OtherOrigin(overridden), flags, method.getName(), method.getDescriptor(), null, null); } } } @@ -760,9 +794,11 @@ public class FunctionCodegen { @Nullable PsiElement origin, @NotNull FunctionDescriptor descriptor, @NotNull Method bridge, - @NotNull Method delegateTo + @NotNull Method delegateTo, + boolean isSpecialBridge, + boolean superCallNeeded ) { - int flags = ACC_PUBLIC | ACC_BRIDGE | ACC_SYNTHETIC; // TODO. + int flags = ACC_PUBLIC | ACC_BRIDGE | (!isSpecialBridge ? ACC_SYNTHETIC : 0) | (isSpecialBridge ? ACC_FINAL : 0); // TODO. MethodVisitor mv = v.newMethod(DiagnosticsPackage.Bridge(descriptor, origin), flags, bridge.getName(), bridge.getDescriptor(), null, null); @@ -783,7 +819,16 @@ public class FunctionCodegen { reg += argTypes[i].getSize(); } - iv.invokevirtual(v.getThisName(), delegateTo.getName(), delegateTo.getDescriptor()); + + if (superCallNeeded) { + ClassDescriptor parentClass = getSuperClassDescriptor((ClassDescriptor) descriptor.getContainingDeclaration()); + assert parentClass != null; + String parentInternalName = typeMapper.mapClass(parentClass).getInternalName(); + iv.invokespecial(parentInternalName, delegateTo.getName(), delegateTo.getDescriptor()); + } + else { + iv.invokevirtual(v.getThisName(), delegateTo.getName(), delegateTo.getDescriptor()); + } StackValue.coerce(delegateTo.getReturnType(), bridge.getReturnType(), iv); iv.areturn(bridge.getReturnType()); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/builtinSpecialBridges.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/builtinSpecialBridges.kt new file mode 100644 index 00000000000..d5dc69afe1d --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/builtinSpecialBridges.kt @@ -0,0 +1,120 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.codegen + +import org.jetbrains.kotlin.backend.common.bridges.* +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.load.java.builtinSpecialOverridden +import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.types.checker.TypeCheckingProcedure +import org.jetbrains.kotlin.utils.singletonOrEmptyList +import java.util.* + +class BridgeForBuiltinSpecial( + val from: Signature, val to: Signature, + val isSpecial: Boolean = false, + val isDelegateToSuper: Boolean = false +) + +object BuiltinSpecialBridgesUtil { + @JvmStatic + public fun generateBridgesForBuiltinSpecial( + function: FunctionDescriptor, + signatureByDescriptor: (FunctionDescriptor) -> Signature + ): Set> { + + val functionHandle = DescriptorBasedFunctionHandle(function) + val fake = !functionHandle.isDeclaration + val overriddenBuiltin = function.builtinSpecialOverridden!! + + val reachableDeclarations = findAllReachableDeclarations(function) + val needGenerateSpecialBridge = needGenerateSpecialBridge(function, reachableDeclarations, overriddenBuiltin) + + // e.g. `getSize()I` + val methodItself = signatureByDescriptor(function) + // e.g. `size()I` + val overriddenBuiltinSignature = signatureByDescriptor(overriddenBuiltin) + + val specialBridge = if (needGenerateSpecialBridge) + BridgeForBuiltinSpecial(overriddenBuiltinSignature, methodItself, isSpecial = true) + else null + + val bridgesToGenerate = reachableDeclarations.mapTo(LinkedHashSet(), signatureByDescriptor) + bridgesToGenerate.remove(overriddenBuiltinSignature) + bridgesToGenerate.remove(methodItself) + + if (fake) { + for (overridden in function.overriddenDescriptors.map { it.original }) { + if (!DescriptorBasedFunctionHandle(overridden).isAbstract) { + bridgesToGenerate.removeAll(findAllReachableDeclarations(overridden).map(signatureByDescriptor)) + } + } + } + + val bridges: MutableSet> = + (bridgesToGenerate.map { BridgeForBuiltinSpecial(it, methodItself) } + specialBridge.singletonOrEmptyList()).toMutableSet() + + if (function.modality == Modality.OPEN && fake) { + val implementation = findConcreteSuperDeclaration(DescriptorBasedFunctionHandle(function)).descriptor + if (!DescriptorUtils.isInterface(implementation.containingDeclaration)) { + bridges.add(BridgeForBuiltinSpecial(methodItself, signatureByDescriptor(implementation), isDelegateToSuper = true)) + } + } + + return bridges + } +} + +private fun findAllReachableDeclarations(functionDescriptor: FunctionDescriptor): MutableSet = + findAllReachableDeclarations(DescriptorBasedFunctionHandle(functionDescriptor)).map { it.descriptor }.toMutableSet() + +private fun needGenerateSpecialBridge( + functionDescriptor: FunctionDescriptor, + reachableDeclarations: Collection, + specialCallableDescriptor: CallableDescriptor +): Boolean { + val classDescriptor = functionDescriptor.containingDeclaration as ClassDescriptor + val builtinContainerDefaultType = (specialCallableDescriptor.containingDeclaration as ClassDescriptor).defaultType + + var superClassDescriptor = DescriptorUtils.getSuperClassDescriptor(classDescriptor) + + while (superClassDescriptor != null) { + val implementsBuiltinDeclaration = + TypeCheckingProcedure.findCorrespondingSupertype(superClassDescriptor.defaultType, builtinContainerDefaultType) != null + + if (superClassDescriptor !is JavaClassDescriptor) { + // Kotlin class + // ? + if (implementsBuiltinDeclaration) return false + } + else { + // java super class inherits builtin class and it's declaration is final + if (implementsBuiltinDeclaration + && reachableDeclarations.any { it.containingDeclaration == superClassDescriptor && it.modality == Modality.FINAL }) { + return false + } + } + + superClassDescriptor = DescriptorUtils.getSuperClassDescriptor(superClassDescriptor as ClassDescriptor) + } + + return true +} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JetTypeMapper.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JetTypeMapper.java index 89f7b82563b..480806be126 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JetTypeMapper.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JetTypeMapper.java @@ -34,6 +34,7 @@ import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.fileClasses.FileClasses; import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil; import org.jetbrains.kotlin.fileClasses.JvmFileClassesProvider; +import org.jetbrains.kotlin.load.java.BuiltinsPropertiesUtilKt; import org.jetbrains.kotlin.load.java.JvmAbi; import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor; import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor; @@ -725,7 +726,13 @@ public class JetTypeMapper { invokeOpcode = superCall || isPrivateFunInvocation ? INVOKESPECIAL : INVOKEVIRTUAL; } - signature = mapSignature(functionDescriptor.getOriginal()); + FunctionDescriptor overriddenSpecialBuiltinFunction = + BuiltinsPropertiesUtilKt.getBuiltinSpecialOverridden(functionDescriptor.getOriginal()); + FunctionDescriptor functionToCall = overriddenSpecialBuiltinFunction != null + ? overriddenSpecialBuiltinFunction.getOriginal() + : functionDescriptor.getOriginal(); + + signature = mapSignature(functionToCall); ClassDescriptor receiver = (currentIsInterface && !originalIsInterface) || currentOwner instanceof FunctionClassDescriptor ? declarationOwner @@ -803,6 +810,9 @@ public class JetTypeMapper { if (platformName != null) return platformName; } + String nameForSpecialFunction = BuiltinsPropertiesUtilKt.getJvmMethodNameIfSpecial(descriptor); + if (nameForSpecialFunction != null) return nameForSpecialFunction; + if (descriptor instanceof PropertyAccessorDescriptor) { PropertyDescriptor property = ((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty(); if (isAnnotationClass(property.getContainingDeclaration())) { diff --git a/compiler/testData/codegen/box/builtinsProperties/bridges.kt b/compiler/testData/codegen/box/builtinsProperties/bridges.kt new file mode 100644 index 00000000000..45216e69ec0 --- /dev/null +++ b/compiler/testData/codegen/box/builtinsProperties/bridges.kt @@ -0,0 +1,101 @@ +import java.util.*; + +interface A0 { + val size: Int get() = 56 +} + +class B0 : Collection, A0 { + override fun isEmpty() = throw UnsupportedOperationException() + override fun contains(o: Any?) = throw UnsupportedOperationException() + override fun iterator() = throw UnsupportedOperationException() + override fun containsAll(c: Collection) = throw UnsupportedOperationException() +} + +open class A1 { + val size: Int = 56 +} + +class B1 : Collection, A1() { + override fun isEmpty() = throw UnsupportedOperationException() + override fun contains(o: Any?) = throw UnsupportedOperationException() + override fun iterator() = throw UnsupportedOperationException() + override fun containsAll(c: Collection) = throw UnsupportedOperationException() +} + +interface I2 { + val size: Int +} + +val list = ArrayList() + +class B2 : ArrayList(list), I2 + +interface I3 { + val size: T +} + +class B3 : ArrayList(list), I3 + +interface I4 { + val size: T get() = 56 as T +} + +class B4 : Collection, I4 { + override fun isEmpty() = throw UnsupportedOperationException() + override fun contains(o: Any?) = throw UnsupportedOperationException() + override fun iterator() = throw UnsupportedOperationException() + override fun containsAll(c: Collection) = throw UnsupportedOperationException() +} + +interface I5 : Collection { + override val size: Int get() = 56 +} + +class B5 : I5 { + override fun isEmpty() = throw UnsupportedOperationException() + override fun contains(o: Any?) = throw UnsupportedOperationException() + override fun iterator() = throw UnsupportedOperationException() + override fun containsAll(c: Collection) = throw UnsupportedOperationException() +} + +fun box(): String { + list.add("1") + + val b0 = B0() + if (b0.size != 56) return "fail 0: ${b0.size}" + var x: Collection = B0() + if (x.size != 56) return "fail 00: ${x.size}" + val a0: A0 = b0 + if (a0.size != 56) return "fail 000: ${a0.size}" + + val b1 = B1() + if (b1.size != 56) return "fail 1: ${b1.size}" + x = B1() + if (x.size != 56) return "fail 2: ${x.size}" + + val b2 = B2() + if (b2.size != 1) return "fail 3: ${b2.size}" + x = B2() + if (x.size != 1) return "fail 4: ${x.size}" + val i2: I2 = b2 + if (i2.size != 1) return "fail 5: ${i2.size}" + + val b3 = B3() + if (b3.size != 1) return "fail 6: ${b3.size}" + x = B3() + if (x.size != 1) return "fail 7: ${x.size}" + val i3: I3 = b3 + if (i3.size != 1) return "fail 8: ${i3.size}" + + val b4 = B4() + if (b4.size != 56) return "fail 9: ${b4.size}" + x = B4() + if (x.size != 56) return "fail 10: ${x.size}" + + val b5 = B5() + if (b5.size != 56) return "fail 11: ${b5.size}" + x = B5() + if (x.size != 56) return "fail 12: ${x.size}" + + return "OK" +} diff --git a/compiler/testData/codegen/box/builtinsProperties/collectionImpl.kt b/compiler/testData/codegen/box/builtinsProperties/collectionImpl.kt new file mode 100644 index 00000000000..7e542f1f01c --- /dev/null +++ b/compiler/testData/codegen/box/builtinsProperties/collectionImpl.kt @@ -0,0 +1,94 @@ +class A1 : MutableCollection { + override val size: Int + get() = 56 + + override fun isEmpty(): Boolean { + throw UnsupportedOperationException() + } + + override fun contains(o: Any?): Boolean { + throw UnsupportedOperationException() + } + + override fun iterator(): MutableIterator { + throw UnsupportedOperationException() + } + + override fun containsAll(c: Collection): Boolean { + throw UnsupportedOperationException() + } + + override fun add(e: String): Boolean { + throw UnsupportedOperationException() + } + + override fun remove(o: Any?): Boolean { + throw UnsupportedOperationException() + } + + override fun addAll(c: Collection): Boolean { + throw UnsupportedOperationException() + } + + override fun removeAll(c: Collection): Boolean { + throw UnsupportedOperationException() + } + + override fun retainAll(c: Collection): Boolean { + throw UnsupportedOperationException() + } + + override fun clear() { + throw UnsupportedOperationException() + } +} + +class A2 : java.util.AbstractCollection() { + override val size: Int + get() = 56 + + override fun iterator(): MutableIterator { + throw UnsupportedOperationException() + } +} + +class A3 : java.util.ArrayList() { + override val size: Int + get() = 56 +} + +interface Sized { + val size: Int +} + +class A4 : java.util.ArrayList(), Sized { + override val size: Int + get() = 56 +} + +fun check56(x: Collection) { + if (x.size != 56) throw java.lang.RuntimeException("fail ${x.size}") +} + +fun box(): String { + val a1 = A1() + if (a1.size != 56) return "fail 1: ${a1.size}" + check56(a1) + + val a2 = A2() + if (a2.size != 56) return "fail 2: ${a2.size}" + check56(a2) + + val a3 = A3() + if (a3.size != 56) return "fail 3: ${a3.size}" + check56(a3) + + val a4 = A4() + if (a4.size != 56) return "fail 4: ${a4.size}" + check56(a4) + + val sized: Sized = a4 + if (sized.size != 56) return "fail 5: ${a4.size}" + + return "OK" +} diff --git a/compiler/testData/codegen/box/builtinsProperties/explicitSuperCall.kt b/compiler/testData/codegen/box/builtinsProperties/explicitSuperCall.kt new file mode 100644 index 00000000000..49f05192011 --- /dev/null +++ b/compiler/testData/codegen/box/builtinsProperties/explicitSuperCall.kt @@ -0,0 +1,10 @@ +class A : java.util.ArrayList() { + override val size: Int get() = super.size + 56 +} + +fun box(): String { + val a = A() + if (a.size != 56) return "fail: ${a.size}" + + return "OK" +} diff --git a/compiler/testData/codegen/box/builtinsProperties/maps.kt b/compiler/testData/codegen/box/builtinsProperties/maps.kt new file mode 100644 index 00000000000..cca4594f5d8 --- /dev/null +++ b/compiler/testData/codegen/box/builtinsProperties/maps.kt @@ -0,0 +1,41 @@ +class A : Map { + override val size: Int get() = 56 + + override fun isEmpty(): Boolean { + throw UnsupportedOperationException() + } + + override fun containsKey(key: Any?): Boolean { + throw UnsupportedOperationException() + } + + override fun containsValue(value: Any?): Boolean { + throw UnsupportedOperationException() + } + + override fun get(key: Any?): String? { + throw UnsupportedOperationException() + } + + override fun keySet(): Set { + throw UnsupportedOperationException() + } + + override fun values(): Collection { + throw UnsupportedOperationException() + } + + override fun entrySet(): Set> { + throw UnsupportedOperationException() + } +} + +fun box(): String { + val a = A() + if (a.size != 56) return "fail 1: ${a.size}" + + val x: Map = a + if (x.size != 56) return "fail 2: ${x.size}" + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxWithJava/properties/collectionSize/Test.java b/compiler/testData/codegen/boxWithJava/properties/collectionSize/Test.java new file mode 100644 index 00000000000..d7ffd983187 --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/properties/collectionSize/Test.java @@ -0,0 +1,5 @@ +public class Test extends java.util.ArrayList { + public final int size() { + return 56; + } +} diff --git a/compiler/testData/codegen/boxWithJava/properties/collectionSize/collectionSize.kt b/compiler/testData/codegen/boxWithJava/properties/collectionSize/collectionSize.kt new file mode 100644 index 00000000000..651f7ac015d --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/properties/collectionSize/collectionSize.kt @@ -0,0 +1,12 @@ + +class OurTest : Test() + +fun box(): String { + val t = OurTest() + val x: MutableCollection = t + + if (t.size != 56) return "fail 1: ${t.size}" + if (x.size != 56) return "fail 1: ${x.size}" + + return "OK" +} diff --git a/compiler/testData/codegen/bytecodeText/builtinFunctions/size.kt b/compiler/testData/codegen/bytecodeText/builtinFunctions/size.kt new file mode 100644 index 00000000000..b0a7ce9b23c --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/builtinFunctions/size.kt @@ -0,0 +1,65 @@ +abstract class A1 : Collection { + override val size: Int get() = 1 +} + +abstract class A2 : Collection { + abstract override val size: Int +} + +abstract class A3 : java.util.AbstractCollection() { + override val size: Int get() = 1 +} + +abstract class A4 : java.util.AbstractCollection() { + abstract override val size: Int +} + +abstract class A5 : java.util.ArrayList() { + override val size: Int get() = 1 +} + +abstract class A6 : java.util.ArrayList() { + abstract override val size: Int +} + +abstract class A7 : MutableList +abstract class A8 : java.util.ArrayList() + +interface A9 : List {} + +fun box( + a1: A1, + a2: A2, + a3: A3, + a4: A4, + a5: A5, + a6: A6, + a7: A7, + a8: A8, + a9: A9, + c1: Collection, + c2: MutableCollection +) { + a1.size + a2.size + a3.size + a4.size + a5.size + a6.size + a7.size + a8.size + a9.size + c1.size + c2.size +} + +/* + +*/ + +// 8 public final bridge size\(\)I +// 8 INVOKEVIRTUAL A[0-9]+\.size \(\)I +// 1 INVOKEINTERFACE A9+\.size \(\)I +// 8 INVOKEVIRTUAL A[0-9]+\.getSize() \(\)I +// 2 INVOKEINTERFACE java\/util\/Collection.size \(\)I +// 4 public abstract getSize\(\)I diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index 68f30251f26..33a2d893379 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -359,6 +359,21 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { } } + @TestMetadata("compiler/testData/codegen/bytecodeText/builtinFunctions") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class BuiltinFunctions extends AbstractBytecodeTextTest { + public void testAllFilesPresentInBuiltinFunctions() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/builtinFunctions"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("size.kt") + public void testSize() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/builtinFunctions/size.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/bytecodeText/conditions") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java index 7a998d04544..4badb87abaf 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java @@ -920,6 +920,39 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { } } + @TestMetadata("compiler/testData/codegen/box/builtinsProperties") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class BuiltinsProperties extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInBuiltinsProperties() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/builtinsProperties"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("bridges.kt") + public void testBridges() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/builtinsProperties/bridges.kt"); + doTest(fileName); + } + + @TestMetadata("collectionImpl.kt") + public void testCollectionImpl() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/builtinsProperties/collectionImpl.kt"); + doTest(fileName); + } + + @TestMetadata("explicitSuperCall.kt") + public void testExplicitSuperCall() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/builtinsProperties/explicitSuperCall.kt"); + doTest(fileName); + } + + @TestMetadata("maps.kt") + public void testMaps() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/builtinsProperties/maps.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/box/casts") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java index d2ecefc82ea..b6e801c42cb 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java @@ -319,6 +319,12 @@ public class BlackBoxWithJavaCodegenTestGenerated extends AbstractBlackBoxCodege doTestWithJava(fileName); } + @TestMetadata("collectionSize") + public void testCollectionSize() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/properties/collectionSize/"); + doTestWithJava(fileName); + } + @TestMetadata("substituteJavaSuperField") public void testSubstituteJavaSuperField() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/properties/substituteJavaSuperField/"); diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/builtinsPropertiesUtil.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/builtinsPropertiesUtil.kt index 9a90217dd7d..6c77db845db 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/builtinsPropertiesUtil.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/builtinsPropertiesUtil.kt @@ -16,21 +16,65 @@ package org.jetbrains.kotlin.load.java +import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe +import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.utils.addToStdlib.check import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult private val BUILTIN_SPECIAL_PROPERTIES_FQ_NAMES = setOf(FqName("kotlin.Collection.size"), FqName("kotlin.Map.size")) private val BUILTIN_SPECIAL_PROPERTIES_SHORT_NAMES = BUILTIN_SPECIAL_PROPERTIES_FQ_NAMES.map { it.shortName() }.toSet() -private fun DeclarationDescriptor.hasBuiltinSpecialPropertyFqName() - = name in BUILTIN_SPECIAL_PROPERTIES_SHORT_NAMES - && fqNameUnsafe.check { it.isSafe }?.toSafe() in BUILTIN_SPECIAL_PROPERTIES_FQ_NAMES +public fun CallableDescriptor.hasBuiltinSpecialPropertyFqName(): Boolean { + if (this is PropertyAccessorDescriptor) return correspondingProperty.hasBuiltinSpecialPropertyFqName() + if (name !in BUILTIN_SPECIAL_PROPERTIES_SHORT_NAMES) return false + + return hasBuiltinSpecialPropertyFqNameImpl() +} + +private fun CallableDescriptor.hasBuiltinSpecialPropertyFqNameImpl(): Boolean { + if (fqNameUnsafe.check { it.isSafe }?.toSafe() in BUILTIN_SPECIAL_PROPERTIES_FQ_NAMES) return true + + if (!fqNameUnsafe.firstSegmentIs(KotlinBuiltIns.BUILT_INS_PACKAGE_NAME)) return false + if (builtIns.builtInsModule != module) return false + + return overriddenDescriptors.any(CallableDescriptor::hasBuiltinSpecialPropertyFqName) +} val Name.isBuiltinSpecialPropertyName: Boolean get() = this in BUILTIN_SPECIAL_PROPERTIES_SHORT_NAMES -val PropertyDescriptor.builtinSpecialOverridden: PropertyDescriptor? - get() = check { hasBuiltinSpecialPropertyFqName() } ?: overriddenDescriptors.firstNotNullResult { it.builtinSpecialOverridden } +private val CallableDescriptor.builtinSpecialPropertyAccessorName: String? + get() = when(this) { + is PropertyAccessorDescriptor -> correspondingProperty.check { it.hasBuiltinSpecialPropertyFqName() }?.name?.asString() + else -> null + } + +@Suppress("UNCHECKED_CAST") +val T.builtinSpecialOverridden: T? get() { + return when (this) { + is PropertyAccessorDescriptor -> check { correspondingProperty.hasBuiltinSpecialPropertyFqName() } + ?: overriddenDescriptors.firstNotNullResult { it.builtinSpecialOverridden } as T? + is PropertyDescriptor -> check { hasBuiltinSpecialPropertyFqName() } + ?: overriddenDescriptors.firstNotNullResult { it.builtinSpecialOverridden } as T? + else -> null + } +} + +fun CallableDescriptor.overridesBuiltinSpecialDeclaration(): Boolean = builtinSpecialOverridden != null + +public val CallableDescriptor.jvmMethodNameIfSpecial: String? + get() = builtinOverriddenThatAffectsJvmName?.builtinSpecialPropertyAccessorName + +public val CallableDescriptor.builtinOverriddenThatAffectsJvmName: CallableDescriptor? + get() = if (hasBuiltinSpecialPropertyFqName() || isFromJava) builtinSpecialOverridden else null + +private val CallableDescriptor.isFromJava: Boolean + get() = propertyIfAccessor is JavaCallableMemberDescriptor + +private val CallableDescriptor.propertyIfAccessor: CallableDescriptor + get() = if (this is PropertyAccessorDescriptor) correspondingProperty else this diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java index f0e1cfac231..e1f1f8286fe 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java @@ -366,6 +366,18 @@ public class DescriptorUtils { return getBuiltIns(classDescriptor).getAnyType(); } + @Nullable + public static ClassDescriptor getSuperClassDescriptor(@NotNull ClassDescriptor classDescriptor) { + Collection superclassTypes = classDescriptor.getTypeConstructor().getSupertypes(); + for (JetType type : superclassTypes) { + ClassDescriptor superClassDescriptor = getClassDescriptorForType(type); + if (superClassDescriptor.getKind() != ClassKind.INTERFACE) { + return superClassDescriptor; + } + } + return null; + } + @NotNull public static ClassDescriptor getClassDescriptorForType(@NotNull JetType type) { return getClassDescriptorForTypeConstructor(type.getConstructor());