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 ClassDescriptor classObject = enumClass.getClassObjectDescriptor();
189 if (classObject != null) {
190 Collection<VariableDescriptor> properties = classObject.getDefaultType().getMemberScope().getProperties(name);
191 if (properties.size() == 1) {
192 VariableDescriptor property = properties.iterator().next();
193 if (property instanceof PropertyDescriptor) {
194 return new EnumValue((PropertyDescriptor) property);
195 }
196 }
197 }
198 }
199 return ErrorValue.create("Unresolved enum entry: " + enumClassName.getInternalName() + "." + name);
200 }
201
202 @Override
203 public void visitEnd() {
204 result.add(annotation);
205 }
206
207 private void setArgumentValueByName(@NotNull Name name, @NotNull CompileTimeConstant<?> argumentValue) {
208 ValueParameterDescriptor parameter = DescriptorResolverUtils.getAnnotationParameterByName(name, annotationClass);
209 if (parameter != null) {
210 annotation.setValueArgument(parameter, argumentValue);
211 }
212 }
213 };
214 }
215
216 @NotNull
217 private ClassDescriptor resolveClass(@NotNull JvmClassName className) {
218 ClassDescriptor annotationClass = javaClassResolver.resolveClass(className.getFqNameForClassNameWithoutDollars(),
219 IGNORE_KOTLIN_SOURCES);
220 return annotationClass != null ? annotationClass : ErrorUtils.getErrorClass();
221 }
222
223 @NotNull
224 @Override
225 public List<AnnotationDescriptor> loadCallableAnnotations(
226 @NotNull ClassOrNamespaceDescriptor container,
227 @NotNull ProtoBuf.Callable proto,
228 @NotNull NameResolver nameResolver,
229 @NotNull AnnotatedCallableKind kind
230 ) {
231 MemberSignature signature = getCallableSignature(proto, nameResolver, kind);
232 if (signature == null) return Collections.emptyList();
233
234 return findClassAndLoadMemberAnnotations(container, proto, nameResolver, kind, signature);
235 }
236
237 @NotNull
238 private List<AnnotationDescriptor> findClassAndLoadMemberAnnotations(
239 @NotNull ClassOrNamespaceDescriptor container,
240 @NotNull ProtoBuf.Callable proto,
241 @NotNull NameResolver nameResolver,
242 @NotNull AnnotatedCallableKind kind,
243 @NotNull MemberSignature signature
244 ) {
245 KotlinJvmBinaryClass kotlinClass = findClassWithMemberAnnotations(container, proto, nameResolver, kind);
246 if (kotlinClass == null) {
247 errorReporter.reportAnnotationLoadingError("Kotlin class for loading member annotations is not found: " + container, null);
248 return Collections.emptyList();
249 }
250
251 List<AnnotationDescriptor> annotations = memberAnnotations.invoke(kotlinClass).get(signature);
252 return annotations == null ? Collections.<AnnotationDescriptor>emptyList() : annotations;
253 }
254
255 @Nullable
256 private KotlinJvmBinaryClass findClassWithMemberAnnotations(
257 @NotNull ClassOrNamespaceDescriptor container,
258 @NotNull ProtoBuf.Callable proto,
259 @NotNull NameResolver nameResolver,
260 @NotNull AnnotatedCallableKind kind
261 ) {
262 if (container instanceof NamespaceDescriptor) {
263 return loadPackageFragmentClassFqName((NamespaceDescriptor) container, proto, nameResolver);
264 }
265 else if (isClassObject(container) && isStaticFieldInOuter(proto)) {
266 // Backing fields of properties of a class object are generated in the outer class
267 return findKotlinClassByDescriptor((ClassOrNamespaceDescriptor) container.getContainingDeclaration());
268 }
269 else if (isTrait(container) && kind == AnnotatedCallableKind.PROPERTY) {
270 NamespaceDescriptor containingPackage = DescriptorUtils.getParentOfType(container, NamespaceDescriptor.class);
271 assert containingPackage != null : "Trait must have a namespace among his parents: " + container;
272
273 if (proto.hasExtension(JavaProtoBuf.implClassName)) {
274 Name tImplName = nameResolver.getName(proto.getExtension(JavaProtoBuf.implClassName));
275 return kotlinClassFinder.find(containingPackage.getFqName().child(tImplName));
276 }
277 return null;
278 }
279
280 return findKotlinClassByDescriptor(container);
281 }
282
283 @Nullable
284 private KotlinJvmBinaryClass loadPackageFragmentClassFqName(
285 @NotNull NamespaceDescriptor container,
286 @NotNull ProtoBuf.Callable proto,
287 @NotNull NameResolver nameResolver
288 ) {
289 if (proto.hasExtension(JavaProtoBuf.implClassName)) {
290 Name name = nameResolver.getName(proto.getExtension(JavaProtoBuf.implClassName));
291 FqName fqName = PackageClassUtils.getPackageClassFqName(container.getFqName()).parent().child(name);
292 return kotlinClassFinder.find(fqName);
293 }
294 return null;
295 }
296
297 private static boolean isStaticFieldInOuter(@NotNull ProtoBuf.Callable proto) {
298 if (!proto.hasExtension(JavaProtoBuf.propertySignature)) return false;
299 JavaProtoBuf.JavaPropertySignature propertySignature = proto.getExtension(JavaProtoBuf.propertySignature);
300 return propertySignature.hasField() && propertySignature.getField().getIsStaticInOuter();
301 }
302
303 @Nullable
304 private static MemberSignature getCallableSignature(
305 @NotNull ProtoBuf.Callable proto,
306 @NotNull NameResolver nameResolver,
307 @NotNull AnnotatedCallableKind kind
308 ) {
309 SignatureDeserializer deserializer = new SignatureDeserializer(nameResolver);
310 switch (kind) {
311 case FUNCTION:
312 if (proto.hasExtension(JavaProtoBuf.methodSignature)) {
313 return deserializer.methodSignature(proto.getExtension(JavaProtoBuf.methodSignature));
314 }
315 break;
316 case PROPERTY_GETTER:
317 if (proto.hasExtension(JavaProtoBuf.propertySignature)) {
318 return deserializer.methodSignature(proto.getExtension(JavaProtoBuf.propertySignature).getGetter());
319 }
320 break;
321 case PROPERTY_SETTER:
322 if (proto.hasExtension(JavaProtoBuf.propertySignature)) {
323 return deserializer.methodSignature(proto.getExtension(JavaProtoBuf.propertySignature).getSetter());
324 }
325 break;
326 case PROPERTY:
327 if (proto.hasExtension(JavaProtoBuf.propertySignature)) {
328 JavaProtoBuf.JavaPropertySignature propertySignature = proto.getExtension(JavaProtoBuf.propertySignature);
329
330 if (propertySignature.hasField()) {
331 JavaProtoBuf.JavaFieldSignature field = propertySignature.getField();
332 String type = deserializer.typeDescriptor(field.getType());
333 Name name = nameResolver.getName(field.getName());
334 return MemberSignature.fromFieldNameAndDesc(name, type);
335 }
336 else if (propertySignature.hasSyntheticMethod()) {
337 return deserializer.methodSignature(propertySignature.getSyntheticMethod());
338 }
339 }
340 break;
341 }
342 return null;
343 }
344
345 @NotNull
346 private Map<MemberSignature, List<AnnotationDescriptor>> loadMemberAnnotationsFromClass(@NotNull KotlinJvmBinaryClass kotlinClass)
347 throws IOException {
348 final Map<MemberSignature, List<AnnotationDescriptor>> memberAnnotations =
349 new HashMap<MemberSignature, List<AnnotationDescriptor>>();
350
351 kotlinClass.loadMemberAnnotations(new KotlinJvmBinaryClass.MemberVisitor() {
352 @Nullable
353 @Override
354 public KotlinJvmBinaryClass.MethodAnnotationVisitor visitMethod(@NotNull Name name, @NotNull String desc) {
355 return new AnnotationVisitorForMethod(MemberSignature.fromMethodNameAndDesc(name, desc));
356 }
357
358 @Nullable
359 @Override
360 public KotlinJvmBinaryClass.AnnotationVisitor visitField(@NotNull Name name, @NotNull String desc) {
361 return new MemberAnnotationVisitor(MemberSignature.fromFieldNameAndDesc(name, desc));
362 }
363
364 class AnnotationVisitorForMethod extends MemberAnnotationVisitor implements KotlinJvmBinaryClass.MethodAnnotationVisitor {
365 public AnnotationVisitorForMethod(@NotNull MemberSignature signature) {
366 super(signature);
367 }
368
369 @Nullable
370 @Override
371 public KotlinJvmBinaryClass.AnnotationArgumentVisitor visitParameterAnnotation(int index, @NotNull JvmClassName className) {
372 MemberSignature paramSignature = MemberSignature.fromMethodSignatureAndParameterIndex(signature, index);
373 List<AnnotationDescriptor> result = memberAnnotations.get(paramSignature);
374 if (result == null) {
375 result = new ArrayList<AnnotationDescriptor>();
376 memberAnnotations.put(paramSignature, result);
377 }
378 return resolveAnnotation(className, result);
379 }
380 }
381
382 class MemberAnnotationVisitor implements KotlinJvmBinaryClass.AnnotationVisitor {
383 private final List<AnnotationDescriptor> result = new ArrayList<AnnotationDescriptor>();
384 protected final MemberSignature signature;
385
386 public MemberAnnotationVisitor(@NotNull MemberSignature signature) {
387 this.signature = signature;
388 }
389
390 @Nullable
391 @Override
392 public KotlinJvmBinaryClass.AnnotationArgumentVisitor visitAnnotation(@NotNull JvmClassName className) {
393 return resolveAnnotation(className, result);
394 }
395
396 @Override
397 public void visitEnd() {
398 if (!result.isEmpty()) {
399 memberAnnotations.put(signature, result);
400 }
401 }
402 }
403 });
404
405 return memberAnnotations;
406 }
407
408 // 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
409 // into a map indexed by these signatures
410 private static final class MemberSignature {
411 private final String signature;
412
413 private MemberSignature(@NotNull String signature) {
414 this.signature = signature;
415 }
416
417 @NotNull
418 public static MemberSignature fromMethodNameAndDesc(@NotNull Name name, @NotNull String desc) {
419 return new MemberSignature(name.asString() + desc);
420 }
421
422 @NotNull
423 public static MemberSignature fromFieldNameAndDesc(@NotNull Name name, @NotNull String desc) {
424 return new MemberSignature(name.asString() + "#" + desc);
425 }
426
427 @NotNull
428 public static MemberSignature fromMethodSignatureAndParameterIndex(@NotNull MemberSignature signature, int index) {
429 return new MemberSignature(signature.signature + "@" + index);
430 }
431
432 @Override
433 public int hashCode() {
434 return signature.hashCode();
435 }
436
437 @Override
438 public boolean equals(Object o) {
439 return o instanceof MemberSignature && signature.equals(((MemberSignature) o).signature);
440 }
441
442 @Override
443 public String toString() {
444 return signature;
445 }
446 }
447
448 private static class SignatureDeserializer {
449 // These types are ordered according to their sorts, this is significant for deserialization
450 private static final char[] PRIMITIVE_TYPES = new char[] { 'V', 'Z', 'C', 'B', 'S', 'I', 'F', 'J', 'D' };
451
452 private final NameResolver nameResolver;
453
454 public SignatureDeserializer(@NotNull NameResolver nameResolver) {
455 this.nameResolver = nameResolver;
456 }
457
458 @NotNull
459 public MemberSignature methodSignature(@NotNull JavaProtoBuf.JavaMethodSignature signature) {
460 Name name = nameResolver.getName(signature.getName());
461
462 StringBuilder sb = new StringBuilder();
463 sb.append('(');
464 for (int i = 0, length = signature.getParameterTypeCount(); i < length; i++) {
465 typeDescriptor(signature.getParameterType(i), sb);
466 }
467 sb.append(')');
468 typeDescriptor(signature.getReturnType(), sb);
469
470 return MemberSignature.fromMethodNameAndDesc(name, sb.toString());
471 }
472
473 @NotNull
474 public String typeDescriptor(@NotNull JavaProtoBuf.JavaType type) {
475 return typeDescriptor(type, new StringBuilder()).toString();
476 }
477
478 @NotNull
479 private StringBuilder typeDescriptor(@NotNull JavaProtoBuf.JavaType type, @NotNull StringBuilder sb) {
480 for (int i = 0; i < type.getArrayDimension(); i++) {
481 sb.append('[');
482 }
483
484 if (type.hasPrimitiveType()) {
485 sb.append(PRIMITIVE_TYPES[type.getPrimitiveType().ordinal()]);
486 }
487 else {
488 sb.append("L");
489 sb.append(fqNameToInternalName(nameResolver.getFqName(type.getClassFqName())));
490 sb.append(";");
491 }
492
493 return sb;
494 }
495
496 @NotNull
497 private static String fqNameToInternalName(@NotNull FqName fqName) {
498 return fqName.asString().replace('.', '/');
499 }
500 }
501
502 @NotNull
503 @Override
504 public List<AnnotationDescriptor> loadValueParameterAnnotations(
505 @NotNull ClassOrNamespaceDescriptor container,
506 @NotNull ProtoBuf.Callable callable,
507 @NotNull NameResolver nameResolver,
508 @NotNull AnnotatedCallableKind kind,
509 @NotNull ProtoBuf.Callable.ValueParameter proto
510 ) {
511 MemberSignature methodSignature = getCallableSignature(callable, nameResolver, kind);
512 if (methodSignature != null) {
513 if (proto.hasExtension(JavaProtoBuf.index)) {
514 MemberSignature paramSignature =
515 MemberSignature.fromMethodSignatureAndParameterIndex(methodSignature, proto.getExtension(JavaProtoBuf.index));
516 return findClassAndLoadMemberAnnotations(container, callable, nameResolver, kind, paramSignature);
517 }
518 }
519
520 return Collections.emptyList();
521 }
522 }