From 8e4954ab67effd43269c07949205159244b8af5e Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Sun, 25 May 2014 12:32:03 +0200 Subject: [PATCH] Generate @NotNull annotations on delegated functions --- .../jet/codegen/AnnotationCodegen.java | 9 ++- .../jet/codegen/ExpressionCodegen.java | 2 +- .../jet/codegen/FunctionCodegen.java | 77 ++++++++++--------- .../jet/codegen/SamWrapperClasses.java | 4 +- .../jet/codegen/SamWrapperCodegen.java | 50 ++++++++---- .../lightClasses/delegation/Function.java | 7 ++ .../lightClasses/delegation/Function.kt | 7 ++ .../lightClasses/delegation/Property.java | 7 ++ .../lightClasses/delegation/Property.kt | 7 ++ .../asJava/KotlinLightClassTestGenerated.java | 28 ++++++- 10 files changed, 137 insertions(+), 61 deletions(-) create mode 100644 compiler/testData/asJava/lightClasses/delegation/Function.java create mode 100644 compiler/testData/asJava/lightClasses/delegation/Function.kt create mode 100644 compiler/testData/asJava/lightClasses/delegation/Property.java create mode 100644 compiler/testData/asJava/lightClasses/delegation/Property.kt diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/AnnotationCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/AnnotationCodegen.java index 4259c6f290f..cb573329da2 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/AnnotationCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/AnnotationCodegen.java @@ -42,6 +42,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.*; +import static org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor.Kind.DELEGATION; import static org.jetbrains.jet.lang.resolve.BindingContextUtils.descriptorToDeclaration; public abstract class AnnotationCodegen { @@ -93,7 +94,13 @@ public abstract class AnnotationCodegen { return; } - PsiElement psiElement = descriptorToDeclaration(bindingContext, (DeclarationDescriptor) annotated); + PsiElement psiElement; + if (annotated instanceof CallableMemberDescriptor && ((CallableMemberDescriptor) annotated).getKind() == DELEGATION) { + psiElement = null; + } + else { + psiElement = descriptorToDeclaration(bindingContext, (DeclarationDescriptor) annotated); + } JetModifierList modifierList = null; if (annotated instanceof ConstructorDescriptor && psiElement instanceof JetClass) { diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java index ee3f6086d85..b92daaf9f44 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java @@ -1948,7 +1948,7 @@ public class ExpressionCodegen extends JetVisitor implem KotlinSyntheticClass.Kind.SAM_LAMBDA); } - Type asmType = state.getSamWrapperClasses().getSamWrapperClass(samType, expression.getContainingJetFile()); + Type asmType = state.getSamWrapperClasses().getSamWrapperClass(samType, expression.getContainingJetFile(), getParentCodegen()); v.anew(asmType); v.dup(); diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java index 2c3f74b8ed4..131c9d65fda 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java @@ -68,7 +68,6 @@ import static org.jetbrains.jet.codegen.JvmSerializationBindings.*; import static org.jetbrains.jet.codegen.binding.CodegenBinding.isLocalNamedFun; import static org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor.Kind.DECLARATION; 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; @@ -764,48 +763,50 @@ public class FunctionCodegen extends ParentCodegenAware { public void genDelegate( FunctionDescriptor functionDescriptor, - ClassDescriptor toClass, - StackValue field, - JvmMethodSignature jvmDelegateMethodSignature, - JvmMethodSignature jvmOverriddenMethodSignature + final ClassDescriptor toClass, + final StackValue field, + final JvmMethodSignature jvmDelegateMethodSignature, + final JvmMethodSignature jvmOverriddenMethodSignature ) { - Method overriddenMethod = jvmOverriddenMethodSignature.getAsmMethod(); - Method delegateMethod = jvmDelegateMethodSignature.getAsmMethod(); + generateMethod(OtherOrigin(functionDescriptor), + jvmDelegateMethodSignature, + functionDescriptor, + new FunctionGenerationStrategy() { + @Override + public void generateBody( + @NotNull MethodVisitor mv, + @NotNull JvmMethodSignature signature, + @NotNull MethodContext context, + @NotNull MemberCodegen parentCodegen + ) { + Method overriddenMethod = jvmOverriddenMethodSignature.getAsmMethod(); + Method delegateMethod = jvmDelegateMethodSignature.getAsmMethod(); - int flags = ACC_PUBLIC; + Type[] argTypes = delegateMethod.getArgumentTypes(); + Type[] originalArgTypes = overriddenMethod.getArgumentTypes(); - MethodVisitor mv = v.newMethod(OtherOrigin(functionDescriptor), flags, delegateMethod.getName(), delegateMethod.getDescriptor(), null, - getThrownExceptions(functionDescriptor, typeMapper)); - if (state.getClassBuilderMode() != ClassBuilderMode.FULL) return; + InstructionAdapter iv = new InstructionAdapter(mv); + iv.load(0, OBJECT_TYPE); + field.put(field.type, iv); + for (int i = 0, reg = 1; i < argTypes.length; i++) { + StackValue.local(reg, argTypes[i]).put(originalArgTypes[i], iv); + //noinspection AssignmentToForLoopParameter + reg += argTypes[i].getSize(); + } - mv.visitCode(); + String internalName = typeMapper.mapType(toClass).getInternalName(); + if (toClass.getKind() == ClassKind.TRAIT) { + iv.invokeinterface(internalName, overriddenMethod.getName(), overriddenMethod.getDescriptor()); + } + else { + iv.invokevirtual(internalName, overriddenMethod.getName(), overriddenMethod.getDescriptor()); + } - Type[] argTypes = delegateMethod.getArgumentTypes(); - Type[] originalArgTypes = overriddenMethod.getArgumentTypes(); + StackValue.onStack(overriddenMethod.getReturnType()).put(delegateMethod.getReturnType(), iv); - InstructionAdapter iv = new InstructionAdapter(mv); - iv.load(0, OBJECT_TYPE); - field.put(field.type, iv); - for (int i = 0, reg = 1; i < argTypes.length; i++) { - StackValue.local(reg, argTypes[i]).put(originalArgTypes[i], iv); - //noinspection AssignmentToForLoopParameter - reg += argTypes[i].getSize(); - } - - String internalName = typeMapper.mapType(toClass).getInternalName(); - if (toClass.getKind() == ClassKind.TRAIT) { - iv.invokeinterface(internalName, overriddenMethod.getName(), overriddenMethod.getDescriptor()); - } - else { - iv.invokevirtual(internalName, overriddenMethod.getName(), overriddenMethod.getDescriptor()); - } - - StackValue.onStack(overriddenMethod.getReturnType()).put(delegateMethod.getReturnType(), iv); - - iv.areturn(delegateMethod.getReturnType()); - endVisit(mv, "Delegate method " + functionDescriptor + " to " + jvmOverriddenMethodSignature, - descriptorToDeclaration(bindingContext, functionDescriptor.getContainingDeclaration())); - - generateBridges(functionDescriptor); + iv.areturn(delegateMethod.getReturnType()); + } + } + ); } } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/SamWrapperClasses.java b/compiler/backend/src/org/jetbrains/jet/codegen/SamWrapperClasses.java index 0d88829d491..23f13042450 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/SamWrapperClasses.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/SamWrapperClasses.java @@ -37,12 +37,12 @@ public class SamWrapperClasses { } @NotNull - public Type getSamWrapperClass(@NotNull final SamType samType, @NotNull final JetFile file) { + public Type getSamWrapperClass(@NotNull final SamType samType, @NotNull final JetFile file, @NotNull final MemberCodegen parentCodegen) { return ContainerUtil.getOrCreate(samInterfaceToWrapperClass, Pair.create(samType, file), new Factory() { @Override public Type create() { - return new SamWrapperCodegen(state, samType).genWrapper(file); + return new SamWrapperCodegen(state, samType, parentCodegen).genWrapper(file); } }); } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/SamWrapperCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/SamWrapperCodegen.java index 82e54bdb209..0a1f6f6e08c 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/SamWrapperCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/SamWrapperCodegen.java @@ -24,8 +24,8 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter; import org.jetbrains.jet.codegen.context.CodegenContext; import org.jetbrains.jet.codegen.state.GenerationState; import org.jetbrains.jet.codegen.state.JetTypeMapper; -import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; -import org.jetbrains.jet.lang.descriptors.SimpleFunctionDescriptor; +import org.jetbrains.jet.lang.descriptors.*; +import org.jetbrains.jet.lang.descriptors.impl.ClassDescriptorImpl; import org.jetbrains.jet.lang.psi.JetFile; import org.jetbrains.jet.lang.resolve.DescriptorUtils; import org.jetbrains.jet.lang.resolve.java.PackageClassUtils; @@ -34,11 +34,12 @@ import org.jetbrains.jet.lang.resolve.name.FqName; import org.jetbrains.jet.lang.resolve.name.Name; import org.jetbrains.jet.lang.types.JetType; -import static org.jetbrains.jet.codegen.AsmUtil.NO_FLAG_PACKAGE_PRIVATE; -import static org.jetbrains.jet.codegen.AsmUtil.writeKotlinSyntheticClassAnnotation; -import static org.jetbrains.jet.lang.resolve.java.diagnostics.DiagnosticsPackage.OtherOrigin; +import java.util.Collections; + +import static org.jetbrains.jet.codegen.AsmUtil.*; import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.OBJECT_TYPE; import static org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames.KotlinSyntheticClass; +import static org.jetbrains.jet.lang.resolve.java.diagnostics.DiagnosticsPackage.OtherOrigin; import static org.jetbrains.org.objectweb.asm.Opcodes.*; public class SamWrapperCodegen { @@ -47,21 +48,37 @@ public class SamWrapperCodegen { private final GenerationState state; private final JetTypeMapper typeMapper; private final SamType samType; + private final MemberCodegen parentCodegen; - public SamWrapperCodegen(@NotNull GenerationState state, @NotNull SamType samType) { + public SamWrapperCodegen(@NotNull GenerationState state, @NotNull SamType samType, @NotNull MemberCodegen parentCodegen) { this.state = state; this.typeMapper = state.getTypeMapper(); this.samType = samType; + this.parentCodegen = parentCodegen; } public Type genWrapper(@NotNull JetFile file) { // Name for generated class, in form of whatever$1 - Type asmType = Type.getObjectType(getWrapperName(file)); + FqName fqName = getWrapperName(file); + Type asmType = asmTypeByFqNameWithoutInnerClasses(fqName); // e.g. (T, T) -> Int JetType functionType = samType.getKotlinFunctionType(); + + ClassDescriptor classDescriptor = new ClassDescriptorImpl( + samType.getJavaClassDescriptor().getContainingDeclaration(), + fqName.shortName(), + Modality.FINAL, + Collections.singleton(samType.getType()) + ); // e.g. compare(T, T) - SimpleFunctionDescriptor erasedInterfaceFunction = samType.getAbstractMethod().getOriginal(); + SimpleFunctionDescriptor erasedInterfaceFunction = samType.getAbstractMethod().getOriginal().copy( + classDescriptor, + Modality.FINAL, + Visibilities.PUBLIC, + CallableMemberDescriptor.Kind.SYNTHESIZED, + /*copyOverrides=*/ false + ); ClassBuilder cv = state.getFactory().newVisitor(OtherOrigin(erasedInterfaceFunction), asmType, file); cv.defineClass(file, @@ -124,7 +141,8 @@ public class SamWrapperCodegen { JetType functionJetType ) { // using static context to avoid creating ClassDescriptor and everything else - FunctionCodegen codegen = new FunctionCodegen(CodegenContext.STATIC, cv, state, null); + FunctionCodegen codegen = new FunctionCodegen(CodegenContext.STATIC.intoClass( + (ClassDescriptor) erasedInterfaceFunction.getContainingDeclaration(), OwnerKind.IMPLEMENTATION, state), cv, state, parentCodegen); FunctionDescriptor invokeFunction = functionJetType.getMemberScope() .getFunctions(Name.identifier("invoke")).iterator().next().getOriginal(); @@ -133,14 +151,14 @@ public class SamWrapperCodegen { } @NotNull - private String getWrapperName(@NotNull JetFile containingFile) { + private FqName getWrapperName(@NotNull JetFile containingFile) { FqName packageClassFqName = PackageClassUtils.getPackageClassFqName(containingFile.getPackageFqName()); - String packageInternalName = AsmUtil.internalNameByFqNameWithoutInnerClasses(packageClassFqName); JavaClassDescriptor descriptor = samType.getJavaClassDescriptor(); - return packageInternalName + "$sam$" + descriptor.getName().asString() + "$" + - Integer.toHexString( - PackagePartClassUtils.getPathHashCode(containingFile.getVirtualFile()) * 31 + - DescriptorUtils.getFqNameSafe(descriptor).hashCode() - ); + String shortName = packageClassFqName.shortName().asString() + "$sam$" + descriptor.getName().asString() + "$" + + Integer.toHexString( + PackagePartClassUtils.getPathHashCode(containingFile.getVirtualFile()) * 31 + + DescriptorUtils.getFqNameSafe(descriptor).hashCode() + ); + return packageClassFqName.parent().child(Name.identifier(shortName)); } } diff --git a/compiler/testData/asJava/lightClasses/delegation/Function.java b/compiler/testData/asJava/lightClasses/delegation/Function.java new file mode 100644 index 00000000000..3f57046f3c5 --- /dev/null +++ b/compiler/testData/asJava/lightClasses/delegation/Function.java @@ -0,0 +1,7 @@ +public final class Derived implements kotlin.jvm.internal.KObject, Base { + @org.jetbrains.annotations.NotNull + public Derived(@org.jetbrains.annotations.NotNull @jet.runtime.typeinfo.JetValueParameter(name = "x") Base x) { /* compiled code */ } + + @org.jetbrains.annotations.NotNull + public java.lang.String baz(@org.jetbrains.annotations.NotNull @jet.runtime.typeinfo.JetValueParameter(name = "s") java.lang.String s) { /* compiled code */ } +} \ No newline at end of file diff --git a/compiler/testData/asJava/lightClasses/delegation/Function.kt b/compiler/testData/asJava/lightClasses/delegation/Function.kt new file mode 100644 index 00000000000..9fda4a47c45 --- /dev/null +++ b/compiler/testData/asJava/lightClasses/delegation/Function.kt @@ -0,0 +1,7 @@ +// Derived + +trait Base { + fun baz(s: String): String +} + +class Derived(x: Base): Base by x \ No newline at end of file diff --git a/compiler/testData/asJava/lightClasses/delegation/Property.java b/compiler/testData/asJava/lightClasses/delegation/Property.java new file mode 100644 index 00000000000..7a570ffd6b7 --- /dev/null +++ b/compiler/testData/asJava/lightClasses/delegation/Property.java @@ -0,0 +1,7 @@ +public final class Derived implements kotlin.jvm.internal.KObject, Base { + @org.jetbrains.annotations.NotNull + public Derived(@org.jetbrains.annotations.NotNull @jet.runtime.typeinfo.JetValueParameter(name = "x") Base x) { /* compiled code */ } + + @org.jetbrains.annotations.NotNull + public java.lang.String getBoo() { /* compiled code */ } +} \ No newline at end of file diff --git a/compiler/testData/asJava/lightClasses/delegation/Property.kt b/compiler/testData/asJava/lightClasses/delegation/Property.kt new file mode 100644 index 00000000000..ce590bfa050 --- /dev/null +++ b/compiler/testData/asJava/lightClasses/delegation/Property.kt @@ -0,0 +1,7 @@ +// Derived + +trait Base { + val boo: String +} + +class Derived(x: Base): Base by x \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/jet/asJava/KotlinLightClassTestGenerated.java b/compiler/tests/org/jetbrains/jet/asJava/KotlinLightClassTestGenerated.java index d5595152e4c..2ce4a5ad997 100644 --- a/compiler/tests/org/jetbrains/jet/asJava/KotlinLightClassTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/asJava/KotlinLightClassTestGenerated.java @@ -16,24 +16,45 @@ package org.jetbrains.jet.asJava; +import junit.framework.Assert; import junit.framework.Test; import junit.framework.TestSuite; + +import java.io.File; +import java.util.regex.Pattern; import org.jetbrains.jet.JetTestUtils; import org.jetbrains.jet.test.InnerTestClasses; import org.jetbrains.jet.test.TestMetadata; -import java.io.File; -import java.util.regex.Pattern; +import org.jetbrains.jet.asJava.AbstractKotlinLightClassTest; /** This class is generated by {@link org.jetbrains.jet.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ @SuppressWarnings("all") @TestMetadata("compiler/testData/asJava/lightClasses") -@InnerTestClasses({KotlinLightClassTestGenerated.NullabilityAnnotations.class}) +@InnerTestClasses({KotlinLightClassTestGenerated.Delegation.class, KotlinLightClassTestGenerated.NullabilityAnnotations.class}) public class KotlinLightClassTestGenerated extends AbstractKotlinLightClassTest { public void testAllFilesPresentInLightClasses() throws Exception { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/asJava/lightClasses"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("compiler/testData/asJava/lightClasses/delegation") + public static class Delegation extends AbstractKotlinLightClassTest { + public void testAllFilesPresentInDelegation() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/asJava/lightClasses/delegation"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("Function.kt") + public void testFunction() throws Exception { + doTest("compiler/testData/asJava/lightClasses/delegation/Function.kt"); + } + + @TestMetadata("Property.kt") + public void testProperty() throws Exception { + doTest("compiler/testData/asJava/lightClasses/delegation/Property.kt"); + } + + } + @TestMetadata("compiler/testData/asJava/lightClasses/nullabilityAnnotations") public static class NullabilityAnnotations extends AbstractKotlinLightClassTest { public void testAllFilesPresentInNullabilityAnnotations() throws Exception { @@ -105,6 +126,7 @@ public class KotlinLightClassTestGenerated extends AbstractKotlinLightClassTest public static Test suite() { TestSuite suite = new TestSuite("KotlinLightClassTestGenerated"); suite.addTestSuite(KotlinLightClassTestGenerated.class); + suite.addTestSuite(Delegation.class); suite.addTestSuite(NullabilityAnnotations.class); return suite; }