diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ClassBodyCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ClassBodyCodegen.java index f47f323b70b..a5c1543356a 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ClassBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ClassBodyCodegen.java @@ -24,6 +24,7 @@ import org.jetbrains.jet.lang.descriptors.ClassDescriptor; import org.jetbrains.jet.lang.descriptors.PropertyDescriptor; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.DescriptorUtils; import java.util.ArrayList; import java.util.Collections; @@ -64,6 +65,8 @@ public abstract class ClassBodyCodegen { generateSyntheticParts(); generateStaticInitializer(); + + generateRemoveInIterator(); } protected abstract void generateDeclaration(); @@ -137,4 +140,12 @@ public abstract class ClassBodyCodegen { } } } + + private void generateRemoveInIterator() { + // generates stub 'remove' function for subclasses of Iterator to be compatible with java.util.Iterator + if (DescriptorUtils.isIteratorWithoutRemoveImpl(descriptor)) { + final MethodVisitor mv = v.getVisitor().visitMethod(ACC_PUBLIC, "remove", "()V", null, null); + CodegenUtil.generateMethodThrow(mv, "java/lang/UnsupportedOperationException", "Mutating method called on a Kotlin Iterator"); + } + } } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/CodegenUtil.java b/compiler/backend/src/org/jetbrains/jet/codegen/CodegenUtil.java index 2b8b0f84199..9a4ebaaaacd 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/CodegenUtil.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/CodegenUtil.java @@ -18,6 +18,9 @@ package org.jetbrains.jet.codegen; import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; +import org.jetbrains.asm4.MethodVisitor; +import org.jetbrains.asm4.Type; +import org.jetbrains.asm4.commons.InstructionAdapter; import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor; import org.jetbrains.jet.lang.psi.*; @@ -121,4 +124,20 @@ public class CodegenUtil { } return flags; } + + public static void generateThrow(MethodVisitor mv, String exception, String message) { + InstructionAdapter instructionAdapter = new InstructionAdapter(mv); + instructionAdapter.anew(Type.getObjectType(exception)); + instructionAdapter.dup(); + instructionAdapter.aconst(message); + instructionAdapter.invokespecial(exception, "", "(Ljava/lang/String;)V"); + instructionAdapter.athrow(); + } + + public static void generateMethodThrow(MethodVisitor mv, String exception, String message) { + mv.visitCode(); + generateThrow(mv, exception, message); + mv.visitMaxs(-1, -1); + mv.visitEnd(); + } } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/StubCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/StubCodegen.java index 258040827d4..a7f9478a6c8 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/StubCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/StubCodegen.java @@ -17,29 +17,22 @@ package org.jetbrains.jet.codegen; import org.jetbrains.asm4.MethodVisitor; -import org.jetbrains.asm4.Type; -import org.jetbrains.asm4.commons.InstructionAdapter; /** * @author Stepan Koltsov */ public class StubCodegen { + private static String STUB_EXCEPTION = "java/lang/RuntimeException"; + private static String STUB_EXCEPTION_MESSAGE = "Stubs are for compiler only, do not add them to runtime classpath"; + private StubCodegen() { } public static void generateStubThrow(MethodVisitor mv) { - InstructionAdapter iv = new InstructionAdapter(mv); - iv.anew(Type.getObjectType("java/lang/RuntimeException")); - iv.dup(); - iv.aconst("Stubs are for compiler only, do not add them to runtime classpath"); - iv.invokespecial("java/lang/RuntimeException", "", "(Ljava/lang/String;)V"); - iv.athrow(); + CodegenUtil.generateThrow(mv, STUB_EXCEPTION, STUB_EXCEPTION_MESSAGE); } public static void generateStubCode(MethodVisitor mv) { - mv.visitCode(); - generateStubThrow(mv); - mv.visitMaxs(-1, -1); - mv.visitEnd(); + CodegenUtil.generateMethodThrow(mv, STUB_EXCEPTION, STUB_EXCEPTION_MESSAGE); } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DescriptorUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DescriptorUtils.java index 9b366580117..9ea1843ee23 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DescriptorUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DescriptorUtils.java @@ -28,7 +28,9 @@ import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe; import org.jetbrains.jet.lang.resolve.name.Name; import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor; import org.jetbrains.jet.lang.types.*; +import org.jetbrains.jet.lang.types.checker.JetTypeChecker; import org.jetbrains.jet.lang.types.lang.JetStandardClasses; +import org.jetbrains.jet.lang.types.lang.JetStandardLibrary; import java.util.*; @@ -345,4 +347,22 @@ public class DescriptorUtils { } return false; } + + public static boolean isIteratorWithoutRemoveImpl(@NotNull ClassDescriptor classDescriptor) { + ClassDescriptor iteratorOfT = JetStandardLibrary.getInstance().getIterator(); + JetType iteratorOfAny = TypeUtils.substituteParameters(iteratorOfT, Collections.singletonList(JetStandardClasses.getAnyType())); + boolean isIterator = JetTypeChecker.INSTANCE.isSubtypeOf(classDescriptor.getDefaultType(), iteratorOfAny); + boolean hasRemove = hasMethod(classDescriptor, Name.identifier("remove")); + return isIterator && !hasRemove; + } + + private static boolean hasMethod(ClassDescriptor classDescriptor, Name name) { + Collection removeFunctions = classDescriptor.getDefaultType().getMemberScope().getFunctions(name); + for (FunctionDescriptor function : removeFunctions) { + if (function.getValueParameters().isEmpty() && function.getTypeParameters().isEmpty()) { + return true; + } + } + return false; + } } diff --git a/compiler/testData/codegen/functions/removeInIterator.java b/compiler/testData/codegen/functions/removeInIterator.java new file mode 100644 index 00000000000..cc0bcd85ae9 --- /dev/null +++ b/compiler/testData/codegen/functions/removeInIterator.java @@ -0,0 +1,5 @@ +public class removeInIterator { + public static void bar(java.util.Iterator it) { + it.remove(); + } +} \ No newline at end of file diff --git a/compiler/testData/codegen/functions/removeInIterator.kt b/compiler/testData/codegen/functions/removeInIterator.kt new file mode 100644 index 00000000000..7cebea1f694 --- /dev/null +++ b/compiler/testData/codegen/functions/removeInIterator.kt @@ -0,0 +1,13 @@ +fun box() : String { + try { + removeInIterator.bar(object : Iterator { + public override fun hasNext(): Boolean = false + public override fun next(): Int = 1 + }) + } + catch (e: UnsupportedOperationException) { + if (e.getMessage() == "Mutating method called on a Kotlin Iterator") + return "OK" + } + return "fail" +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/jet/codegen/FunctionGenTest.java b/compiler/tests/org/jetbrains/jet/codegen/FunctionGenTest.java index b7f90099a60..6b33773c522 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/FunctionGenTest.java +++ b/compiler/tests/org/jetbrains/jet/codegen/FunctionGenTest.java @@ -180,5 +180,9 @@ public class FunctionGenTest extends CodegenTestCase { public void testReferencesStaticInnerClassMethodTwoLevels() throws Exception { blackBoxFileWithJava("functions/referencesStaticInnerClassMethodL2.kt"); } + + public void testRemoveInIterator() throws Exception { + blackBoxFileWithJava("functions/removeInIterator.kt"); + } } }