From 35e956609a49ae51762e46b4fc51e3490ff4fe26 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Wed, 22 Oct 2014 16:48:30 +0400 Subject: [PATCH] Rewrite mutable collection stub method generation The main problem of the previous approach was that we were only generating erased method signatures, which was incorrect in case a class also had a member from another supertype with the same signature as the substituted one from the collection. Javac issues compilation errors when compiling Java code against such classes. Also all the needed method stub signatures were hardcoded in generateBuiltInMethodStubs() and the case of MutableListIterator was missing --- .../codegen/CollectionStubMethodGenerator.kt | 232 ++++++++++++++++++ .../codegen/ImplementationBodyCodegen.java | 78 +----- .../jet/lang/resolve/OverrideResolver.java | 62 ++--- .../box/builtinStubMethods/ListIterator.kt | 25 ++ .../box/builtinStubMethods/abstractMember.kt | 20 ++ .../delegationToArrayList.kt | 47 ++++ .../extendJavaCollections/abstractList.kt | 20 ++ .../extendJavaCollections/abstractMap.kt | 22 ++ .../extendJavaCollections/abstractSet.kt | 20 ++ .../arrayList.kt} | 0 .../extendJavaCollections/hashMap.kt | 20 ++ .../extendJavaCollections/hashSet.kt | 20 ++ .../implementationInTrait.kt | 29 +++ .../inheritedImplementations.kt | 21 ++ .../manyTypeParametersWithUpperBounds.kt | 32 +++ .../nonTrivialSubstitution.kt | 19 ++ .../nonTrivialUpperBound.kt | 13 + .../twoListsInSupertypes.kt | 26 ++ .../extendJavaCollections/Test.java | 20 ++ .../extendJavaCollections.kt | 19 ++ .../substitutedList/Test.java | 12 + .../substitutedList/substitutedList.kt | 22 ++ .../BlackBoxCodegenTestGenerated.java | 100 +++++++- .../BlackBoxWithJavaCodegenTestGenerated.java | 25 +- .../jet/lang/types/DescriptorSubstitutor.java | 3 +- 25 files changed, 795 insertions(+), 112 deletions(-) create mode 100644 compiler/backend/src/org/jetbrains/jet/codegen/CollectionStubMethodGenerator.kt create mode 100644 compiler/testData/codegen/box/builtinStubMethods/ListIterator.kt create mode 100644 compiler/testData/codegen/box/builtinStubMethods/abstractMember.kt create mode 100644 compiler/testData/codegen/box/builtinStubMethods/delegationToArrayList.kt create mode 100644 compiler/testData/codegen/box/builtinStubMethods/extendJavaCollections/abstractList.kt create mode 100644 compiler/testData/codegen/box/builtinStubMethods/extendJavaCollections/abstractMap.kt create mode 100644 compiler/testData/codegen/box/builtinStubMethods/extendJavaCollections/abstractSet.kt rename compiler/testData/codegen/box/builtinStubMethods/{noMethodsForClassExtendingMutableCollection.kt => extendJavaCollections/arrayList.kt} (100%) create mode 100644 compiler/testData/codegen/box/builtinStubMethods/extendJavaCollections/hashMap.kt create mode 100644 compiler/testData/codegen/box/builtinStubMethods/extendJavaCollections/hashSet.kt create mode 100644 compiler/testData/codegen/box/builtinStubMethods/implementationInTrait.kt create mode 100644 compiler/testData/codegen/box/builtinStubMethods/inheritedImplementations.kt create mode 100644 compiler/testData/codegen/box/builtinStubMethods/manyTypeParametersWithUpperBounds.kt create mode 100644 compiler/testData/codegen/box/builtinStubMethods/nonTrivialSubstitution.kt create mode 100644 compiler/testData/codegen/box/builtinStubMethods/nonTrivialUpperBound.kt create mode 100644 compiler/testData/codegen/box/builtinStubMethods/twoListsInSupertypes.kt create mode 100644 compiler/testData/codegen/boxWithJava/builtinStubMethods/extendJavaCollections/Test.java create mode 100644 compiler/testData/codegen/boxWithJava/builtinStubMethods/extendJavaCollections/extendJavaCollections.kt create mode 100644 compiler/testData/codegen/boxWithJava/builtinStubMethods/substitutedList/Test.java create mode 100644 compiler/testData/codegen/boxWithJava/builtinStubMethods/substitutedList/substitutedList.kt diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/CollectionStubMethodGenerator.kt b/compiler/backend/src/org/jetbrains/jet/codegen/CollectionStubMethodGenerator.kt new file mode 100644 index 00000000000..08b213f3877 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/jet/codegen/CollectionStubMethodGenerator.kt @@ -0,0 +1,232 @@ +/* + * Copyright 2010-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.codegen + +import org.jetbrains.jet.codegen.state.GenerationState +import org.jetbrains.jet.lang.descriptors.* +import org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor.Kind.* +import org.jetbrains.jet.lang.descriptors.impl.* +import org.jetbrains.org.objectweb.asm.Opcodes.* +import org.jetbrains.jet.lang.resolve.java.diagnostics.JvmDeclarationOrigin +import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter +import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns +import org.jetbrains.jet.lang.types.* +import org.jetbrains.jet.lang.resolve.scopes.JetScope +import org.jetbrains.jet.lang.descriptors.annotations.Annotations +import org.jetbrains.jet.lang.resolve.OverrideResolver +import org.jetbrains.jet.lang.resolve.OverridingUtil +import java.util.LinkedHashSet +import org.jetbrains.jet.lang.resolve.name.Name +import java.util.ArrayList +import org.jetbrains.jet.lang.types.checker.JetTypeChecker +import java.util.HashSet + +/** + * Generates exception-throwing stubs for methods from mutable collection classes not implemented in Kotlin classes which inherit only from + * Kotlin's read-only collections. This is required on JVM because Kotlin's read-only collections are mapped to mutable JDK collections + */ +class CollectionStubMethodGenerator( + state: GenerationState, + private val descriptor: ClassDescriptor, + private val functionCodegen: FunctionCodegen, + private val v: ClassBuilder +) { + private val typeMapper = state.getTypeMapper() + + fun generate() { + val superCollectionClasses = findRelevantSuperCollectionClasses() + if (superCollectionClasses.isEmpty()) return + + val methodStubsToGenerate = LinkedHashSet() + val syntheticStubsToGenerate = LinkedHashSet() + + for ((readOnlyClass, mutableClass) in superCollectionClasses) { + // To determine which method stubs we need to generate, we create a synthetic class (named 'child' here) which inherits from + // our class ('descriptor') and the corresponding MutableCollection class (for example; the process is the same for every + // built-in read-only/mutable class pair). We then construct and bind fake overrides in this synthetic class with the usual + // override resolution process. Resulting fake overrides with originals in MutableCollection are considered as candidates for + // method stubs or bridges to the actual implementation that happened to be present in the class + val (child, typeParameters) = createSyntheticSubclass() + // If the original class has any type parameters, we copied them and now we need to substitute types of the newly created type + // parameters as arguments for the type parameters of the original class + val parentType = newType(descriptor, typeParameters.map { TypeProjectionImpl(it.getDefaultType()) }) + + // Now we need to determine the arguments which should be substituted for the MutableCollection super class. To do that, + // we look for type arguments which were substituted in the inheritance of the original class from Collection and use them + // to construct the needed MutableCollection type. Since getAllSupertypes() may return several types which correspond to the + // Collection class descriptor, we find the most specific one (which is guaranteed to exist by front-end) + val readOnlyCollectionType = TypeUtils.getAllSupertypes(parentType).findMostSpecificTypeForClass(readOnlyClass) + val mutableCollectionType = newType(mutableClass, readOnlyCollectionType.getArguments()) + + child.addSupertype(parentType) + child.addSupertype(mutableCollectionType) + child.createTypeConstructor() + + // Bind fake overrides and for each fake override originated from the MutableCollection, save its signature to generate a stub + // or save its descriptor to generate all the needed bridges + for (method in findFakeOverridesForMethodsFromMutableCollection(child, mutableClass)) { + if (method.getModality() == Modality.ABSTRACT) { + // If the fake override is abstract and it's _declared_ as abstract in the class, skip it because the method is already + // present in the bytecode (abstract) and we don't want a duplicate signature error + if (method.findOverriddenFromDirectSuperClass(descriptor)?.getKind() == DECLARATION) continue + + // Otherwise we can safely generate the stub with the substituted signature + val signature = method.signature() + methodStubsToGenerate.add(signature) + + // If the substituted signature differs from the original one in MutableCollection, we should also generate a stub with + // the original (erased) signature. It doesn't really matter if this is a bridge method delegating to the first stub or + // a method with its own exception-throwing code, for simplicity we do the latter here. + // What _does_ matter though, is that these two methods can't be both non-synthetic at once: javac issues compilation + // errors when compiling Java against such classes because one of them doesn't seem to override the generic method + // declared in the Java Collection interface (can't override generic with erased). So we maintain an additional set of + // methods which need to be generated with the ACC_SYNTHETIC flag + val originalSignature = method.findOverriddenFromDirectSuperClass(mutableClass)!!.getOriginal().signature() + if (originalSignature != signature) { + syntheticStubsToGenerate.add(originalSignature) + } + } + else { + // If the fake override is non-abstract, its implementation is already present in the class or inherited from one of its + // super classes, but is not related to the MutableCollection hierarchy. So maybe it uses more specific return types + // and we may need to generate some bridges + functionCodegen.generateBridges(method) + } + } + } + + for (signature in methodStubsToGenerate) { + generateMethodStub(signature, synthetic = false) + } + + for (signature in syntheticStubsToGenerate) { + generateMethodStub(signature, synthetic = true) + } + } + + private data class CollectionClassPair( + val readOnlyClass: ClassDescriptor, + val mutableClass: ClassDescriptor + ) + + private fun findRelevantSuperCollectionClasses(): Collection { + fun pair(readOnlyClass: ClassDescriptor, mutableClass: ClassDescriptor) = CollectionClassPair(readOnlyClass, mutableClass) + + val collectionClasses = with(KotlinBuiltIns.getInstance()) { + listOf( + pair(getCollection(), getMutableCollection()), + pair(getSet(), getMutableSet()), + pair(getList(), getMutableList()), + pair(getMap(), getMutableMap()), + pair(getMapEntry(), getMutableMapEntry()), + pair(getIterable(), getMutableIterable()), + pair(getIterator(), getMutableIterator()), + pair(getListIterator(), getMutableListIterator()) + ) + } + + val allSuperClasses = TypeUtils.getAllSupertypes(descriptor.getDefaultType()).classes().toHashSet() + + val ourSuperCollectionClasses = collectionClasses.filter { pair -> + pair.readOnlyClass in allSuperClasses && pair.mutableClass !in allSuperClasses + } + if (ourSuperCollectionClasses.isEmpty()) return listOf() + + // Filter out built-in classes which are overridden by other built-in classes in the list, to avoid duplicating methods. + val redundantClasses = ourSuperCollectionClasses.flatMapTo(HashSet()) { pair -> + pair.readOnlyClass.getTypeConstructor().getSupertypes().classes() + } + return ourSuperCollectionClasses.filter { klass -> klass.readOnlyClass !in redundantClasses } + } + + private fun Collection.classes(): Collection = + this.map { it.getConstructor().getDeclarationDescriptor() as ClassDescriptor } + + private fun findFakeOverridesForMethodsFromMutableCollection( + klass: ClassDescriptor, + mutableCollectionClass: ClassDescriptor + ): List { + val result = ArrayList() + + OverrideResolver.generateOverridesInAClass(klass, listOf(), object : OverridingUtil.DescriptorSink { + override fun addToScope(fakeOverride: CallableMemberDescriptor) { + if (fakeOverride !is FunctionDescriptor) return + if (fakeOverride.findOverriddenFromDirectSuperClass(mutableCollectionClass) != null) { + result.add(fakeOverride) + } + } + + override fun conflict(fromSuper: CallableMemberDescriptor, fromCurrent: CallableMemberDescriptor) { + // Ignore conflicts here + // TODO: report a warning that javac will prohibit use/inheritance from such class + } + }) + + return result + } + + private fun Collection.findMostSpecificTypeForClass(klass: ClassDescriptor): JetType { + val types = this.filter { it.getConstructor().getDeclarationDescriptor() == klass } + if (types.isEmpty()) error("No supertype of $klass in $this") + if (types.size == 1) return types.first() + // Find the first type in the list such that it's a subtype of every other type in that list + return types.first { type -> + types.all { other -> JetTypeChecker.DEFAULT.isSubtypeOf(type, other) } + } + } + + private fun createSyntheticSubclass(): Pair> { + val child = MutableClassDescriptor(descriptor.getContainingDeclaration(), JetScope.EMPTY, ClassKind.CLASS, false, + Name.special(""), descriptor.getSource()) + child.setModality(Modality.FINAL) + child.setVisibility(Visibilities.PUBLIC) + val typeParameters = descriptor.getTypeConstructor().getParameters() + val newTypeParameters = ArrayList(typeParameters.size()) + DescriptorSubstitutor.substituteTypeParameters(typeParameters, TypeSubstitutor.EMPTY, child, newTypeParameters) + child.setTypeParameterDescriptors(typeParameters) + return Pair(child, newTypeParameters) + } + + private fun FunctionDescriptor.findOverriddenFromDirectSuperClass(classDescriptor: ClassDescriptor): FunctionDescriptor? { + return this.getOverriddenDescriptors().firstOrNull { it.getContainingDeclaration() == classDescriptor } + } + + private fun newType(classDescriptor: ClassDescriptor, typeArguments: List): JetType { + return JetTypeImpl(Annotations.EMPTY, classDescriptor.getTypeConstructor(), false, typeArguments, + classDescriptor.getMemberScope(typeArguments)) + } + + private fun FunctionDescriptor.signature(): String { + val method = typeMapper.mapSignature(this).getAsmMethod() + return method.getName() + method.getDescriptor() + } + + private fun generateMethodStub(signature: String, synthetic: Boolean) { + // TODO: investigate if it makes sense to generate abstract stubs in traits + var access = ACC_PUBLIC + if (descriptor.getKind() == ClassKind.TRAIT) access = access or ACC_ABSTRACT + if (synthetic) access = access or ACC_SYNTHETIC + + val paren = signature.indexOf('(') + val mv = v.newMethod(JvmDeclarationOrigin.NO_ORIGIN, access, signature.substring(0, paren), signature.substring(paren), null, null) + if (descriptor.getKind() != ClassKind.TRAIT) { + mv.visitCode() + AsmUtil.genThrow(InstructionAdapter(mv), "java/lang/UnsupportedOperationException", "Mutating immutable collection") + FunctionCodegen.endVisit(mv, "built-in stub for $signature", null) + } + } +} diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java index bde814f81df..a266b846fd6 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java @@ -427,7 +427,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { generateFunctionsForDataClasses(); - generateBuiltinMethodStubs(); + new CollectionStubMethodGenerator(state, descriptor, functionCodegen, v).generate(); generateToArray(); @@ -510,82 +510,6 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { } } - @NotNull - private Set collectSignaturesOfExistingNonAbstractMethods() { - Set existingMethodSignatures = new HashSet(); - - for (DeclarationDescriptor member : descriptor.getDefaultType().getMemberScope().getAllDescriptors()) { - if (!(member instanceof FunctionDescriptor)) continue; - FunctionDescriptor function = (FunctionDescriptor) member; - if (function.getModality() == Modality.ABSTRACT) continue; - for (FunctionDescriptor overridden : DescriptorUtils.getAllOverriddenDescriptors(function)) { - Method overriddenMethod = typeMapper.mapSignature(overridden.getOriginal()).getAsmMethod(); - existingMethodSignatures.add(overriddenMethod.getName() + overriddenMethod.getDescriptor()); - } - - Method method = typeMapper.mapSignature(function.getOriginal()).getAsmMethod(); - existingMethodSignatures.add(method.getName() + method.getDescriptor()); - } - - return existingMethodSignatures; - } - - private void generateMethodStubs(@NotNull Set signatures) { - if (signatures.isEmpty()) return; - - Set existingMethodSignatures = collectSignaturesOfExistingNonAbstractMethods(); - - for (String signature : signatures) { - if (!existingMethodSignatures.add(signature)) return; - int access = descriptor.getKind() == ClassKind.TRAIT ? - ACC_PUBLIC | ACC_ABSTRACT : - ACC_PUBLIC; - int paren = signature.indexOf('('); - MethodVisitor mv = v.newMethod(NO_ORIGIN, access, signature.substring(0, paren), signature.substring(paren), null, null); - if (descriptor.getKind() != ClassKind.TRAIT) { - mv.visitCode(); - genThrow(new InstructionAdapter(mv), "java/lang/UnsupportedOperationException", "Mutating immutable collection"); - FunctionCodegen.endVisit(mv, "built-in stub for " + signature, null); - } - } - } - - private void generateBuiltinMethodStubs() { - KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance(); - Set methodStubs = new LinkedHashSet(); - if (isSubclass(descriptor, builtIns.getCollection())) { - methodStubs.add("add(Ljava/lang/Object;)Z"); - methodStubs.add("remove(Ljava/lang/Object;)Z"); - methodStubs.add("addAll(Ljava/util/Collection;)Z"); - methodStubs.add("removeAll(Ljava/util/Collection;)Z"); - methodStubs.add("retainAll(Ljava/util/Collection;)Z"); - methodStubs.add("clear()V"); - } - - if (isSubclass(descriptor, builtIns.getList())) { - methodStubs.add("set(ILjava/lang/Object;)Ljava/lang/Object;"); - methodStubs.add("add(ILjava/lang/Object;)V"); - methodStubs.add("remove(I)Ljava/lang/Object;"); - } - - if (isSubclass(descriptor, builtIns.getMap())) { - methodStubs.add("put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); - methodStubs.add("remove(Ljava/lang/Object;)Ljava/lang/Object;"); - methodStubs.add("putAll(Ljava/util/Map;)V"); - methodStubs.add("clear()V"); - } - - if (isSubclass(descriptor, builtIns.getMapEntry())) { - methodStubs.add("setValue(Ljava/lang/Object;)Ljava/lang/Object;"); - } - - if (isSubclass(descriptor, builtIns.getIterator())) { - methodStubs.add("remove()V"); - } - - generateMethodStubs(methodStubs); - } - private void generateFunctionsForDataClasses() { if (!KotlinBuiltIns.getInstance().isData(descriptor)) return; diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/OverrideResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/OverrideResolver.java index b3007ff8ba6..ddc812c7107 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/OverrideResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/OverrideResolver.java @@ -121,12 +121,40 @@ public class OverrideResolver { } private void generateOverridesInAClass(@NotNull final MutableClassDescriptor classDescriptor) { + generateOverridesInAClass(classDescriptor, classDescriptor.getDeclaredCallableMembers(), new OverridingUtil.DescriptorSink() { + @Override + public void addToScope(@NotNull CallableMemberDescriptor fakeOverride) { + if (fakeOverride instanceof PropertyDescriptor) { + classDescriptor.getBuilder().addPropertyDescriptor((PropertyDescriptor) fakeOverride); + } + else if (fakeOverride instanceof SimpleFunctionDescriptor) { + classDescriptor.getBuilder().addFunctionDescriptor((SimpleFunctionDescriptor) fakeOverride); + } + else { + throw new IllegalStateException(fakeOverride.getClass().getName()); + } + } + + @Override + public void conflict(@NotNull CallableMemberDescriptor fromSuper, @NotNull CallableMemberDescriptor fromCurrent) { + JetDeclaration declaration = (JetDeclaration) DescriptorToSourceUtils.descriptorToDeclaration(fromCurrent); + //noinspection ConstantConditions + trace.report(CONFLICTING_OVERLOADS.on(declaration, fromCurrent, fromCurrent.getContainingDeclaration().getName().asString())); + } + }); + + resolveUnknownVisibilities(classDescriptor.getAllCallableMembers(), trace); + } + + public static void generateOverridesInAClass( + @NotNull ClassDescriptor classDescriptor, + @NotNull Collection membersFromCurrent, + @NotNull OverridingUtil.DescriptorSink sink + ) { List membersFromSupertypes = getCallableMembersFromSupertypes(classDescriptor); - + MultiMap membersFromCurrentByName = groupDescriptorsByName(membersFromCurrent); MultiMap membersFromSupertypesByName = groupDescriptorsByName(membersFromSupertypes); - MultiMap membersFromCurrentByName = groupDescriptorsByName(classDescriptor.getDeclaredCallableMembers()); - Set memberNames = new LinkedHashSet(); memberNames.addAll(membersFromSupertypesByName.keySet()); memberNames.addAll(membersFromCurrentByName.keySet()); @@ -135,34 +163,8 @@ public class OverrideResolver { Collection fromSupertypes = membersFromSupertypesByName.get(memberName); Collection fromCurrent = membersFromCurrentByName.get(memberName); - OverridingUtil.generateOverridesInFunctionGroup( - memberName, - fromSupertypes, - fromCurrent, - classDescriptor, - new OverridingUtil.DescriptorSink() { - @Override - public void addToScope(@NotNull CallableMemberDescriptor fakeOverride) { - if (fakeOverride instanceof PropertyDescriptor) { - classDescriptor.getBuilder().addPropertyDescriptor((PropertyDescriptor) fakeOverride); - } - else if (fakeOverride instanceof SimpleFunctionDescriptor) { - classDescriptor.getBuilder().addFunctionDescriptor((SimpleFunctionDescriptor) fakeOverride); - } - else { - throw new IllegalStateException(fakeOverride.getClass().getName()); - } - } - - @Override - public void conflict(@NotNull CallableMemberDescriptor fromSuper, @NotNull CallableMemberDescriptor fromCurrent) { - JetDeclaration declaration = (JetDeclaration) DescriptorToSourceUtils.descriptorToDeclaration(fromCurrent); - //noinspection ConstantConditions - trace.report(CONFLICTING_OVERLOADS.on(declaration, fromCurrent, fromCurrent.getContainingDeclaration().getName().asString())); - } - }); + OverridingUtil.generateOverridesInFunctionGroup(memberName, fromSupertypes, fromCurrent, classDescriptor, sink); } - resolveUnknownVisibilities(classDescriptor.getAllCallableMembers(), trace); } public static void resolveUnknownVisibilities( diff --git a/compiler/testData/codegen/box/builtinStubMethods/ListIterator.kt b/compiler/testData/codegen/box/builtinStubMethods/ListIterator.kt new file mode 100644 index 00000000000..c20ffc26ff7 --- /dev/null +++ b/compiler/testData/codegen/box/builtinStubMethods/ListIterator.kt @@ -0,0 +1,25 @@ +class MyListIterator : ListIterator { + override fun next(): T = null!! + override fun hasNext(): Boolean = null!! + override fun hasPrevious(): Boolean = null!! + override fun previous(): T = null!! + override fun nextIndex(): Int = null!! + override fun previousIndex(): Int = null!! +} + +fun expectUoe(block: () -> Any) { + try { + block() + throw AssertionError() + } catch (e: UnsupportedOperationException) { + } +} + +fun box(): String { + val list = MyListIterator() as MutableListIterator + + expectUoe { list.set("") } + expectUoe { list.add("") } + + return "OK" +} diff --git a/compiler/testData/codegen/box/builtinStubMethods/abstractMember.kt b/compiler/testData/codegen/box/builtinStubMethods/abstractMember.kt new file mode 100644 index 00000000000..40a42011eb3 --- /dev/null +++ b/compiler/testData/codegen/box/builtinStubMethods/abstractMember.kt @@ -0,0 +1,20 @@ +abstract class A : Iterator { + abstract fun remove(): Unit +} + +class B(var result: String) : A() { + override fun next() = "" + override fun hasNext() = false + override fun remove() { + result = "OK" + } +} + +fun box(): String { + val a = B("Fail") as MutableIterator + a.next() + a.hasNext() + a.remove() + + return (a as B).result +} diff --git a/compiler/testData/codegen/box/builtinStubMethods/delegationToArrayList.kt b/compiler/testData/codegen/box/builtinStubMethods/delegationToArrayList.kt new file mode 100644 index 00000000000..b235c682914 --- /dev/null +++ b/compiler/testData/codegen/box/builtinStubMethods/delegationToArrayList.kt @@ -0,0 +1,47 @@ +import java.util.ArrayList + +class A : List by ArrayList() + +class B : List by A() + +fun expectUoe(block: () -> Any) { + try { + block() + throw AssertionError() + } catch (e: UnsupportedOperationException) { + } +} + +fun box(): String { + val a = A() as MutableList + expectUoe { a.add("") } + expectUoe { a.remove("") } + expectUoe { a.addAll(a) } + expectUoe { a.addAll(0, a) } + expectUoe { a.removeAll(a) } + expectUoe { a.retainAll(a) } + expectUoe { a.clear() } + expectUoe { a.add(0, "") } + expectUoe { a.set(0, "") } + expectUoe { a.remove(0) } + a.listIterator() + a.listIterator(0) + a.subList(0, 0) + + val b = B() as MutableList + expectUoe { b.add("") } + expectUoe { b.remove("") } + expectUoe { b.addAll(b) } + expectUoe { b.addAll(0, b) } + expectUoe { b.removeAll(b) } + expectUoe { b.retainAll(b) } + expectUoe { b.clear() } + expectUoe { b.add(0, "") } + expectUoe { b.set(0, "") } + expectUoe { b.remove(0) } + b.listIterator() + b.listIterator(0) + b.subList(0, 0) + + return "OK" +} diff --git a/compiler/testData/codegen/box/builtinStubMethods/extendJavaCollections/abstractList.kt b/compiler/testData/codegen/box/builtinStubMethods/extendJavaCollections/abstractList.kt new file mode 100644 index 00000000000..e339325fe33 --- /dev/null +++ b/compiler/testData/codegen/box/builtinStubMethods/extendJavaCollections/abstractList.kt @@ -0,0 +1,20 @@ +import java.util.AbstractList + +class A : AbstractList() { + override fun get(index: Int): String = "" + override fun size(): Int = 0 +} + +fun box(): String { + val a = A() + val b = A() + + a.addAll(b) + a.addAll(0, b) + a.removeAll(b) + a.retainAll(b) + a.clear() + a.remove("") + + return "OK" +} diff --git a/compiler/testData/codegen/box/builtinStubMethods/extendJavaCollections/abstractMap.kt b/compiler/testData/codegen/box/builtinStubMethods/extendJavaCollections/abstractMap.kt new file mode 100644 index 00000000000..59f2d50e8ad --- /dev/null +++ b/compiler/testData/codegen/box/builtinStubMethods/extendJavaCollections/abstractMap.kt @@ -0,0 +1,22 @@ +import java.util.AbstractMap +import java.util.Collections + +class A : AbstractMap() { + override fun entrySet(): Set> = Collections.emptySet() +} + +fun box(): String { + val a = A() + val b = A() + + a.remove(0) + + a.putAll(b) + a.clear() + + a.keySet() + a.values() + a.entrySet() + + return "OK" +} diff --git a/compiler/testData/codegen/box/builtinStubMethods/extendJavaCollections/abstractSet.kt b/compiler/testData/codegen/box/builtinStubMethods/extendJavaCollections/abstractSet.kt new file mode 100644 index 00000000000..78cd670b04a --- /dev/null +++ b/compiler/testData/codegen/box/builtinStubMethods/extendJavaCollections/abstractSet.kt @@ -0,0 +1,20 @@ +import java.util.HashSet + +class A : HashSet() + +fun box(): String { + val a = A() + val b = A() + + a.iterator() + + a.add(0L) + a.remove(0L) + + a.addAll(b) + a.removeAll(b) + a.retainAll(b) + a.clear() + + return "OK" +} diff --git a/compiler/testData/codegen/box/builtinStubMethods/noMethodsForClassExtendingMutableCollection.kt b/compiler/testData/codegen/box/builtinStubMethods/extendJavaCollections/arrayList.kt similarity index 100% rename from compiler/testData/codegen/box/builtinStubMethods/noMethodsForClassExtendingMutableCollection.kt rename to compiler/testData/codegen/box/builtinStubMethods/extendJavaCollections/arrayList.kt diff --git a/compiler/testData/codegen/box/builtinStubMethods/extendJavaCollections/hashMap.kt b/compiler/testData/codegen/box/builtinStubMethods/extendJavaCollections/hashMap.kt new file mode 100644 index 00000000000..246391e18d9 --- /dev/null +++ b/compiler/testData/codegen/box/builtinStubMethods/extendJavaCollections/hashMap.kt @@ -0,0 +1,20 @@ +import java.util.HashMap + +class A : HashMap() + +fun box(): String { + val a = A() + val b = A() + + a.put("", 0.0) + a.remove("") + + a.putAll(b) + a.clear() + + a.keySet() + a.values() + a.entrySet() + + return "OK" +} diff --git a/compiler/testData/codegen/box/builtinStubMethods/extendJavaCollections/hashSet.kt b/compiler/testData/codegen/box/builtinStubMethods/extendJavaCollections/hashSet.kt new file mode 100644 index 00000000000..78cd670b04a --- /dev/null +++ b/compiler/testData/codegen/box/builtinStubMethods/extendJavaCollections/hashSet.kt @@ -0,0 +1,20 @@ +import java.util.HashSet + +class A : HashSet() + +fun box(): String { + val a = A() + val b = A() + + a.iterator() + + a.add(0L) + a.remove(0L) + + a.addAll(b) + a.removeAll(b) + a.retainAll(b) + a.clear() + + return "OK" +} diff --git a/compiler/testData/codegen/box/builtinStubMethods/implementationInTrait.kt b/compiler/testData/codegen/box/builtinStubMethods/implementationInTrait.kt new file mode 100644 index 00000000000..d08887a8854 --- /dev/null +++ b/compiler/testData/codegen/box/builtinStubMethods/implementationInTrait.kt @@ -0,0 +1,29 @@ +trait Addable { + fun add(s: String): Boolean = true +} + +class C : Addable, List { + override fun size(): Int = null!! + override fun isEmpty(): Boolean = null!! + override fun contains(o: Any?): Boolean = null!! + override fun iterator(): Iterator = null!! + override fun containsAll(c: Collection): Boolean = null!! + override fun get(index: Int): String = null!! + override fun indexOf(o: Any?): Int = null!! + override fun lastIndexOf(o: Any?): Int = null!! + override fun listIterator(): ListIterator = null!! + override fun listIterator(index: Int): ListIterator = null!! + override fun subList(fromIndex: Int, toIndex: Int): List = null!! +} + +fun box(): String { + try { + val a = C() + if (!a.add("")) return "Fail 1" + if (!(a as Addable).add("")) return "Fail 2" + if (!(a as MutableList).add("")) return "Fail 3" + return "OK" + } catch (e: UnsupportedOperationException) { + return "Fail: no stub method should be generated" + } +} diff --git a/compiler/testData/codegen/box/builtinStubMethods/inheritedImplementations.kt b/compiler/testData/codegen/box/builtinStubMethods/inheritedImplementations.kt new file mode 100644 index 00000000000..ff5f0de6418 --- /dev/null +++ b/compiler/testData/codegen/box/builtinStubMethods/inheritedImplementations.kt @@ -0,0 +1,21 @@ +open class SetStringImpl { + fun add(s: String): Boolean = false + fun remove(o: Any?): Boolean = false + fun clear(): Unit {} +} + +class S : Set, SetStringImpl() { + override fun size(): Int = 0 + override fun isEmpty(): Boolean = true + override fun contains(o: Any?): Boolean = false + override fun iterator(): Iterator = null!! + override fun containsAll(c: Collection) = false +} + +fun box(): String { + val s = S() as MutableSet + s.add("") + s.remove("") + s.clear() + return "OK" +} diff --git a/compiler/testData/codegen/box/builtinStubMethods/manyTypeParametersWithUpperBounds.kt b/compiler/testData/codegen/box/builtinStubMethods/manyTypeParametersWithUpperBounds.kt new file mode 100644 index 00000000000..31bc1608dbe --- /dev/null +++ b/compiler/testData/codegen/box/builtinStubMethods/manyTypeParametersWithUpperBounds.kt @@ -0,0 +1,32 @@ +import java.util.Collections + +class A : Set { + override fun size(): Int = 0 + override fun isEmpty(): Boolean = true + override fun contains(o: Any?): Boolean = false + override fun iterator(): Iterator = Collections.emptySet().iterator() + override fun containsAll(c: Collection): Boolean = c.isEmpty() +} + +fun expectUoe(block: () -> Any) { + try { + block() + throw AssertionError() + } catch (e: UnsupportedOperationException) { + } +} + +fun box(): String { + val a = A() as MutableSet + + a.iterator() + + expectUoe { a.add(42) } + expectUoe { a.remove(42) } + expectUoe { a.addAll(a) } + expectUoe { a.removeAll(a) } + expectUoe { a.retainAll(a) } + expectUoe { a.clear() } + + return "OK" +} diff --git a/compiler/testData/codegen/box/builtinStubMethods/nonTrivialSubstitution.kt b/compiler/testData/codegen/box/builtinStubMethods/nonTrivialSubstitution.kt new file mode 100644 index 00000000000..382a5d9bf5c --- /dev/null +++ b/compiler/testData/codegen/box/builtinStubMethods/nonTrivialSubstitution.kt @@ -0,0 +1,19 @@ +import java.util.ArrayList + +class MyCollection : Collection>> { + override fun iterator() = null!! + override fun size(): Int = null!! + override fun isEmpty(): Boolean = null!! + override fun contains(o: Any?): Boolean = null!! + override fun containsAll(c: Collection): Boolean = null!! +} + +fun box(): String { + val c = MyCollection() as MutableCollection>> + try { + c.add(ArrayList()) + return "Fail" + } catch (e: UnsupportedOperationException) { + return "OK" + } +} diff --git a/compiler/testData/codegen/box/builtinStubMethods/nonTrivialUpperBound.kt b/compiler/testData/codegen/box/builtinStubMethods/nonTrivialUpperBound.kt new file mode 100644 index 00000000000..470eaaaf9e6 --- /dev/null +++ b/compiler/testData/codegen/box/builtinStubMethods/nonTrivialUpperBound.kt @@ -0,0 +1,13 @@ +class MyIterator : Iterator { + override fun next() = null!! + override fun hasNext() = null!! +} + +fun box(): String { + try { + (MyIterator() as MutableIterator).remove() + return "Fail" + } catch (e: UnsupportedOperationException) { + return "OK" + } +} diff --git a/compiler/testData/codegen/box/builtinStubMethods/twoListsInSupertypes.kt b/compiler/testData/codegen/box/builtinStubMethods/twoListsInSupertypes.kt new file mode 100644 index 00000000000..d989ac5ba15 --- /dev/null +++ b/compiler/testData/codegen/box/builtinStubMethods/twoListsInSupertypes.kt @@ -0,0 +1,26 @@ +trait ListAny : List +trait ListString : List + +trait AddStringImpl { + fun add(s: String) {} +} + +class A : ListAny, ListString, AddStringImpl { + override fun size(): Int = 0 + override fun isEmpty(): Boolean = true + override fun contains(o: Any?): Boolean = false + override fun iterator(): Iterator = null!! + override fun containsAll(c: Collection): Boolean = false + override fun get(index: Int): String = null!! + override fun indexOf(o: Any?): Int = -1 + override fun lastIndexOf(o: Any?): Int = -1 + override fun listIterator(): ListIterator = null!! + override fun listIterator(index: Int): ListIterator = null!! + override fun subList(fromIndex: Int, toIndex: Int): List = null!! +} + +fun box(): String { + val a = A() as MutableList + a.add("Fail") + return "OK" +} diff --git a/compiler/testData/codegen/boxWithJava/builtinStubMethods/extendJavaCollections/Test.java b/compiler/testData/codegen/boxWithJava/builtinStubMethods/extendJavaCollections/Test.java new file mode 100644 index 00000000000..486b274dcab --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/builtinStubMethods/extendJavaCollections/Test.java @@ -0,0 +1,20 @@ +import java.lang.*; +import java.util.*; + +public class Test { + public static class IterableImpl implements Iterable { + public Iterator iterator() { return new IteratorImpl(); } + } + + public static class IteratorImpl implements Iterator { + public boolean hasNext() { return false; } + public String next() { return null; } + public void remove() { } + } + + public static class MapEntryImpl implements Map.Entry { + public String getKey() { return null; } + public String getValue() { return null; } + public String setValue(String s) { return null; } + } +} diff --git a/compiler/testData/codegen/boxWithJava/builtinStubMethods/extendJavaCollections/extendJavaCollections.kt b/compiler/testData/codegen/boxWithJava/builtinStubMethods/extendJavaCollections/extendJavaCollections.kt new file mode 100644 index 00000000000..b63591c3be1 --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/builtinStubMethods/extendJavaCollections/extendJavaCollections.kt @@ -0,0 +1,19 @@ +class MyIterable : Test.IterableImpl() +class MyIterator : Test.IteratorImpl() +class MyMapEntry : Test.MapEntryImpl() + +fun box(): String { + MyIterable().iterator() + + val a = MyIterator() + a.hasNext() + a.next() + a.remove() + + val b = MyMapEntry() + b.getKey() + b.getValue() + b.setValue(null) + + return "OK" +} diff --git a/compiler/testData/codegen/boxWithJava/builtinStubMethods/substitutedList/Test.java b/compiler/testData/codegen/boxWithJava/builtinStubMethods/substitutedList/Test.java new file mode 100644 index 00000000000..434b2d7a2a5 --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/builtinStubMethods/substitutedList/Test.java @@ -0,0 +1,12 @@ +class Test { + interface A { + boolean add(String s); + } + + static class D extends C {} + + void test() { + A a = new D(); + a.add("lol"); + } +} diff --git a/compiler/testData/codegen/boxWithJava/builtinStubMethods/substitutedList/substitutedList.kt b/compiler/testData/codegen/boxWithJava/builtinStubMethods/substitutedList/substitutedList.kt new file mode 100644 index 00000000000..e9b64a54ca1 --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/builtinStubMethods/substitutedList/substitutedList.kt @@ -0,0 +1,22 @@ +abstract class C : Test.A, List { + override fun size(): Int = null!! + override fun isEmpty(): Boolean = null!! + override fun contains(o: Any?): Boolean = null!! + override fun iterator(): Iterator = null!! + override fun containsAll(c: Collection): Boolean = null!! + override fun get(index: Int): String = null!! + override fun indexOf(o: Any?): Int = null!! + override fun lastIndexOf(o: Any?): Int = null!! + override fun listIterator(): ListIterator = null!! + override fun listIterator(index: Int): ListIterator = null!! + override fun subList(fromIndex: Int, toIndex: Int): List = null!! +} + +fun box(): String { + try { + Test().test() + return "Fail" + } catch (e: UnsupportedOperationException) { + return "OK" + } +} diff --git a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java index c461e7885f4..f5ab39109a4 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java @@ -670,8 +670,15 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { @TestMetadata("compiler/testData/codegen/box/builtinStubMethods") @TestDataPath("$PROJECT_ROOT") + @InnerTestClasses({BuiltinStubMethods.ExtendJavaCollections.class}) @RunWith(JUnit3RunnerWithInners.class) public static class BuiltinStubMethods extends AbstractBlackBoxCodegenTest { + @TestMetadata("abstractMember.kt") + public void testAbstractMember() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/builtinStubMethods/abstractMember.kt"); + doTest(fileName); + } + public void testAllFilesPresentInBuiltinStubMethods() throws Exception { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/builtinStubMethods"), Pattern.compile("^(.+)\\.kt$"), true); } @@ -682,12 +689,30 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("delegationToArrayList.kt") + public void testDelegationToArrayList() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/builtinStubMethods/delegationToArrayList.kt"); + doTest(fileName); + } + @TestMetadata("dontGenerateBodyInTrait.kt") public void testDontGenerateBodyInTrait() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/builtinStubMethods/dontGenerateBodyInTrait.kt"); doTest(fileName); } + @TestMetadata("implementationInTrait.kt") + public void testImplementationInTrait() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/builtinStubMethods/implementationInTrait.kt"); + doTest(fileName); + } + + @TestMetadata("inheritedImplementations.kt") + public void testInheritedImplementations() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/builtinStubMethods/inheritedImplementations.kt"); + doTest(fileName); + } + @TestMetadata("Iterator.kt") public void testIterator() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/builtinStubMethods/Iterator.kt"); @@ -706,6 +731,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("ListIterator.kt") + public void testListIterator() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/builtinStubMethods/ListIterator.kt"); + doTest(fileName); + } + @TestMetadata("ListWithAllImplementations.kt") public void testListWithAllImplementations() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/builtinStubMethods/ListWithAllImplementations.kt"); @@ -718,6 +749,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("manyTypeParametersWithUpperBounds.kt") + public void testManyTypeParametersWithUpperBounds() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/builtinStubMethods/manyTypeParametersWithUpperBounds.kt"); + doTest(fileName); + } + @TestMetadata("Map.kt") public void testMap() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/builtinStubMethods/Map.kt"); @@ -742,9 +779,15 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } - @TestMetadata("noMethodsForClassExtendingMutableCollection.kt") - public void testNoMethodsForClassExtendingMutableCollection() throws Exception { - String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/builtinStubMethods/noMethodsForClassExtendingMutableCollection.kt"); + @TestMetadata("nonTrivialSubstitution.kt") + public void testNonTrivialSubstitution() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/builtinStubMethods/nonTrivialSubstitution.kt"); + doTest(fileName); + } + + @TestMetadata("nonTrivialUpperBound.kt") + public void testNonTrivialUpperBound() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/builtinStubMethods/nonTrivialUpperBound.kt"); doTest(fileName); } @@ -753,6 +796,57 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/builtinStubMethods/SubstitutedList.kt"); doTest(fileName); } + + @TestMetadata("twoListsInSupertypes.kt") + public void testTwoListsInSupertypes() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/builtinStubMethods/twoListsInSupertypes.kt"); + doTest(fileName); + } + + @TestMetadata("compiler/testData/codegen/box/builtinStubMethods/extendJavaCollections") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ExtendJavaCollections extends AbstractBlackBoxCodegenTest { + @TestMetadata("abstractList.kt") + public void testAbstractList() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/builtinStubMethods/extendJavaCollections/abstractList.kt"); + doTest(fileName); + } + + @TestMetadata("abstractMap.kt") + public void testAbstractMap() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/builtinStubMethods/extendJavaCollections/abstractMap.kt"); + doTest(fileName); + } + + @TestMetadata("abstractSet.kt") + public void testAbstractSet() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/builtinStubMethods/extendJavaCollections/abstractSet.kt"); + doTest(fileName); + } + + public void testAllFilesPresentInExtendJavaCollections() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/builtinStubMethods/extendJavaCollections"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("arrayList.kt") + public void testArrayList() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/builtinStubMethods/extendJavaCollections/arrayList.kt"); + doTest(fileName); + } + + @TestMetadata("hashMap.kt") + public void testHashMap() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/builtinStubMethods/extendJavaCollections/hashMap.kt"); + doTest(fileName); + } + + @TestMetadata("hashSet.kt") + public void testHashSet() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/builtinStubMethods/extendJavaCollections/hashSet.kt"); + doTest(fileName); + } + } } @TestMetadata("compiler/testData/codegen/box/casts") diff --git a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java index 77ebf816fec..d6a36d2fcd8 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java @@ -30,7 +30,7 @@ import java.util.regex.Pattern; @SuppressWarnings("all") @TestMetadata("compiler/testData/codegen/boxWithJava") @TestDataPath("$PROJECT_ROOT") -@InnerTestClasses({BlackBoxWithJavaCodegenTestGenerated.PlatformStatic.class, BlackBoxWithJavaCodegenTestGenerated.Properties.class}) +@InnerTestClasses({BlackBoxWithJavaCodegenTestGenerated.BuiltinStubMethods.class, BlackBoxWithJavaCodegenTestGenerated.PlatformStatic.class, BlackBoxWithJavaCodegenTestGenerated.Properties.class}) @RunWith(JUnit3RunnerWithInners.class) public class BlackBoxWithJavaCodegenTestGenerated extends AbstractBlackBoxCodegenTest { public void testAllFilesPresentInBoxWithJava() throws Exception { @@ -49,6 +49,29 @@ public class BlackBoxWithJavaCodegenTestGenerated extends AbstractBlackBoxCodege doTestWithJava(fileName); } + @TestMetadata("compiler/testData/codegen/boxWithJava/builtinStubMethods") + @TestDataPath("$PROJECT_ROOT") + @InnerTestClasses({}) + @RunWith(JUnit3RunnerWithInners.class) + public static class BuiltinStubMethods extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInBuiltinStubMethods() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithJava/builtinStubMethods"), Pattern.compile("^([^\\.]+)$"), true); + } + + @TestMetadata("extendJavaCollections") + public void testExtendJavaCollections() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/builtinStubMethods/extendJavaCollections/"); + doTestWithJava(fileName); + } + + @TestMetadata("substitutedList") + public void testSubstitutedList() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/builtinStubMethods/substitutedList/"); + doTestWithJava(fileName); + } + + } + @TestMetadata("compiler/testData/codegen/boxWithJava/platformStatic") @TestDataPath("$PROJECT_ROOT") @InnerTestClasses({}) diff --git a/core/descriptors/src/org/jetbrains/jet/lang/types/DescriptorSubstitutor.java b/core/descriptors/src/org/jetbrains/jet/lang/types/DescriptorSubstitutor.java index 45fccc7d0f3..c6163371c21 100644 --- a/core/descriptors/src/org/jetbrains/jet/lang/types/DescriptorSubstitutor.java +++ b/core/descriptors/src/org/jetbrains/jet/lang/types/DescriptorSubstitutor.java @@ -16,6 +16,7 @@ package org.jetbrains.jet.lang.types; +import org.jetbrains.annotations.Mutable; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; import org.jetbrains.jet.lang.descriptors.SourceElement; @@ -35,7 +36,7 @@ public class DescriptorSubstitutor { @NotNull List typeParameters, @NotNull final TypeSubstitutor originalSubstitutor, @NotNull DeclarationDescriptor newContainingDeclaration, - @NotNull List result + @NotNull @Mutable List result ) { final Map mutableSubstitution = new HashMap(); TypeSubstitutor substitutor = TypeSubstitutor.create(new TypeSubstitution() {