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