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