From 6a1566a6dc8eb55106f3c2cde0ec41d7a6683c39 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Tue, 6 Oct 2015 14:10:20 +0300 Subject: [PATCH] Make JVM backend work with `Collection.size` as val 0. Such properties are called special because their accessor JVM name differs from usual one 1. When making call to such property, always choose special name 2. When generating Kotlin class inheriting such property generate `final bridge int size() { return this.getSize(); }` 3. If there is no `size` declaration in current class generate `bridge int getSize() { // super-call }` --- .../kotlin/backend/common/bridges/bridges.kt | 4 +- .../kotlin/backend/common/bridges/impl.kt | 2 +- .../kotlin/codegen/ClassBodyCodegen.java | 1 - .../kotlin/codegen/FunctionCodegen.java | 77 ++++++++--- .../kotlin/codegen/builtinSpecialBridges.kt | 120 ++++++++++++++++++ .../kotlin/codegen/state/JetTypeMapper.java | 12 +- .../codegen/box/builtinsProperties/bridges.kt | 101 +++++++++++++++ .../box/builtinsProperties/collectionImpl.kt | 94 ++++++++++++++ .../builtinsProperties/explicitSuperCall.kt | 10 ++ .../codegen/box/builtinsProperties/maps.kt | 41 ++++++ .../properties/collectionSize/Test.java | 5 + .../collectionSize/collectionSize.kt | 12 ++ .../bytecodeText/builtinFunctions/size.kt | 65 ++++++++++ .../codegen/BytecodeTextTestGenerated.java | 15 +++ .../BlackBoxCodegenTestGenerated.java | 33 +++++ .../BlackBoxWithJavaCodegenTestGenerated.java | 6 + .../load/java/builtinsPropertiesUtil.kt | 54 +++++++- .../kotlin/resolve/DescriptorUtils.java | 12 ++ 18 files changed, 638 insertions(+), 26 deletions(-) create mode 100644 compiler/backend/src/org/jetbrains/kotlin/codegen/builtinSpecialBridges.kt create mode 100644 compiler/testData/codegen/box/builtinsProperties/bridges.kt create mode 100644 compiler/testData/codegen/box/builtinsProperties/collectionImpl.kt create mode 100644 compiler/testData/codegen/box/builtinsProperties/explicitSuperCall.kt create mode 100644 compiler/testData/codegen/box/builtinsProperties/maps.kt create mode 100644 compiler/testData/codegen/boxWithJava/properties/collectionSize/Test.java create mode 100644 compiler/testData/codegen/boxWithJava/properties/collectionSize/collectionSize.kt create mode 100644 compiler/testData/codegen/bytecodeText/builtinFunctions/size.kt 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());