Generate toArray methods defined in classes inherited from collections as public even if they are declared with different visibility in Kotlin.

This commit is contained in:
Dmitry Petrov
2016-12-20 10:55:09 +03:00
parent c5d03578a3
commit 8fe9de1d35
10 changed files with 230 additions and 30 deletions
@@ -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());
}
@@ -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;
@@ -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()
}
@@ -0,0 +1,50 @@
// TARGET_BACKEND: JVM
// WITH_RUNTIME
// FILE: SingletonCollection.kt
package test
open class SingletonCollection<T>(val value: T) : AbstractCollection<T>() {
override val size = 1
override fun iterator(): Iterator<T> = listOf(value).iterator()
protected fun toArray(): Array<Any?> =
arrayOf<Any?>(value)
protected fun <E> toArray(a: Array<E>): Array<E> {
a[0] = value as E
return a
}
}
// FILE: DerivedSingletonCollection.kt
package test2
import test.*
class DerivedSingletonCollection<T>(value: T) : SingletonCollection<T>(value)
// FILE: box.kt
import test.*
import test2.*
fun box(): String {
val sc = SingletonCollection(42)
val test1 = (sc as java.util.Collection<Int>).toArray()
if (test1[0] != 42) return "Failed #1"
val test2 = arrayOf<Any?>(0)
(sc as java.util.Collection<Int>).toArray(test2)
if (test2[0] != 42) return "Failed #2"
val dsc = DerivedSingletonCollection(42)
val test3 = (dsc as java.util.Collection<Int>).toArray()
if (test3[0] != 42) return "Failed #3"
val test4 = arrayOf<Any?>(0)
(dsc as java.util.Collection<Int>).toArray(test4)
if (test4[0] != 42) return "Failed #4"
return "OK"
}
@@ -0,0 +1,68 @@
// TARGET_BACKEND: JVM
// WITH_RUNTIME
// FILE: SingletonCollection.kt
package test
open class SingletonCollection<T>(val value: T) : AbstractCollection<T>() {
override val size = 1
override fun iterator(): Iterator<T> = listOf(value).iterator()
protected open fun toArray(): Array<Any?> =
arrayOf<Any?>(value)
protected open fun <E> toArray(a: Array<E>): Array<E> {
a[0] = value as E
return a
}
}
// FILE: JavaSingletonCollection.java
import test.*;
public class JavaSingletonCollection<T> extends SingletonCollection<T> {
public JavaSingletonCollection(T value) {
super(value);
}
}
// FILE: JavaSingletonCollection2.java
import test.*;
public class JavaSingletonCollection2<T> extends SingletonCollection<T> {
public JavaSingletonCollection2(T value) {
super(value);
}
public Object[] toArray() {
return super.toArray();
}
public <E> E[] toArray(E[] arr) {
return super.toArray(arr);
}
}
// FILE: box.kt
import test.*
fun box(): String {
val jsc = JavaSingletonCollection(42) as java.util.Collection<Int>
val test3 = jsc.toArray()
if (test3[0] != 42) return "Failed #3"
val test4 = arrayOf<Any?>(0)
jsc.toArray(test4)
if (test4[0] != 42) return "Failed #4"
val jsc2 = JavaSingletonCollection2(42) as java.util.Collection<Int>
val test5 = jsc2.toArray()
if (test5[0] != 42) return "Failed #5"
val test6 = arrayOf<Any?>(0)
jsc2.toArray(test6)
if (test6[0] != 42) return "Failed #6"
return "OK"
}
@@ -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 <init>(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 <init>(p0: java.lang.Object): void
}
@@ -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 <init>(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[]
}
@@ -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");
@@ -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");
@@ -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");