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 }