Generate @NotNull annotations on delegated functions

This commit is contained in:
Andrey Breslav
2014-05-25 12:32:03 +02:00
parent afca70eb41
commit 8e4954ab67
10 changed files with 137 additions and 61 deletions
@@ -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) {
@@ -1948,7 +1948,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> 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();
@@ -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());
}
}
);
}
}
@@ -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<Type>() {
@Override
public Type create() {
return new SamWrapperCodegen(state, samType).genWrapper(file);
return new SamWrapperCodegen(state, samType, parentCodegen).genWrapper(file);
}
});
}
@@ -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));
}
}
@@ -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 */ }
}
@@ -0,0 +1,7 @@
// Derived
trait Base {
fun baz(s: String): String
}
class Derived(x: Base): Base by x
@@ -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 */ }
}
@@ -0,0 +1,7 @@
// Derived
trait Base {
val boo: String
}
class Derived(x: Base): Base by x
@@ -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;
}