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