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.lang.resolve.kotlin;
018    
019    import org.jetbrains.annotations.NotNull;
020    import org.jetbrains.annotations.Nullable;
021    import org.jetbrains.jet.descriptors.serialization.JavaProtoBuf;
022    import org.jetbrains.jet.descriptors.serialization.NameResolver;
023    import org.jetbrains.jet.descriptors.serialization.ProtoBuf;
024    import org.jetbrains.jet.descriptors.serialization.SerializationPackage;
025    import org.jetbrains.jet.descriptors.serialization.descriptors.AnnotatedCallableKind;
026    import org.jetbrains.jet.descriptors.serialization.descriptors.AnnotationLoader;
027    import org.jetbrains.jet.lang.descriptors.*;
028    import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
029    import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptorImpl;
030    import org.jetbrains.jet.lang.descriptors.annotations.Annotations;
031    import org.jetbrains.jet.lang.descriptors.annotations.AnnotationsImpl;
032    import org.jetbrains.jet.lang.resolve.constants.*;
033    import org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames;
034    import org.jetbrains.jet.lang.resolve.java.resolver.DescriptorResolverUtils;
035    import org.jetbrains.jet.lang.resolve.java.resolver.ErrorReporter;
036    import org.jetbrains.jet.lang.resolve.kotlin.KotlinJvmBinaryClass.AnnotationArrayArgumentVisitor;
037    import org.jetbrains.jet.lang.resolve.name.ClassId;
038    import org.jetbrains.jet.lang.resolve.name.Name;
039    import org.jetbrains.jet.lang.types.ErrorUtils;
040    
041    import javax.inject.Inject;
042    import java.io.IOException;
043    import java.util.ArrayList;
044    import java.util.HashMap;
045    import java.util.List;
046    import java.util.Map;
047    
048    import static org.jetbrains.jet.lang.resolve.kotlin.DescriptorLoadersStorage.MemberSignature;
049    import static org.jetbrains.jet.lang.resolve.kotlin.DeserializedResolverUtils.javaClassIdToKotlinClassId;
050    
051    public class AnnotationDescriptorLoader extends BaseDescriptorLoader implements AnnotationLoader {
052    
053        private ModuleDescriptor module;
054    
055        @Inject
056        public void setModule(ModuleDescriptor module) {
057            this.module = module;
058        }
059    
060        @Inject
061        @Override
062        public void setStorage(@NotNull DescriptorLoadersStorage storage) {
063            this.storage = storage;
064        }
065    
066        @Inject
067        @Override
068        public void setKotlinClassFinder(@NotNull KotlinClassFinder kotlinClassFinder) {
069            this.kotlinClassFinder = kotlinClassFinder;
070        }
071    
072        @Inject
073        @Override
074        public void setErrorReporter(@NotNull ErrorReporter errorReporter) {
075            this.errorReporter = errorReporter;
076        }
077    
078        @NotNull
079        @Override
080        public Annotations loadClassAnnotations(@NotNull ClassDescriptor descriptor, @NotNull ProtoBuf.Class classProto) {
081            KotlinJvmBinaryClass kotlinClass = findKotlinClassByDescriptor(descriptor);
082            if (kotlinClass == null) {
083                // This means that the resource we're constructing the descriptor from is no longer present: KotlinClassFinder had found the
084                // class earlier, but it can't now
085                errorReporter.reportLoadingError("Kotlin class for loading class annotations is not found: " + descriptor, null);
086                return Annotations.EMPTY;
087            }
088            try {
089                return loadClassAnnotationsFromClass(kotlinClass);
090            }
091            catch (IOException e) {
092                errorReporter.reportLoadingError("Error loading member annotations from Kotlin class: " + kotlinClass, e);
093                return Annotations.EMPTY;
094            }
095        }
096    
097        @NotNull
098        private Annotations loadClassAnnotationsFromClass(@NotNull KotlinJvmBinaryClass kotlinClass) throws IOException {
099            final List<AnnotationDescriptor> result = new ArrayList<AnnotationDescriptor>();
100    
101            kotlinClass.loadClassAnnotations(new KotlinJvmBinaryClass.AnnotationVisitor() {
102                @Nullable
103                @Override
104                public KotlinJvmBinaryClass.AnnotationArgumentVisitor visitAnnotation(@NotNull ClassId classId) {
105                    return resolveAnnotation(classId, result, module);
106                }
107    
108                @Override
109                public void visitEnd() {
110                }
111            });
112    
113            return new AnnotationsImpl(result);
114        }
115    
116        @Nullable
117        public static KotlinJvmBinaryClass.AnnotationArgumentVisitor resolveAnnotation(
118                @NotNull ClassId classId,
119                @NotNull final List<AnnotationDescriptor> result,
120                @NotNull final ModuleDescriptor moduleDescriptor
121        ) {
122            if (JvmAnnotationNames.isSpecialAnnotation(classId, true)) return null;
123    
124            final ClassDescriptor annotationClass = resolveClass(classId, moduleDescriptor);
125    
126            return new KotlinJvmBinaryClass.AnnotationArgumentVisitor() {
127                private final Map<ValueParameterDescriptor, CompileTimeConstant<?>> arguments = new HashMap<ValueParameterDescriptor, CompileTimeConstant<?>>();
128    
129                @Override
130                public void visit(@Nullable Name name, @Nullable Object value) {
131                    if (name != null) {
132                        setArgumentValueByName(name, createConstant(name, value));
133                    }
134                }
135    
136                @Override
137                public void visitEnum(@NotNull Name name, @NotNull ClassId enumClassId, @NotNull Name enumEntryName) {
138                    setArgumentValueByName(name, enumEntryValue(enumClassId, enumEntryName));
139                }
140    
141                @Nullable
142                @Override
143                public AnnotationArrayArgumentVisitor visitArray(@NotNull final Name name) {
144                    return new KotlinJvmBinaryClass.AnnotationArrayArgumentVisitor() {
145                        private final ArrayList<CompileTimeConstant<?>> elements = new ArrayList<CompileTimeConstant<?>>();
146    
147                        @Override
148                        public void visit(@Nullable Object value) {
149                            elements.add(createConstant(name, value));
150                        }
151    
152                        @Override
153                        public void visitEnum(@NotNull ClassId enumClassId, @NotNull Name enumEntryName) {
154                            elements.add(enumEntryValue(enumClassId, enumEntryName));
155                        }
156    
157                        @Override
158                        public void visitEnd() {
159                            ValueParameterDescriptor parameter = DescriptorResolverUtils.getAnnotationParameterByName(name, annotationClass);
160                            if (parameter != null) {
161                                elements.trimToSize();
162                                arguments.put(parameter, new ArrayValue(elements, parameter.getType(), true, false));
163                            }
164                        }
165                    };
166                }
167    
168                @NotNull
169                private CompileTimeConstant<?> enumEntryValue(@NotNull ClassId enumClassId, @NotNull Name name) {
170                    ClassDescriptor enumClass = resolveClass(enumClassId, moduleDescriptor);
171                    if (enumClass.getKind() == ClassKind.ENUM_CLASS) {
172                        ClassifierDescriptor classifier = enumClass.getUnsubstitutedInnerClassesScope().getClassifier(name);
173                        if (classifier instanceof ClassDescriptor) {
174                            return new EnumValue((ClassDescriptor) classifier, false);
175                        }
176                    }
177                    return ErrorValue.create("Unresolved enum entry: " + enumClassId + "." + name);
178                }
179    
180                @Override
181                public void visitEnd() {
182                    result.add(new AnnotationDescriptorImpl(
183                            annotationClass.getDefaultType(),
184                            arguments
185                    ));
186                }
187    
188                @NotNull
189                private CompileTimeConstant<?> createConstant(@Nullable Name name, @Nullable Object value) {
190                    CompileTimeConstant<?> argument = ConstantsPackage.createCompileTimeConstant(value, true, false, false, null);
191                    return argument != null ? argument : ErrorValue.create("Unsupported annotation argument: " + name);
192                }
193    
194                private void setArgumentValueByName(@NotNull Name name, @NotNull CompileTimeConstant<?> argumentValue) {
195                    ValueParameterDescriptor parameter = DescriptorResolverUtils.getAnnotationParameterByName(name, annotationClass);
196                    if (parameter != null) {
197                        arguments.put(parameter, argumentValue);
198                    }
199                }
200            };
201        }
202    
203        @NotNull
204        private static ClassDescriptor resolveClass(@NotNull ClassId javaClassId, @NotNull ModuleDescriptor moduleDescriptor) {
205            ClassId classId = javaClassIdToKotlinClassId(javaClassId);
206            ClassDescriptor classDescriptor = SerializationPackage.findClassAcrossModuleDependencies(moduleDescriptor, classId);
207            return classDescriptor != null ? classDescriptor : ErrorUtils.createErrorClass(classId.asSingleFqName().asString());
208        }
209    
210        @NotNull
211        @Override
212        public Annotations loadCallableAnnotations(
213                @NotNull ClassOrPackageFragmentDescriptor container,
214                @NotNull ProtoBuf.Callable proto,
215                @NotNull NameResolver nameResolver,
216                @NotNull AnnotatedCallableKind kind
217        ) {
218            MemberSignature signature = getCallableSignature(proto, nameResolver, kind);
219            if (signature == null) return Annotations.EMPTY;
220    
221            return findClassAndLoadMemberAnnotations(container, proto, nameResolver, kind, signature);
222        }
223    
224        @NotNull
225        private Annotations findClassAndLoadMemberAnnotations(
226                @NotNull ClassOrPackageFragmentDescriptor container,
227                @NotNull ProtoBuf.Callable proto,
228                @NotNull NameResolver nameResolver,
229                @NotNull AnnotatedCallableKind kind,
230                @NotNull MemberSignature signature
231        ) {
232            KotlinJvmBinaryClass kotlinClass = findClassWithAnnotationsAndInitializers(container, proto, nameResolver, kind);
233            if (kotlinClass == null) {
234                errorReporter.reportLoadingError("Kotlin class for loading member annotations is not found: " + container, null);
235                return Annotations.EMPTY;
236            }
237    
238            List<AnnotationDescriptor> annotations = storage.getStorage().invoke(kotlinClass).getMemberAnnotations().get(signature);
239            return annotations == null ? Annotations.EMPTY : new AnnotationsImpl(annotations);
240        }
241    
242        @NotNull
243        @Override
244        public Annotations loadValueParameterAnnotations(
245                @NotNull ClassOrPackageFragmentDescriptor container,
246                @NotNull ProtoBuf.Callable callable,
247                @NotNull NameResolver nameResolver,
248                @NotNull AnnotatedCallableKind kind,
249                @NotNull ProtoBuf.Callable.ValueParameter proto
250        ) {
251            MemberSignature methodSignature = getCallableSignature(callable, nameResolver, kind);
252            if (methodSignature != null) {
253                if (proto.hasExtension(JavaProtoBuf.index)) {
254                    MemberSignature paramSignature =
255                            MemberSignature.fromMethodSignatureAndParameterIndex(methodSignature, proto.getExtension(JavaProtoBuf.index));
256                    return findClassAndLoadMemberAnnotations(container, callable, nameResolver, kind, paramSignature);
257                }
258            }
259    
260            return Annotations.EMPTY;
261        }
262    }