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