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.NotNull;
021import org.jetbrains.annotations.Nullable;
022import org.jetbrains.asm4.AnnotationVisitor;
023import org.jetbrains.asm4.ClassVisitor;
024import org.jetbrains.asm4.FieldVisitor;
025import org.jetbrains.asm4.MethodVisitor;
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.AnnotationArgumentVisitor;
030import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
031import org.jetbrains.jet.lang.psi.JetAnnotationEntry;
032import org.jetbrains.jet.lang.psi.JetClass;
033import org.jetbrains.jet.lang.psi.JetModifierList;
034import org.jetbrains.jet.lang.psi.JetModifierListOwner;
035import org.jetbrains.jet.lang.resolve.BindingContext;
036import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
037import org.jetbrains.jet.lang.resolve.constants.*;
038import org.jetbrains.jet.lang.resolve.constants.StringValue;
039import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
040
041import java.lang.annotation.RetentionPolicy;
042import java.util.List;
043import java.util.Map;
044
045import static org.jetbrains.jet.lang.resolve.BindingContextUtils.descriptorToDeclaration;
046
047public abstract class AnnotationCodegen {
048    private final JetTypeMapper typeMapper;
049    private final BindingContext bindingContext;
050
051    private AnnotationCodegen(JetTypeMapper mapper) {
052        typeMapper = mapper;
053        bindingContext = typeMapper.getBindingContext();
054    }
055
056    public void genAnnotations(Annotated annotated) {
057        if (annotated == null) {
058            return;
059        }
060
061        if (!(annotated instanceof DeclarationDescriptor)) {
062            return;
063        }
064
065        PsiElement psiElement = descriptorToDeclaration(bindingContext, (DeclarationDescriptor) annotated);
066
067        JetModifierList modifierList = null;
068        if (annotated instanceof ConstructorDescriptor && psiElement instanceof JetClass) {
069            modifierList = ((JetClass) psiElement).getPrimaryConstructorModifierList();
070        }
071        else if (psiElement instanceof JetModifierListOwner) {
072            modifierList = ((JetModifierListOwner) psiElement).getModifierList();
073        }
074
075        if (modifierList == null) {
076            return;
077        }
078
079        List<JetAnnotationEntry> annotationEntries = modifierList.getAnnotationEntries();
080        for (JetAnnotationEntry annotationEntry : annotationEntries) {
081            ResolvedCall<? extends CallableDescriptor> resolvedCall =
082                    bindingContext.get(BindingContext.RESOLVED_CALL, annotationEntry.getCalleeExpression());
083            if (resolvedCall == null) continue; // Skipping annotations if they are not resolved. Needed for JetLightClass generation
084
085            AnnotationDescriptor annotationDescriptor = bindingContext.get(BindingContext.ANNOTATION, annotationEntry);
086            if (annotationDescriptor == null) continue; // Skipping annotations if they are not resolved. Needed for JetLightClass generation
087
088            genAnnotation(annotationDescriptor);
089        }
090    }
091
092    private void genAnnotation(AnnotationDescriptor annotationDescriptor) {
093        ClassifierDescriptor classifierDescriptor = annotationDescriptor.getType().getConstructor().getDeclarationDescriptor();
094        RetentionPolicy rp = getRetentionPolicy(classifierDescriptor, typeMapper);
095        if (rp == RetentionPolicy.SOURCE) {
096            return;
097        }
098
099        String internalName = typeMapper.mapType(annotationDescriptor.getType()).getDescriptor();
100        AnnotationVisitor annotationVisitor = visitAnnotation(internalName, rp == RetentionPolicy.RUNTIME);
101
102        genAnnotationArguments(annotationDescriptor, annotationVisitor);
103        annotationVisitor.visitEnd();
104    }
105
106    private void genAnnotationArguments(AnnotationDescriptor annotationDescriptor, AnnotationVisitor annotationVisitor) {
107        for (Map.Entry<ValueParameterDescriptor, CompileTimeConstant<?>> entry : annotationDescriptor.getAllValueArguments().entrySet()) {
108            ValueParameterDescriptor descriptor = entry.getKey();
109            String name = descriptor.getName().asString();
110            genAnnotationArgument(name, entry.getValue(), annotationVisitor);
111        }
112    }
113
114    private void genAnnotationArgument(
115            @Nullable final String name,
116            @NotNull CompileTimeConstant<?> value,
117            @NotNull final AnnotationVisitor annotationVisitor
118    ) {
119        AnnotationArgumentVisitor argumentVisitor = new AnnotationArgumentVisitor<Void, Void>() {
120            @Override
121            public Void visitLongValue(@NotNull LongValue value, Void data) {
122                return visitSimpleValue(value);
123            }
124
125            @Override
126            public Void visitIntValue(IntValue value, Void data) {
127                return visitSimpleValue(value);
128            }
129
130            @Override
131            public Void visitShortValue(ShortValue value, Void data) {
132                return visitSimpleValue(value);
133            }
134
135            @Override
136            public Void visitByteValue(ByteValue value, Void data) {
137                return visitSimpleValue(value);
138            }
139
140            @Override
141            public Void visitDoubleValue(DoubleValue value, Void data) {
142                return visitSimpleValue(value);
143            }
144
145            @Override
146            public Void visitFloatValue(FloatValue value, Void data) {
147                return visitSimpleValue(value);
148            }
149
150            @Override
151            public Void visitBooleanValue(BooleanValue value, Void data) {
152                return visitSimpleValue(value);
153            }
154
155            @Override
156            public Void visitCharValue(CharValue value, Void data) {
157                return visitSimpleValue(value);
158            }
159
160            @Override
161            public Void visitStringValue(StringValue value, Void data) {
162                return visitSimpleValue(value);
163            }
164
165            @Override
166            public Void visitEnumValue(EnumValue value, Void data) {
167                String propertyName = value.getValue().getName().asString();
168                annotationVisitor.visitEnum(name, typeMapper.mapType(value.getType(KotlinBuiltIns.getInstance())).getDescriptor(), propertyName);
169                return null;
170            }
171
172            @Override
173            public Void visitArrayValue(ArrayValue value, Void data) {
174                AnnotationVisitor visitor = annotationVisitor.visitArray(name);
175                for (CompileTimeConstant<?> argument : value.getValue()) {
176                    genAnnotationArgument(null, argument, visitor);
177                }
178                visitor.visitEnd();
179                return null;
180            }
181
182            @Override
183            public Void visitAnnotationValue(AnnotationValue value, Void data) {
184                String internalAnnName = typeMapper.mapType(value.getValue().getType()).getDescriptor();
185                AnnotationVisitor visitor = annotationVisitor.visitAnnotation(name, internalAnnName);
186                genAnnotationArguments(value.getValue(), visitor);
187                visitor.visitEnd();
188                return null;
189            }
190
191            @Override
192            public Void visitJavaClassValue(JavaClassValue value, Void data) {
193                annotationVisitor.visit(name, typeMapper.mapType(value.getValue()));
194                return null;
195            }
196
197            private Void visitSimpleValue(CompileTimeConstant value) {
198                annotationVisitor.visit(name, value.getValue());
199                return null;
200            }
201
202            @Override
203            public Void visitErrorValue(ErrorValue value, Void data) {
204                return visitUnsupportedValue(value);
205            }
206
207            @Override
208            public Void visitNullValue(NullValue value, Void data) {
209                return visitUnsupportedValue(value);
210            }
211
212            private Void visitUnsupportedValue(CompileTimeConstant value) {
213                throw new IllegalStateException("Don't know how to compile annotation value " + value);
214            }
215        };
216
217        value.accept(argumentVisitor, null);
218    }
219
220    private static RetentionPolicy getRetentionPolicy(ClassifierDescriptor descriptor, JetTypeMapper typeMapper) {
221        RetentionPolicy rp = RetentionPolicy.RUNTIME;
222        /*
223        @todo : when JavaDescriptoResolver provides ennough info
224        for (AnnotationDescriptor annotationDescriptor : descriptor.getAnnotations()) {
225            String internalName = typeMapper.mapType(annotationDescriptor.getType()).getInternalName();
226            if("java/lang/annotation/RetentionPolicy".equals(internalName)) {
227                CompileTimeConstant<?> compileTimeConstant = annotationDescriptor.getValueArguments().get(0);
228                System.out.println(compileTimeConstant);
229                break;
230            }
231        }
232        */
233        return rp;  //To change body of created methods use File | Settings | File Templates.
234    }
235
236    abstract AnnotationVisitor visitAnnotation(String descr, boolean visible);
237
238    public static AnnotationCodegen forClass(final ClassVisitor cv, JetTypeMapper mapper) {
239        return new AnnotationCodegen(mapper) {
240            @Override
241            AnnotationVisitor visitAnnotation(String descr, boolean visible) {
242                return cv.visitAnnotation(descr, visible);
243            }
244        };
245    }
246
247    public static AnnotationCodegen forMethod(final MethodVisitor mv, JetTypeMapper mapper) {
248        return new AnnotationCodegen(mapper) {
249            @Override
250            AnnotationVisitor visitAnnotation(String descr, boolean visible) {
251                return mv.visitAnnotation(descr, visible);
252            }
253        };
254    }
255
256    public static AnnotationCodegen forField(final FieldVisitor fv, JetTypeMapper mapper) {
257        return new AnnotationCodegen(mapper) {
258            @Override
259            AnnotationVisitor visitAnnotation(String descr, boolean visible) {
260                return fv.visitAnnotation(descr, visible);
261            }
262        };
263    }
264
265    public static AnnotationCodegen forParameter(final int parameter, final MethodVisitor mv, JetTypeMapper mapper) {
266        return new AnnotationCodegen(mapper) {
267            @Override
268            AnnotationVisitor visitAnnotation(String descr, boolean visible) {
269                return mv.visitParameterAnnotation(parameter, descr, visible);
270            }
271        };
272    }
273}