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.builtins.KotlinBuiltIns;
022    import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
023    import org.jetbrains.kotlin.descriptors.*;
024    import org.jetbrains.kotlin.descriptors.annotations.Annotated;
025    import org.jetbrains.kotlin.descriptors.annotations.AnnotationArgumentVisitor;
026    import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
027    import org.jetbrains.kotlin.load.java.JvmAnnotationNames;
028    import org.jetbrains.kotlin.name.FqName;
029    import org.jetbrains.kotlin.resolve.constants.*;
030    import org.jetbrains.kotlin.resolve.constants.StringValue;
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.Retention;
038    import java.lang.annotation.RetentionPolicy;
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 annotated.getAnnotations().findAnnotation(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        );
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            if (annotated == null) {
087                return;
088            }
089    
090            if (!(annotated instanceof DeclarationDescriptor)) {
091                return;
092            }
093    
094            Set<String> annotationDescriptorsAlreadyPresent = new HashSet<String>();
095    
096            for (AnnotationDescriptor annotation : annotated.getAnnotations()) {
097                String descriptor = genAnnotation(annotation);
098                if (descriptor != null) {
099                    annotationDescriptorsAlreadyPresent.add(descriptor);
100                }
101            }
102    
103            generateAdditionalAnnotations(annotated, returnType, annotationDescriptorsAlreadyPresent);
104        }
105    
106        private void generateAdditionalAnnotations(
107                @NotNull Annotated annotated,
108                @Nullable Type returnType,
109                @NotNull Set<String> annotationDescriptorsAlreadyPresent
110        ) {
111            if (annotated instanceof CallableDescriptor) {
112                CallableDescriptor descriptor = (CallableDescriptor) annotated;
113    
114                // No need to annotate privates, synthetic accessors and their parameters
115                if (isInvisibleFromTheOutside(descriptor)) return;
116                if (descriptor instanceof ValueParameterDescriptor && isInvisibleFromTheOutside(descriptor.getContainingDeclaration())) return;
117    
118                if (returnType != null && !AsmUtil.isPrimitive(returnType)) {
119                    generateNullabilityAnnotation(descriptor.getReturnType(), annotationDescriptorsAlreadyPresent);
120                }
121            }
122        }
123    
124        private static boolean isInvisibleFromTheOutside(@Nullable DeclarationDescriptor descriptor) {
125            if (descriptor instanceof CallableMemberDescriptor && JetTypeMapper.isAccessor((CallableMemberDescriptor) descriptor)) return false;
126            if (descriptor instanceof MemberDescriptor) {
127                return AsmUtil.getVisibilityAccessFlag((MemberDescriptor) descriptor) == Opcodes.ACC_PRIVATE;
128            }
129            return false;
130        }
131    
132        private void generateNullabilityAnnotation(@Nullable JetType type, @NotNull Set<String> annotationDescriptorsAlreadyPresent) {
133            if (type == null) return;
134    
135            if (isBareTypeParameterWithNullableUpperBound(type)) {
136                // This is to account for the case of, say
137                //   class Function<R> { fun invoke(): R }
138                // it would be a shame to put @Nullable on the return type of the function, and force all callers to check for null,
139                // so we put no annotations
140                return;
141            }
142    
143            if (TypesPackage.isFlexible(type)) {
144                // A flexible type whose lower bound in not-null and upper bound is nullable, should not be annotated
145                Flexibility flexibility = TypesPackage.flexibility(type);
146    
147                if (!TypeUtils.isNullableType(flexibility.getLowerBound()) && TypeUtils.isNullableType(flexibility.getUpperBound())) {
148                    AnnotationDescriptor notNull = type.getAnnotations().findAnnotation(JvmAnnotationNames.JETBRAINS_NOT_NULL_ANNOTATION);
149                    if (notNull != null) {
150                        generateAnnotationIfNotPresent(annotationDescriptorsAlreadyPresent, NotNull.class);
151                    }
152                    return;
153                }
154            }
155    
156            boolean isNullableType = TypeUtils.isNullableType(type);
157    
158            Class<?> annotationClass = isNullableType ? Nullable.class : NotNull.class;
159    
160            generateAnnotationIfNotPresent(annotationDescriptorsAlreadyPresent, annotationClass);
161        }
162    
163        private void generateAnnotationIfNotPresent(Set<String> annotationDescriptorsAlreadyPresent, Class<?> annotationClass) {
164            String descriptor = Type.getType(annotationClass).getDescriptor();
165            if (!annotationDescriptorsAlreadyPresent.contains(descriptor)) {
166                visitAnnotation(descriptor, false).visitEnd();
167            }
168        }
169    
170        private static boolean isBareTypeParameterWithNullableUpperBound(@NotNull JetType type) {
171            ClassifierDescriptor classifier = type.getConstructor().getDeclarationDescriptor();
172            return !type.isMarkedNullable() && classifier instanceof TypeParameterDescriptor && TypeUtils.hasNullableSuperType(type);
173        }
174    
175        public void generateAnnotationDefaultValue(@NotNull CompileTimeConstant value, @NotNull JetType expectedType) {
176            AnnotationVisitor visitor = visitAnnotation(null, false);  // Parameters are unimportant
177            genCompileTimeValue(null, value, expectedType, visitor);
178            visitor.visitEnd();
179        }
180    
181        @Nullable
182        private String genAnnotation(@NotNull AnnotationDescriptor annotationDescriptor) {
183            ClassifierDescriptor classifierDescriptor = annotationDescriptor.getType().getConstructor().getDeclarationDescriptor();
184            assert classifierDescriptor != null : "Annotation descriptor has no class: " + annotationDescriptor;
185            RetentionPolicy rp = getRetentionPolicy(classifierDescriptor);
186            if (rp == RetentionPolicy.SOURCE && typeMapper.getClassBuilderMode() != ClassBuilderMode.LIGHT_CLASSES) {
187                return null;
188            }
189    
190            String descriptor = typeMapper.mapType(annotationDescriptor.getType()).getDescriptor();
191            AnnotationVisitor annotationVisitor = visitAnnotation(descriptor, rp == RetentionPolicy.RUNTIME);
192    
193            genAnnotationArguments(annotationDescriptor, annotationVisitor);
194            annotationVisitor.visitEnd();
195    
196            return descriptor;
197        }
198    
199        private void genAnnotationArguments(AnnotationDescriptor annotationDescriptor, AnnotationVisitor annotationVisitor) {
200            for (Map.Entry<ValueParameterDescriptor, CompileTimeConstant<?>> entry : annotationDescriptor.getAllValueArguments().entrySet()) {
201                ValueParameterDescriptor descriptor = entry.getKey();
202                String name = descriptor.getName().asString();
203                genCompileTimeValue(name, entry.getValue(), descriptor.getType(), annotationVisitor);
204            }
205        }
206    
207        private void genCompileTimeValue(
208                @Nullable final String name,
209                @NotNull CompileTimeConstant<?> value,
210                @NotNull final JetType expectedType,
211                @NotNull final AnnotationVisitor annotationVisitor
212        ) {
213            AnnotationArgumentVisitor argumentVisitor = new AnnotationArgumentVisitor<Void, Void>() {
214                @Override
215                public Void visitLongValue(@NotNull LongValue value, Void data) {
216                    return visitSimpleValue(value);
217                }
218    
219                @Override
220                public Void visitIntValue(IntValue value, Void data) {
221                    return visitSimpleValue(value);
222                }
223    
224                @Override
225                public Void visitShortValue(ShortValue value, Void data) {
226                    return visitSimpleValue(value);
227                }
228    
229                @Override
230                public Void visitByteValue(ByteValue value, Void data) {
231                    return visitSimpleValue(value);
232                }
233    
234                @Override
235                public Void visitDoubleValue(DoubleValue value, Void data) {
236                    return visitSimpleValue(value);
237                }
238    
239                @Override
240                public Void visitFloatValue(FloatValue value, Void data) {
241                    return visitSimpleValue(value);
242                }
243    
244                @Override
245                public Void visitBooleanValue(BooleanValue value, Void data) {
246                    return visitSimpleValue(value);
247                }
248    
249                @Override
250                public Void visitCharValue(CharValue value, Void data) {
251                    return visitSimpleValue(value);
252                }
253    
254                @Override
255                public Void visitStringValue(StringValue value, Void data) {
256                    return visitSimpleValue(value);
257                }
258    
259                @Override
260                public Void visitEnumValue(EnumValue value, Void data) {
261                    String propertyName = value.getValue().getName().asString();
262                    annotationVisitor.visitEnum(name, typeMapper.mapType(value.getType(KotlinBuiltIns.getInstance())).getDescriptor(), propertyName);
263                    return null;
264                }
265    
266                @Override
267                public Void visitArrayValue(ArrayValue value, Void data) {
268                    AnnotationVisitor visitor = annotationVisitor.visitArray(name);
269                    for (CompileTimeConstant<?> argument : value.getValue()) {
270                        genCompileTimeValue(null, argument, value.getType(KotlinBuiltIns.getInstance()), visitor);
271                    }
272                    visitor.visitEnd();
273                    return null;
274                }
275    
276                @Override
277                public Void visitAnnotationValue(AnnotationValue value, Void data) {
278                    String internalAnnName = typeMapper.mapType(value.getValue().getType()).getDescriptor();
279                    AnnotationVisitor visitor = annotationVisitor.visitAnnotation(name, internalAnnName);
280                    genAnnotationArguments(value.getValue(), visitor);
281                    visitor.visitEnd();
282                    return null;
283                }
284    
285                @Override
286                public Void visitJavaClassValue(JavaClassValue value, Void data) {
287                    return visitClassOfType(value.getValue());
288                }
289    
290                @Override
291                public Void visitKClassValue(KClassValue value, Void data) {
292                    return visitClassOfType(value.getValue());
293                }
294    
295                private Void visitClassOfType(@NotNull JetType type) {
296                    annotationVisitor.visit(name, typeMapper.mapType(type));
297                    return null;
298                }
299    
300                @Override
301                public Void visitNumberTypeValue(IntegerValueTypeConstant value, Void data) {
302                    Object numberType = value.getValue(expectedType);
303                    annotationVisitor.visit(name, numberType);
304                    return null;
305                }
306    
307                private Void visitSimpleValue(CompileTimeConstant value) {
308                    annotationVisitor.visit(name, value.getValue());
309                    return null;
310                }
311    
312                @Override
313                public Void visitErrorValue(ErrorValue value, Void data) {
314                    return visitUnsupportedValue(value);
315                }
316    
317                @Override
318                public Void visitNullValue(NullValue value, Void data) {
319                    return visitUnsupportedValue(value);
320                }
321    
322                private Void visitUnsupportedValue(CompileTimeConstant value) {
323                    throw new IllegalStateException("Don't know how to compile annotation value " + value);
324                }
325            };
326    
327            value.accept(argumentVisitor, null);
328        }
329    
330        @NotNull
331        private RetentionPolicy getRetentionPolicy(@NotNull Annotated descriptor) {
332            AnnotationDescriptor retentionAnnotation = descriptor.getAnnotations().findAnnotation(new FqName(Retention.class.getName()));
333            if (retentionAnnotation != null) {
334                Collection<CompileTimeConstant<?>> valueArguments = retentionAnnotation.getAllValueArguments().values();
335                if (!valueArguments.isEmpty()) {
336                    CompileTimeConstant<?> compileTimeConstant = valueArguments.iterator().next();
337                    if (compileTimeConstant instanceof EnumValue) {
338                        ClassDescriptor enumEntry = ((EnumValue) compileTimeConstant).getValue();
339                        JetType classObjectType = getClassObjectType(enumEntry);
340                        if (classObjectType != null) {
341                            if ("java/lang/annotation/RetentionPolicy".equals(typeMapper.mapType(classObjectType).getInternalName())) {
342                                return RetentionPolicy.valueOf(enumEntry.getName().asString());
343                            }
344                        }
345                    }
346                }
347            }
348    
349            return RetentionPolicy.CLASS;
350        }
351    
352        @NotNull
353        abstract AnnotationVisitor visitAnnotation(String descr, boolean visible);
354    
355        public static AnnotationCodegen forClass(final ClassVisitor cv, JetTypeMapper mapper) {
356            return new AnnotationCodegen(mapper) {
357                @NotNull
358                @Override
359                AnnotationVisitor visitAnnotation(String descr, boolean visible) {
360                    return safe(cv.visitAnnotation(descr, visible));
361                }
362            };
363        }
364    
365        public static AnnotationCodegen forMethod(final MethodVisitor mv, JetTypeMapper mapper) {
366            return new AnnotationCodegen(mapper) {
367                @NotNull
368                @Override
369                AnnotationVisitor visitAnnotation(String descr, boolean visible) {
370                    return safe(mv.visitAnnotation(descr, visible));
371                }
372            };
373        }
374    
375        public static AnnotationCodegen forField(final FieldVisitor fv, JetTypeMapper mapper) {
376            return new AnnotationCodegen(mapper) {
377                @NotNull
378                @Override
379                AnnotationVisitor visitAnnotation(String descr, boolean visible) {
380                    return safe(fv.visitAnnotation(descr, visible));
381                }
382            };
383        }
384    
385        public static AnnotationCodegen forParameter(final int parameter, final MethodVisitor mv, JetTypeMapper mapper) {
386            return new AnnotationCodegen(mapper) {
387                @NotNull
388                @Override
389                AnnotationVisitor visitAnnotation(String descr, boolean visible) {
390                    return safe(mv.visitParameterAnnotation(parameter, descr, visible));
391                }
392            };
393        }
394    
395        public static AnnotationCodegen forAnnotationDefaultValue(final MethodVisitor mv, JetTypeMapper mapper) {
396            return new AnnotationCodegen(mapper) {
397                @NotNull
398                @Override
399                AnnotationVisitor visitAnnotation(String descr, boolean visible) {
400                    return safe(mv.visitAnnotationDefault());
401                }
402            };
403        }
404    
405        @NotNull
406        private static AnnotationVisitor safe(@Nullable AnnotationVisitor av) {
407            return av == null ? NO_ANNOTATION_VISITOR : av;
408        }
409    }