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 jet.Function1;
020 import org.jetbrains.annotations.NotNull;
021 import org.jetbrains.annotations.Nullable;
022 import org.jetbrains.jet.descriptors.serialization.JavaProtoBuf;
023 import org.jetbrains.jet.descriptors.serialization.NameResolver;
024 import org.jetbrains.jet.descriptors.serialization.ProtoBuf;
025 import org.jetbrains.jet.descriptors.serialization.descriptors.AnnotationDeserializer;
026 import org.jetbrains.jet.lang.descriptors.*;
027 import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
028 import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptorImpl;
029 import org.jetbrains.jet.lang.resolve.DescriptorUtils;
030 import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
031 import org.jetbrains.jet.lang.resolve.constants.EnumValue;
032 import org.jetbrains.jet.lang.resolve.constants.ErrorValue;
033 import org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames;
034 import org.jetbrains.jet.lang.resolve.java.JvmClassName;
035 import org.jetbrains.jet.lang.resolve.java.PackageClassUtils;
036 import org.jetbrains.jet.lang.resolve.java.resolver.*;
037 import org.jetbrains.jet.lang.resolve.name.FqName;
038 import org.jetbrains.jet.lang.resolve.name.Name;
039 import org.jetbrains.jet.lang.types.ErrorUtils;
040 import org.jetbrains.jet.storage.LockBasedStorageManager;
041 import org.jetbrains.jet.storage.MemoizedFunctionToNotNull;
042
043 import javax.inject.Inject;
044 import java.io.IOException;
045 import java.util.*;
046
047 import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isClassObject;
048 import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isTrait;
049 import static org.jetbrains.jet.lang.resolve.java.DescriptorSearchRule.IGNORE_KOTLIN_SOURCES;
050 import static org.jetbrains.jet.lang.resolve.kotlin.DeserializedResolverUtils.kotlinFqNameToJavaFqName;
051 import static org.jetbrains.jet.lang.resolve.kotlin.DeserializedResolverUtils.naiveKotlinFqName;
052
053 public class AnnotationDescriptorDeserializer implements AnnotationDeserializer {
054 private JavaClassResolver javaClassResolver;
055 private KotlinClassFinder kotlinClassFinder;
056 private ErrorReporter errorReporter;
057
058 // TODO: a single instance of StorageManager for all computations in resolve-java
059 private final LockBasedStorageManager storageManager = new LockBasedStorageManager();
060
061 private final MemoizedFunctionToNotNull<KotlinJvmBinaryClass, Map<MemberSignature, List<AnnotationDescriptor>>> memberAnnotations =
062 storageManager.createMemoizedFunction(
063 new Function1<KotlinJvmBinaryClass, Map<MemberSignature, List<AnnotationDescriptor>>>() {
064 @NotNull
065 @Override
066 public Map<MemberSignature, List<AnnotationDescriptor>> invoke(@NotNull KotlinJvmBinaryClass kotlinClass) {
067 try {
068 return loadMemberAnnotationsFromClass(kotlinClass);
069 }
070 catch (IOException e) {
071 errorReporter.reportAnnotationLoadingError(
072 "Error loading member annotations from Kotlin class: " + kotlinClass, e);
073 return Collections.emptyMap();
074 }
075 }
076 });
077
078 @Inject
079 public void setJavaClassResolver(JavaClassResolver javaClassResolver) {
080 this.javaClassResolver = javaClassResolver;
081 }
082
083 @Inject
084 public void setKotlinClassFinder(KotlinClassFinder kotlinClassFinder) {
085 this.kotlinClassFinder = kotlinClassFinder;
086 }
087
088 @Inject
089 public void setErrorReporter(ErrorReporter errorReporter) {
090 this.errorReporter = errorReporter;
091 }
092
093 @NotNull
094 @Override
095 public List<AnnotationDescriptor> loadClassAnnotations(@NotNull ClassDescriptor descriptor, @NotNull ProtoBuf.Class classProto) {
096 KotlinJvmBinaryClass kotlinClass = findKotlinClassByDescriptor(descriptor);
097 if (kotlinClass == null) {
098 // This means that the resource we're constructing the descriptor from is no longer present: KotlinClassFinder had found the
099 // class earlier, but it can't now
100 errorReporter.reportAnnotationLoadingError("Kotlin class for loading class annotations is not found: " + descriptor, null);
101 return Collections.emptyList();
102 }
103 try {
104 return loadClassAnnotationsFromClass(kotlinClass);
105 }
106 catch (IOException e) {
107 errorReporter.reportAnnotationLoadingError("Error loading member annotations from Kotlin class: " + kotlinClass, e);
108 return Collections.emptyList();
109 }
110 }
111
112 @Nullable
113 private KotlinJvmBinaryClass findKotlinClassByDescriptor(@NotNull ClassOrNamespaceDescriptor descriptor) {
114 if (descriptor instanceof ClassDescriptor) {
115 return kotlinClassFinder.find(kotlinFqNameToJavaFqName(naiveKotlinFqName((ClassDescriptor) descriptor)));
116 }
117 else if (descriptor instanceof NamespaceDescriptor) {
118 return kotlinClassFinder.find(PackageClassUtils.getPackageClassFqName(DescriptorUtils.getFQName(descriptor).toSafe()));
119 }
120 else {
121 throw new IllegalStateException("Unrecognized descriptor: " + descriptor);
122 }
123 }
124
125 @NotNull
126 private List<AnnotationDescriptor> loadClassAnnotationsFromClass(@NotNull KotlinJvmBinaryClass kotlinClass) throws IOException {
127 final List<AnnotationDescriptor> result = new ArrayList<AnnotationDescriptor>();
128
129 kotlinClass.loadClassAnnotations(new KotlinJvmBinaryClass.AnnotationVisitor() {
130 @Nullable
131 @Override
132 public KotlinJvmBinaryClass.AnnotationArgumentVisitor visitAnnotation(@NotNull JvmClassName className) {
133 return resolveAnnotation(className, result);
134 }
135
136 @Override
137 public void visitEnd() {
138 }
139 });
140
141 return result;
142 }
143
144 private static boolean ignoreAnnotation(@NotNull JvmClassName className) {
145 return className.equals(JvmClassName.byFqNameWithoutInnerClasses(JvmAnnotationNames.KOTLIN_CLASS))
146 || className.equals(JvmClassName.byFqNameWithoutInnerClasses(JvmAnnotationNames.KOTLIN_PACKAGE))
147 || className.equals(JvmClassName.byFqNameWithoutInnerClasses(JavaAnnotationResolver.JETBRAINS_NOT_NULL_ANNOTATION))
148 || className.equals(JvmClassName.byFqNameWithoutInnerClasses(JavaAnnotationResolver.JETBRAINS_NULLABLE_ANNOTATION))
149 || className.getInternalName().startsWith("jet/runtime/typeinfo/");
150 }
151
152 @Nullable
153 private KotlinJvmBinaryClass.AnnotationArgumentVisitor resolveAnnotation(
154 @NotNull JvmClassName className,
155 @NotNull final List<AnnotationDescriptor> result
156 ) {
157 if (ignoreAnnotation(className)) return null;
158
159 final ClassDescriptor annotationClass = resolveClass(className);
160 final AnnotationDescriptorImpl annotation = new AnnotationDescriptorImpl();
161 annotation.setAnnotationType(annotationClass.getDefaultType());
162
163 return new KotlinJvmBinaryClass.AnnotationArgumentVisitor() {
164 @Override
165 public void visit(@Nullable Name name, @Nullable Object value) {
166 if (name != null) {
167 CompileTimeConstant<?> argument = JavaAnnotationArgumentResolver.resolveCompileTimeConstantValue(value, null);
168 setArgumentValueByName(name, argument != null ? argument : ErrorValue.create("Unsupported annotation argument: " + name));
169 }
170 }
171
172 @Override
173 public void visitEnum(@NotNull Name name, @NotNull JvmClassName enumClassName, @NotNull Name enumEntryName) {
174 setArgumentValueByName(name, enumEntryValue(enumClassName, enumEntryName));
175 }
176
177 @Nullable
178 @Override
179 public KotlinJvmBinaryClass.AnnotationArgumentVisitor visitArray(@NotNull Name name) {
180 // TODO: support arrays
181 return null;
182 }
183
184 @NotNull
185 private CompileTimeConstant<?> enumEntryValue(@NotNull JvmClassName enumClassName, @NotNull Name name) {
186 ClassDescriptor enumClass = resolveClass(enumClassName);
187 if (enumClass.getKind() == ClassKind.ENUM_CLASS) {
188 ClassifierDescriptor classifier = enumClass.getUnsubstitutedInnerClassesScope().getClassifier(name);
189 if (classifier instanceof ClassDescriptor) {
190 return new EnumValue((ClassDescriptor) classifier);
191 }
192 }
193 return ErrorValue.create("Unresolved enum entry: " + enumClassName.getInternalName() + "." + name);
194 }
195
196 @Override
197 public void visitEnd() {
198 result.add(annotation);
199 }
200
201 private void setArgumentValueByName(@NotNull Name name, @NotNull CompileTimeConstant<?> argumentValue) {
202 ValueParameterDescriptor parameter = DescriptorResolverUtils.getAnnotationParameterByName(name, annotationClass);
203 if (parameter != null) {
204 annotation.setValueArgument(parameter, argumentValue);
205 }
206 }
207 };
208 }
209
210 @NotNull
211 private ClassDescriptor resolveClass(@NotNull JvmClassName className) {
212 ClassDescriptor annotationClass = javaClassResolver.resolveClass(className.getFqNameForClassNameWithoutDollars(),
213 IGNORE_KOTLIN_SOURCES);
214 return annotationClass != null ? annotationClass : ErrorUtils.getErrorClass();
215 }
216
217 @NotNull
218 @Override
219 public List<AnnotationDescriptor> loadCallableAnnotations(
220 @NotNull ClassOrNamespaceDescriptor container,
221 @NotNull ProtoBuf.Callable proto,
222 @NotNull NameResolver nameResolver,
223 @NotNull AnnotatedCallableKind kind
224 ) {
225 MemberSignature signature = getCallableSignature(proto, nameResolver, kind);
226 if (signature == null) return Collections.emptyList();
227
228 return findClassAndLoadMemberAnnotations(container, proto, nameResolver, kind, signature);
229 }
230
231 @NotNull
232 private List<AnnotationDescriptor> findClassAndLoadMemberAnnotations(
233 @NotNull ClassOrNamespaceDescriptor container,
234 @NotNull ProtoBuf.Callable proto,
235 @NotNull NameResolver nameResolver,
236 @NotNull AnnotatedCallableKind kind,
237 @NotNull MemberSignature signature
238 ) {
239 KotlinJvmBinaryClass kotlinClass = findClassWithMemberAnnotations(container, proto, nameResolver, kind);
240 if (kotlinClass == null) {
241 errorReporter.reportAnnotationLoadingError("Kotlin class for loading member annotations is not found: " + container, null);
242 return Collections.emptyList();
243 }
244
245 List<AnnotationDescriptor> annotations = memberAnnotations.invoke(kotlinClass).get(signature);
246 return annotations == null ? Collections.<AnnotationDescriptor>emptyList() : annotations;
247 }
248
249 @Nullable
250 private KotlinJvmBinaryClass findClassWithMemberAnnotations(
251 @NotNull ClassOrNamespaceDescriptor container,
252 @NotNull ProtoBuf.Callable proto,
253 @NotNull NameResolver nameResolver,
254 @NotNull AnnotatedCallableKind kind
255 ) {
256 if (container instanceof NamespaceDescriptor) {
257 return loadPackageFragmentClassFqName((NamespaceDescriptor) container, proto, nameResolver);
258 }
259 else if (isClassObject(container) && isStaticFieldInOuter(proto)) {
260 // Backing fields of properties of a class object are generated in the outer class
261 return findKotlinClassByDescriptor((ClassOrNamespaceDescriptor) container.getContainingDeclaration());
262 }
263 else if (isTrait(container) && kind == AnnotatedCallableKind.PROPERTY) {
264 NamespaceDescriptor containingPackage = DescriptorUtils.getParentOfType(container, NamespaceDescriptor.class);
265 assert containingPackage != null : "Trait must have a namespace among his parents: " + container;
266
267 if (proto.hasExtension(JavaProtoBuf.implClassName)) {
268 Name tImplName = nameResolver.getName(proto.getExtension(JavaProtoBuf.implClassName));
269 return kotlinClassFinder.find(containingPackage.getFqName().child(tImplName));
270 }
271 return null;
272 }
273
274 return findKotlinClassByDescriptor(container);
275 }
276
277 @Nullable
278 private KotlinJvmBinaryClass loadPackageFragmentClassFqName(
279 @NotNull NamespaceDescriptor container,
280 @NotNull ProtoBuf.Callable proto,
281 @NotNull NameResolver nameResolver
282 ) {
283 if (proto.hasExtension(JavaProtoBuf.implClassName)) {
284 Name name = nameResolver.getName(proto.getExtension(JavaProtoBuf.implClassName));
285 FqName fqName = PackageClassUtils.getPackageClassFqName(container.getFqName()).parent().child(name);
286 return kotlinClassFinder.find(fqName);
287 }
288 return null;
289 }
290
291 private static boolean isStaticFieldInOuter(@NotNull ProtoBuf.Callable proto) {
292 if (!proto.hasExtension(JavaProtoBuf.propertySignature)) return false;
293 JavaProtoBuf.JavaPropertySignature propertySignature = proto.getExtension(JavaProtoBuf.propertySignature);
294 return propertySignature.hasField() && propertySignature.getField().getIsStaticInOuter();
295 }
296
297 @Nullable
298 private static MemberSignature getCallableSignature(
299 @NotNull ProtoBuf.Callable proto,
300 @NotNull NameResolver nameResolver,
301 @NotNull AnnotatedCallableKind kind
302 ) {
303 SignatureDeserializer deserializer = new SignatureDeserializer(nameResolver);
304 switch (kind) {
305 case FUNCTION:
306 if (proto.hasExtension(JavaProtoBuf.methodSignature)) {
307 return deserializer.methodSignature(proto.getExtension(JavaProtoBuf.methodSignature));
308 }
309 break;
310 case PROPERTY_GETTER:
311 if (proto.hasExtension(JavaProtoBuf.propertySignature)) {
312 return deserializer.methodSignature(proto.getExtension(JavaProtoBuf.propertySignature).getGetter());
313 }
314 break;
315 case PROPERTY_SETTER:
316 if (proto.hasExtension(JavaProtoBuf.propertySignature)) {
317 return deserializer.methodSignature(proto.getExtension(JavaProtoBuf.propertySignature).getSetter());
318 }
319 break;
320 case PROPERTY:
321 if (proto.hasExtension(JavaProtoBuf.propertySignature)) {
322 JavaProtoBuf.JavaPropertySignature propertySignature = proto.getExtension(JavaProtoBuf.propertySignature);
323
324 if (propertySignature.hasField()) {
325 JavaProtoBuf.JavaFieldSignature field = propertySignature.getField();
326 String type = deserializer.typeDescriptor(field.getType());
327 Name name = nameResolver.getName(field.getName());
328 return MemberSignature.fromFieldNameAndDesc(name, type);
329 }
330 else if (propertySignature.hasSyntheticMethod()) {
331 return deserializer.methodSignature(propertySignature.getSyntheticMethod());
332 }
333 }
334 break;
335 }
336 return null;
337 }
338
339 @NotNull
340 private Map<MemberSignature, List<AnnotationDescriptor>> loadMemberAnnotationsFromClass(@NotNull KotlinJvmBinaryClass kotlinClass)
341 throws IOException {
342 final Map<MemberSignature, List<AnnotationDescriptor>> memberAnnotations =
343 new HashMap<MemberSignature, List<AnnotationDescriptor>>();
344
345 kotlinClass.loadMemberAnnotations(new KotlinJvmBinaryClass.MemberVisitor() {
346 @Nullable
347 @Override
348 public KotlinJvmBinaryClass.MethodAnnotationVisitor visitMethod(@NotNull Name name, @NotNull String desc) {
349 return new AnnotationVisitorForMethod(MemberSignature.fromMethodNameAndDesc(name, desc));
350 }
351
352 @Nullable
353 @Override
354 public KotlinJvmBinaryClass.AnnotationVisitor visitField(@NotNull Name name, @NotNull String desc) {
355 return new MemberAnnotationVisitor(MemberSignature.fromFieldNameAndDesc(name, desc));
356 }
357
358 class AnnotationVisitorForMethod extends MemberAnnotationVisitor implements KotlinJvmBinaryClass.MethodAnnotationVisitor {
359 public AnnotationVisitorForMethod(@NotNull MemberSignature signature) {
360 super(signature);
361 }
362
363 @Nullable
364 @Override
365 public KotlinJvmBinaryClass.AnnotationArgumentVisitor visitParameterAnnotation(int index, @NotNull JvmClassName className) {
366 MemberSignature paramSignature = MemberSignature.fromMethodSignatureAndParameterIndex(signature, index);
367 List<AnnotationDescriptor> result = memberAnnotations.get(paramSignature);
368 if (result == null) {
369 result = new ArrayList<AnnotationDescriptor>();
370 memberAnnotations.put(paramSignature, result);
371 }
372 return resolveAnnotation(className, result);
373 }
374 }
375
376 class MemberAnnotationVisitor implements KotlinJvmBinaryClass.AnnotationVisitor {
377 private final List<AnnotationDescriptor> result = new ArrayList<AnnotationDescriptor>();
378 protected final MemberSignature signature;
379
380 public MemberAnnotationVisitor(@NotNull MemberSignature signature) {
381 this.signature = signature;
382 }
383
384 @Nullable
385 @Override
386 public KotlinJvmBinaryClass.AnnotationArgumentVisitor visitAnnotation(@NotNull JvmClassName className) {
387 return resolveAnnotation(className, result);
388 }
389
390 @Override
391 public void visitEnd() {
392 if (!result.isEmpty()) {
393 memberAnnotations.put(signature, result);
394 }
395 }
396 }
397 });
398
399 return memberAnnotations;
400 }
401
402 // The purpose of this class is to hold a unique signature of either a method or a field, so that annotations on a member can be put
403 // into a map indexed by these signatures
404 private static final class MemberSignature {
405 private final String signature;
406
407 private MemberSignature(@NotNull String signature) {
408 this.signature = signature;
409 }
410
411 @NotNull
412 public static MemberSignature fromMethodNameAndDesc(@NotNull Name name, @NotNull String desc) {
413 return new MemberSignature(name.asString() + desc);
414 }
415
416 @NotNull
417 public static MemberSignature fromFieldNameAndDesc(@NotNull Name name, @NotNull String desc) {
418 return new MemberSignature(name.asString() + "#" + desc);
419 }
420
421 @NotNull
422 public static MemberSignature fromMethodSignatureAndParameterIndex(@NotNull MemberSignature signature, int index) {
423 return new MemberSignature(signature.signature + "@" + index);
424 }
425
426 @Override
427 public int hashCode() {
428 return signature.hashCode();
429 }
430
431 @Override
432 public boolean equals(Object o) {
433 return o instanceof MemberSignature && signature.equals(((MemberSignature) o).signature);
434 }
435
436 @Override
437 public String toString() {
438 return signature;
439 }
440 }
441
442 private static class SignatureDeserializer {
443 // These types are ordered according to their sorts, this is significant for deserialization
444 private static final char[] PRIMITIVE_TYPES = new char[] { 'V', 'Z', 'C', 'B', 'S', 'I', 'F', 'J', 'D' };
445
446 private final NameResolver nameResolver;
447
448 public SignatureDeserializer(@NotNull NameResolver nameResolver) {
449 this.nameResolver = nameResolver;
450 }
451
452 @NotNull
453 public MemberSignature methodSignature(@NotNull JavaProtoBuf.JavaMethodSignature signature) {
454 Name name = nameResolver.getName(signature.getName());
455
456 StringBuilder sb = new StringBuilder();
457 sb.append('(');
458 for (int i = 0, length = signature.getParameterTypeCount(); i < length; i++) {
459 typeDescriptor(signature.getParameterType(i), sb);
460 }
461 sb.append(')');
462 typeDescriptor(signature.getReturnType(), sb);
463
464 return MemberSignature.fromMethodNameAndDesc(name, sb.toString());
465 }
466
467 @NotNull
468 public String typeDescriptor(@NotNull JavaProtoBuf.JavaType type) {
469 return typeDescriptor(type, new StringBuilder()).toString();
470 }
471
472 @NotNull
473 private StringBuilder typeDescriptor(@NotNull JavaProtoBuf.JavaType type, @NotNull StringBuilder sb) {
474 for (int i = 0; i < type.getArrayDimension(); i++) {
475 sb.append('[');
476 }
477
478 if (type.hasPrimitiveType()) {
479 sb.append(PRIMITIVE_TYPES[type.getPrimitiveType().ordinal()]);
480 }
481 else {
482 sb.append("L");
483 sb.append(fqNameToInternalName(nameResolver.getFqName(type.getClassFqName())));
484 sb.append(";");
485 }
486
487 return sb;
488 }
489
490 @NotNull
491 private static String fqNameToInternalName(@NotNull FqName fqName) {
492 return fqName.asString().replace('.', '/');
493 }
494 }
495
496 @NotNull
497 @Override
498 public List<AnnotationDescriptor> loadValueParameterAnnotations(
499 @NotNull ClassOrNamespaceDescriptor container,
500 @NotNull ProtoBuf.Callable callable,
501 @NotNull NameResolver nameResolver,
502 @NotNull AnnotatedCallableKind kind,
503 @NotNull ProtoBuf.Callable.ValueParameter proto
504 ) {
505 MemberSignature methodSignature = getCallableSignature(callable, nameResolver, kind);
506 if (methodSignature != null) {
507 if (proto.hasExtension(JavaProtoBuf.index)) {
508 MemberSignature paramSignature =
509 MemberSignature.fromMethodSignatureAndParameterIndex(methodSignature, proto.getExtension(JavaProtoBuf.index));
510 return findClassAndLoadMemberAnnotations(container, callable, nameResolver, kind, paramSignature);
511 }
512 }
513
514 return Collections.emptyList();
515 }
516 }