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