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