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.types.lang.KotlinBuiltIns;
040    
041    import java.lang.annotation.RetentionPolicy;
042    import java.util.List;
043    import java.util.Map;
044    
045    import static org.jetbrains.jet.lang.resolve.BindingContextUtils.descriptorToDeclaration;
046    
047    public 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    }