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) {
if (declaration instanceof KtProperty || declaration instanceof KtNamedFunction) {
genFunctionOrProperty(declaration);
if (declaration instanceof KtProperty || declaration instanceof KtNamedFunction || declaration instanceof KtTypeAlias) {
genSimpleMember(declaration);
}
else if (declaration instanceof KtClassOrObject) {
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.Nullable;
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.inline.*;
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.types.ErrorUtils;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.org.objectweb.asm.AnnotationVisitor;
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.*;
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
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.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.DescriptorUtils.*;
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.*;
@@ -182,10 +181,10 @@ public abstract class MemberCodegen<T extends KtElement/* TODO: & JetDeclaration
v.done();
}
public void genFunctionOrProperty(@NotNull KtDeclaration functionOrProperty) {
if (functionOrProperty instanceof KtNamedFunction) {
public void genSimpleMember(@NotNull KtDeclaration declaration) {
if (declaration instanceof KtNamedFunction) {
try {
functionCodegen.gen((KtNamedFunction) functionOrProperty);
functionCodegen.gen((KtNamedFunction) declaration);
}
catch (ProcessCanceledException e) {
throw e;
@@ -194,12 +193,12 @@ public abstract class MemberCodegen<T extends KtElement/* TODO: & JetDeclaration
throw 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 {
propertyCodegen.gen((KtProperty) functionOrProperty);
propertyCodegen.gen((KtProperty) declaration);
}
catch (ProcessCanceledException e) {
throw e;
@@ -208,14 +207,46 @@ public abstract class MemberCodegen<T extends KtElement/* TODO: & JetDeclaration
throw 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 {
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(
@NotNull CodegenContext parentContext,
@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.state.GenerationState
import org.jetbrains.kotlin.config.IncrementalCompilation
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
@@ -104,7 +101,7 @@ class MultifileClassCodegen(
result
}
private val delegateGenerationTasks = hashMapOf<CallableMemberDescriptor, () -> Unit>()
private val delegateGenerationTasks = hashMapOf<MemberDescriptor, () -> Unit>()
private fun getSuperClassForPart(partInternalName: String) =
if (shouldGeneratePartHierarchy)
@@ -249,17 +246,17 @@ class MultifileClassCodegen(
val facadeContext = state.rootContext.intoMultifileClass(packageFragment, facadeClassType, partType)
val memberCodegen = createCodegenForDelegatesInMultifileFacade(facadeContext)
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)
if (descriptor !is CallableMemberDescriptor) {
if (descriptor !is MemberDescriptor) {
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 (AsmUtil.getVisibilityAccessFlag(descriptor) == Opcodes.ACC_PRIVATE) return false
@@ -272,7 +269,7 @@ class MultifileClassCodegen(
return true
}
private fun addDelegateGenerationTaskIfNeeded(callable: CallableMemberDescriptor, task: () -> Unit) {
private fun addDelegateGenerationTaskIfNeeded(callable: MemberDescriptor, task: () -> Unit) {
if (shouldGenerateInFacade(callable)) {
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.KtNamedFunction
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.psi.KtTypeAlias
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.jvm.diagnostics.MultifileClass
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
@@ -127,8 +128,8 @@ class MultifileClassPartCodegen(
override fun generateBody() {
for (declaration in element.declarations) {
if (declaration is KtNamedFunction || declaration is KtProperty) {
genFunctionOrProperty(declaration)
if (declaration is KtNamedFunction || declaration is KtProperty || declaration is KtTypeAlias) {
genSimpleMember(declaration)
}
}
@@ -91,8 +91,8 @@ public class PackagePartCodegen extends MemberCodegen<KtFile> {
@Override
protected void generateBody() {
for (KtDeclaration declaration : element.getDeclarations()) {
if (declaration instanceof KtNamedFunction || declaration instanceof KtProperty) {
genFunctionOrProperty(declaration);
if (declaration instanceof KtNamedFunction || declaration instanceof KtProperty || declaration instanceof KtTypeAlias) {
genSimpleMember(declaration);
}
}
@@ -224,8 +224,8 @@ public class ScriptCodegen extends MemberCodegen<KtScript> {
private void genMembers() {
for (KtDeclaration declaration : scriptDeclaration.getDeclarations()) {
if (declaration instanceof KtProperty || declaration instanceof KtNamedFunction) {
genFunctionOrProperty(declaration);
if (declaration instanceof KtProperty || declaration instanceof KtNamedFunction || declaration instanceof KtTypeAlias) {
genSimpleMember(declaration);
}
else if (declaration instanceof KtClassOrObject) {
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);
}
@TestMetadata("annotationsOnTypeAliases.kt")
public void testAnnotationsOnTypeAliases() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/annotations/annotationsOnTypeAliases.kt");
doTest(fileName);
}
@TestMetadata("defaultParameterValues.kt")
public void testDefaultParameterValues() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/annotations/defaultParameterValues.kt");
@@ -41,6 +41,12 @@ public class CompileKotlinAgainstKotlinTestGenerated extends AbstractCompileKotl
doTest(fileName);
}
@TestMetadata("annotationsOnTypeAliases.kt")
public void testAnnotationsOnTypeAliases() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/compileKotlinAgainstKotlin/annotationsOnTypeAliases.kt");
doTest(fileName);
}
@TestMetadata("callsToMultifileClassFromOtherPackage.kt")
public void testCallsToMultifileClassFromOtherPackage() throws Exception {
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_PROPERTIES_ARRAY_NAME = "$$delegatedProperties";
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";
@@ -62,6 +64,11 @@ public final class JvmAbi {
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) {
return name.startsWith(GET_PREFIX) || name.startsWith(IS_PREFIX);
}