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