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