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