diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java index cd070ce9629..9dbd7d4a9ec 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java @@ -58,8 +58,7 @@ import org.jetbrains.jet.lang.resolve.java.JvmAbi; 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.JetType; -import org.jetbrains.jet.lang.types.TypeUtils; +import org.jetbrains.jet.lang.types.*; import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; import org.jetbrains.jet.lexer.JetTokens; @@ -461,9 +460,83 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { generateFunctionsForDataClasses(); + generateBuiltinMethodStubs(); + genClosureFields(context.closure, v, state.getTypeMapper()); } + private List getBuiltinMethodStubSignatures() { + List result = Lists.newArrayList(); + + KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance(); + if (isSubclass(descriptor, builtIns.getList())) { + TypeParameterDescriptor listTypeParameter = builtIns.getList().getTypeConstructor().getParameters().get(0); + TypeSubstitutor deepSubstitutor = SubstitutionUtils.buildDeepSubstitutor(descriptor.getDefaultType()); + TypeProjection substitute = deepSubstitutor.substitute(new TypeProjection(listTypeParameter.getDefaultType())); + assert substitute != null : "Couldn't substitute: " + descriptor; + ClassifierDescriptor classifier = substitute.getType().getConstructor().getDeclarationDescriptor(); + assert classifier != null : "No classifier: " + substitute.getType(); + + if (CodegenUtil.getDeclaredFunctionByRawSignature(descriptor, Name.identifier("add"), + builtIns.getBoolean(), classifier) == null) { + result.add("add(Ljava/lang/Object;)Z"); + } + + if (CodegenUtil.getDeclaredFunctionByRawSignature(descriptor, Name.identifier("remove"), + builtIns.getBoolean(), builtIns.getAny()) == null) { + result.add("remove(Ljava/lang/Object;)Z"); + } + + if (CodegenUtil.getDeclaredFunctionByRawSignature(descriptor, Name.identifier("addAll"), + builtIns.getBoolean(), builtIns.getCollection()) == null) { + result.add("addAll(Ljava/util/Collection;)Z"); + } + + if (CodegenUtil.getDeclaredFunctionByRawSignature(descriptor, Name.identifier("removeAll"), + builtIns.getBoolean(), builtIns.getCollection()) == null) { + result.add("removeAll(Ljava/util/Collection;)Z"); + } + + if (CodegenUtil.getDeclaredFunctionByRawSignature(descriptor, Name.identifier("retainAll"), + builtIns.getBoolean(), builtIns.getCollection()) == null) { + result.add("retainAll(Ljava/util/Collection;)Z"); + } + + if (CodegenUtil.getDeclaredFunctionByRawSignature(descriptor, Name.identifier("clear"), builtIns.getUnit()) == null){ + result.add("clear()V"); + } + + if (CodegenUtil.getDeclaredFunctionByRawSignature(descriptor, Name.identifier("set"), + classifier, builtIns.getInt(), classifier) == null) { + result.add("set(ILjava/lang/Object;)Ljava/lang/Object;"); + } + + if (CodegenUtil.getDeclaredFunctionByRawSignature(descriptor, Name.identifier("add"), + builtIns.getUnit(), builtIns.getInt(), classifier) == null) { + result.add("add(ILjava/lang/Object;)V"); + } + + if (CodegenUtil.getDeclaredFunctionByRawSignature(descriptor, Name.identifier("remove"), + classifier, builtIns.getInt()) == null) { + result.add("remove(I)Ljava/lang/Object;"); + } + } + + return result; + } + + + private void generateBuiltinMethodStubs() { + for (String signature : getBuiltinMethodStubSignatures()) { + int parenthOpen = signature.indexOf('('); + String name = signature.substring(0, parenthOpen); + String desc = signature.substring(parenthOpen); + + MethodVisitor mv = v.getVisitor().visitMethod(ACC_PUBLIC, name, desc, null, null); + AsmUtil.genMethodThrow(mv, "java/lang/UnsupportedOperationException", "Mutating immutable collection"); + } + } + private List getDataProperties() { ArrayList result = Lists.newArrayList(); for (JetParameter parameter : getPrimaryConstructorParameters()) { diff --git a/compiler/testData/codegen/box/builtinStubMethods/List.kt b/compiler/testData/codegen/box/builtinStubMethods/List.kt new file mode 100644 index 00000000000..b96520dea74 --- /dev/null +++ b/compiler/testData/codegen/box/builtinStubMethods/List.kt @@ -0,0 +1,41 @@ +class MyList: List { + override fun size(): Int = 0 + override fun isEmpty(): Boolean = true + override fun contains(o: Any?): Boolean = false + override fun iterator(): Iterator = throw Error() + override fun toArray(): Array = throw Error() + override fun toArray(a: Array): Array = throw Error() + override fun containsAll(c: Collection): Boolean = false + override fun get(index: Int): T = throw IndexOutOfBoundsException() + override fun indexOf(o: Any?): Int = -1 + override fun lastIndexOf(o: Any?): Int = -1 + override fun listIterator(): ListIterator = throw Error() + override fun listIterator(index: Int): ListIterator = throw Error() + override fun subList(fromIndex: Int, toIndex: Int): List = this + override fun hashCode(): Int = 0 + override fun equals(other: Any?): Boolean = false +} + +fun expectUoe(block: () -> Any) { + try { + block() + throw AssertionError() + } catch (e: UnsupportedOperationException) { + } +} + +fun box(): String { + val list = MyList() as MutableList + + expectUoe { list.add("") } + expectUoe { list.remove("") } + expectUoe { list.addAll(list) } + expectUoe { list.removeAll(list) } + expectUoe { list.retainAll(list) } + expectUoe { list.clear() } + expectUoe { list.set(0, "") } + expectUoe { list.add(0, "") } + expectUoe { list.remove(0) } + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/builtinStubMethods/ListWithAllImplementations.kt b/compiler/testData/codegen/box/builtinStubMethods/ListWithAllImplementations.kt new file mode 100644 index 00000000000..3e5987aee62 --- /dev/null +++ b/compiler/testData/codegen/box/builtinStubMethods/ListWithAllImplementations.kt @@ -0,0 +1,44 @@ +class MyList(val v: T): List { + override fun size(): Int = 0 + override fun isEmpty(): Boolean = true + override fun contains(o: Any?): Boolean = false + override fun iterator(): Iterator = throw Error() + override fun toArray(): Array = throw Error() + override fun toArray(a: Array): Array = throw Error() + override fun containsAll(c: Collection): Boolean = false + override fun get(index: Int): T = v + override fun indexOf(o: Any?): Int = -1 + override fun lastIndexOf(o: Any?): Int = -1 + override fun listIterator(): ListIterator = throw Error() + override fun listIterator(index: Int): ListIterator = throw Error() + override fun subList(fromIndex: Int, toIndex: Int): List = throw Error() + override fun hashCode(): Int = 0 + override fun equals(other: Any?): Boolean = false + + public fun add(e: T): Boolean = true + public fun remove(o: Any?): Boolean = true + public fun addAll(c: Collection): Boolean = true + public fun addAll(index: Int, c: Collection): Boolean = true + public fun removeAll(c: Collection): Boolean = true + public fun retainAll(c: Collection): Boolean = true + public fun clear() {} + public fun set(index: Int, element: T): T = element + public fun add(index: Int, element: T) {} + public fun remove(index: Int): T = v +} + +fun box(): String { + val list = MyList("") as MutableList + + list.add("") + list.remove("") + list.addAll(list) + list.removeAll(list) + list.retainAll(list) + list.clear() + list.set(0, "") + list.add(0, "") + list.remove(0) + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/builtinStubMethods/ListWithAllInheritedImplementations.kt b/compiler/testData/codegen/box/builtinStubMethods/ListWithAllInheritedImplementations.kt new file mode 100644 index 00000000000..f515125dfaa --- /dev/null +++ b/compiler/testData/codegen/box/builtinStubMethods/ListWithAllInheritedImplementations.kt @@ -0,0 +1,46 @@ +open class Super(val v: T) { + public fun add(e: T): Boolean = true + public fun remove(o: Any?): Boolean = true + public fun addAll(c: Collection): Boolean = true + public fun addAll(index: Int, c: Collection): Boolean = true + public fun removeAll(c: Collection): Boolean = true + public fun retainAll(c: Collection): Boolean = true + public fun clear() {} + public fun set(index: Int, element: T): T = element + public fun add(index: Int, element: T) {} + public fun remove(index: Int): T = v +} + +class MyList(v: T): Super(v), List { + override fun size(): Int = 0 + override fun isEmpty(): Boolean = true + override fun contains(o: Any?): Boolean = false + override fun iterator(): Iterator = throw Error() + override fun toArray(): Array = throw Error() + override fun toArray(a: Array): Array = throw Error() + override fun containsAll(c: Collection): Boolean = false + override fun get(index: Int): T = v + override fun indexOf(o: Any?): Int = -1 + override fun lastIndexOf(o: Any?): Int = -1 + override fun listIterator(): ListIterator = throw Error() + override fun listIterator(index: Int): ListIterator = throw Error() + override fun subList(fromIndex: Int, toIndex: Int): List = throw Error() + override fun hashCode(): Int = 0 + override fun equals(other: Any?): Boolean = false +} + +fun box(): String { + val list = MyList("") as MutableList + + list.add("") + list.remove("") + list.addAll(list) + list.removeAll(list) + list.retainAll(list) + list.clear() + list.set(0, "") + list.add(0, "") + list.remove(0) + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/builtinStubMethods/SubstitutedList.kt b/compiler/testData/codegen/box/builtinStubMethods/SubstitutedList.kt new file mode 100644 index 00000000000..088cf6fbcde --- /dev/null +++ b/compiler/testData/codegen/box/builtinStubMethods/SubstitutedList.kt @@ -0,0 +1,41 @@ +class MyList: List { + override fun size(): Int = 0 + override fun isEmpty(): Boolean = true + override fun contains(o: Any?): Boolean = false + override fun iterator(): Iterator = throw Error() + override fun toArray(): Array = throw Error() + override fun toArray(a: Array): Array = throw Error() + override fun containsAll(c: Collection): Boolean = false + override fun get(index: Int): String = throw IndexOutOfBoundsException() + override fun indexOf(o: Any?): Int = -1 + override fun lastIndexOf(o: Any?): Int = -1 + override fun listIterator(): ListIterator = throw Error() + override fun listIterator(index: Int): ListIterator = throw Error() + override fun subList(fromIndex: Int, toIndex: Int): List = this + override fun hashCode(): Int = 0 + override fun equals(other: Any?): Boolean = false +} + +fun expectUoe(block: () -> Any) { + try { + block() + throw AssertionError() + } catch (e: UnsupportedOperationException) { + } +} + +fun box(): String { + val list = MyList() as MutableList + + expectUoe { list.add("") } + expectUoe { list.remove("") } + expectUoe { list.addAll(list) } + expectUoe { list.removeAll(list) } + expectUoe { list.retainAll(list) } + expectUoe { list.clear() } + expectUoe { list.set(0, "") } + expectUoe { list.add(0, "") } + expectUoe { list.remove(0) } + + 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 4a93bd4e2e5..3f5b22a282b 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.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.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); @@ -418,6 +418,34 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { } + @TestMetadata("compiler/testData/codegen/box/builtinStubMethods") + public static class BuiltinStubMethods extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInBuiltinStubMethods() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/codegen/box/builtinStubMethods"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("List.kt") + public void testList() throws Exception { + doTest("compiler/testData/codegen/box/builtinStubMethods/List.kt"); + } + + @TestMetadata("ListWithAllImplementations.kt") + public void testListWithAllImplementations() throws Exception { + doTest("compiler/testData/codegen/box/builtinStubMethods/ListWithAllImplementations.kt"); + } + + @TestMetadata("ListWithAllInheritedImplementations.kt") + public void testListWithAllInheritedImplementations() throws Exception { + doTest("compiler/testData/codegen/box/builtinStubMethods/ListWithAllInheritedImplementations.kt"); + } + + @TestMetadata("SubstitutedList.kt") + public void testSubstitutedList() throws Exception { + doTest("compiler/testData/codegen/box/builtinStubMethods/SubstitutedList.kt"); + } + + } + @TestMetadata("compiler/testData/codegen/box/callableReference") public static class CallableReference extends AbstractBlackBoxCodegenTest { @TestMetadata("abstractClassMember.kt") @@ -4354,6 +4382,7 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { suite.addTestSuite(BlackBoxCodegenTestGenerated.class); suite.addTestSuite(Arrays.class); suite.addTestSuite(Bridges.class); + suite.addTestSuite(BuiltinStubMethods.class); suite.addTestSuite(CallableReference.class); suite.addTestSuite(Casts.class); suite.addTestSuite(Classes.class);