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