From 41d1f277dca15ff8cdb25dfbe7137de1a52922db Mon Sep 17 00:00:00 2001 From: Evgeny Gerashchenko Date: Thu, 26 Sep 2013 17:46:30 +0400 Subject: [PATCH] Generated default implementations for Collection.toArray(). #KT-3352 in progress --- .../codegen/ImplementationBodyCodegen.java | 60 ++++++++++++++++++ .../testData/codegen/box/toArray/toArray.kt | 21 +++++++ .../box/toArray/toArrayAlreadyPresent.kt | 30 +++++++++ .../BlackBoxCodegenTestGenerated.java | 21 ++++++- .../src/jet/runtime/CollectionToArray.java | 63 +++++++++++++++++++ 5 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 compiler/testData/codegen/box/toArray/toArray.kt create mode 100644 compiler/testData/codegen/box/toArray/toArrayAlreadyPresent.kt create mode 100644 runtime/src/jet/runtime/CollectionToArray.java diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java index d53f8e9d594..d91dea56204 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java @@ -51,6 +51,7 @@ import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.BindingContextUtils; import org.jetbrains.jet.lang.resolve.DescriptorUtils; import org.jetbrains.jet.lang.resolve.OverridingUtil; +import org.jetbrains.jet.lang.resolve.calls.CallResolverUtil; import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall; import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant; import org.jetbrains.jet.lang.resolve.java.AsmTypeConstants; @@ -59,6 +60,7 @@ import org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames; import org.jetbrains.jet.lang.resolve.java.JvmClassName; import org.jetbrains.jet.lang.resolve.name.Name; import org.jetbrains.jet.lang.types.*; +import org.jetbrains.jet.lang.types.checker.JetTypeChecker; import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; import org.jetbrains.jet.lexer.JetTokens; @@ -462,9 +464,67 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { generateBuiltinMethodStubs(); + generateToArray(); + genClosureFields(context.closure, v, state.getTypeMapper()); } + private boolean isGenericToArrayPresent() { + Collection functions = descriptor.getDefaultType().getMemberScope().getFunctions(Name.identifier("toArray")); + for (FunctionDescriptor function : functions) { + if (CallResolverUtil.isOrOverridesSynthesized(function)) { + continue; + } + + if (function.getValueParameters().size() != 1 || function.getTypeParameters().size() != 1) { + continue; + } + + JetType arrayType = KotlinBuiltIns.getInstance().getArrayType(function.getTypeParameters().get(0).getDefaultType()); + JetType returnType = function.getReturnType(); + assert returnType != null : function.toString(); + JetType paramType = function.getValueParameters().get(0).getType(); + if (JetTypeChecker.INSTANCE.equalTypes(arrayType, returnType) && JetTypeChecker.INSTANCE.equalTypes(arrayType, paramType)) { + return true; + } + } + return false; + + } + + private void generateToArray() { + KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance(); + if (isSubclass(descriptor, builtIns.getCollection())) { + if (CodegenUtil.getDeclaredFunctionByRawSignature(descriptor, Name.identifier("toArray"), builtIns.getArray()) == null) { + MethodVisitor mv = v.getVisitor().visitMethod(ACC_PUBLIC, "toArray", "()[Ljava/lang/Object;", null, null); + InstructionAdapter iv = new InstructionAdapter(mv); + + mv.visitCode(); + + iv.load(0, classAsmType); + iv.invokestatic("jet/runtime/CollectionToArray", "toArray", "(Ljava/util/Collection;)[Ljava/lang/Object;"); + iv.areturn(Type.getObjectType("[Ljava/lang/Object;")); + + FunctionCodegen.endVisit(mv, "toArray", myClass); + } + + if (!isGenericToArrayPresent()) { + MethodVisitor mv = v.getVisitor().visitMethod(ACC_PUBLIC, "toArray", "([Ljava/lang/Object;)[Ljava/lang/Object;", null, null); + InstructionAdapter iv = new InstructionAdapter(mv); + + mv.visitCode(); + + iv.load(0, classAsmType); + iv.load(1, Type.getObjectType("[Ljava/lang/Object;")); + + iv.invokestatic("jet/runtime/CollectionToArray", "toArray", "(Ljava/util/Collection;[Ljava/lang/Object;)[Ljava/lang/Object;"); + iv.areturn(Type.getObjectType("[Ljava/lang/Object;")); + + FunctionCodegen.endVisit(mv, "toArray", myClass); + } + } + } + private void generateMethodStub( @NotNull String name, @NotNull String desc, diff --git a/compiler/testData/codegen/box/toArray/toArray.kt b/compiler/testData/codegen/box/toArray/toArray.kt new file mode 100644 index 00000000000..6d217ea2db0 --- /dev/null +++ b/compiler/testData/codegen/box/toArray/toArray.kt @@ -0,0 +1,21 @@ +import java.util.Arrays + +class MyCollection(val delegate: Collection): Collection by delegate + +fun box(): String { + val collection = MyCollection(Arrays.asList(2, 3, 9)) as java.util.Collection<*> + + val array1 = collection.toArray() + val array2 = collection.toArray(Array(3, { 0 })) + + if (array1 !is Array) return (array1 as Object).getClass().toString() + if (array2 !is Array) return (array2 as Object).getClass().toString() + + val s1 = Arrays.toString(array1) + val s2 = Arrays.toString(array2) + + if (s1 != "[2, 3, 9]") return "s1 = $s1" + if (s2 != "[2, 3, 9]") return "s2 = $s2" + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/toArray/toArrayAlreadyPresent.kt b/compiler/testData/codegen/box/toArray/toArrayAlreadyPresent.kt new file mode 100644 index 00000000000..d8f89680fd0 --- /dev/null +++ b/compiler/testData/codegen/box/toArray/toArrayAlreadyPresent.kt @@ -0,0 +1,30 @@ +import java.util.Arrays + +class MyCollection(val delegate: Collection): Collection by delegate { + public fun toArray(): Array = Array(3, { it }) + public fun toArray(array: Array): Array { + val asIntArray = array as Array + asIntArray[0] = 0 + asIntArray[1] = 1 + asIntArray[2] = 2 + return array + } +} + +fun box(): String { + val collection = MyCollection(Arrays.asList(2, 3, 9)) as java.util.Collection<*> + + val array1 = collection.toArray() + val array2 = collection.toArray(Array(3, { 0 })) + + if (array1 !is Array) return (array1 as Object).getClass().toString() + if (array2 !is Array) return (array2 as Object).getClass().toString() + + val s1 = Arrays.toString(array1) + val s2 = Arrays.toString(array2) + + if (s1 != "[0, 1, 2]") return "s1 = $s1" + if (s2 != "[0, 1, 2]") return "s2 = $s2" + + return "OK" +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java index 992cc0a9ee9..38a763f61e6 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java @@ -31,7 +31,7 @@ import org.jetbrains.jet.codegen.generated.AbstractBlackBoxCodegenTest; /** This class is generated by {@link org.jetbrains.jet.generators.tests.GenerateTests}. DO NOT MODIFY MANUALLY */ @SuppressWarnings("all") @TestMetadata("compiler/testData/codegen/box") -@InnerTestClasses({BlackBoxCodegenTestGenerated.Arrays.class, BlackBoxCodegenTestGenerated.Bridges.class, BlackBoxCodegenTestGenerated.BuiltinStubMethods.class, BlackBoxCodegenTestGenerated.CallableReference.class, BlackBoxCodegenTestGenerated.Casts.class, BlackBoxCodegenTestGenerated.Classes.class, BlackBoxCodegenTestGenerated.Closures.class, BlackBoxCodegenTestGenerated.Constants.class, BlackBoxCodegenTestGenerated.ControlStructures.class, BlackBoxCodegenTestGenerated.DefaultArguments.class, BlackBoxCodegenTestGenerated.DelegatedProperty.class, BlackBoxCodegenTestGenerated.Elvis.class, BlackBoxCodegenTestGenerated.Enum.class, BlackBoxCodegenTestGenerated.ExclExcl.class, BlackBoxCodegenTestGenerated.ExtensionFunctions.class, BlackBoxCodegenTestGenerated.ExtensionProperties.class, BlackBoxCodegenTestGenerated.FieldRename.class, BlackBoxCodegenTestGenerated.Finally.class, BlackBoxCodegenTestGenerated.Functions.class, BlackBoxCodegenTestGenerated.InnerNested.class, BlackBoxCodegenTestGenerated.Instructions.class, BlackBoxCodegenTestGenerated.Intrinsics.class, BlackBoxCodegenTestGenerated.Labels.class, BlackBoxCodegenTestGenerated.LocalClasses.class, BlackBoxCodegenTestGenerated.MultiDecl.class, BlackBoxCodegenTestGenerated.Namespace.class, BlackBoxCodegenTestGenerated.Objects.class, BlackBoxCodegenTestGenerated.OperatorConventions.class, BlackBoxCodegenTestGenerated.PrimitiveTypes.class, BlackBoxCodegenTestGenerated.Properties.class, BlackBoxCodegenTestGenerated.Reflection.class, BlackBoxCodegenTestGenerated.SafeCall.class, BlackBoxCodegenTestGenerated.SamConstructors.class, BlackBoxCodegenTestGenerated.Strings.class, BlackBoxCodegenTestGenerated.Super.class, BlackBoxCodegenTestGenerated.Traits.class, BlackBoxCodegenTestGenerated.TypeInfo.class, BlackBoxCodegenTestGenerated.Unit.class, BlackBoxCodegenTestGenerated.Vararg.class, BlackBoxCodegenTestGenerated.When.class}) +@InnerTestClasses({BlackBoxCodegenTestGenerated.Arrays.class, BlackBoxCodegenTestGenerated.Bridges.class, BlackBoxCodegenTestGenerated.BuiltinStubMethods.class, BlackBoxCodegenTestGenerated.CallableReference.class, BlackBoxCodegenTestGenerated.Casts.class, BlackBoxCodegenTestGenerated.Classes.class, BlackBoxCodegenTestGenerated.Closures.class, BlackBoxCodegenTestGenerated.Constants.class, BlackBoxCodegenTestGenerated.ControlStructures.class, BlackBoxCodegenTestGenerated.DefaultArguments.class, BlackBoxCodegenTestGenerated.DelegatedProperty.class, BlackBoxCodegenTestGenerated.Elvis.class, BlackBoxCodegenTestGenerated.Enum.class, BlackBoxCodegenTestGenerated.ExclExcl.class, BlackBoxCodegenTestGenerated.ExtensionFunctions.class, BlackBoxCodegenTestGenerated.ExtensionProperties.class, BlackBoxCodegenTestGenerated.FieldRename.class, BlackBoxCodegenTestGenerated.Finally.class, BlackBoxCodegenTestGenerated.Functions.class, BlackBoxCodegenTestGenerated.InnerNested.class, BlackBoxCodegenTestGenerated.Instructions.class, BlackBoxCodegenTestGenerated.Intrinsics.class, BlackBoxCodegenTestGenerated.Labels.class, BlackBoxCodegenTestGenerated.LocalClasses.class, BlackBoxCodegenTestGenerated.MultiDecl.class, BlackBoxCodegenTestGenerated.Namespace.class, BlackBoxCodegenTestGenerated.Objects.class, BlackBoxCodegenTestGenerated.OperatorConventions.class, BlackBoxCodegenTestGenerated.PrimitiveTypes.class, BlackBoxCodegenTestGenerated.Properties.class, BlackBoxCodegenTestGenerated.Reflection.class, BlackBoxCodegenTestGenerated.SafeCall.class, BlackBoxCodegenTestGenerated.SamConstructors.class, BlackBoxCodegenTestGenerated.Strings.class, BlackBoxCodegenTestGenerated.Super.class, BlackBoxCodegenTestGenerated.ToArray.class, BlackBoxCodegenTestGenerated.Traits.class, BlackBoxCodegenTestGenerated.TypeInfo.class, BlackBoxCodegenTestGenerated.Unit.class, BlackBoxCodegenTestGenerated.Vararg.class, BlackBoxCodegenTestGenerated.When.class}) public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { public void testAllFilesPresentInBox() throws Exception { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/codegen/box"), Pattern.compile("^(.+)\\.kt$"), true); @@ -4142,6 +4142,24 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { } + @TestMetadata("compiler/testData/codegen/box/toArray") + public static class ToArray extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInToArray() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/codegen/box/toArray"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("toArray.kt") + public void testToArray() throws Exception { + doTest("compiler/testData/codegen/box/toArray/toArray.kt"); + } + + @TestMetadata("toArrayAlreadyPresent.kt") + public void testToArrayAlreadyPresent() throws Exception { + doTest("compiler/testData/codegen/box/toArray/toArrayAlreadyPresent.kt"); + } + + } + @TestMetadata("compiler/testData/codegen/box/traits") public static class Traits extends AbstractBlackBoxCodegenTest { public void testAllFilesPresentInTraits() throws Exception { @@ -4450,6 +4468,7 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { suite.addTestSuite(SamConstructors.class); suite.addTestSuite(Strings.class); suite.addTestSuite(Super.class); + suite.addTestSuite(ToArray.class); suite.addTestSuite(Traits.class); suite.addTestSuite(TypeInfo.class); suite.addTestSuite(Unit.class); diff --git a/runtime/src/jet/runtime/CollectionToArray.java b/runtime/src/jet/runtime/CollectionToArray.java new file mode 100644 index 00000000000..2b5434f58b5 --- /dev/null +++ b/runtime/src/jet/runtime/CollectionToArray.java @@ -0,0 +1,63 @@ +package jet.runtime; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; + +public class CollectionToArray { + // Implementation copied from AbstractCollection + + public static Object[] toArray(Collection collection) { + Object[] r = new Object[collection.size()]; + Iterator it = collection.iterator(); + for (int i = 0; i < r.length; i++) { + if (! it.hasNext()) // fewer elements than expected + return Arrays.copyOf(r, i); + r[i] = it.next(); + } + return it.hasNext() ? finishToArray(r, it) : r; + } + + public static T[] toArray(Collection collection, T[] a) { + // Estimate size of array; be prepared to see more or fewer elements + int size = collection.size(); + T[] r = a.length >= size ? a : + (T[])java.lang.reflect.Array + .newInstance(a.getClass().getComponentType(), size); + Iterator it = collection.iterator(); + + for (int i = 0; i < r.length; i++) { + if (! it.hasNext()) { // fewer elements than expected + if (a != r) + return Arrays.copyOf(r, i); + r[i] = null; // null-terminate + return r; + } + r[i] = (T)it.next(); + } + return it.hasNext() ? finishToArray(r, it) : r; + } + + private static T[] finishToArray(T[] r, Iterator it) { + int i = r.length; + while (it.hasNext()) { + int cap = r.length; + if (i == cap) { + int newCap = ((cap / 2) + 1) * 3; + if (newCap <= cap) { // integer overflow + if (cap == Integer.MAX_VALUE) + throw new OutOfMemoryError + ("Required array size too large"); + newCap = Integer.MAX_VALUE; + } + r = Arrays.copyOf(r, newCap); + } + r[i++] = (T)it.next(); + } + // trim if overallocated + return (i == r.length) ? r : Arrays.copyOf(r, i); + } + + private CollectionToArray() { + } +}