Annotations on type aliases: generate synthetic method for type alias annotations.

This commit is contained in:
Dmitry Petrov
2016-10-10 17:13:48 +03:00
parent d2d8f72ffc
commit 3d0288ffed
11 changed files with 144 additions and 31 deletions
@@ -120,8 +120,8 @@ public abstract class ClassBodyCodegen extends MemberCodegen<KtClassOrObject> {
} }
protected void generateDeclaration(KtDeclaration declaration) { protected void generateDeclaration(KtDeclaration declaration) {
if (declaration instanceof KtProperty || declaration instanceof KtNamedFunction) { if (declaration instanceof KtProperty || declaration instanceof KtNamedFunction || declaration instanceof KtTypeAlias) {
genFunctionOrProperty(declaration); genSimpleMember(declaration);
} }
else if (declaration instanceof KtClassOrObject) { else if (declaration instanceof KtClassOrObject) {
if (declaration instanceof KtEnumEntry && !enumEntryNeedSubclass(bindingContext, (KtEnumEntry) declaration)) { if (declaration instanceof KtEnumEntry && !enumEntryNeedSubclass(bindingContext, (KtEnumEntry) declaration)) {
@@ -24,6 +24,7 @@ import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.backend.common.CodegenUtil; import org.jetbrains.kotlin.backend.common.CodegenUtil;
import org.jetbrains.kotlin.codegen.annotation.AnnotatedSimple;
import org.jetbrains.kotlin.codegen.context.*; import org.jetbrains.kotlin.codegen.context.*;
import org.jetbrains.kotlin.codegen.inline.*; import org.jetbrains.kotlin.codegen.inline.*;
import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension; import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension;
@@ -56,10 +57,7 @@ import org.jetbrains.kotlin.storage.LockBasedStorageManager;
import org.jetbrains.kotlin.storage.NotNullLazyValue; import org.jetbrains.kotlin.storage.NotNullLazyValue;
import org.jetbrains.kotlin.types.ErrorUtils; import org.jetbrains.kotlin.types.ErrorUtils;
import org.jetbrains.kotlin.types.KotlinType; import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.org.objectweb.asm.AnnotationVisitor; import org.jetbrains.org.objectweb.asm.*;
import org.jetbrains.org.objectweb.asm.Label;
import org.jetbrains.org.objectweb.asm.MethodVisitor;
import org.jetbrains.org.objectweb.asm.Type;
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter; import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
import org.jetbrains.org.objectweb.asm.commons.Method; import org.jetbrains.org.objectweb.asm.commons.Method;
@@ -67,6 +65,7 @@ import java.util.*;
import static org.jetbrains.kotlin.codegen.AsmUtil.*; import static org.jetbrains.kotlin.codegen.AsmUtil.*;
import static org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.SYNTHESIZED; import static org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.SYNTHESIZED;
import static org.jetbrains.kotlin.resolve.BindingContext.TYPE_ALIAS;
import static org.jetbrains.kotlin.resolve.BindingContext.VARIABLE; import static org.jetbrains.kotlin.resolve.BindingContext.VARIABLE;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.*; import static org.jetbrains.kotlin.resolve.DescriptorUtils.*;
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.*; import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.*;
@@ -182,10 +181,10 @@ public abstract class MemberCodegen<T extends KtElement/* TODO: & JetDeclaration
v.done(); v.done();
} }
public void genFunctionOrProperty(@NotNull KtDeclaration functionOrProperty) { public void genSimpleMember(@NotNull KtDeclaration declaration) {
if (functionOrProperty instanceof KtNamedFunction) { if (declaration instanceof KtNamedFunction) {
try { try {
functionCodegen.gen((KtNamedFunction) functionOrProperty); functionCodegen.gen((KtNamedFunction) declaration);
} }
catch (ProcessCanceledException e) { catch (ProcessCanceledException e) {
throw e; throw e;
@@ -194,12 +193,12 @@ public abstract class MemberCodegen<T extends KtElement/* TODO: & JetDeclaration
throw e; throw e;
} }
catch (Exception e) { catch (Exception e) {
throw new CompilationException("Failed to generate function " + functionOrProperty.getName(), e, functionOrProperty); throw new CompilationException("Failed to generate function " + declaration.getName(), e, declaration);
} }
} }
else if (functionOrProperty instanceof KtProperty) { else if (declaration instanceof KtProperty) {
try { try {
propertyCodegen.gen((KtProperty) functionOrProperty); propertyCodegen.gen((KtProperty) declaration);
} }
catch (ProcessCanceledException e) { catch (ProcessCanceledException e) {
throw e; throw e;
@@ -208,14 +207,46 @@ public abstract class MemberCodegen<T extends KtElement/* TODO: & JetDeclaration
throw e; throw e;
} }
catch (Exception e) { catch (Exception e) {
throw new CompilationException("Failed to generate property " + functionOrProperty.getName(), e, functionOrProperty); throw new CompilationException("Failed to generate property " + declaration.getName(), e, declaration);
} }
} }
else if (declaration instanceof KtTypeAlias) {
genTypeAlias((KtTypeAlias) declaration);
}
else { else {
throw new IllegalArgumentException("Unknown parameter: " + functionOrProperty); throw new IllegalArgumentException("Unknown parameter: " + declaration);
} }
} }
private void genTypeAlias(@NotNull KtTypeAlias typeAlias) {
if (!state.getClassBuilderMode().generateMetadata) return;
TypeAliasDescriptor typeAliasDescriptor = bindingContext.get(TYPE_ALIAS, typeAlias);
if (typeAliasDescriptor == null) {
throw ExceptionLogger.logDescriptorNotFound("Type alias " + typeAlias.getName() + " should have a descriptor", typeAlias);
}
genTypeAliasAnnotationsMethodIfRequired(typeAliasDescriptor);
}
private void genTypeAliasAnnotationsMethodIfRequired(TypeAliasDescriptor typeAliasDescriptor) {
boolean isAnnotationsMethodOwner = CodegenContextUtil.isImplClassOwner(context);
Annotations annotations = typeAliasDescriptor.getAnnotations();
if (!isAnnotationsMethodOwner || annotations.getAllAnnotations().isEmpty()) return;
int flags = ACC_DEPRECATED | ACC_PRIVATE | ACC_STATIC | ACC_SYNTHETIC;
String name = JvmAbi.getSyntheticMethodNameForAnnotatedTypeAlias(typeAliasDescriptor.getName());
String desc = "()V";
Method syntheticMethod = new Method(name, desc);
MethodVisitor mv = v.newMethod(JvmDeclarationOriginKt.OtherOrigin(typeAliasDescriptor), flags, syntheticMethod.getName(),
syntheticMethod.getDescriptor(), null, null);
AnnotationCodegen.forMethod(mv, this, typeMapper).genAnnotations(new AnnotatedSimple(annotations), Type.VOID_TYPE, null);
mv.visitCode();
mv.visitInsn(Opcodes.RETURN);
mv.visitEnd();
}
public static void genClassOrObject( public static void genClassOrObject(
@NotNull CodegenContext parentContext, @NotNull CodegenContext parentContext,
@NotNull KtClassOrObject aClass, @NotNull KtClassOrObject aClass,
@@ -24,10 +24,7 @@ import org.jetbrains.kotlin.codegen.context.FieldOwnerContext
import org.jetbrains.kotlin.codegen.context.MethodContext import org.jetbrains.kotlin.codegen.context.MethodContext
import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.config.IncrementalCompilation import org.jetbrains.kotlin.config.IncrementalCompilation
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil
import org.jetbrains.kotlin.load.java.JvmAnnotationNames import org.jetbrains.kotlin.load.java.JvmAnnotationNames
@@ -104,7 +101,7 @@ class MultifileClassCodegen(
result result
} }
private val delegateGenerationTasks = hashMapOf<CallableMemberDescriptor, () -> Unit>() private val delegateGenerationTasks = hashMapOf<MemberDescriptor, () -> Unit>()
private fun getSuperClassForPart(partInternalName: String) = private fun getSuperClassForPart(partInternalName: String) =
if (shouldGeneratePartHierarchy) if (shouldGeneratePartHierarchy)
@@ -249,17 +246,17 @@ class MultifileClassCodegen(
val facadeContext = state.rootContext.intoMultifileClass(packageFragment, facadeClassType, partType) val facadeContext = state.rootContext.intoMultifileClass(packageFragment, facadeClassType, partType)
val memberCodegen = createCodegenForDelegatesInMultifileFacade(facadeContext) val memberCodegen = createCodegenForDelegatesInMultifileFacade(facadeContext)
for (declaration in file.declarations) { for (declaration in file.declarations) {
if (declaration is KtNamedFunction || declaration is KtProperty) { if (declaration is KtNamedFunction || declaration is KtProperty || declaration is KtTypeAlias) {
val descriptor = state.bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, declaration) val descriptor = state.bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, declaration)
if (descriptor !is CallableMemberDescriptor) { if (descriptor !is MemberDescriptor) {
throw AssertionError("Expected callable member, was " + descriptor + " for " + declaration.text) throw AssertionError("Expected callable member, was " + descriptor + " for " + declaration.text)
} }
addDelegateGenerationTaskIfNeeded(descriptor, { memberCodegen.genFunctionOrProperty(declaration) }) addDelegateGenerationTaskIfNeeded(descriptor, { memberCodegen.genSimpleMember(declaration) })
} }
} }
} }
private fun shouldGenerateInFacade(descriptor: CallableMemberDescriptor): Boolean { private fun shouldGenerateInFacade(descriptor: MemberDescriptor): Boolean {
if (Visibilities.isPrivate(descriptor.visibility)) return false if (Visibilities.isPrivate(descriptor.visibility)) return false
if (AsmUtil.getVisibilityAccessFlag(descriptor) == Opcodes.ACC_PRIVATE) return false if (AsmUtil.getVisibilityAccessFlag(descriptor) == Opcodes.ACC_PRIVATE) return false
@@ -272,7 +269,7 @@ class MultifileClassCodegen(
return true return true
} }
private fun addDelegateGenerationTaskIfNeeded(callable: CallableMemberDescriptor, task: () -> Unit) { private fun addDelegateGenerationTaskIfNeeded(callable: MemberDescriptor, task: () -> Unit) {
if (shouldGenerateInFacade(callable)) { if (shouldGenerateInFacade(callable)) {
delegateGenerationTasks[callable] = task delegateGenerationTasks[callable] = task
} }
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader.MultifileClassK
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.psi.KtTypeAlias
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.jvm.diagnostics.MultifileClass import org.jetbrains.kotlin.resolve.jvm.diagnostics.MultifileClass
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
@@ -127,8 +128,8 @@ class MultifileClassPartCodegen(
override fun generateBody() { override fun generateBody() {
for (declaration in element.declarations) { for (declaration in element.declarations) {
if (declaration is KtNamedFunction || declaration is KtProperty) { if (declaration is KtNamedFunction || declaration is KtProperty || declaration is KtTypeAlias) {
genFunctionOrProperty(declaration) genSimpleMember(declaration)
} }
} }
@@ -91,8 +91,8 @@ public class PackagePartCodegen extends MemberCodegen<KtFile> {
@Override @Override
protected void generateBody() { protected void generateBody() {
for (KtDeclaration declaration : element.getDeclarations()) { for (KtDeclaration declaration : element.getDeclarations()) {
if (declaration instanceof KtNamedFunction || declaration instanceof KtProperty) { if (declaration instanceof KtNamedFunction || declaration instanceof KtProperty || declaration instanceof KtTypeAlias) {
genFunctionOrProperty(declaration); genSimpleMember(declaration);
} }
} }
@@ -224,8 +224,8 @@ public class ScriptCodegen extends MemberCodegen<KtScript> {
private void genMembers() { private void genMembers() {
for (KtDeclaration declaration : scriptDeclaration.getDeclarations()) { for (KtDeclaration declaration : scriptDeclaration.getDeclarations()) {
if (declaration instanceof KtProperty || declaration instanceof KtNamedFunction) { if (declaration instanceof KtProperty || declaration instanceof KtNamedFunction || declaration instanceof KtTypeAlias) {
genFunctionOrProperty(declaration); genSimpleMember(declaration);
} }
else if (declaration instanceof KtClassOrObject) { else if (declaration instanceof KtClassOrObject) {
genClassOrObject((KtClassOrObject) declaration); genClassOrObject((KtClassOrObject) declaration);
@@ -0,0 +1,31 @@
// WITH_RUNTIME
// FULL_JDK
import kotlin.annotation.AnnotationTarget.*
import kotlin.annotation.AnnotationRetention.*
import java.lang.Class
@Target(TYPEALIAS)
@Retention(RUNTIME)
annotation class Ann(val x: Int)
class C {
@Ann(1)
typealias TA = Any
}
@Ann(2)
typealias TA = Any
fun Class<*>.assertHasDeclaredMethodWithAnn() {
if (!declaredMethods.any { it.isSynthetic && it.getAnnotation(Ann::class.java) != null }) {
throw java.lang.AssertionError("Class ${this.simpleName} has no declared method with annotation @Ann")
}
}
fun box(): String {
Class.forName("AnnotationsOnTypeAliasesKt").assertHasDeclaredMethodWithAnn()
Class.forName("C").assertHasDeclaredMethodWithAnn()
return "OK"
}
@@ -0,0 +1,34 @@
// FILE: A.kt
package a
import kotlin.annotation.AnnotationTarget.*
import kotlin.annotation.AnnotationRetention.*
import java.lang.Class
@Target(TYPEALIAS)
@Retention(RUNTIME)
annotation class Ann(val x: Int)
class C {
@Ann(1)
typealias TA = Any
}
@Ann(2)
typealias TA = Any
// FILE: B.kt
import a.Ann
fun Class<*>.assertHasDeclaredMethodWithAnn() {
if (!declaredMethods.any { it.isSynthetic && it.getAnnotation(Ann::class.java) != null }) {
throw java.lang.AssertionError("Class ${this.simpleName} has no declared method with annotation @Ann")
}
}
fun box(): String {
Class.forName("a.AKt").assertHasDeclaredMethodWithAnn()
Class.forName("a.C").assertHasDeclaredMethodWithAnn()
return "OK"
}
@@ -73,6 +73,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("annotationsOnTypeAliases.kt")
public void testAnnotationsOnTypeAliases() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/annotations/annotationsOnTypeAliases.kt");
doTest(fileName);
}
@TestMetadata("defaultParameterValues.kt") @TestMetadata("defaultParameterValues.kt")
public void testDefaultParameterValues() throws Exception { public void testDefaultParameterValues() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/annotations/defaultParameterValues.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/annotations/defaultParameterValues.kt");
@@ -41,6 +41,12 @@ public class CompileKotlinAgainstKotlinTestGenerated extends AbstractCompileKotl
doTest(fileName); doTest(fileName);
} }
@TestMetadata("annotationsOnTypeAliases.kt")
public void testAnnotationsOnTypeAliases() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/compileKotlinAgainstKotlin/annotationsOnTypeAliases.kt");
doTest(fileName);
}
@TestMetadata("callsToMultifileClassFromOtherPackage.kt") @TestMetadata("callsToMultifileClassFromOtherPackage.kt")
public void testCallsToMultifileClassFromOtherPackage() throws Exception { public void testCallsToMultifileClassFromOtherPackage() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/compileKotlinAgainstKotlin/callsToMultifileClassFromOtherPackage.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/compileKotlinAgainstKotlin/callsToMultifileClassFromOtherPackage.kt");
@@ -45,7 +45,9 @@ public final class JvmAbi {
public static final String DELEGATED_PROPERTY_NAME_SUFFIX = "$delegate"; public static final String DELEGATED_PROPERTY_NAME_SUFFIX = "$delegate";
public static final String DELEGATED_PROPERTIES_ARRAY_NAME = "$$delegatedProperties"; public static final String DELEGATED_PROPERTIES_ARRAY_NAME = "$$delegatedProperties";
public static final String DELEGATE_SUPER_FIELD_PREFIX = "$$delegate_"; public static final String DELEGATE_SUPER_FIELD_PREFIX = "$$delegate_";
public static final String ANNOTATED_PROPERTY_METHOD_NAME_SUFFIX = "$annotations"; private static final String ANNOTATIONS_SUFFIX = "$annotations";
public static final String ANNOTATED_PROPERTY_METHOD_NAME_SUFFIX = ANNOTATIONS_SUFFIX;
public static final String ANNOTATED_TYPEALIAS_METHOD_NAME_SUFFIX = ANNOTATIONS_SUFFIX;
public static final String INSTANCE_FIELD = "INSTANCE"; public static final String INSTANCE_FIELD = "INSTANCE";
@@ -62,6 +64,11 @@ public final class JvmAbi {
return propertyName.asString() + ANNOTATED_PROPERTY_METHOD_NAME_SUFFIX; return propertyName.asString() + ANNOTATED_PROPERTY_METHOD_NAME_SUFFIX;
} }
@NotNull
public static String getSyntheticMethodNameForAnnotatedTypeAlias(@NotNull Name typeAliasName) {
return typeAliasName.asString() + ANNOTATED_TYPEALIAS_METHOD_NAME_SUFFIX;
}
public static boolean isGetterName(@NotNull String name) { public static boolean isGetterName(@NotNull String name) {
return name.startsWith(GET_PREFIX) || name.startsWith(IS_PREFIX); return name.startsWith(GET_PREFIX) || name.startsWith(IS_PREFIX);
} }