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