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