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
017package org.jetbrains.jet.codegen;
018
019import com.intellij.psi.PsiElement;
020import org.jetbrains.annotations.Nullable;
021import org.jetbrains.asm4.AnnotationVisitor;
022import org.jetbrains.asm4.ClassVisitor;
023import org.jetbrains.asm4.FieldVisitor;
024import org.jetbrains.asm4.MethodVisitor;
025import org.jetbrains.jet.codegen.intrinsics.IntrinsicMethods;
026import org.jetbrains.jet.codegen.state.JetTypeMapper;
027import org.jetbrains.jet.lang.descriptors.*;
028import org.jetbrains.jet.lang.descriptors.annotations.Annotated;
029import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
030import org.jetbrains.jet.lang.psi.*;
031import org.jetbrains.jet.lang.resolve.BindingContext;
032import org.jetbrains.jet.lang.resolve.calls.model.DefaultValueArgument;
033import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
034import org.jetbrains.jet.lang.resolve.calls.model.ResolvedValueArgument;
035import org.jetbrains.jet.lang.resolve.calls.model.VarargValueArgument;
036import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
037import org.jetbrains.jet.lang.resolve.name.Name;
038import org.jetbrains.jet.lang.types.JetType;
039
040import java.lang.annotation.RetentionPolicy;
041import java.util.List;
042import java.util.Map;
043
044import static org.jetbrains.jet.lang.resolve.BindingContextUtils.descriptorToDeclaration;
045
046public abstract class AnnotationCodegen {
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            return;
076        }
077
078        List<JetAnnotationEntry> annotationEntries = modifierList.getAnnotationEntries();
079        for (JetAnnotationEntry annotationEntry : annotationEntries) {
080            ResolvedCall<? extends CallableDescriptor> resolvedCall =
081                    bindingContext.get(BindingContext.RESOLVED_CALL, annotationEntry.getCalleeExpression());
082            if (resolvedCall == null) continue; // Skipping annotations if they are not resolved. Needed for JetLightClass generation
083
084            AnnotationDescriptor annotationDescriptor = bindingContext.get(BindingContext.ANNOTATION, annotationEntry);
085            if (annotationDescriptor == null) continue; // Skipping annotations if they are not resolved. Needed for JetLightClass generation
086
087            JetType type = annotationDescriptor.getType();
088            genAnnotation(resolvedCall, type);
089        }
090    }
091
092    private void genAnnotation(
093            ResolvedCall<? extends CallableDescriptor> resolvedCall,
094            JetType type
095    ) {
096        ClassifierDescriptor classifierDescriptor = type.getConstructor().getDeclarationDescriptor();
097        RetentionPolicy rp = getRetentionPolicy(classifierDescriptor, typeMapper);
098        if (rp == RetentionPolicy.SOURCE) {
099            return;
100        }
101
102        String internalName = typeMapper.mapType(type).getDescriptor();
103        AnnotationVisitor annotationVisitor = visitAnnotation(internalName, rp == RetentionPolicy.RUNTIME);
104
105        getAnnotation(resolvedCall, annotationVisitor);
106
107        annotationVisitor.visitEnd();
108    }
109
110    private void getAnnotation(ResolvedCall<? extends CallableDescriptor> resolvedCall, AnnotationVisitor annotationVisitor) {
111        for (Map.Entry<ValueParameterDescriptor, ResolvedValueArgument> entry : resolvedCall.getValueArguments().entrySet()) {
112            ResolvedValueArgument valueArgument = entry.getValue();
113            if (valueArgument instanceof DefaultValueArgument) {
114                continue;
115            }
116
117            Name keyName = entry.getKey().getName();
118            genAnnotationValueArgument(annotationVisitor, valueArgument, keyName.asString());
119        }
120    }
121
122    private void genAnnotationValueArgument(AnnotationVisitor annotationVisitor, ResolvedValueArgument valueArgument, String keyName) {
123        List<ValueArgument> valueArguments = valueArgument.getArguments();
124        if (valueArgument instanceof VarargValueArgument) {
125            AnnotationVisitor visitor = annotationVisitor.visitArray(keyName);
126            for (ValueArgument argument : valueArguments) {
127                genAnnotationExpressionValue(visitor, null, argument.getArgumentExpression());
128            }
129            visitor.visitEnd();
130        }
131        else {
132            assert valueArguments.size() == 1 : "Number of arguments on " + keyName + " = " + valueArguments.size(); // todo
133            JetExpression expression = valueArguments.get(0).getArgumentExpression();
134            genAnnotationExpressionValue(annotationVisitor, keyName, expression);
135        }
136    }
137
138    private void genAnnotationExpressionValue(AnnotationVisitor annotationVisitor, @Nullable String keyName, JetExpression expression) {
139        CompileTimeConstant<?> compileTimeConstant =
140                bindingContext.get(BindingContext.COMPILE_TIME_VALUE, expression);
141
142        if (compileTimeConstant != null) {
143            Object value = compileTimeConstant.getValue();
144            annotationVisitor.visit(keyName, value);
145            return;
146        }
147
148        if (expression instanceof JetDotQualifiedExpression) {
149            JetDotQualifiedExpression qualifiedExpression = (JetDotQualifiedExpression) expression;
150            ResolvedCall<? extends CallableDescriptor> call =
151                    bindingContext.get(BindingContext.RESOLVED_CALL, qualifiedExpression.getSelectorExpression());
152            if (call != null) {
153                if (call.getResultingDescriptor() instanceof PropertyDescriptor) {
154                    PropertyDescriptor descriptor = (PropertyDescriptor) call.getResultingDescriptor();
155                    annotationVisitor.visitEnum(keyName, typeMapper.mapType(descriptor).getDescriptor(), descriptor.getName().asString());
156                    return;
157                }
158            }
159        }
160        else {
161            if (expression instanceof JetCallExpression) {
162                JetCallExpression callExpression = (JetCallExpression) expression;
163                ResolvedCall<? extends CallableDescriptor> call =
164                        bindingContext.get(BindingContext.RESOLVED_CALL, callExpression.getCalleeExpression());
165                if (call != null) {
166                    List<AnnotationDescriptor> annotations = call.getResultingDescriptor().getOriginal().getAnnotations();
167                    String value = null;
168                    if (annotations != null) {
169                        for (AnnotationDescriptor annotation : annotations) {
170                            //noinspection ConstantConditions
171                            if ("Intrinsic".equals(annotation.getType().getConstructor().getDeclarationDescriptor().getName().asString())) {
172                                value = (String) annotation.getAllValueArguments().values().iterator().next().getValue();
173                                break;
174                            }
175                        }
176                    }
177                    if (IntrinsicMethods.KOTLIN_JAVA_CLASS_FUNCTION.equals(value)) {
178                        //noinspection ConstantConditions
179                        annotationVisitor.visit(keyName, typeMapper
180                                .mapType(call.getResultingDescriptor().getReturnType().getArguments().get(0).getType()));
181                        return;
182                    }
183                    else if (IntrinsicMethods.KOTLIN_ARRAYS_ARRAY.equals(value)) {
184                        AnnotationVisitor visitor = annotationVisitor.visitArray(keyName);
185                        VarargValueArgument next = (VarargValueArgument) call.getValueArguments().values().iterator().next();
186                        for (ValueArgument argument : next.getArguments()) {
187                            genAnnotationExpressionValue(visitor, null, argument.getArgumentExpression());
188                        }
189                        visitor.visitEnd();
190                        return;
191                    }
192                    else if (call.getResultingDescriptor() instanceof ConstructorDescriptor) {
193                        ConstructorDescriptor descriptor = (ConstructorDescriptor) call.getResultingDescriptor();
194                        AnnotationVisitor visitor = annotationVisitor.visitAnnotation(keyName, typeMapper
195                                .mapType(descriptor.getContainingDeclaration()).getDescriptor());
196                        getAnnotation(call, visitor);
197                        visitor.visitEnd();
198                        return;
199                    }
200                }
201            }
202        }
203
204        throw new IllegalStateException("Don't know how to compile annotation value");
205    }
206
207    private static RetentionPolicy getRetentionPolicy(ClassifierDescriptor descriptor, JetTypeMapper typeMapper) {
208        RetentionPolicy rp = RetentionPolicy.RUNTIME;
209        /*
210        @todo : when JavaDescriptoResolver provides ennough info
211        for (AnnotationDescriptor annotationDescriptor : descriptor.getAnnotations()) {
212            String internalName = typeMapper.mapType(annotationDescriptor.getType()).getInternalName();
213            if("java/lang/annotation/RetentionPolicy".equals(internalName)) {
214                CompileTimeConstant<?> compileTimeConstant = annotationDescriptor.getValueArguments().get(0);
215                System.out.println(compileTimeConstant);
216                break;
217            }
218        }
219        */
220        return rp;  //To change body of created methods use File | Settings | File Templates.
221    }
222
223    abstract AnnotationVisitor visitAnnotation(String descr, boolean visible);
224
225    public static AnnotationCodegen forClass(final ClassVisitor cv, JetTypeMapper mapper) {
226        return new AnnotationCodegen(mapper) {
227            @Override
228            AnnotationVisitor visitAnnotation(String descr, boolean visible) {
229                return cv.visitAnnotation(descr, visible);
230            }
231        };
232    }
233
234    public static AnnotationCodegen forMethod(final MethodVisitor mv, JetTypeMapper mapper) {
235        return new AnnotationCodegen(mapper) {
236            @Override
237            AnnotationVisitor visitAnnotation(String descr, boolean visible) {
238                return mv.visitAnnotation(descr, visible);
239            }
240        };
241    }
242
243    public static AnnotationCodegen forField(final FieldVisitor fv, JetTypeMapper mapper) {
244        return new AnnotationCodegen(mapper) {
245            @Override
246            AnnotationVisitor visitAnnotation(String descr, boolean visible) {
247                return fv.visitAnnotation(descr, visible);
248            }
249        };
250    }
251
252    public static AnnotationCodegen forParameter(final int parameter, final MethodVisitor mv, JetTypeMapper mapper) {
253        return new AnnotationCodegen(mapper) {
254            @Override
255            AnnotationVisitor visitAnnotation(String descr, boolean visible) {
256                return mv.visitParameterAnnotation(parameter, descr, visible);
257            }
258        };
259    }
260}