Support @property annotations

This commit is contained in:
Yan Zhulanow
2015-07-16 18:36:29 +03:00
parent b1a28bcc6a
commit 93579564c8
6 changed files with 17 additions and 5 deletions
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.codegen.context.*;
import org.jetbrains.kotlin.codegen.state.GenerationState; import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.codegen.state.JetTypeMapper; import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget; import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget;
import org.jetbrains.kotlin.load.java.JvmAbi; import org.jetbrains.kotlin.load.java.JvmAbi;
import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.psi.*;
@@ -110,8 +111,12 @@ public class PropertyCodegen {
} }
else { else {
assert declaration != null : "Declaration is null for different context: " + context; assert declaration != null : "Declaration is null for different context: " + context;
if (!generateBackingField(declaration, descriptor)) {
generateSyntheticMethodIfNeeded(descriptor); List<AnnotationDescriptor> propertyTargetedAnnotations =
descriptor.getAnnotations().getUseSiteTargetedAnnotations(AnnotationUseSiteTarget.PROPERTY);
if (!generateBackingField(declaration, descriptor) || !propertyTargetedAnnotations.isEmpty()) {
generateSyntheticMethodIfNeeded(descriptor, !propertyTargetedAnnotations.isEmpty());
} }
} }
@@ -210,8 +215,8 @@ public class PropertyCodegen {
// Annotations on properties without backing fields are stored in bytecode on an empty synthetic method. This way they're still // Annotations on properties without backing fields are stored in bytecode on an empty synthetic method. This way they're still
// accessible via reflection, and 'deprecated' and 'private' flags prevent this method from being called accidentally // accessible via reflection, and 'deprecated' and 'private' flags prevent this method from being called accidentally
private void generateSyntheticMethodIfNeeded(@NotNull PropertyDescriptor descriptor) { private void generateSyntheticMethodIfNeeded(@NotNull PropertyDescriptor descriptor, boolean hasPropertyTargetedAnnotations) {
if (descriptor.getAnnotations().isEmpty()) return; if (descriptor.getAnnotations().isEmpty() && !hasPropertyTargetedAnnotations) return;
ReceiverParameterDescriptor receiver = descriptor.getExtensionReceiverParameter(); ReceiverParameterDescriptor receiver = descriptor.getExtensionReceiverParameter();
String name = JvmAbi.getSyntheticMethodNameForAnnotatedProperty(descriptor.getName()); String name = JvmAbi.getSyntheticMethodNameForAnnotatedProperty(descriptor.getName());
@@ -220,7 +225,7 @@ public class PropertyCodegen {
if (!isTrait(context.getContextDescriptor()) || kind == OwnerKind.TRAIT_IMPL) { if (!isTrait(context.getContextDescriptor()) || kind == OwnerKind.TRAIT_IMPL) {
int flags = ACC_DEPRECATED | ACC_FINAL | ACC_PRIVATE | ACC_STATIC | ACC_SYNTHETIC; int flags = ACC_DEPRECATED | ACC_FINAL | ACC_PRIVATE | ACC_STATIC | ACC_SYNTHETIC;
MethodVisitor mv = v.newMethod(OtherOrigin(descriptor), flags, name, desc, null, null); MethodVisitor mv = v.newMethod(OtherOrigin(descriptor), flags, name, desc, null, null);
AnnotationCodegen.forMethod(mv, typeMapper).genAnnotations(descriptor, Type.VOID_TYPE); AnnotationCodegen.forMethod(mv, typeMapper).genAnnotations(descriptor, Type.VOID_TYPE, AnnotationUseSiteTarget.PROPERTY);
mv.visitCode(); mv.visitCode();
mv.visitInsn(Opcodes.RETURN); mv.visitInsn(Opcodes.RETURN);
mv.visitEnd(); mv.visitEnd();
@@ -133,6 +133,7 @@ public interface Errors {
DiagnosticFactory0<PsiElement> INAPPLICABLE_FIELD_TARGET = DiagnosticFactory0.create(ERROR); DiagnosticFactory0<PsiElement> INAPPLICABLE_FIELD_TARGET = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> INAPPLICABLE_FIELD_TARGET_NO_BACKING_FIELD = DiagnosticFactory0.create(ERROR); DiagnosticFactory0<PsiElement> INAPPLICABLE_FIELD_TARGET_NO_BACKING_FIELD = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> INAPPLICABLE_PROPERTY_TARGET = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> INAPPLICABLE_GET_TARGET = DiagnosticFactory0.create(ERROR); DiagnosticFactory0<PsiElement> INAPPLICABLE_GET_TARGET = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> INAPPLICABLE_SET_TARGET = DiagnosticFactory0.create(ERROR); DiagnosticFactory0<PsiElement> INAPPLICABLE_SET_TARGET = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> INAPPLICABLE_TARGET_PROPERTY_IMMUTABLE = DiagnosticFactory0.create(ERROR); DiagnosticFactory0<PsiElement> INAPPLICABLE_TARGET_PROPERTY_IMMUTABLE = DiagnosticFactory0.create(ERROR);
@@ -126,6 +126,7 @@ public class DefaultErrorMessages {
MAP.put(INAPPLICABLE_FIELD_TARGET, "''@field:'' annotations could be applied only to property declarations"); MAP.put(INAPPLICABLE_FIELD_TARGET, "''@field:'' annotations could be applied only to property declarations");
MAP.put(INAPPLICABLE_FIELD_TARGET_NO_BACKING_FIELD, "Property has neither a backing field nor a delegate"); MAP.put(INAPPLICABLE_FIELD_TARGET_NO_BACKING_FIELD, "Property has neither a backing field nor a delegate");
MAP.put(INAPPLICABLE_PROPERTY_TARGET, "''@property:'' annotations could be applied only to property declarations");
MAP.put(INAPPLICABLE_GET_TARGET, "''@get:'' annotations could be applied only to property declarations"); MAP.put(INAPPLICABLE_GET_TARGET, "''@get:'' annotations could be applied only to property declarations");
MAP.put(INAPPLICABLE_SET_TARGET, "''@set:'' annotations could be applied only to property declarations"); MAP.put(INAPPLICABLE_SET_TARGET, "''@set:'' annotations could be applied only to property declarations");
MAP.put(INAPPLICABLE_TARGET_PROPERTY_IMMUTABLE, "Property must be mutable"); MAP.put(INAPPLICABLE_TARGET_PROPERTY_IMMUTABLE, "Property must be mutable");
@@ -38,6 +38,7 @@ public class JetAnnotationUseSiteTarget : JetElementImplStub<KotlinPlaceHolderSt
return when (node.getElementType()) { return when (node.getElementType()) {
JetTokens.FIELD_KEYWORD -> AnnotationUseSiteTarget.FIELD JetTokens.FIELD_KEYWORD -> AnnotationUseSiteTarget.FIELD
JetTokens.FILE_KEYWORD -> AnnotationUseSiteTarget.FILE JetTokens.FILE_KEYWORD -> AnnotationUseSiteTarget.FILE
JetTokens.PROPERTY_KEYWORD -> AnnotationUseSiteTarget.PROPERTY
JetTokens.GET_KEYWORD -> AnnotationUseSiteTarget.PROPERTY_GETTER JetTokens.GET_KEYWORD -> AnnotationUseSiteTarget.PROPERTY_GETTER
JetTokens.SET_KEYWORD -> AnnotationUseSiteTarget.PROPERTY_SETTER JetTokens.SET_KEYWORD -> AnnotationUseSiteTarget.PROPERTY_SETTER
else -> throw IllegalStateException("Unknown annotation target " + node.getText()) else -> throw IllegalStateException("Unknown annotation target " + node.getText())
@@ -234,6 +234,9 @@ public class ModifiersChecker {
break; break;
case FILE: case FILE:
throw new IllegalArgumentException("@file annotations are not allowed here"); throw new IllegalArgumentException("@file annotations are not allowed here");
case PROPERTY:
reportIfNotPropertyDescriptor(descriptor, annotation, INAPPLICABLE_PROPERTY_TARGET);
break;
case PROPERTY_GETTER: case PROPERTY_GETTER:
reportIfNotPropertyDescriptor(descriptor, annotation, INAPPLICABLE_GET_TARGET); reportIfNotPropertyDescriptor(descriptor, annotation, INAPPLICABLE_GET_TARGET);
break; break;
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.descriptors.annotations
public enum class AnnotationUseSiteTarget(renderName: String? = null) { public enum class AnnotationUseSiteTarget(renderName: String? = null) {
FIELD(), FIELD(),
FILE(), FILE(),
PROPERTY(),
PROPERTY_GETTER("get"), PROPERTY_GETTER("get"),
PROPERTY_SETTER("set"); PROPERTY_SETTER("set");