diff --git a/compiler/backend-common/backend-common.iml b/compiler/backend-common/backend-common.iml
index 4184658ad62..d0975d60831 100644
--- a/compiler/backend-common/backend-common.iml
+++ b/compiler/backend-common/backend-common.iml
@@ -7,8 +7,8 @@
-
-
+
+
diff --git a/compiler/backend-common/src/bridges/bridges.kt b/compiler/backend-common/src/bridges/bridges.kt
new file mode 100644
index 00000000000..7ebc16845de
--- /dev/null
+++ b/compiler/backend-common/src/bridges/bridges.kt
@@ -0,0 +1,117 @@
+/*
+ * 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.bridges
+
+import org.jetbrains.jet.utils.DFS
+import java.util.HashSet
+
+public trait FunctionHandle {
+ val isDeclaration: Boolean
+ val isAbstract: Boolean
+
+ fun getOverridden(): Iterable
+}
+
+public data class Bridge(
+ public val from: Signature,
+ public val to: Signature
+) {
+ override fun toString() = "$from -> $to"
+}
+
+
+public fun generateBridges(
+ function: Function,
+ signature: (Function) -> Signature
+): Set> {
+ // If it's an abstract function, no bridges are needed: when an implementation will appear in some concrete subclass, all necessary
+ // bridges will be generated there
+ if (function.isAbstract) return setOf()
+
+ val fake = !function.isDeclaration
+
+ // If it's a concrete fake override and all of its super-functions are concrete, then every possible bridge is already generated
+ // into some of the super-classes and will be inherited in this class
+ if (fake && function.getOverridden().none { it.isAbstract }) return setOf()
+
+ val implementation = findConcreteSuperDeclaration(function)
+
+ val bridgesToGenerate = findAllReachableDeclarations(function).mapTo(HashSet(), signature)
+
+ if (fake) {
+ // If it's a concrete fake override, some of the bridges may be inherited from the super-classes. Specifically, bridges for all
+ // declarations that are reachable from all concrete immediate super-functions of the given function. Note that all such bridges are
+ // guaranteed to delegate to the same implementation as bridges for the given function, that's why it's safe to inherit them
+ [suppress("UNCHECKED_CAST")]
+ for (overridden in function.getOverridden() as Iterable) {
+ if (!overridden.isAbstract) {
+ bridgesToGenerate.removeAll(findAllReachableDeclarations(overridden).map(signature))
+ }
+ }
+ }
+
+ val method = signature(implementation)
+ bridgesToGenerate.remove(method)
+ return bridgesToGenerate.map { Bridge(it, method) }.toSet()
+}
+
+private fun findAllReachableDeclarations(function: Function): MutableSet {
+ val collector = object : DFS.NodeHandlerWithListResult() {
+ override fun afterChildren(current: Function?) {
+ if (current != null && current.isDeclaration) {
+ result.add(current)
+ }
+ }
+ }
+ [suppress("UNCHECKED_CAST")]
+ DFS.dfs(listOf(function), { it!!.getOverridden() as Iterable }, collector)
+ return HashSet(collector.result())
+}
+
+/**
+ * Given a concrete function, finds an implementation (a concrete declaration) of this function in the supertypes.
+ * The implementation is guaranteed to exist because if it wouldn't, the given function would've been abstract
+ */
+private fun findConcreteSuperDeclaration(function: Function): Function {
+ require(!function.isAbstract, "Only concrete functions have implementations: $function")
+
+ if (function.isDeclaration) return function
+
+ // To find an implementation of a concrete fake override, we should find among all its super-functions such function that:
+ // 1) it's a concrete declaration
+ // 2) it's not reachable from any other declaration reachable from the given function
+ // The compiler guarantees that there will be exactly one such function.
+ // The following algorithm is used: first, we find all declarations reachable from the given function. Then for each such declaration
+ // we remove from that result all declarations reachable from it. The result is now guaranteed to have exactly one concrete declaration
+ // (and possibly some abstract declarations, which don't matter)
+
+ val result = findAllReachableDeclarations(function)
+ val toRemove = HashSet()
+ for (declaration in result) {
+ val reachable = findAllReachableDeclarations(declaration)
+ reachable.remove(declaration)
+ toRemove.addAll(reachable)
+ }
+ result.removeAll(toRemove)
+
+ val concreteRelevantDeclarations = result.filter { !it.isAbstract }
+ if (concreteRelevantDeclarations.size != 1) {
+ error("Concrete fake override $function should have exactly one concrete super-declaration: $concreteRelevantDeclarations")
+ }
+
+ return concreteRelevantDeclarations[0]
+}
diff --git a/compiler/backend-common/src/bridges/impl.kt b/compiler/backend-common/src/bridges/impl.kt
new file mode 100644
index 00000000000..d3ee805f462
--- /dev/null
+++ b/compiler/backend-common/src/bridges/impl.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.bridges
+
+import org.jetbrains.jet.lang.descriptors.FunctionDescriptor
+import org.jetbrains.jet.lang.descriptors.Modality
+
+public fun generateBridgesForFunctionDescriptor(
+ descriptor: FunctionDescriptor,
+ signature: (FunctionDescriptor) -> Signature
+): Set> {
+ return generateBridges(DescriptorBasedFunctionHandle(descriptor), { signature(it.descriptor) })
+}
+
+private data class DescriptorBasedFunctionHandle(val descriptor: FunctionDescriptor) : FunctionHandle {
+ override val isDeclaration: Boolean = descriptor.getKind().isReal()
+ override val isAbstract: Boolean = descriptor.getModality() == Modality.ABSTRACT
+
+ override fun getOverridden(): Iterable {
+ return descriptor.getOverriddenDescriptors().map { DescriptorBasedFunctionHandle(it.getOriginal()) }
+ }
+}
diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ClassBodyCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ClassBodyCodegen.java
index 9fa2ef06d9a..648b52e0aaf 100644
--- a/compiler/backend/src/org/jetbrains/jet/codegen/ClassBodyCodegen.java
+++ b/compiler/backend/src/org/jetbrains/jet/codegen/ClassBodyCodegen.java
@@ -18,8 +18,6 @@ package org.jetbrains.jet.codegen;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import org.jetbrains.org.objectweb.asm.MethodVisitor;
-import org.jetbrains.org.objectweb.asm.Type;
import org.jetbrains.jet.codegen.context.ClassContext;
import org.jetbrains.jet.codegen.state.GenerationState;
import org.jetbrains.jet.lang.descriptors.*;
@@ -29,13 +27,15 @@ import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.DescriptorUtils;
import org.jetbrains.jet.lang.resolve.name.Name;
+import org.jetbrains.org.objectweb.asm.MethodVisitor;
+import org.jetbrains.org.objectweb.asm.Type;
import java.util.Collections;
import java.util.List;
+import static org.jetbrains.jet.codegen.binding.CodegenBinding.enumEntryNeedSubclass;
import static org.jetbrains.org.objectweb.asm.Opcodes.ACC_STATIC;
import static org.jetbrains.org.objectweb.asm.Opcodes.RETURN;
-import static org.jetbrains.jet.codegen.binding.CodegenBinding.enumEntryNeedSubclass;
public abstract class ClassBodyCodegen extends MemberCodegen {
protected final JetClassOrObject myClass;
@@ -104,7 +104,7 @@ public abstract class ClassBodyCodegen extends MemberCodegen {
if (memberDescriptor instanceof FunctionDescriptor) {
FunctionDescriptor member = (FunctionDescriptor) memberDescriptor;
if (member.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
- functionCodegen.generateBridgesForFakeOverride(member);
+ functionCodegen.generateBridges(member);
}
}
}
diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java
index 68b13498a04..e0f1637f0a6 100644
--- a/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java
+++ b/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java
@@ -23,10 +23,11 @@ import com.intellij.util.ArrayUtil;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import kotlin.Function1;
-import kotlin.KotlinPackage;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.codegen.binding.CodegenBinding;
+import org.jetbrains.jet.codegen.bridges.Bridge;
+import org.jetbrains.jet.codegen.bridges.BridgesPackage;
import org.jetbrains.jet.codegen.context.CodegenContext;
import org.jetbrains.jet.codegen.context.MethodContext;
import org.jetbrains.jet.codegen.context.PackageFacadeContext;
@@ -45,7 +46,6 @@ import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
import org.jetbrains.jet.lang.resolve.constants.JavaClassValue;
import org.jetbrains.jet.lang.resolve.java.JvmAbi;
import org.jetbrains.jet.lang.resolve.name.FqName;
-import org.jetbrains.jet.utils.DFS;
import org.jetbrains.org.objectweb.asm.AnnotationVisitor;
import org.jetbrains.org.objectweb.asm.Label;
import org.jetbrains.org.objectweb.asm.MethodVisitor;
@@ -56,7 +56,10 @@ import org.jetbrains.org.objectweb.asm.util.TraceMethodVisitor;
import java.io.PrintWriter;
import java.io.StringWriter;
-import java.util.*;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
import static org.jetbrains.jet.codegen.AsmUtil.*;
import static org.jetbrains.jet.codegen.JvmSerializationBindings.*;
@@ -417,108 +420,31 @@ public class FunctionCodegen extends ParentCodegenAwareImpl {
return bytecode;
}
- private void generateBridges(@NotNull FunctionDescriptor descriptor) {
+ public void generateBridges(@NotNull FunctionDescriptor descriptor) {
if (descriptor instanceof ConstructorDescriptor) return;
if (owner.getContextKind() == OwnerKind.TRAIT_IMPL) return;
if (isTrait(descriptor.getContainingDeclaration())) return;
- Set bridgesToGenerate = findAllReachableDeclarations(descriptor);
-
- Method method = typeMapper.mapSignature(descriptor).getAsmMethod();
- bridgesToGenerate.remove(method);
+ Set> bridgesToGenerate = BridgesPackage.generateBridgesForFunctionDescriptor(
+ descriptor,
+ new Function1() {
+ @Override
+ public Method invoke(FunctionDescriptor descriptor) {
+ return typeMapper.mapSignature(descriptor).getAsmMethod();
+ }
+ }
+ );
if (!bridgesToGenerate.isEmpty()) {
- PsiElement origin = callableDescriptorToDeclaration(bindingContext, descriptor);
- for (Method bridge : bridgesToGenerate) {
- generateBridge(origin, bridge, method);
+ PsiElement origin = descriptor.getKind().isReal()
+ ? callableDescriptorToDeclaration(bindingContext, descriptor)
+ : null;
+ for (Bridge bridge : bridgesToGenerate) {
+ generateBridge(origin, bridge.getFrom(), bridge.getTo());
}
}
}
- public void generateBridgesForFakeOverride(@NotNull FunctionDescriptor descriptor) {
- if (owner.getContextKind() == OwnerKind.TRAIT_IMPL) return;
-
- // If it's an abstract fake override, no bridges are needed: when an implementation will appear in some subclass, all necessary
- // bridges will be generated there
- if (descriptor.getModality() == Modality.ABSTRACT) return;
-
- // If it's a concrete fake override and all of its super-functions are non-abstract, then every possible bridge is already generated
- // into some of the super-classes and will be inherited in this class
- if (!hasAbstractSuperFunction(descriptor)) return;
-
- FunctionDescriptor implementation = findNonAbstractDeclaration(descriptor);
-
- Set bridgesToGenerate = findAllReachableDeclarations(descriptor);
- // TODO: remove also all declarations reachable from all reachable non-abstract fake overrides in classes
- bridgesToGenerate.removeAll(findAllReachableDeclarations(implementation));
-
- Method method = typeMapper.mapSignature(implementation).getAsmMethod();
- for (Method bridge : bridgesToGenerate) {
- generateBridge(null, bridge, method);
- }
- }
-
- private static boolean hasAbstractSuperFunction(@NotNull FunctionDescriptor descriptor) {
- for (FunctionDescriptor overridden : descriptor.getOverriddenDescriptors()) {
- if (overridden.getOriginal().getModality() == Modality.ABSTRACT) {
- return true;
- }
- }
- return false;
- }
-
- @NotNull
- private Set findAllReachableDeclarations(@NotNull FunctionDescriptor descriptor) {
- DFS.Neighbors neighbors = new DFS.Neighbors() {
- @NotNull
- @Override
- public Iterable getNeighbors(FunctionDescriptor current) {
- return KotlinPackage.map(current.getOverriddenDescriptors(), new Function1() {
- @Override
- public FunctionDescriptor invoke(FunctionDescriptor descriptor) {
- return descriptor.getOriginal();
- }
- });
- }
- };
- DFS.NodeHandlerWithListResult collector =
- new DFS.NodeHandlerWithListResult() {
- @Override
- public void afterChildren(FunctionDescriptor current) {
- if (current.getKind().isReal()) {
- result.add(typeMapper.mapSignature(current).getAsmMethod());
- }
- }
- };
- DFS.dfs(Collections.singleton(descriptor), neighbors, collector);
- return new HashSet(collector.result());
- }
-
- /**
- * Given a non-abstract function of any kind, finds an implementation (a non-abstract non-fake-override) of this function
- * in the supertypes. The implementation is guaranteed to exist because if it wouldn't, the given function would've been abstract.
- */
- @NotNull
- private static FunctionDescriptor findNonAbstractDeclaration(@NotNull FunctionDescriptor descriptor) {
- if (descriptor.getModality() == Modality.ABSTRACT) {
- throw new IllegalArgumentException("Only non-abstract functions have implementations: " + descriptor);
- }
-
- if (descriptor.getKind().isReal()) {
- return descriptor;
- }
-
- for (FunctionDescriptor overriddenSubstituted : descriptor.getOverriddenDescriptors()) {
- FunctionDescriptor overridden = overriddenSubstituted.getOriginal();
- if (overridden.getModality() != Modality.ABSTRACT) {
- return findNonAbstractDeclaration(overridden);
- }
- }
-
- throw new IllegalStateException("No non-abstract declaration found for non-abstract function: " +
- descriptor + "\nOverridden: " + descriptor.getOverriddenDescriptors());
- }
-
@NotNull
private static String[] getThrownExceptions(@NotNull FunctionDescriptor function, @NotNull final JetTypeMapper mapper) {
AnnotationDescriptor annotation = function.getAnnotations().findAnnotation(new FqName("kotlin.throws"));
@@ -767,23 +693,6 @@ public class FunctionCodegen extends ParentCodegenAwareImpl {
return true;
}
- private static boolean differentMethods(Method method, Method overridden) {
- if (!method.getReturnType().equals(overridden.getReturnType())) {
- return true;
- }
- Type[] methodArgumentTypes = method.getArgumentTypes();
- Type[] overriddenArgumentTypes = overridden.getArgumentTypes();
- if (methodArgumentTypes.length != overriddenArgumentTypes.length) {
- return true;
- }
- for (int i = 0; i != methodArgumentTypes.length; ++i) {
- if (!methodArgumentTypes[i].equals(overriddenArgumentTypes[i])) {
- return true;
- }
- }
- return false;
- }
-
private void generateBridge(@Nullable PsiElement origin, @NotNull Method bridge, @NotNull Method delegateTo) {
int flags = ACC_PUBLIC | ACC_BRIDGE | ACC_SYNTHETIC; // TODO.
diff --git a/compiler/testData/codegen/box/bridges/complexMultiInheritance.kt b/compiler/testData/codegen/box/bridges/complexMultiInheritance.kt
new file mode 100644
index 00000000000..cce9635c317
--- /dev/null
+++ b/compiler/testData/codegen/box/bridges/complexMultiInheritance.kt
@@ -0,0 +1,25 @@
+open class A {
+ open fun foo(): Any = "A"
+}
+
+trait B : A
+
+open class C : A() {
+ override fun foo(): Int = 222
+}
+
+trait D {
+ fun foo(): Number
+}
+
+class E : B, C(), D
+
+fun box(): String {
+ val e = E()
+ if (e.foo() != 222) return "Fail 1"
+ if ((e : D).foo() != 222) return "Fail 2"
+ if ((e : C).foo() != 222) return "Fail 3"
+ if ((e : B).foo() != 222) return "Fail 4"
+ if ((e : A).foo() != 222) return "Fail 5"
+ return "OK"
+}
diff --git a/compiler/tests/org/jetbrains/jet/codegen/BridgeTest.kt b/compiler/tests/org/jetbrains/jet/codegen/BridgeTest.kt
new file mode 100644
index 00000000000..d980273efa9
--- /dev/null
+++ b/compiler/tests/org/jetbrains/jet/codegen/BridgeTest.kt
@@ -0,0 +1,563 @@
+/*
+ * 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 junit.framework.TestCase
+import org.jetbrains.jet.codegen.bridges.*
+import kotlin.test.assertEquals
+import java.util.HashSet
+import org.jetbrains.jet.utils.DFS
+
+class BridgeTest : TestCase() {
+ private class Fun(val text: String) : FunctionHandle {
+ override val isDeclaration: Boolean get() = text[1] == 'D'
+ override val isAbstract: Boolean get() = text[0] == '-'
+
+ val signature: Char get() = text[2]
+
+ val overriddenFunctions: MutableList = arrayListOf()
+ override fun getOverridden() = overriddenFunctions
+
+ override fun toString() = text
+ }
+
+ private class Meth(val function: Fun) {
+ override fun equals(other: Any?) = other is Meth && other.function.signature == function.signature
+ override fun hashCode() = function.signature.hashCode()
+ override fun toString() = function.toString()
+ }
+
+ private fun v(text: String): Fun {
+ assert(text.length == 3, "Function vertex representation should consist of 3 characters: $text")
+ assert(text[0] in setOf('-', '+'), "First character should be '-' for abstract functions or '+' for concrete ones: $text")
+ assert(text[1] in setOf('D', 'F'), "Second character should be 'D' for declarations or 'F' for fake overrides: $text")
+ assert(text[2].isDigit(),
+ "Third character should be a number that represents a signature (same numbers mean the same method signatures)")
+ return Fun(text)
+ }
+
+ private fun bridge(from: Fun, to: Fun): Bridge = Bridge(Meth(from), Meth(to))
+
+ /**
+ * Constructs a graph out of the given pairs of vertices. First vertex should be a function in the derived class,
+ * second -- the corresponding overridden function in a superclass.
+ *
+ * Checks that the graph satisfies the following conditions:
+ * 1. Each fake override should have a super-declaration
+ * 2. Each concrete fake override should have exactly one concrete super-declaration. More accurately, for each concrete
+ * fake override F there is a concrete declaration D in supertypes such that every other concrete super-declaration of F
+ * is either reachable from D or is reachable from any abstract super-declaration of F (or both). This condition is effectively
+ * equivalent to the compiler guarantee that each class inherits not more than one implementation of each function.
+ *
+ * NOTE: abstract fake overrides CAN have concrete super-declarations! (traits with required classes)
+ *
+ * NOTE 2: the graph validation procedure probably doesn't cover all the possible cases compared to the analogous code in the compiler.
+ * There may be bugs here and they should be fixed accordingly.
+ */
+ private fun graph(vararg edges: Pair) {
+ for ((from, to) in edges) {
+ from.overriddenFunctions.add(to)
+ }
+
+ fun findAllReachableDeclarations(from: Fun): MutableSet {
+ val handler = object : DFS.NodeHandlerWithListResult() {
+ override fun afterChildren(current: Fun?) {
+ if (current!!.isDeclaration) {
+ result.add(current)
+ }
+ }
+ }
+ DFS.dfs(listOf(from), { it!!.getOverridden() }, handler)
+ val result = HashSet(handler.result())
+ result.remove(from)
+ return result
+ }
+
+ val vertices = edges.flatMapTo(HashSet()) { pair -> listOf(pair.first, pair.second) }
+
+ for (vertex in vertices) {
+ val directConcreteSuperFunctions = vertex.overriddenFunctions.filter { !it.isAbstract }
+ assert(directConcreteSuperFunctions.size <= 1,
+ "Incorrect test data: function $vertex has more than one direct concrete super-function: ${vertex.overriddenFunctions}\n" +
+ "This is not allowed because only classes can contain implementations (concrete functions), and having more than one " +
+ "concrete super-function means having more than one superclass, which is prohibited in Kotlin")
+
+ if (vertex.isDeclaration) continue
+
+ val superDeclarations = findAllReachableDeclarations(vertex)
+ assert(!superDeclarations.isEmpty(), "Incorrect test data: fake override vertex $vertex has no super-declarations")
+
+ // Remove all declarations inherited by other declarations
+ val toRemove = HashSet()
+ for (superDeclaration in superDeclarations) {
+ toRemove.addAll(findAllReachableDeclarations(superDeclaration))
+ }
+ superDeclarations.removeAll(toRemove)
+ val concreteDeclarations = superDeclarations.filter { !it.isAbstract }
+
+ if (!vertex.isAbstract) {
+ assert(!concreteDeclarations.isEmpty(),
+ "Incorrect test data: concrete fake override vertex $vertex has no concrete super-declarations")
+ assert(concreteDeclarations.size == 1,
+ "Incorrect test data: concrete fake override vertex $vertex has more than one concrete super-declaration: " +
+ "$concreteDeclarations")
+ }
+ }
+ }
+
+ private fun doTest(function: Fun, expectedBridges: Set>) {
+ val actualBridges = generateBridges(function, ::Meth)
+ assert(actualBridges.firstOrNull { it.from == it.to } == null,
+ "A bridge invoking itself was generated, which makes no sense, since it will result in StackOverflowError once called" +
+ ": $actualBridges")
+ assertEquals(expectedBridges, actualBridges, "Expected and actual bridge sets differ for function $function")
+ }
+
+ // ------------------------------------------------------------------------------------------------------------------
+
+ // Simple tests with no bridges
+
+ fun testOneVertexAbstract() {
+ val a = v("-D1")
+ graph()
+ doTest(a, setOf())
+ }
+
+ fun testOneVertexConcrete() {
+ val a = v("+D1")
+ graph()
+ doTest(a, setOf())
+ }
+
+ fun testSimpleFakeOverrideSameSignature() {
+ val a = v("+D1")
+ val b = v("+F1")
+ graph(b to a)
+ doTest(a, setOf())
+ doTest(b, setOf())
+ }
+
+ fun testSimpleFakeOverrideDifferentSignature() {
+ val a = v("+D1")
+ val b = v("+F2")
+ graph(b to a)
+ doTest(a, setOf())
+ doTest(b, setOf())
+ }
+
+ fun testSimpleDeclarationSameSignature() {
+ val a = v("+D1")
+ val b = v("+D1")
+ graph(b to a)
+ doTest(a, setOf())
+ doTest(b, setOf())
+ }
+
+ fun testSimpleAbstractDeclarationSameSignature() {
+ val a = v("-D1")
+ val b = v("-D1")
+ graph(b to a)
+ doTest(a, setOf())
+ doTest(b, setOf())
+ }
+
+ fun testSimpleAbstractDeclarationOverridesConcreteSameSignature() {
+ val a = v("+D1")
+ val b = v("-D1")
+ graph(b to a)
+ doTest(a, setOf())
+ doTest(b, setOf())
+ }
+
+ // Simple tests where declaration "a" is inherited by declaration "b" with a different signature.
+ // Note that we don't generate bridges near abstract declarations in contrast to javac
+
+ fun testSimpleConcreteDeclarationDifferentSignature() {
+ val a = v("+D1")
+ val b = v("+D2")
+ graph(b to a)
+ doTest(b, setOf(bridge(a, b)))
+ }
+
+ fun testSimpleAbstractDeclarationDifferentSignature() {
+ val a = v("-D1")
+ val b = v("-D2")
+ graph(b to a)
+ doTest(b, setOf())
+ }
+
+ fun testSimpleAbstractDeclarationOverridesConcreteDifferentSignature() {
+ val a = v("+D1")
+ val b = v("-D2")
+ graph(b to a)
+ doTest(b, setOf())
+ }
+
+ fun testSimpleConcreteDeclarationOverridesAbstractDifferentSignature() {
+ val a = v("-D1")
+ val b = v("+D2")
+ graph(b to a)
+ doTest(b, setOf(bridge(a, b)))
+ }
+
+ // Simple tests where declaration overrides declaration through a fake override in the super class, with a different signature
+
+ fun testSimpleConcreteDeclarationOverridesConcreteThroughFakeOverride() {
+ val a = v("+D1")
+ val b = v("+F2")
+ val c = v("+D3")
+ graph(b to a, c to b)
+ doTest(a, setOf())
+ doTest(b, setOf())
+ doTest(c, setOf(bridge(a, c)))
+ }
+
+ fun testSimpleConcreteDeclarationOverridesAbstractThroughFakeOverride() {
+ val a = v("-D1")
+ val b = v("-F2")
+ val c = v("+D3")
+ graph(b to a, c to b)
+ doTest(a, setOf())
+ doTest(b, setOf())
+ doTest(c, setOf(bridge(a, c)))
+ }
+
+ fun testSimpleAbstractDeclarationOverridesConcreteThroughFakeOverride() {
+ val a = v("+D1")
+ val b = v("+F2")
+ val c = v("-D3")
+ graph(b to a, c to b)
+ doTest(a, setOf())
+ doTest(b, setOf())
+ doTest(c, setOf())
+ }
+
+ fun testSimpleAbstractDeclarationOverridesAbstractThroughFakeOverride() {
+ val a = v("-D1")
+ val b = v("-F2")
+ val c = v("-D3")
+ graph(b to a, c to b)
+ doTest(a, setOf())
+ doTest(b, setOf())
+ doTest(c, setOf())
+ }
+
+ // Declaration "c" overrides two declarations "a" and "b"
+
+ fun testAbstractDeclarationOverridesTwoAbstractDeclarations() {
+ val a = v("-D1")
+ val b = v("-D2")
+ val c = v("-D3")
+ graph(c to a, c to b)
+ doTest(c, setOf())
+ }
+
+ fun testAbstractDeclarationOverridesAbstractAndConcreteDeclarations() {
+ val a = v("-D1")
+ val b = v("+D2")
+ val c = v("-D3")
+ graph(c to a, c to b)
+ doTest(c, setOf())
+ }
+
+ fun testConcreteDeclarationOverridesTwoAbstractDeclarations() {
+ val a = v("-D1")
+ val b = v("-D2")
+ val c = v("+D3")
+ graph(c to a, c to b)
+ doTest(c, setOf(bridge(a, c), bridge(b, c)))
+ }
+
+ fun testConcreteDeclarationOverridesAbstractAndConcreteDeclarations() {
+ val a = v("-D1")
+ val b = v("+D2")
+ val c = v("+D3")
+ graph(c to a, c to b)
+ doTest(c, setOf(bridge(a, c), bridge(b, c)))
+ }
+
+ // Declaration "c" overrides declaration "a" and declaration "b", which in turn overrides "a".
+ // We still need to generate both bridges near "c" to avoid several consecutive bridges in a call stack
+
+ fun testConcreteDeclarationOverridesTwoInheritingDeclarations() {
+ val a = v("-D1")
+ val b = v("+D2")
+ val c = v("+D3")
+ graph(c to a, c to b, b to a)
+ doTest(a, setOf())
+ doTest(b, setOf(bridge(a, b)))
+ doTest(c, setOf(bridge(a, c), bridge(b, c)))
+ }
+
+ fun testConcreteDeclarationOverridesAbstractDeclarationOverridingConcrete() {
+ val a = v("+D1")
+ val b = v("-D2")
+ val c = v("+D3")
+ graph(c to a, c to b, b to a)
+ doTest(a, setOf())
+ doTest(b, setOf())
+ doTest(c, setOf(bridge(a, c), bridge(b, c)))
+ }
+
+ // Diamonds where the sink (vertex "d") is a declaration: bridges from all super-declarations to "d" should be present
+
+ fun testDiamondAbstractDeclarations() {
+ val a = v("-D1")
+ val b = v("-D2")
+ val c = v("-D3")
+ val d = v("-D4")
+ graph(b to a, c to a, d to b, d to c)
+ doTest(a, setOf())
+ doTest(b, setOf())
+ doTest(c, setOf())
+ doTest(d, setOf())
+ }
+
+ fun testDiamondMixedDeclarations() {
+ val a = v("-D1")
+ val b = v("+D2")
+ val c = v("-D3")
+ val d = v("+D4")
+ graph(b to a, c to a, d to b, d to c)
+ doTest(a, setOf())
+ doTest(b, setOf(bridge(a, b)))
+ doTest(c, setOf())
+ doTest(d, setOf(bridge(a, d), bridge(b, d), bridge(c, d)))
+ }
+
+ fun testDiamondAbstractFakeOverridesInTheMiddle() {
+ val a = v("-D1")
+ val b = v("-F2")
+ val c = v("-F3")
+ val d = v("+D4")
+ graph(b to a, c to a, d to b, d to c)
+ doTest(a, setOf())
+ doTest(b, setOf())
+ doTest(c, setOf())
+ doTest(d, setOf(bridge(a, d)))
+ }
+
+ // Fake override "c" overrides declarations "a" and "b": a bridge is needed if signatures are different and there's an implementation
+
+ fun testAbstractFakeOverride() {
+ val a = v("-D1")
+ val b = v("-D2")
+ val c = v("-F3")
+ graph(c to a, c to b)
+ doTest(c, setOf())
+ }
+
+ fun testFakeOverrideSameSuperDeclarations() {
+ val a = v("-D1")
+ val b = v("+D1")
+ val c = v("+F2")
+ graph(c to a, c to b)
+ doTest(c, setOf())
+ }
+
+ fun testFakeOverrideAbstractAndConcreteDeclarations() {
+ val a = v("-D1")
+ val b = v("+D2")
+ val c = v("+F3")
+ graph(c to a, c to b)
+ doTest(c, setOf(bridge(a, b)))
+ }
+
+ fun testFakeOverrideInheritingDeclarations() {
+ val a = v("-D1")
+ val b = v("+D2")
+ val c = v("+F3")
+ graph(c to a, c to b, b to a)
+ doTest(a, setOf())
+ doTest(b, setOf(bridge(a, b)))
+ // Here bridge a->b is not needed in c, it's already present in b
+ doTest(c, setOf())
+ }
+
+ // Diamonds where the sink (vertex "d") is a fake override
+
+ fun testDiamondFakeOverrideAbstractFakeAndConcrete() {
+ val a = v("-D1")
+ val b = v("-F2")
+ val c = v("+D3")
+ val d = v("+F4")
+ graph(b to a, c to a, d to b, d to c)
+ doTest(c, setOf(bridge(a, c)))
+ doTest(d, setOf())
+ }
+
+ fun testDiamondFakeOverrideAbstractAndConcrete() {
+ val a = v("-D1")
+ val b = v("-D2")
+ val c = v("+D3")
+ val d = v("+F4")
+ graph(b to a, c to a, d to b, d to c)
+ doTest(b, setOf())
+ doTest(c, setOf(bridge(a, c)))
+ doTest(d, setOf(bridge(b, c)))
+ }
+
+ fun testDiamondFakeOverrideAbstractOverridesConcrete() {
+ val a = v("+D1")
+ val b = v("+D2")
+ val c = v("-D3")
+ val d = v("+F4")
+ graph(b to a, c to a, d to b, d to c)
+ doTest(b, setOf(bridge(a, b)))
+ doTest(c, setOf())
+ doTest(d, setOf(bridge(c, b)))
+ }
+
+ // More complex tests where a fake override inherits several declarations
+
+ fun testFakeOverrideInheritingDeclarationsAndAbstract() {
+ val a = v("-D1")
+ val b = v("-D2")
+ val c = v("+D3")
+ val e = v("+F4")
+ graph(e to a, e to b, e to c, c to b)
+ doTest(a, setOf())
+ doTest(b, setOf())
+ doTest(c, setOf(bridge(b, c)))
+ doTest(e, setOf(bridge(a, c)))
+ }
+
+ fun testFakeOverrideManyDeclarations() {
+ val a = v("-D1")
+ val b = v("+D2")
+ val c = v("-D3")
+ val d = v("-D4")
+ val e = v("+F5")
+ graph(e to a, e to b, e to c, e to d)
+ doTest(e, setOf(bridge(a, b), bridge(c, b), bridge(d, b)))
+ }
+
+ fun testFakeOverrideTwoDeclarationsThroughFakeOverrides() {
+ val a = v("+D1")
+ val b = v("+F2")
+ val c = v("-D3")
+ val d = v("-F4")
+ val e = v("+F5")
+ graph(b to a, d to c, e to b, e to d)
+ doTest(e, setOf(bridge(c, a)))
+ }
+
+ fun testFakeOverrideMisleadingImplementation() {
+ val a = v("+D1")
+ val b = v("-D2")
+ val c = v("-D3")
+ val d = v("+D4")
+ val e = v("+F5")
+ val f = v("+F6")
+ graph(c to a, e to d, f to b, f to c, f to e)
+ doTest(e, setOf())
+ // Although "a" is a concrete declaration, it's overridden with abstract in "c" and all bridges should delegate to "d" instead
+ doTest(f, setOf(bridge(a, d), bridge(b, d), bridge(c, d)))
+ }
+
+ // Fake override overrides another fake override (or declaration) with some bridges already present there
+
+ fun testFakeOverrideInheritsBridgeFromFakeOverride() {
+ val a = v("-D1")
+ val b = v("+D2")
+ val c = v("+F3")
+ val d = v("+F4")
+ graph(c to a, c to b, d to c)
+ doTest(c, setOf(bridge(a, b)))
+ doTest(d, setOf())
+ }
+
+ fun testFakeOverrideInheritsBridgesAndAbstract() {
+ val a = v("-D1")
+ val b = v("+D2")
+ val c = v("+F3")
+ val d = v("-D4")
+ val e = v("+F5")
+ graph(c to a, c to b, e to c, e to d)
+ doTest(c, setOf(bridge(a, b)))
+ // It's important that "e" shouldn't have "a->b" bridge, because it's inherited from "c"
+ doTest(e, setOf(bridge(d, b)))
+ }
+
+ fun testFakeOverrideInheritsBridgeFromDeclaration() {
+ val a = v("+D1")
+ val b = v("-D2")
+ val c = v("+F3")
+ val d = v("+D4")
+ val e = v("-D5")
+ val f = v("+F6")
+ graph(c to a, c to b, d to c, f to d, f to e)
+ doTest(c, setOf(bridge(b, a)))
+ doTest(d, setOf(bridge(a, d), bridge(b, d)))
+ doTest(f, setOf(bridge(e, d)))
+ }
+
+ fun testFakeOverrideDiamondWithExtraAbstract() {
+ val a = v("+D1")
+ val b = v("-F2")
+ val c = v("+D3")
+ val d = v("-D4")
+ val e = v("+F5")
+ graph(b to a, c to a, e to b, e to c, e to d)
+ doTest(c, setOf(bridge(a, c)))
+ // It's important that implementation of "e" is "c", not "a", so a bridge "d->c" should exist
+ doTest(e, setOf(bridge(d, c)))
+ }
+
+ fun testLongTreeOfFakeOverrideBridgeInheritance() {
+ val a = v("+D1")
+ val b = v("-D2")
+ val c = v("-D3")
+ val d = v("+F4")
+ val e = v("-D5")
+ val f = v("-D6")
+ val g = v("+F7")
+ val h = v("-D8")
+ val i = v("-D9")
+ val j = v("+F0")
+ graph(d to a, d to b, d to c,
+ g to d, g to e, g to f,
+ j to g, j to h, j to i)
+ doTest(d, setOf(bridge(b, a), bridge(c, a)))
+ doTest(g, setOf(bridge(e, a), bridge(f, a)))
+ doTest(j, setOf(bridge(h, a), bridge(i, a)))
+ }
+
+ fun testFakeOverrideShouldNotInheritBridgeFromAbstractDeclaration() {
+ val a = v("-D1")
+ val b = v("+D2")
+ val c = v("+F3")
+ val d = v("-D4")
+ val e = v("+D5")
+ val f = v("+F6")
+ graph(c to a, c to b, d to c, f to d, f to e)
+ doTest(c, setOf(bridge(a, b)))
+ // Although "f" has a concrete fake override "c" in its hierarchy, we should NOT silently inherit a bridge from it,
+ // because it corresponds to another implementation ("b"). Instead a bunch of new bridges should be generated, delegating to "e"
+ doTest(f, setOf(bridge(a, e), bridge(b, e), bridge(d, e)))
+ }
+
+ fun testFakeOverrideShouldNotInheritBridgeFromAbstractFakeOverride() {
+ val a = v("+D1")
+ val b = v("-D2")
+ val c = v("-F3")
+ val d = v("+F4")
+ graph(c to a, c to b, d to a, d to c)
+ doTest(c, setOf())
+ doTest(d, setOf(bridge(b, a)))
+ }
+}
diff --git a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java
index a7643d4176c..cbba360a843 100644
--- a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java
+++ b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java
@@ -285,6 +285,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/codegen/box/bridges"), Pattern.compile("^(.+)\\.kt$"), true);
}
+ @TestMetadata("complexMultiInheritance.kt")
+ public void testComplexMultiInheritance() throws Exception {
+ doTest("compiler/testData/codegen/box/bridges/complexMultiInheritance.kt");
+ }
+
@TestMetadata("delegation.kt")
public void testDelegation() throws Exception {
doTest("compiler/testData/codegen/box/bridges/delegation.kt");
diff --git a/core/util.runtime/src/org/jetbrains/jet/utils/DFS.java b/core/util.runtime/src/org/jetbrains/jet/utils/DFS.java
index 99dfd5d71cb..0cef37a5c9f 100644
--- a/core/util.runtime/src/org/jetbrains/jet/utils/DFS.java
+++ b/core/util.runtime/src/org/jetbrains/jet/utils/DFS.java
@@ -16,6 +16,7 @@
package org.jetbrains.jet.utils;
+import kotlin.jvm.KotlinSignature;
import org.jetbrains.annotations.NotNull;
import java.util.*;
@@ -87,6 +88,7 @@ public class DFS {
}
public interface Neighbors {
+ @KotlinSignature("fun getNeighbors(current: N?): Iterable")
@NotNull
Iterable getNeighbors(N current);
}
@@ -123,9 +125,11 @@ public class DFS {
}
public static abstract class NodeHandlerWithListResult extends AbstractNodeHandler> {
+ @NotNull
protected final LinkedList result = new LinkedList();
@Override
+ @NotNull
public List result() {
return result;
}