diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java index 8e9424a6f66..c704030b3dc 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java @@ -63,6 +63,7 @@ import java.util.Set; import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isBoolean; import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isPrimitiveClass; +import static org.jetbrains.kotlin.codegen.CodegenUtilKt.isToArrayFromCollection; import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isConstOrHasJvmFieldAnnotation; import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isJvmInterface; import static org.jetbrains.kotlin.descriptors.annotations.AnnotationUtilKt.isInlineOnlyOrReified; @@ -354,6 +355,10 @@ public class AsmUtil { return NO_FLAG_PACKAGE_PRIVATE; } + if (isToArrayFromCollection(memberDescriptor)) { + return ACC_PUBLIC; + } + if (memberDescriptor instanceof ConstructorDescriptor && isAnonymousObject(memberDescriptor.getContainingDeclaration())) { return getVisibilityAccessFlagForAnonymous((ClassDescriptor) memberDescriptor.getContainingDeclaration()); } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java index 0a9e6d36295..c5c24aabed0 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java @@ -62,7 +62,6 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver; import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver; import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue; import org.jetbrains.kotlin.types.KotlinType; -import org.jetbrains.kotlin.types.checker.KotlinTypeChecker; import org.jetbrains.org.objectweb.asm.FieldVisitor; import org.jetbrains.org.objectweb.asm.Label; import org.jetbrains.org.objectweb.asm.MethodVisitor; @@ -74,6 +73,8 @@ import java.util.*; import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.FQ_NAMES; import static org.jetbrains.kotlin.codegen.AsmUtil.*; +import static org.jetbrains.kotlin.codegen.CodegenUtilKt.isGenericToArray; +import static org.jetbrains.kotlin.codegen.CodegenUtilKt.isNonGenericToArray; import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.*; import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.enumEntryNeedSubclass; import static org.jetbrains.kotlin.resolve.BindingContextUtils.getDelegationConstructorCall; @@ -397,35 +398,6 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { } } - private boolean isGenericToArray(@NotNull FunctionDescriptor function) { - if (function.getValueParameters().size() != 1 || function.getTypeParameters().size() != 1) { - return false; - } - - KotlinType returnType = function.getReturnType(); - assert returnType != null : function.toString(); - KotlinType paramType = function.getValueParameters().get(0).getType(); - if (KotlinBuiltIns.isArray(returnType) && KotlinBuiltIns.isArray(paramType)) { - KotlinType elementType = function.getTypeParameters().get(0).getDefaultType(); - KotlinBuiltIns builtIns = DescriptorUtilsKt.getBuiltIns(descriptor); - if (KotlinTypeChecker.DEFAULT.equalTypes(elementType, builtIns.getArrayElementType(returnType)) - && KotlinTypeChecker.DEFAULT.equalTypes(elementType, builtIns.getArrayElementType(paramType))) { - return true; - } - } - - return false; - } - - private static boolean isNonGenericToArray(@NotNull FunctionDescriptor function) { - if (!function.getValueParameters().isEmpty() || !function.getTypeParameters().isEmpty()) { - return false; - } - - KotlinType returnType = function.getReturnType(); - return returnType != null && KotlinBuiltIns.isArray(returnType); - } - private void generateToArray() { if (descriptor.getKind() == ClassKind.INTERFACE) return; diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt index 8ce1571752e..3f0ff1513dc 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt @@ -38,15 +38,18 @@ import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.DescriptorUtils.isSubclass import org.jetbrains.kotlin.resolve.annotations.hasJvmStaticAnnotation import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfoBefore import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory +import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin import org.jetbrains.kotlin.serialization.deserialization.PLATFORM_DEPENDENT_ANNOTATION_FQ_NAME import org.jetbrains.kotlin.types.ErrorUtils import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.types.checker.KotlinTypeChecker import org.jetbrains.kotlin.utils.DFS import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult import org.jetbrains.org.objectweb.asm.Label @@ -297,3 +300,34 @@ fun KtExpression?.asmType(typeMapper: KotlinTypeMapper, bindingContext: BindingC fun KtExpression?.kotlinType(bindingContext: BindingContext) = this?.let(bindingContext::getType) +fun FunctionDescriptor.isGenericToArray(): Boolean { + if (valueParameters.size != 1 || typeParameters.size != 1) return false + + val returnType = returnType ?: throw AssertionError(toString()) + val paramType = valueParameters[0].type + + if (!KotlinBuiltIns.isArray(returnType) || !KotlinBuiltIns.isArray(paramType)) return false + + val elementType = typeParameters[0].defaultType + return KotlinTypeChecker.DEFAULT.equalTypes(elementType, builtIns.getArrayElementType(returnType)) && + KotlinTypeChecker.DEFAULT.equalTypes(elementType, builtIns.getArrayElementType(paramType)) +} + +fun FunctionDescriptor.isNonGenericToArray(): Boolean { + if (!valueParameters.isEmpty() || !typeParameters.isEmpty()) return false + + val returnType = returnType + return returnType != null && KotlinBuiltIns.isArray(returnType) +} + +fun MemberDescriptor.isToArrayFromCollection(): Boolean { + if (this !is FunctionDescriptor) return false + + val containingClassDescriptor = containingDeclaration as? ClassDescriptor ?: return false + if (containingClassDescriptor.source == SourceElement.NO_SOURCE) return false + + val collectionClass = builtIns.collection + if (!isSubclass(containingClassDescriptor, collectionClass)) return false + + return isGenericToArray() || isNonGenericToArray() +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/toArray/toArrayShouldBePublic.kt b/compiler/testData/codegen/box/toArray/toArrayShouldBePublic.kt new file mode 100644 index 00000000000..3e0d76bede1 --- /dev/null +++ b/compiler/testData/codegen/box/toArray/toArrayShouldBePublic.kt @@ -0,0 +1,50 @@ +// TARGET_BACKEND: JVM +// WITH_RUNTIME + +// FILE: SingletonCollection.kt +package test + +open class SingletonCollection(val value: T) : AbstractCollection() { + override val size = 1 + override fun iterator(): Iterator = listOf(value).iterator() + + protected fun toArray(): Array = + arrayOf(value) + + protected fun toArray(a: Array): Array { + a[0] = value as E + return a + } +} + +// FILE: DerivedSingletonCollection.kt +package test2 + +import test.* + +class DerivedSingletonCollection(value: T) : SingletonCollection(value) + +// FILE: box.kt +import test.* +import test2.* + +fun box(): String { + val sc = SingletonCollection(42) + + val test1 = (sc as java.util.Collection).toArray() + if (test1[0] != 42) return "Failed #1" + + val test2 = arrayOf(0) + (sc as java.util.Collection).toArray(test2) + if (test2[0] != 42) return "Failed #2" + + val dsc = DerivedSingletonCollection(42) + val test3 = (dsc as java.util.Collection).toArray() + if (test3[0] != 42) return "Failed #3" + + val test4 = arrayOf(0) + (dsc as java.util.Collection).toArray(test4) + if (test4[0] != 42) return "Failed #4" + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/toArray/toArrayShouldBePublicWithJava.kt b/compiler/testData/codegen/box/toArray/toArrayShouldBePublicWithJava.kt new file mode 100644 index 00000000000..ff00b887294 --- /dev/null +++ b/compiler/testData/codegen/box/toArray/toArrayShouldBePublicWithJava.kt @@ -0,0 +1,68 @@ +// TARGET_BACKEND: JVM +// WITH_RUNTIME + +// FILE: SingletonCollection.kt +package test + +open class SingletonCollection(val value: T) : AbstractCollection() { + override val size = 1 + override fun iterator(): Iterator = listOf(value).iterator() + + protected open fun toArray(): Array = + arrayOf(value) + + protected open fun toArray(a: Array): Array { + a[0] = value as E + return a + } +} + +// FILE: JavaSingletonCollection.java +import test.*; + +public class JavaSingletonCollection extends SingletonCollection { + public JavaSingletonCollection(T value) { + super(value); + } +} + +// FILE: JavaSingletonCollection2.java +import test.*; + +public class JavaSingletonCollection2 extends SingletonCollection { + public JavaSingletonCollection2(T value) { + super(value); + } + + public Object[] toArray() { + return super.toArray(); + } + + public E[] toArray(E[] arr) { + return super.toArray(arr); + } +} + + +// FILE: box.kt +import test.* + +fun box(): String { + val jsc = JavaSingletonCollection(42) as java.util.Collection + val test3 = jsc.toArray() + if (test3[0] != 42) return "Failed #3" + + val test4 = arrayOf(0) + jsc.toArray(test4) + if (test4[0] != 42) return "Failed #4" + + val jsc2 = JavaSingletonCollection2(42) as java.util.Collection + val test5 = jsc2.toArray() + if (test5[0] != 42) return "Failed #5" + + val test6 = arrayOf(0) + jsc2.toArray(test6) + if (test6[0] != 42) return "Failed #6" + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/light-analysis/toArray/toArrayShouldBePublic.txt b/compiler/testData/codegen/light-analysis/toArray/toArrayShouldBePublic.txt new file mode 100644 index 00000000000..de5a011bb9d --- /dev/null +++ b/compiler/testData/codegen/light-analysis/toArray/toArrayShouldBePublic.txt @@ -0,0 +1,20 @@ +public final class BoxKt { + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} + + +public class test/SingletonCollection { + private final field size: int + private final field value: java.lang.Object + public method (p0: java.lang.Object): void + public method getSize(): int + public final method getValue(): java.lang.Object + public @org.jetbrains.annotations.NotNull method iterator(): java.util.Iterator + public final @org.jetbrains.annotations.NotNull method toArray(): java.lang.Object[] + public final @org.jetbrains.annotations.NotNull method toArray(@org.jetbrains.annotations.NotNull p0: java.lang.Object[]): java.lang.Object[] +} + + +public final class test2/DerivedSingletonCollection { + public method (p0: java.lang.Object): void +} diff --git a/compiler/testData/codegen/light-analysis/toArray/toArrayShouldBePublicWithJava.txt b/compiler/testData/codegen/light-analysis/toArray/toArrayShouldBePublicWithJava.txt new file mode 100644 index 00000000000..7b6a3b3ad37 --- /dev/null +++ b/compiler/testData/codegen/light-analysis/toArray/toArrayShouldBePublicWithJava.txt @@ -0,0 +1,15 @@ +public final class BoxKt { + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} + + +public class test/SingletonCollection { + private final field size: int + private final field value: java.lang.Object + public method (p0: java.lang.Object): void + public method getSize(): int + public final method getValue(): java.lang.Object + public @org.jetbrains.annotations.NotNull method iterator(): java.util.Iterator + public @org.jetbrains.annotations.NotNull method toArray(): java.lang.Object[] + public @org.jetbrains.annotations.NotNull method toArray(@org.jetbrains.annotations.NotNull p0: java.lang.Object[]): java.lang.Object[] +} diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 480648a141a..300fce456c3 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -16367,6 +16367,18 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } + @TestMetadata("toArrayShouldBePublic.kt") + public void testToArrayShouldBePublic() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/toArray/toArrayShouldBePublic.kt"); + doTest(fileName); + } + + @TestMetadata("toArrayShouldBePublicWithJava.kt") + public void testToArrayShouldBePublicWithJava() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/toArray/toArrayShouldBePublicWithJava.kt"); + doTest(fileName); + } + @TestMetadata("toTypedArray.kt") public void testToTypedArray() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/toArray/toTypedArray.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 2c8d0f87572..bd832db0c74 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -16367,6 +16367,18 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("toArrayShouldBePublic.kt") + public void testToArrayShouldBePublic() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/toArray/toArrayShouldBePublic.kt"); + doTest(fileName); + } + + @TestMetadata("toArrayShouldBePublicWithJava.kt") + public void testToArrayShouldBePublicWithJava() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/toArray/toArrayShouldBePublicWithJava.kt"); + doTest(fileName); + } + @TestMetadata("toTypedArray.kt") public void testToTypedArray() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/toArray/toTypedArray.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java index 961b8691fb8..664bcf3eca7 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java @@ -16367,6 +16367,18 @@ public class LightAnalysisModeCodegenTestGenerated extends AbstractLightAnalysis doTest(fileName); } + @TestMetadata("toArrayShouldBePublic.kt") + public void testToArrayShouldBePublic() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/toArray/toArrayShouldBePublic.kt"); + doTest(fileName); + } + + @TestMetadata("toArrayShouldBePublicWithJava.kt") + public void testToArrayShouldBePublicWithJava() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/toArray/toArrayShouldBePublicWithJava.kt"); + doTest(fileName); + } + @TestMetadata("toTypedArray.kt") public void testToTypedArray() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/toArray/toTypedArray.kt");