Generate bridges for fake overrides when needed

#KT-3985 Fixed
 #KT-4145 Fixed
This commit is contained in:
Alexander Udalov
2014-03-27 19:33:42 +04:00
parent 2c2c918447
commit 04c237cc22
17 changed files with 300 additions and 97 deletions
@@ -27,6 +27,7 @@ import org.jetbrains.jet.lang.descriptors.annotations.Annotations;
import org.jetbrains.jet.lang.descriptors.impl.SimpleFunctionDescriptorImpl;
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 java.util.Collections;
@@ -98,6 +99,17 @@ public abstract class ClassBodyCodegen extends MemberCodegen {
}
}
if (!DescriptorUtils.isTrait(descriptor)) {
for (DeclarationDescriptor memberDescriptor : descriptor.getDefaultType().getMemberScope().getAllDescriptors()) {
if (memberDescriptor instanceof FunctionDescriptor) {
FunctionDescriptor member = (FunctionDescriptor) memberDescriptor;
if (member.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
functionCodegen.generateBridgesForFakeOverride(member);
}
}
}
}
generatePrimaryConstructorProperties(propertyCodegen, myClass);
}
@@ -22,6 +22,8 @@ import com.intellij.psi.PsiElement;
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.org.objectweb.asm.AnnotationVisitor;
@@ -50,6 +52,7 @@ 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 java.io.PrintWriter;
import java.io.StringWriter;
@@ -63,6 +66,7 @@ import static org.jetbrains.jet.codegen.binding.CodegenBinding.isLocalNamedFun;
import static org.jetbrains.jet.lang.resolve.BindingContextUtils.callableDescriptorToDeclaration;
import static org.jetbrains.jet.lang.resolve.BindingContextUtils.descriptorToDeclaration;
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isFunctionLiteral;
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isTrait;
import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.OBJECT_TYPE;
import static org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames.OLD_JET_VALUE_PARAMETER_ANNOTATION;
@@ -110,10 +114,11 @@ public class FunctionCodegen extends ParentCodegenAwareImpl {
@NotNull MethodContext methodContext,
@NotNull FunctionGenerationStrategy strategy
) {
OwnerKind methodContextKind = methodContext.getContextKind();
Method asmMethod = jvmSignature.getAsmMethod();
MethodVisitor mv = v.newMethod(origin,
getMethodAsmFlags(functionDescriptor, methodContext.getContextKind()),
getMethodAsmFlags(functionDescriptor, methodContextKind),
asmMethod.getName(),
asmMethod.getDescriptor(),
jvmSignature.getGenericsSignature(),
@@ -133,27 +138,26 @@ public class FunctionCodegen extends ParentCodegenAwareImpl {
generateJetValueParameterAnnotations(mv, functionDescriptor, jvmSignature);
if (state.getClassBuilderMode() == ClassBuilderMode.LIGHT_CLASSES ||
isAbstractMethod(functionDescriptor, methodContext.getContextKind())) {
if (state.getClassBuilderMode() == ClassBuilderMode.LIGHT_CLASSES || isAbstractMethod(functionDescriptor, methodContextKind)) {
generateLocalVariableTable(
mv,
jvmSignature,
functionDescriptor,
getThisTypeForFunction(functionDescriptor, methodContext, state.getTypeMapper()),
getThisTypeForFunction(functionDescriptor, methodContext, typeMapper),
new Label(),
new Label(),
methodContext.getContextKind()
methodContextKind
);
return;
}
else {
generateMethodBody(mv, functionDescriptor, methodContext, jvmSignature, strategy, getParentCodegen());
endVisit(mv, null, origin);
methodContext.recordSyntheticAccessorIfNeeded(functionDescriptor, bindingContext);
}
generateMethodBody(mv, functionDescriptor, methodContext, jvmSignature, strategy, getParentCodegen());
endVisit(mv, null, origin);
generateBridgeIfNeeded(functionDescriptor);
methodContext.recordSyntheticAccessorIfNeeded(functionDescriptor, bindingContext);
generateBridges(functionDescriptor);
}
private void generateParameterAnnotations(
@@ -413,49 +417,108 @@ public class FunctionCodegen extends ParentCodegenAwareImpl {
return bytecode;
}
private void generateBridgeIfNeeded(@NotNull FunctionDescriptor functionDescriptor) {
if (functionDescriptor instanceof ConstructorDescriptor) return;
private void generateBridges(@NotNull FunctionDescriptor descriptor) {
if (descriptor instanceof ConstructorDescriptor) return;
if (owner.getContextKind() == OwnerKind.TRAIT_IMPL) return;
if (isTrait(descriptor.getContainingDeclaration())) return;
Method method = typeMapper.mapSignature(functionDescriptor).getAsmMethod();
Set<Method> bridgesToGenerate = findAllReachableDeclarations(descriptor);
Queue<FunctionDescriptor> bfsQueue = new LinkedList<FunctionDescriptor>();
Set<FunctionDescriptor> visited = new HashSet<FunctionDescriptor>();
for (FunctionDescriptor overriddenDescriptor : functionDescriptor.getOverriddenDescriptors()) {
FunctionDescriptor orig = overriddenDescriptor.getOriginal();
if (visited.add(orig)) {
bfsQueue.offer(orig);
}
}
Set<Method> bridgesToGenerate = new HashSet<Method>();
while (!bfsQueue.isEmpty()) {
FunctionDescriptor descriptor = bfsQueue.poll();
if (descriptor.getKind() == CallableMemberDescriptor.Kind.DECLARATION) {
Method overridden = typeMapper.mapSignature(descriptor).getAsmMethod();
if (differentMethods(method, overridden)) {
bridgesToGenerate.add(overridden);
}
continue;
}
for (FunctionDescriptor overriddenDescriptor : descriptor.getOverriddenDescriptors()) {
FunctionDescriptor orig = overriddenDescriptor.getOriginal();
if (visited.add(orig)) {
bfsQueue.offer(orig);
}
}
}
Method method = typeMapper.mapSignature(descriptor).getAsmMethod();
bridgesToGenerate.remove(method);
if (!bridgesToGenerate.isEmpty()) {
PsiElement origin = callableDescriptorToDeclaration(bindingContext, functionDescriptor);
PsiElement origin = callableDescriptorToDeclaration(bindingContext, descriptor);
for (Method bridge : bridgesToGenerate) {
generateBridge(origin, bridge, method);
}
}
}
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<Method> 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<Method> findAllReachableDeclarations(@NotNull FunctionDescriptor descriptor) {
DFS.Neighbors<FunctionDescriptor> neighbors = new DFS.Neighbors<FunctionDescriptor>() {
@NotNull
@Override
public Iterable<FunctionDescriptor> getNeighbors(FunctionDescriptor current) {
return KotlinPackage.map(current.getOverriddenDescriptors(), new Function1<FunctionDescriptor, FunctionDescriptor>() {
@Override
public FunctionDescriptor invoke(FunctionDescriptor descriptor) {
return descriptor.getOriginal();
}
});
}
};
DFS.NodeHandlerWithListResult<FunctionDescriptor, Method> collector =
new DFS.NodeHandlerWithListResult<FunctionDescriptor, Method>() {
@Override
public void afterChildren(FunctionDescriptor current) {
if (current.getKind() == CallableMemberDescriptor.Kind.DECLARATION) {
result.add(typeMapper.mapSignature(current).getAsmMethod());
}
}
};
DFS.dfs(Collections.singleton(descriptor), neighbors, collector);
return new HashSet<Method>(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() != CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
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"));
@@ -799,6 +862,6 @@ public class FunctionCodegen extends ParentCodegenAwareImpl {
endVisit(mv, "Delegate method " + functionDescriptor + " to " + jvmOverriddenMethodSignature,
descriptorToDeclaration(bindingContext, functionDescriptor.getContainingDeclaration()));
generateBridgeIfNeeded(functionDescriptor);
generateBridges(functionDescriptor);
}
}
@@ -0,0 +1,19 @@
// KT-4145
trait A {
fun foo(): Any
}
open class B {
fun foo(): String = "A"
}
open class C: B(), A
fun box(): String {
val a: A = C()
if (a.foo() != "A") return "Fail 1"
if ((a as B).foo() != "A") return "Fail 2"
if ((a as C).foo() != "A") return "Fail 3"
return "OK"
}
@@ -0,0 +1,21 @@
// KT-3985
trait Trait<T> {
fun f(): T
}
open class Class {
fun f(): String = throw UnsupportedOperationException()
}
class Foo: Class(), Trait<String> {
}
fun box(): String {
try {
(Foo() : Trait<String>).f()
} catch (e: UnsupportedOperationException) {
return "OK"
}
return "Fail"
}
@@ -0,0 +1,28 @@
var result = ""
trait D1 {
fun foo(): D1 {
result += "D1"
return this
}
}
trait F2 : D1
trait D3 : F2 {
override fun foo(): D3 {
result += "D3"
return this
}
}
class D4 : D3
fun box(): String {
val x = D4()
x.foo()
(x : D3).foo()
(x : F2).foo()
(x : D1).foo()
return if (result == "D3D3D3D3") "OK" else "Fail: $result"
}
@@ -0,0 +1,25 @@
trait D1 {
fun foo(): Any
}
trait D2 {
fun foo(): Number
}
trait F3 : D1, D2
open class D4 {
fun foo(): Int = 42
}
class F5 : F3, D4()
fun box(): String {
val z = F5()
var result = z.foo()
result += (z : D4).foo()
result += (z : F3).foo() as Int
result += (z : D2).foo() as Int
result += (z : D1).foo() as Int
return if (result == 5 * 42) "OK" else "Fail: $result"
}
@@ -279,6 +279,7 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
}
@TestMetadata("compiler/testData/codegen/box/bridges")
@InnerTestClasses({Bridges.SubstitutionInSuperClass.class})
public static class Bridges extends AbstractBlackBoxCodegenTest {
public void testAllFilesPresentInBridges() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/codegen/box/bridges"), Pattern.compile("^(.+)\\.kt$"), true);
@@ -294,16 +295,31 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest("compiler/testData/codegen/box/bridges/delegationProperty.kt");
}
@TestMetadata("delegationToTraitImpl.kt")
public void testDelegationToTraitImpl() throws Exception {
doTest("compiler/testData/codegen/box/bridges/delegationToTraitImpl.kt");
}
@TestMetadata("diamond.kt")
public void testDiamond() throws Exception {
doTest("compiler/testData/codegen/box/bridges/diamond.kt");
}
@TestMetadata("fakeCovariantOverride.kt")
public void testFakeCovariantOverride() throws Exception {
doTest("compiler/testData/codegen/box/bridges/fakeCovariantOverride.kt");
}
@TestMetadata("fakeGenericCovariantOverride.kt")
public void testFakeGenericCovariantOverride() throws Exception {
doTest("compiler/testData/codegen/box/bridges/fakeGenericCovariantOverride.kt");
}
@TestMetadata("fakeOverrideOfTraitImpl.kt")
public void testFakeOverrideOfTraitImpl() throws Exception {
doTest("compiler/testData/codegen/box/bridges/fakeOverrideOfTraitImpl.kt");
}
@TestMetadata("fakeOverrideWithSeveralSuperDeclarations.kt")
public void testFakeOverrideWithSeveralSuperDeclarations() throws Exception {
doTest("compiler/testData/codegen/box/bridges/fakeOverrideWithSeveralSuperDeclarations.kt");
}
@TestMetadata("kt1939.kt")
public void testKt1939() throws Exception {
doTest("compiler/testData/codegen/box/bridges/kt1939.kt");
@@ -404,56 +420,16 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest("compiler/testData/codegen/box/bridges/simpleReturnType.kt");
}
@TestMetadata("simpleTraitImpl.kt")
public void testSimpleTraitImpl() throws Exception {
doTest("compiler/testData/codegen/box/bridges/simpleTraitImpl.kt");
}
@TestMetadata("simpleUpperBound.kt")
public void testSimpleUpperBound() throws Exception {
doTest("compiler/testData/codegen/box/bridges/simpleUpperBound.kt");
}
@TestMetadata("substitutionInSuperClass.kt")
public void testSubstitutionInSuperClass() throws Exception {
doTest("compiler/testData/codegen/box/bridges/substitutionInSuperClass.kt");
}
@TestMetadata("substitutionInSuperClassAbstractFun.kt")
public void testSubstitutionInSuperClassAbstractFun() throws Exception {
doTest("compiler/testData/codegen/box/bridges/substitutionInSuperClassAbstractFun.kt");
}
@TestMetadata("substitutionInSuperClassBoundedTypeArguments.kt")
public void testSubstitutionInSuperClassBoundedTypeArguments() throws Exception {
doTest("compiler/testData/codegen/box/bridges/substitutionInSuperClassBoundedTypeArguments.kt");
}
@TestMetadata("substitutionInSuperClassDelegation.kt")
public void testSubstitutionInSuperClassDelegation() throws Exception {
doTest("compiler/testData/codegen/box/bridges/substitutionInSuperClassDelegation.kt");
}
@TestMetadata("substitutionInSuperClassEnum.kt")
public void testSubstitutionInSuperClassEnum() throws Exception {
doTest("compiler/testData/codegen/box/bridges/substitutionInSuperClassEnum.kt");
}
@TestMetadata("substitutionInSuperClassGenericMethod.kt")
public void testSubstitutionInSuperClassGenericMethod() throws Exception {
doTest("compiler/testData/codegen/box/bridges/substitutionInSuperClassGenericMethod.kt");
}
@TestMetadata("substitutionInSuperClassObject.kt")
public void testSubstitutionInSuperClassObject() throws Exception {
doTest("compiler/testData/codegen/box/bridges/substitutionInSuperClassObject.kt");
}
@TestMetadata("substitutionInSuperClassProperty.kt")
public void testSubstitutionInSuperClassProperty() throws Exception {
doTest("compiler/testData/codegen/box/bridges/substitutionInSuperClassProperty.kt");
}
@TestMetadata("substitutionInSuperClassUpperBound.kt")
public void testSubstitutionInSuperClassUpperBound() throws Exception {
doTest("compiler/testData/codegen/box/bridges/substitutionInSuperClassUpperBound.kt");
}
@TestMetadata("twoParentsWithDifferentMethodsTwoBridges.kt")
public void testTwoParentsWithDifferentMethodsTwoBridges() throws Exception {
doTest("compiler/testData/codegen/box/bridges/twoParentsWithDifferentMethodsTwoBridges.kt");
@@ -464,6 +440,65 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest("compiler/testData/codegen/box/bridges/twoParentsWithTheSameMethodOneBridge.kt");
}
@TestMetadata("compiler/testData/codegen/box/bridges/substitutionInSuperClass")
public static class SubstitutionInSuperClass extends AbstractBlackBoxCodegenTest {
@TestMetadata("abstractFun.kt")
public void testAbstractFun() throws Exception {
doTest("compiler/testData/codegen/box/bridges/substitutionInSuperClass/abstractFun.kt");
}
public void testAllFilesPresentInSubstitutionInSuperClass() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/codegen/box/bridges/substitutionInSuperClass"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("boundedTypeArguments.kt")
public void testBoundedTypeArguments() throws Exception {
doTest("compiler/testData/codegen/box/bridges/substitutionInSuperClass/boundedTypeArguments.kt");
}
@TestMetadata("delegation.kt")
public void testDelegation() throws Exception {
doTest("compiler/testData/codegen/box/bridges/substitutionInSuperClass/delegation.kt");
}
@TestMetadata("enum.kt")
public void testEnum() throws Exception {
doTest("compiler/testData/codegen/box/bridges/substitutionInSuperClass/enum.kt");
}
@TestMetadata("genericMethod.kt")
public void testGenericMethod() throws Exception {
doTest("compiler/testData/codegen/box/bridges/substitutionInSuperClass/genericMethod.kt");
}
@TestMetadata("object.kt")
public void testObject() throws Exception {
doTest("compiler/testData/codegen/box/bridges/substitutionInSuperClass/object.kt");
}
@TestMetadata("property.kt")
public void testProperty() throws Exception {
doTest("compiler/testData/codegen/box/bridges/substitutionInSuperClass/property.kt");
}
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
doTest("compiler/testData/codegen/box/bridges/substitutionInSuperClass/simple.kt");
}
@TestMetadata("upperBound.kt")
public void testUpperBound() throws Exception {
doTest("compiler/testData/codegen/box/bridges/substitutionInSuperClass/upperBound.kt");
}
}
public static Test innerSuite() {
TestSuite suite = new TestSuite("Bridges");
suite.addTestSuite(Bridges.class);
suite.addTestSuite(SubstitutionInSuperClass.class);
return suite;
}
}
@TestMetadata("compiler/testData/codegen/box/builtinStubMethods")
@@ -5475,7 +5510,7 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
suite.addTestSuite(BlackBoxCodegenTestGenerated.class);
suite.addTestSuite(Arrays.class);
suite.addTestSuite(BinaryOp.class);
suite.addTestSuite(Bridges.class);
suite.addTest(Bridges.innerSuite());
suite.addTestSuite(BuiltinStubMethods.class);
suite.addTest(CallableReference.innerSuite());
suite.addTestSuite(Casts.class);