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.KotlinTypeMapper;
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 KotlinTypeMapper typeMapper;
071
072 private AnnotationCodegen(KotlinTypeMapper 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 if (annotated instanceof ClassDescriptor
112 && !applicableTargets.contains(KotlinTarget.CLASS)
113 && !applicableTargets.contains(KotlinTarget.ANNOTATION_CLASS)) {
114 ClassDescriptor classDescriptor = (ClassDescriptor) annotated;
115 if (classDescriptor.getVisibility() == Visibilities.LOCAL) {
116 assert applicableTargets.contains(KotlinTarget.EXPRESSION) :
117 "Inconsistent target list for object literal annotation: " + applicableTargets + " on " + annotated;
118 continue;
119 }
120 }
121
122 String descriptor = genAnnotation(annotation);
123 if (descriptor != null) {
124 annotationDescriptorsAlreadyPresent.add(descriptor);
125 }
126 }
127
128 generateAdditionalAnnotations(annotated, returnType, annotationDescriptorsAlreadyPresent);
129 }
130
131 private void generateAdditionalAnnotations(
132 @NotNull Annotated annotated,
133 @Nullable Type returnType,
134 @NotNull Set<String> annotationDescriptorsAlreadyPresent
135 ) {
136 Annotated unwrapped = annotated;
137 if (annotated instanceof WrappedAnnotated) {
138 unwrapped = ((WrappedAnnotated) annotated).getOriginalAnnotated();
139 }
140
141 if (unwrapped instanceof CallableDescriptor) {
142 CallableDescriptor descriptor = (CallableDescriptor) unwrapped;
143
144 // No need to annotate privates, synthetic accessors and their parameters
145 if (isInvisibleFromTheOutside(descriptor)) return;
146 if (descriptor instanceof ValueParameterDescriptor && isInvisibleFromTheOutside(descriptor.getContainingDeclaration())) return;
147
148 if (returnType != null && !AsmUtil.isPrimitive(returnType)) {
149 generateNullabilityAnnotation(descriptor.getReturnType(), annotationDescriptorsAlreadyPresent);
150 }
151 }
152 if (unwrapped instanceof ClassDescriptor) {
153 ClassDescriptor classDescriptor = (ClassDescriptor) unwrapped;
154 if (classDescriptor.getKind() == ClassKind.ANNOTATION_CLASS) {
155 generateDocumentedAnnotation(classDescriptor, annotationDescriptorsAlreadyPresent);
156 generateRetentionAnnotation(classDescriptor, annotationDescriptorsAlreadyPresent);
157 generateTargetAnnotation(classDescriptor, annotationDescriptorsAlreadyPresent);
158 }
159 }
160 }
161
162 private static boolean isInvisibleFromTheOutside(@Nullable DeclarationDescriptor descriptor) {
163 if (descriptor instanceof CallableMemberDescriptor && KotlinTypeMapper.isAccessor((CallableMemberDescriptor) descriptor)) return false;
164 if (descriptor instanceof MemberDescriptor) {
165 return AsmUtil.getVisibilityAccessFlag((MemberDescriptor) descriptor) == Opcodes.ACC_PRIVATE;
166 }
167 return false;
168 }
169
170 private void generateNullabilityAnnotation(@Nullable KotlinType type, @NotNull Set<String> annotationDescriptorsAlreadyPresent) {
171 if (type == null) return;
172
173 if (isBareTypeParameterWithNullableUpperBound(type)) {
174 // This is to account for the case of, say
175 // class Function<R> { fun invoke(): R }
176 // it would be a shame to put @Nullable on the return type of the function, and force all callers to check for null,
177 // so we put no annotations
178 return;
179 }
180
181 if (FlexibleTypesKt.isFlexible(type)) {
182 // A flexible type whose lower bound in not-null and upper bound is nullable, should not be annotated
183 Flexibility flexibility = FlexibleTypesKt.flexibility(type);
184
185 if (!TypeUtils.isNullableType(flexibility.getLowerBound()) && TypeUtils.isNullableType(flexibility.getUpperBound())) {
186 AnnotationDescriptor notNull = type.getAnnotations().findAnnotation(JvmAnnotationNames.JETBRAINS_NOT_NULL_ANNOTATION);
187 if (notNull != null) {
188 generateAnnotationIfNotPresent(annotationDescriptorsAlreadyPresent, NotNull.class);
189 }
190 return;
191 }
192 }
193
194 boolean isNullableType = TypeUtils.isNullableType(type);
195
196 Class<?> annotationClass = isNullableType ? Nullable.class : NotNull.class;
197
198 generateAnnotationIfNotPresent(annotationDescriptorsAlreadyPresent, annotationClass);
199 }
200
201 private static final Map<KotlinTarget, ElementType> annotationTargetMap =
202 new EnumMap<KotlinTarget, ElementType>(KotlinTarget.class);
203
204 static {
205 annotationTargetMap.put(KotlinTarget.CLASS, ElementType.TYPE);
206 annotationTargetMap.put(KotlinTarget.ANNOTATION_CLASS, ElementType.ANNOTATION_TYPE);
207 annotationTargetMap.put(KotlinTarget.CONSTRUCTOR, ElementType.CONSTRUCTOR);
208 annotationTargetMap.put(KotlinTarget.LOCAL_VARIABLE, ElementType.LOCAL_VARIABLE);
209 annotationTargetMap.put(KotlinTarget.FUNCTION, ElementType.METHOD);
210 annotationTargetMap.put(KotlinTarget.PROPERTY_GETTER, ElementType.METHOD);
211 annotationTargetMap.put(KotlinTarget.PROPERTY_SETTER, ElementType.METHOD);
212 annotationTargetMap.put(KotlinTarget.FIELD, ElementType.FIELD);
213 annotationTargetMap.put(KotlinTarget.VALUE_PARAMETER, ElementType.PARAMETER);
214 }
215
216 private void generateTargetAnnotation(@NotNull ClassDescriptor classDescriptor, @NotNull Set<String> annotationDescriptorsAlreadyPresent) {
217 String descriptor = Type.getType(Target.class).getDescriptor();
218 if (!annotationDescriptorsAlreadyPresent.add(descriptor)) return;
219 Set<KotlinTarget> targets = AnnotationChecker.Companion.applicableTargetSet(classDescriptor);
220 Set<ElementType> javaTargets;
221 if (targets == null) {
222 javaTargets = getJavaTargetList(classDescriptor);
223 if (javaTargets == null) return;
224 }
225 else {
226 javaTargets = EnumSet.noneOf(ElementType.class);
227 for (KotlinTarget target : targets) {
228 if (annotationTargetMap.get(target) == null) continue;
229 javaTargets.add(annotationTargetMap.get(target));
230 }
231 }
232 AnnotationVisitor visitor = visitAnnotation(descriptor, true);
233 AnnotationVisitor arrayVisitor = visitor.visitArray("value");
234 for (ElementType javaTarget : javaTargets) {
235 arrayVisitor.visitEnum(null, Type.getType(ElementType.class).getDescriptor(), javaTarget.name());
236 }
237 arrayVisitor.visitEnd();
238 visitor.visitEnd();
239 }
240
241 private void generateRetentionAnnotation(@NotNull ClassDescriptor classDescriptor, @NotNull Set<String> annotationDescriptorsAlreadyPresent) {
242 RetentionPolicy policy = getRetentionPolicy(classDescriptor);
243 String descriptor = Type.getType(Retention.class).getDescriptor();
244 if (!annotationDescriptorsAlreadyPresent.add(descriptor)) return;
245 AnnotationVisitor visitor = visitAnnotation(descriptor, true);
246 visitor.visitEnum("value", Type.getType(RetentionPolicy.class).getDescriptor(), policy.name());
247 visitor.visitEnd();
248 }
249
250 private void generateDocumentedAnnotation(@NotNull ClassDescriptor classDescriptor, @NotNull Set<String> annotationDescriptorsAlreadyPresent) {
251 boolean documented = DescriptorUtilsKt.isDocumentedAnnotation(classDescriptor);
252 if (!documented) return;
253 String descriptor = Type.getType(Documented.class).getDescriptor();
254 if (!annotationDescriptorsAlreadyPresent.add(descriptor)) return;
255 AnnotationVisitor visitor = visitAnnotation(descriptor, true);
256 visitor.visitEnd();
257 }
258
259 private void generateAnnotationIfNotPresent(Set<String> annotationDescriptorsAlreadyPresent, Class<?> annotationClass) {
260 String descriptor = Type.getType(annotationClass).getDescriptor();
261 if (!annotationDescriptorsAlreadyPresent.contains(descriptor)) {
262 visitAnnotation(descriptor, false).visitEnd();
263 }
264 }
265
266 private static boolean isBareTypeParameterWithNullableUpperBound(@NotNull KotlinType type) {
267 ClassifierDescriptor classifier = type.getConstructor().getDeclarationDescriptor();
268 return !type.isMarkedNullable() && classifier instanceof TypeParameterDescriptor && TypeUtils.hasNullableSuperType(type);
269 }
270
271 public void generateAnnotationDefaultValue(@NotNull ConstantValue<?> value, @NotNull KotlinType expectedType) {
272 AnnotationVisitor visitor = visitAnnotation(null, false); // Parameters are unimportant
273 genCompileTimeValue(null, value, visitor);
274 visitor.visitEnd();
275 }
276
277 @Nullable
278 private String genAnnotation(@NotNull AnnotationDescriptor annotationDescriptor) {
279 ClassifierDescriptor classifierDescriptor = annotationDescriptor.getType().getConstructor().getDeclarationDescriptor();
280 assert classifierDescriptor != null : "Annotation descriptor has no class: " + annotationDescriptor;
281 RetentionPolicy rp = getRetentionPolicy(classifierDescriptor);
282 if (rp == RetentionPolicy.SOURCE && !typeMapper.getClassBuilderMode().generateSourceRetentionAnnotations) {
283 return null;
284 }
285
286 String descriptor = typeMapper.mapType(annotationDescriptor.getType()).getDescriptor();
287 AnnotationVisitor annotationVisitor = visitAnnotation(descriptor, rp == RetentionPolicy.RUNTIME);
288
289 genAnnotationArguments(annotationDescriptor, annotationVisitor);
290 annotationVisitor.visitEnd();
291
292 return descriptor;
293 }
294
295 private void genAnnotationArguments(AnnotationDescriptor annotationDescriptor, AnnotationVisitor annotationVisitor) {
296 for (Map.Entry<ValueParameterDescriptor, ConstantValue<?>> entry : annotationDescriptor.getAllValueArguments().entrySet()) {
297 ValueParameterDescriptor descriptor = entry.getKey();
298 String name = descriptor.getName().asString();
299 genCompileTimeValue(name, entry.getValue(), annotationVisitor);
300 }
301 }
302
303 private void genCompileTimeValue(
304 @Nullable final String name,
305 @NotNull ConstantValue<?> value,
306 @NotNull final AnnotationVisitor annotationVisitor
307 ) {
308 AnnotationArgumentVisitor argumentVisitor = new AnnotationArgumentVisitor<Void, Void>() {
309 @Override
310 public Void visitLongValue(@NotNull LongValue value, Void data) {
311 return visitSimpleValue(value);
312 }
313
314 @Override
315 public Void visitIntValue(IntValue value, Void data) {
316 return visitSimpleValue(value);
317 }
318
319 @Override
320 public Void visitShortValue(ShortValue value, Void data) {
321 return visitSimpleValue(value);
322 }
323
324 @Override
325 public Void visitByteValue(ByteValue value, Void data) {
326 return visitSimpleValue(value);
327 }
328
329 @Override
330 public Void visitDoubleValue(DoubleValue value, Void data) {
331 return visitSimpleValue(value);
332 }
333
334 @Override
335 public Void visitFloatValue(FloatValue value, Void data) {
336 return visitSimpleValue(value);
337 }
338
339 @Override
340 public Void visitBooleanValue(BooleanValue value, Void data) {
341 return visitSimpleValue(value);
342 }
343
344 @Override
345 public Void visitCharValue(CharValue value, Void data) {
346 return visitSimpleValue(value);
347 }
348
349 @Override
350 public Void visitStringValue(StringValue value, Void data) {
351 return visitSimpleValue(value);
352 }
353
354 @Override
355 public Void visitEnumValue(EnumValue value, Void data) {
356 String propertyName = value.getValue().getName().asString();
357 annotationVisitor.visitEnum(name, typeMapper.mapType(value.getType()).getDescriptor(), propertyName);
358 return null;
359 }
360
361 @Override
362 public Void visitArrayValue(ArrayValue value, Void data) {
363 AnnotationVisitor visitor = annotationVisitor.visitArray(name);
364 for (ConstantValue<?> argument : value.getValue()) {
365 genCompileTimeValue(null, argument, visitor);
366 }
367 visitor.visitEnd();
368 return null;
369 }
370
371 @Override
372 public Void visitAnnotationValue(AnnotationValue value, Void data) {
373 String internalAnnName = typeMapper.mapType(value.getValue().getType()).getDescriptor();
374 AnnotationVisitor visitor = annotationVisitor.visitAnnotation(name, internalAnnName);
375 genAnnotationArguments(value.getValue(), visitor);
376 visitor.visitEnd();
377 return null;
378 }
379
380 @Override
381 public Void visitKClassValue(KClassValue value, Void data) {
382 annotationVisitor.visit(name, typeMapper.mapType(value.getValue()));
383 return null;
384 }
385
386 private Void visitSimpleValue(ConstantValue<?> value) {
387 annotationVisitor.visit(name, value.getValue());
388 return null;
389 }
390
391 @Override
392 public Void visitErrorValue(ErrorValue value, Void data) {
393 return visitUnsupportedValue(value);
394 }
395
396 @Override
397 public Void visitNullValue(NullValue value, Void data) {
398 return visitUnsupportedValue(value);
399 }
400
401 private Void visitUnsupportedValue(ConstantValue<?> value) {
402 ClassBuilderMode mode = typeMapper.getClassBuilderMode();
403 if (mode.generateBodies) {
404 throw new IllegalStateException("Don't know how to compile annotation value " + value);
405 } else {
406 return null;
407 }
408 }
409 };
410
411 value.accept(argumentVisitor, null);
412 }
413
414 private static final Map<KotlinRetention, RetentionPolicy> annotationRetentionMap =
415 new EnumMap<KotlinRetention, RetentionPolicy>(KotlinRetention.class);
416
417 static {
418 annotationRetentionMap.put(KotlinRetention.SOURCE, RetentionPolicy.SOURCE);
419 annotationRetentionMap.put(KotlinRetention.BINARY, RetentionPolicy.CLASS);
420 annotationRetentionMap.put(KotlinRetention.RUNTIME, RetentionPolicy.RUNTIME);
421 }
422
423 @Nullable
424 private Set<ElementType> getJavaTargetList(ClassDescriptor descriptor) {
425 AnnotationDescriptor targetAnnotation = descriptor.getAnnotations().findAnnotation(new FqName(Target.class.getName()));
426 if (targetAnnotation != null) {
427 Collection<ConstantValue<?>> valueArguments = targetAnnotation.getAllValueArguments().values();
428 if (!valueArguments.isEmpty()) {
429 ConstantValue<?> compileTimeConstant = valueArguments.iterator().next();
430 if (compileTimeConstant instanceof ArrayValue) {
431 List<? extends ConstantValue<?>> values = ((ArrayValue) compileTimeConstant).getValue();
432 Set<ElementType> result = EnumSet.noneOf(ElementType.class);
433 for (ConstantValue<?> value : values) {
434 if (value instanceof EnumValue) {
435 ClassDescriptor enumEntry = ((EnumValue) value).getValue();
436 KotlinType classObjectType = DescriptorUtilsKt.getClassValueType(enumEntry);
437 if (classObjectType != null) {
438 if ("java/lang/annotation/ElementType".equals(typeMapper.mapType(classObjectType).getInternalName())) {
439 result.add(ElementType.valueOf(enumEntry.getName().asString()));
440 }
441 }
442 }
443 }
444 return result;
445 }
446 }
447 }
448 return null;
449 }
450
451 @NotNull
452 private RetentionPolicy getRetentionPolicy(@NotNull Annotated descriptor) {
453 KotlinRetention retention = DescriptorUtilsKt.getAnnotationRetention(descriptor);
454 if (retention != null) {
455 return annotationRetentionMap.get(retention);
456 }
457 AnnotationDescriptor retentionAnnotation = descriptor.getAnnotations().findAnnotation(new FqName(Retention.class.getName()));
458 if (retentionAnnotation != null) {
459 Collection<ConstantValue<?>> valueArguments = retentionAnnotation.getAllValueArguments().values();
460 if (!valueArguments.isEmpty()) {
461 ConstantValue<?> compileTimeConstant = valueArguments.iterator().next();
462 if (compileTimeConstant instanceof EnumValue) {
463 ClassDescriptor enumEntry = ((EnumValue) compileTimeConstant).getValue();
464 KotlinType classObjectType = DescriptorUtilsKt.getClassValueType(enumEntry);
465 if (classObjectType != null) {
466 if ("java/lang/annotation/RetentionPolicy".equals(typeMapper.mapType(classObjectType).getInternalName())) {
467 return RetentionPolicy.valueOf(enumEntry.getName().asString());
468 }
469 }
470 }
471 }
472 }
473
474 return RetentionPolicy.RUNTIME;
475 }
476
477 @NotNull
478 abstract AnnotationVisitor visitAnnotation(String descr, boolean visible);
479
480 public static AnnotationCodegen forClass(final ClassVisitor cv, KotlinTypeMapper mapper) {
481 return new AnnotationCodegen(mapper) {
482 @NotNull
483 @Override
484 AnnotationVisitor visitAnnotation(String descr, boolean visible) {
485 return safe(cv.visitAnnotation(descr, visible));
486 }
487 };
488 }
489
490 public static AnnotationCodegen forMethod(final MethodVisitor mv, KotlinTypeMapper mapper) {
491 return new AnnotationCodegen(mapper) {
492 @NotNull
493 @Override
494 AnnotationVisitor visitAnnotation(String descr, boolean visible) {
495 return safe(mv.visitAnnotation(descr, visible));
496 }
497 };
498 }
499
500 public static AnnotationCodegen forField(final FieldVisitor fv, KotlinTypeMapper mapper) {
501 return new AnnotationCodegen(mapper) {
502 @NotNull
503 @Override
504 AnnotationVisitor visitAnnotation(String descr, boolean visible) {
505 return safe(fv.visitAnnotation(descr, visible));
506 }
507 };
508 }
509
510 public static AnnotationCodegen forParameter(final int parameter, final MethodVisitor mv, KotlinTypeMapper mapper) {
511 return new AnnotationCodegen(mapper) {
512 @NotNull
513 @Override
514 AnnotationVisitor visitAnnotation(String descr, boolean visible) {
515 return safe(mv.visitParameterAnnotation(parameter, descr, visible));
516 }
517 };
518 }
519
520 public static AnnotationCodegen forAnnotationDefaultValue(final MethodVisitor mv, KotlinTypeMapper mapper) {
521 return new AnnotationCodegen(mapper) {
522 @NotNull
523 @Override
524 AnnotationVisitor visitAnnotation(String descr, boolean visible) {
525 return safe(mv.visitAnnotationDefault());
526 }
527 };
528 }
529
530 @NotNull
531 private static AnnotationVisitor safe(@Nullable AnnotationVisitor av) {
532 return av == null ? NO_ANNOTATION_VISITOR : av;
533 }
534 }