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