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