001 /*
002 * Copyright 2010-2015 JetBrains s.r.o.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017 package org.jetbrains.kotlin.codegen;
018
019 import org.jetbrains.annotations.NotNull;
020 import org.jetbrains.annotations.Nullable;
021 import org.jetbrains.kotlin.codegen.annotation.WrappedAnnotated;
022 import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
023 import org.jetbrains.kotlin.descriptors.*;
024 import org.jetbrains.kotlin.descriptors.annotations.*;
025 import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor;
026 import org.jetbrains.kotlin.load.java.JvmAnnotationNames;
027 import org.jetbrains.kotlin.name.FqName;
028 import org.jetbrains.kotlin.resolve.AnnotationChecker;
029 import org.jetbrains.kotlin.resolve.constants.*;
030 import org.jetbrains.kotlin.resolve.constants.StringValue;
031 import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
032 import org.jetbrains.kotlin.types.*;
033 import org.jetbrains.org.objectweb.asm.*;
034
035 import java.lang.annotation.*;
036 import java.util.*;
037
038 public abstract class AnnotationCodegen {
039
040 public static final class JvmFlagAnnotation {
041 private final FqName fqName;
042 private final int jvmFlag;
043
044 public JvmFlagAnnotation(@NotNull String fqName, int jvmFlag) {
045 this.fqName = new FqName(fqName);
046 this.jvmFlag = jvmFlag;
047 }
048
049 public boolean hasAnnotation(@NotNull Annotated annotated) {
050 return Annotations.Companion.findAnyAnnotation(annotated.getAnnotations(), fqName) != null;
051 }
052
053 public int getJvmFlag() {
054 return jvmFlag;
055 }
056 }
057
058 public static final List<JvmFlagAnnotation> FIELD_FLAGS = Arrays.asList(
059 new JvmFlagAnnotation("kotlin.jvm.Volatile", Opcodes.ACC_VOLATILE),
060 new JvmFlagAnnotation("kotlin.jvm.Transient", Opcodes.ACC_TRANSIENT)
061 );
062
063 public static final List<JvmFlagAnnotation> METHOD_FLAGS = Arrays.asList(
064 new JvmFlagAnnotation("kotlin.jvm.Strictfp", Opcodes.ACC_STRICT),
065 new JvmFlagAnnotation("kotlin.jvm.Synchronized", Opcodes.ACC_SYNCHRONIZED)
066 );
067
068 private static final AnnotationVisitor NO_ANNOTATION_VISITOR = new AnnotationVisitor(Opcodes.ASM5) {};
069
070 private final JetTypeMapper typeMapper;
071
072 private AnnotationCodegen(JetTypeMapper mapper) {
073 typeMapper = mapper;
074 }
075
076 /**
077 * @param returnType can be null if not applicable (e.g. {@code annotated} is a class)
078 */
079 public void genAnnotations(@Nullable Annotated annotated, @Nullable Type returnType) {
080 genAnnotations(annotated, returnType, null);
081 }
082
083 public void genAnnotations(@Nullable Annotated annotated, @Nullable Type returnType, @Nullable AnnotationUseSiteTarget allowedTarget) {
084 if (annotated == null) {
085 return;
086 }
087
088 Set<String> annotationDescriptorsAlreadyPresent = new HashSet<String>();
089
090 Annotations annotations = annotated.getAnnotations();
091
092 for (AnnotationWithTarget annotationWithTarget : annotations.getAllAnnotations()) {
093 AnnotationDescriptor annotation = annotationWithTarget.getAnnotation();
094 AnnotationUseSiteTarget annotationTarget = annotationWithTarget.getTarget();
095
096 // Skip targeted annotations by default
097 if (allowedTarget == null && annotationTarget != null) continue;
098
099 // Skip if the target is not the same
100 if (allowedTarget != null && annotationTarget != null && allowedTarget != annotationTarget) continue;
101
102 Set<KotlinTarget> applicableTargets = AnnotationChecker.applicableTargetSet(annotation);
103 if (annotated instanceof AnonymousFunctionDescriptor
104 && !applicableTargets.contains(KotlinTarget.FUNCTION)
105 && !applicableTargets.contains(KotlinTarget.PROPERTY_GETTER)
106 && !applicableTargets.contains(KotlinTarget.PROPERTY_SETTER)) {
107 assert (applicableTargets.contains(KotlinTarget.EXPRESSION)) :
108 "Inconsistent target list for lambda annotation: " + applicableTargets + " on " + annotated;
109 continue;
110 }
111
112 String descriptor = genAnnotation(annotation);
113 if (descriptor != null) {
114 annotationDescriptorsAlreadyPresent.add(descriptor);
115 }
116 }
117
118 generateAdditionalAnnotations(annotated, returnType, annotationDescriptorsAlreadyPresent);
119 }
120
121 private void generateAdditionalAnnotations(
122 @NotNull Annotated annotated,
123 @Nullable Type returnType,
124 @NotNull Set<String> annotationDescriptorsAlreadyPresent
125 ) {
126 Annotated unwrapped = annotated;
127 if (annotated instanceof WrappedAnnotated) {
128 unwrapped = ((WrappedAnnotated) annotated).getOriginalAnnotated();
129 }
130
131 if (unwrapped instanceof CallableDescriptor) {
132 CallableDescriptor descriptor = (CallableDescriptor) unwrapped;
133
134 // No need to annotate privates, synthetic accessors and their parameters
135 if (isInvisibleFromTheOutside(descriptor)) return;
136 if (descriptor instanceof ValueParameterDescriptor && isInvisibleFromTheOutside(descriptor.getContainingDeclaration())) return;
137
138 if (returnType != null && !AsmUtil.isPrimitive(returnType)) {
139 generateNullabilityAnnotation(descriptor.getReturnType(), annotationDescriptorsAlreadyPresent);
140 }
141 }
142 if (unwrapped instanceof ClassDescriptor) {
143 ClassDescriptor classDescriptor = (ClassDescriptor) unwrapped;
144 if (classDescriptor.getKind() == ClassKind.ANNOTATION_CLASS) {
145 generateDocumentedAnnotation(classDescriptor, annotationDescriptorsAlreadyPresent);
146 generateRetentionAnnotation(classDescriptor, annotationDescriptorsAlreadyPresent);
147 generateTargetAnnotation(classDescriptor, annotationDescriptorsAlreadyPresent);
148 }
149 }
150 }
151
152 private static boolean isInvisibleFromTheOutside(@Nullable DeclarationDescriptor descriptor) {
153 if (descriptor instanceof CallableMemberDescriptor && JetTypeMapper.isAccessor((CallableMemberDescriptor) descriptor)) return false;
154 if (descriptor instanceof MemberDescriptor) {
155 return AsmUtil.getVisibilityAccessFlag((MemberDescriptor) descriptor) == Opcodes.ACC_PRIVATE;
156 }
157 return false;
158 }
159
160 private void generateNullabilityAnnotation(@Nullable KotlinType type, @NotNull Set<String> annotationDescriptorsAlreadyPresent) {
161 if (type == null) return;
162
163 if (isBareTypeParameterWithNullableUpperBound(type)) {
164 // This is to account for the case of, say
165 // class Function<R> { fun invoke(): R }
166 // it would be a shame to put @Nullable on the return type of the function, and force all callers to check for null,
167 // so we put no annotations
168 return;
169 }
170
171 if (FlexibleTypesKt.isFlexible(type)) {
172 // A flexible type whose lower bound in not-null and upper bound is nullable, should not be annotated
173 Flexibility flexibility = FlexibleTypesKt.flexibility(type);
174
175 if (!TypeUtils.isNullableType(flexibility.getLowerBound()) && TypeUtils.isNullableType(flexibility.getUpperBound())) {
176 AnnotationDescriptor notNull = type.getAnnotations().findAnnotation(JvmAnnotationNames.JETBRAINS_NOT_NULL_ANNOTATION);
177 if (notNull != null) {
178 generateAnnotationIfNotPresent(annotationDescriptorsAlreadyPresent, NotNull.class);
179 }
180 return;
181 }
182 }
183
184 boolean isNullableType = TypeUtils.isNullableType(type);
185
186 Class<?> annotationClass = isNullableType ? Nullable.class : NotNull.class;
187
188 generateAnnotationIfNotPresent(annotationDescriptorsAlreadyPresent, annotationClass);
189 }
190
191 private static final Map<KotlinTarget, ElementType> annotationTargetMap =
192 new EnumMap<KotlinTarget, ElementType>(KotlinTarget.class);
193
194 static {
195 annotationTargetMap.put(KotlinTarget.CLASS, ElementType.TYPE);
196 annotationTargetMap.put(KotlinTarget.ANNOTATION_CLASS, ElementType.ANNOTATION_TYPE);
197 annotationTargetMap.put(KotlinTarget.CONSTRUCTOR, ElementType.CONSTRUCTOR);
198 annotationTargetMap.put(KotlinTarget.LOCAL_VARIABLE, ElementType.LOCAL_VARIABLE);
199 annotationTargetMap.put(KotlinTarget.FUNCTION, ElementType.METHOD);
200 annotationTargetMap.put(KotlinTarget.PROPERTY_GETTER, ElementType.METHOD);
201 annotationTargetMap.put(KotlinTarget.PROPERTY_SETTER, ElementType.METHOD);
202 annotationTargetMap.put(KotlinTarget.FIELD, ElementType.FIELD);
203 annotationTargetMap.put(KotlinTarget.VALUE_PARAMETER, ElementType.PARAMETER);
204 }
205
206 private void generateTargetAnnotation(@NotNull ClassDescriptor classDescriptor, @NotNull Set<String> annotationDescriptorsAlreadyPresent) {
207 String descriptor = Type.getType(Target.class).getDescriptor();
208 if (!annotationDescriptorsAlreadyPresent.add(descriptor)) return;
209 Set<KotlinTarget> targets = AnnotationChecker.Companion.applicableTargetSet(classDescriptor);
210 Set<ElementType> javaTargets;
211 if (targets == null) {
212 javaTargets = getJavaTargetList(classDescriptor);
213 if (javaTargets == null) return;
214 }
215 else {
216 javaTargets = EnumSet.noneOf(ElementType.class);
217 for (KotlinTarget target : targets) {
218 if (annotationTargetMap.get(target) == null) continue;
219 javaTargets.add(annotationTargetMap.get(target));
220 }
221 }
222 AnnotationVisitor visitor = visitAnnotation(descriptor, true);
223 AnnotationVisitor arrayVisitor = visitor.visitArray("value");
224 for (ElementType javaTarget : javaTargets) {
225 arrayVisitor.visitEnum(null, Type.getType(ElementType.class).getDescriptor(), javaTarget.name());
226 }
227 arrayVisitor.visitEnd();
228 visitor.visitEnd();
229 }
230
231 private void generateRetentionAnnotation(@NotNull ClassDescriptor classDescriptor, @NotNull Set<String> annotationDescriptorsAlreadyPresent) {
232 RetentionPolicy policy = getRetentionPolicy(classDescriptor);
233 String descriptor = Type.getType(Retention.class).getDescriptor();
234 if (!annotationDescriptorsAlreadyPresent.add(descriptor)) return;
235 AnnotationVisitor visitor = visitAnnotation(descriptor, true);
236 visitor.visitEnum("value", Type.getType(RetentionPolicy.class).getDescriptor(), policy.name());
237 visitor.visitEnd();
238 }
239
240 private void generateDocumentedAnnotation(@NotNull ClassDescriptor classDescriptor, @NotNull Set<String> annotationDescriptorsAlreadyPresent) {
241 boolean documented = DescriptorUtilsKt.isDocumentedAnnotation(classDescriptor);
242 if (!documented) return;
243 String descriptor = Type.getType(Documented.class).getDescriptor();
244 if (!annotationDescriptorsAlreadyPresent.add(descriptor)) return;
245 AnnotationVisitor visitor = visitAnnotation(descriptor, true);
246 visitor.visitEnd();
247 }
248
249 private void generateAnnotationIfNotPresent(Set<String> annotationDescriptorsAlreadyPresent, Class<?> annotationClass) {
250 String descriptor = Type.getType(annotationClass).getDescriptor();
251 if (!annotationDescriptorsAlreadyPresent.contains(descriptor)) {
252 visitAnnotation(descriptor, false).visitEnd();
253 }
254 }
255
256 private static boolean isBareTypeParameterWithNullableUpperBound(@NotNull KotlinType type) {
257 ClassifierDescriptor classifier = type.getConstructor().getDeclarationDescriptor();
258 return !type.isMarkedNullable() && classifier instanceof TypeParameterDescriptor && TypeUtils.hasNullableSuperType(type);
259 }
260
261 public void generateAnnotationDefaultValue(@NotNull ConstantValue<?> value, @NotNull KotlinType expectedType) {
262 AnnotationVisitor visitor = visitAnnotation(null, false); // Parameters are unimportant
263 genCompileTimeValue(null, value, visitor);
264 visitor.visitEnd();
265 }
266
267 @Nullable
268 private String genAnnotation(@NotNull AnnotationDescriptor annotationDescriptor) {
269 ClassifierDescriptor classifierDescriptor = annotationDescriptor.getType().getConstructor().getDeclarationDescriptor();
270 assert classifierDescriptor != null : "Annotation descriptor has no class: " + annotationDescriptor;
271 RetentionPolicy rp = getRetentionPolicy(classifierDescriptor);
272 if (rp == RetentionPolicy.SOURCE && typeMapper.getClassBuilderMode() != ClassBuilderMode.LIGHT_CLASSES) {
273 return null;
274 }
275
276 String descriptor = typeMapper.mapType(annotationDescriptor.getType()).getDescriptor();
277 AnnotationVisitor annotationVisitor = visitAnnotation(descriptor, rp == RetentionPolicy.RUNTIME);
278
279 genAnnotationArguments(annotationDescriptor, annotationVisitor);
280 annotationVisitor.visitEnd();
281
282 return descriptor;
283 }
284
285 private void genAnnotationArguments(AnnotationDescriptor annotationDescriptor, AnnotationVisitor annotationVisitor) {
286 for (Map.Entry<ValueParameterDescriptor, ConstantValue<?>> entry : annotationDescriptor.getAllValueArguments().entrySet()) {
287 ValueParameterDescriptor descriptor = entry.getKey();
288 String name = descriptor.getName().asString();
289 genCompileTimeValue(name, entry.getValue(), annotationVisitor);
290 }
291 }
292
293 private void genCompileTimeValue(
294 @Nullable final String name,
295 @NotNull ConstantValue<?> value,
296 @NotNull final AnnotationVisitor annotationVisitor
297 ) {
298 AnnotationArgumentVisitor argumentVisitor = new AnnotationArgumentVisitor<Void, Void>() {
299 @Override
300 public Void visitLongValue(@NotNull LongValue value, Void data) {
301 return visitSimpleValue(value);
302 }
303
304 @Override
305 public Void visitIntValue(IntValue value, Void data) {
306 return visitSimpleValue(value);
307 }
308
309 @Override
310 public Void visitShortValue(ShortValue value, Void data) {
311 return visitSimpleValue(value);
312 }
313
314 @Override
315 public Void visitByteValue(ByteValue value, Void data) {
316 return visitSimpleValue(value);
317 }
318
319 @Override
320 public Void visitDoubleValue(DoubleValue value, Void data) {
321 return visitSimpleValue(value);
322 }
323
324 @Override
325 public Void visitFloatValue(FloatValue value, Void data) {
326 return visitSimpleValue(value);
327 }
328
329 @Override
330 public Void visitBooleanValue(BooleanValue value, Void data) {
331 return visitSimpleValue(value);
332 }
333
334 @Override
335 public Void visitCharValue(CharValue value, Void data) {
336 return visitSimpleValue(value);
337 }
338
339 @Override
340 public Void visitStringValue(StringValue value, Void data) {
341 return visitSimpleValue(value);
342 }
343
344 @Override
345 public Void visitEnumValue(EnumValue value, Void data) {
346 String propertyName = value.getValue().getName().asString();
347 annotationVisitor.visitEnum(name, typeMapper.mapType(value.getType()).getDescriptor(), propertyName);
348 return null;
349 }
350
351 @Override
352 public Void visitArrayValue(ArrayValue value, Void data) {
353 AnnotationVisitor visitor = annotationVisitor.visitArray(name);
354 for (ConstantValue<?> argument : value.getValue()) {
355 genCompileTimeValue(null, argument, visitor);
356 }
357 visitor.visitEnd();
358 return null;
359 }
360
361 @Override
362 public Void visitAnnotationValue(AnnotationValue value, Void data) {
363 String internalAnnName = typeMapper.mapType(value.getValue().getType()).getDescriptor();
364 AnnotationVisitor visitor = annotationVisitor.visitAnnotation(name, internalAnnName);
365 genAnnotationArguments(value.getValue(), visitor);
366 visitor.visitEnd();
367 return null;
368 }
369
370 @Override
371 public Void visitKClassValue(KClassValue value, Void data) {
372 annotationVisitor.visit(name, typeMapper.mapType(value.getValue()));
373 return null;
374 }
375
376 private Void visitSimpleValue(ConstantValue<?> value) {
377 annotationVisitor.visit(name, value.getValue());
378 return null;
379 }
380
381 @Override
382 public Void visitErrorValue(ErrorValue value, Void data) {
383 return visitUnsupportedValue(value);
384 }
385
386 @Override
387 public Void visitNullValue(NullValue value, Void data) {
388 return visitUnsupportedValue(value);
389 }
390
391 private Void visitUnsupportedValue(ConstantValue<?> value) {
392 ClassBuilderMode mode = typeMapper.getClassBuilderMode();
393 switch (mode) {
394 case FULL:
395 throw new IllegalStateException("Don't know how to compile annotation value " + value);
396 case LIGHT_CLASSES:
397 return null;
398 default:
399 throw new IllegalStateException("Unknown builder mode: " + mode);
400 }
401 }
402 };
403
404 value.accept(argumentVisitor, null);
405 }
406
407 private static final Map<KotlinRetention, RetentionPolicy> annotationRetentionMap =
408 new EnumMap<KotlinRetention, RetentionPolicy>(KotlinRetention.class);
409
410 static {
411 annotationRetentionMap.put(KotlinRetention.SOURCE, RetentionPolicy.SOURCE);
412 annotationRetentionMap.put(KotlinRetention.BINARY, RetentionPolicy.CLASS);
413 annotationRetentionMap.put(KotlinRetention.RUNTIME, RetentionPolicy.RUNTIME);
414 }
415
416 @Nullable
417 private Set<ElementType> getJavaTargetList(ClassDescriptor descriptor) {
418 AnnotationDescriptor targetAnnotation = descriptor.getAnnotations().findAnnotation(new FqName(Target.class.getName()));
419 if (targetAnnotation != null) {
420 Collection<ConstantValue<?>> valueArguments = targetAnnotation.getAllValueArguments().values();
421 if (!valueArguments.isEmpty()) {
422 ConstantValue<?> compileTimeConstant = valueArguments.iterator().next();
423 if (compileTimeConstant instanceof ArrayValue) {
424 List<? extends ConstantValue<?>> values = ((ArrayValue) compileTimeConstant).getValue();
425 Set<ElementType> result = EnumSet.noneOf(ElementType.class);
426 for (ConstantValue<?> value : values) {
427 if (value instanceof EnumValue) {
428 ClassDescriptor enumEntry = ((EnumValue) value).getValue();
429 KotlinType classObjectType = DescriptorUtilsKt.getClassValueType(enumEntry);
430 if (classObjectType != null) {
431 if ("java/lang/annotation/ElementType".equals(typeMapper.mapType(classObjectType).getInternalName())) {
432 result.add(ElementType.valueOf(enumEntry.getName().asString()));
433 }
434 }
435 }
436 }
437 return result;
438 }
439 }
440 }
441 return null;
442 }
443
444 @NotNull
445 private RetentionPolicy getRetentionPolicy(@NotNull Annotated descriptor) {
446 KotlinRetention retention = DescriptorUtilsKt.getAnnotationRetention(descriptor);
447 if (retention != null) {
448 return annotationRetentionMap.get(retention);
449 }
450 AnnotationDescriptor retentionAnnotation = descriptor.getAnnotations().findAnnotation(new FqName(Retention.class.getName()));
451 if (retentionAnnotation != null) {
452 Collection<ConstantValue<?>> valueArguments = retentionAnnotation.getAllValueArguments().values();
453 if (!valueArguments.isEmpty()) {
454 ConstantValue<?> compileTimeConstant = valueArguments.iterator().next();
455 if (compileTimeConstant instanceof EnumValue) {
456 ClassDescriptor enumEntry = ((EnumValue) compileTimeConstant).getValue();
457 KotlinType classObjectType = DescriptorUtilsKt.getClassValueType(enumEntry);
458 if (classObjectType != null) {
459 if ("java/lang/annotation/RetentionPolicy".equals(typeMapper.mapType(classObjectType).getInternalName())) {
460 return RetentionPolicy.valueOf(enumEntry.getName().asString());
461 }
462 }
463 }
464 }
465 }
466
467 return RetentionPolicy.RUNTIME;
468 }
469
470 @NotNull
471 abstract AnnotationVisitor visitAnnotation(String descr, boolean visible);
472
473 public static AnnotationCodegen forClass(final ClassVisitor cv, JetTypeMapper mapper) {
474 return new AnnotationCodegen(mapper) {
475 @NotNull
476 @Override
477 AnnotationVisitor visitAnnotation(String descr, boolean visible) {
478 return safe(cv.visitAnnotation(descr, visible));
479 }
480 };
481 }
482
483 public static AnnotationCodegen forMethod(final MethodVisitor mv, JetTypeMapper mapper) {
484 return new AnnotationCodegen(mapper) {
485 @NotNull
486 @Override
487 AnnotationVisitor visitAnnotation(String descr, boolean visible) {
488 return safe(mv.visitAnnotation(descr, visible));
489 }
490 };
491 }
492
493 public static AnnotationCodegen forField(final FieldVisitor fv, JetTypeMapper mapper) {
494 return new AnnotationCodegen(mapper) {
495 @NotNull
496 @Override
497 AnnotationVisitor visitAnnotation(String descr, boolean visible) {
498 return safe(fv.visitAnnotation(descr, visible));
499 }
500 };
501 }
502
503 public static AnnotationCodegen forParameter(final int parameter, final MethodVisitor mv, JetTypeMapper mapper) {
504 return new AnnotationCodegen(mapper) {
505 @NotNull
506 @Override
507 AnnotationVisitor visitAnnotation(String descr, boolean visible) {
508 return safe(mv.visitParameterAnnotation(parameter, descr, visible));
509 }
510 };
511 }
512
513 public static AnnotationCodegen forAnnotationDefaultValue(final MethodVisitor mv, JetTypeMapper mapper) {
514 return new AnnotationCodegen(mapper) {
515 @NotNull
516 @Override
517 AnnotationVisitor visitAnnotation(String descr, boolean visible) {
518 return safe(mv.visitAnnotationDefault());
519 }
520 };
521 }
522
523 @NotNull
524 private static AnnotationVisitor safe(@Nullable AnnotationVisitor av) {
525 return av == null ? NO_ANNOTATION_VISITOR : av;
526 }
527 }