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