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 }