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.JavaProtoBuf;
022 import org.jetbrains.jet.descriptors.serialization.NameResolver;
023 import org.jetbrains.jet.descriptors.serialization.ProtoBuf;
024 import org.jetbrains.jet.descriptors.serialization.descriptors.AnnotationDeserializer;
025 import org.jetbrains.jet.lang.descriptors.*;
026 import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
027 import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptorImpl;
028 import org.jetbrains.jet.lang.descriptors.annotations.Annotations;
029 import org.jetbrains.jet.lang.descriptors.annotations.AnnotationsImpl;
030 import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
031 import org.jetbrains.jet.lang.resolve.constants.ConstantsPackage;
032 import org.jetbrains.jet.lang.resolve.constants.EnumValue;
033 import org.jetbrains.jet.lang.resolve.constants.ErrorValue;
034 import org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames;
035 import org.jetbrains.jet.lang.resolve.java.JvmClassName;
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.name.Name;
039 import org.jetbrains.jet.lang.types.DependencyClassByQualifiedNameResolver;
040 import org.jetbrains.jet.lang.types.ErrorUtils;
041
042 import javax.inject.Inject;
043 import java.io.IOException;
044 import java.util.ArrayList;
045 import java.util.List;
046
047 import static org.jetbrains.jet.descriptors.serialization.descriptors.Deserializers.AnnotatedCallableKind;
048 import static org.jetbrains.jet.lang.resolve.kotlin.DescriptorDeserializersStorage.MemberSignature;
049
050 public class AnnotationDescriptorDeserializer extends BaseDescriptorDeserializer implements AnnotationDeserializer {
051 @Inject
052 @Override
053 public void setStorage(@NotNull DescriptorDeserializersStorage storage) {
054 this.storage = storage;
055 }
056
057 @Inject
058 @Override
059 public void setClassResolver(@NotNull DependencyClassByQualifiedNameResolver classResolver) {
060 this.classResolver = classResolver;
061 }
062
063 @Inject
064 @Override
065 public void setKotlinClassFinder(@NotNull KotlinClassFinder kotlinClassFinder) {
066 this.kotlinClassFinder = kotlinClassFinder;
067 }
068
069 @Inject
070 @Override
071 public void setErrorReporter(@NotNull ErrorReporter errorReporter) {
072 this.errorReporter = errorReporter;
073 }
074
075 @NotNull
076 @Override
077 public Annotations loadClassAnnotations(@NotNull ClassDescriptor descriptor, @NotNull ProtoBuf.Class classProto) {
078 KotlinJvmBinaryClass kotlinClass = findKotlinClassByDescriptor(descriptor);
079 if (kotlinClass == null) {
080 // This means that the resource we're constructing the descriptor from is no longer present: KotlinClassFinder had found the
081 // class earlier, but it can't now
082 errorReporter.reportLoadingError("Kotlin class for loading class annotations is not found: " + descriptor, null);
083 return Annotations.EMPTY;
084 }
085 try {
086 return loadClassAnnotationsFromClass(kotlinClass);
087 }
088 catch (IOException e) {
089 errorReporter.reportLoadingError("Error loading member annotations from Kotlin class: " + kotlinClass, e);
090 return Annotations.EMPTY;
091 }
092 }
093
094 @NotNull
095 private Annotations loadClassAnnotationsFromClass(@NotNull KotlinJvmBinaryClass kotlinClass) throws IOException {
096 final List<AnnotationDescriptor> result = new ArrayList<AnnotationDescriptor>();
097
098 kotlinClass.loadClassAnnotations(new KotlinJvmBinaryClass.AnnotationVisitor() {
099 @Nullable
100 @Override
101 public KotlinJvmBinaryClass.AnnotationArgumentVisitor visitAnnotation(@NotNull JvmClassName className) {
102 return resolveAnnotation(className, result, classResolver);
103 }
104
105 @Override
106 public void visitEnd() {
107 }
108 });
109
110 return new AnnotationsImpl(result);
111 }
112
113 @Nullable
114 public static KotlinJvmBinaryClass.AnnotationArgumentVisitor resolveAnnotation(
115 @NotNull JvmClassName className,
116 @NotNull final List<AnnotationDescriptor> result,
117 @NotNull final DependencyClassByQualifiedNameResolver classResolver
118 ) {
119 if (JvmAnnotationNames.isSpecialAnnotation(className)) return null;
120
121 final ClassDescriptor annotationClass = resolveClass(className, classResolver);
122 final AnnotationDescriptorImpl annotation = new AnnotationDescriptorImpl();
123 annotation.setAnnotationType(annotationClass.getDefaultType());
124
125 return new KotlinJvmBinaryClass.AnnotationArgumentVisitor() {
126 @Override
127 public void visit(@Nullable Name name, @Nullable Object value) {
128 if (name != null) {
129 CompileTimeConstant<?> argument = ConstantsPackage.createCompileTimeConstant(value, true, false, null);
130 setArgumentValueByName(name, argument != null ? argument : ErrorValue.create("Unsupported annotation argument: " + name));
131 }
132 }
133
134 @Override
135 public void visitEnum(@NotNull Name name, @NotNull JvmClassName enumClassName, @NotNull Name enumEntryName) {
136 setArgumentValueByName(name, enumEntryValue(enumClassName, enumEntryName));
137 }
138
139 @Nullable
140 @Override
141 public KotlinJvmBinaryClass.AnnotationArgumentVisitor visitArray(@NotNull Name name) {
142 // TODO: support arrays
143 return null;
144 }
145
146 @NotNull
147 private CompileTimeConstant<?> enumEntryValue(@NotNull JvmClassName enumClassName, @NotNull Name name) {
148 ClassDescriptor enumClass = resolveClass(enumClassName, classResolver);
149 if (enumClass.getKind() == ClassKind.ENUM_CLASS) {
150 ClassifierDescriptor classifier = enumClass.getUnsubstitutedInnerClassesScope().getClassifier(name);
151 if (classifier instanceof ClassDescriptor) {
152 return new EnumValue((ClassDescriptor) classifier);
153 }
154 }
155 return ErrorValue.create("Unresolved enum entry: " + enumClassName.getInternalName() + "." + name);
156 }
157
158 @Override
159 public void visitEnd() {
160 result.add(annotation);
161 }
162
163 private void setArgumentValueByName(@NotNull Name name, @NotNull CompileTimeConstant<?> argumentValue) {
164 ValueParameterDescriptor parameter = DescriptorResolverUtils.getAnnotationParameterByName(name, annotationClass);
165 if (parameter != null) {
166 annotation.setValueArgument(parameter, argumentValue);
167 }
168 }
169 };
170 }
171
172 @NotNull
173 private static ClassDescriptor resolveClass(@NotNull JvmClassName className, DependencyClassByQualifiedNameResolver classResolver) {
174 ClassDescriptor annotationClass = classResolver.resolveClass(className.getFqNameForClassNameWithoutDollars());
175 return annotationClass != null ? annotationClass : ErrorUtils.getErrorClass();
176 }
177
178 @NotNull
179 @Override
180 public Annotations loadCallableAnnotations(
181 @NotNull ClassOrPackageFragmentDescriptor container,
182 @NotNull ProtoBuf.Callable proto,
183 @NotNull NameResolver nameResolver,
184 @NotNull AnnotatedCallableKind kind
185 ) {
186 MemberSignature signature = getCallableSignature(proto, nameResolver, kind);
187 if (signature == null) return Annotations.EMPTY;
188
189 return findClassAndLoadMemberAnnotations(container, proto, nameResolver, kind, signature);
190 }
191
192 @NotNull
193 private Annotations findClassAndLoadMemberAnnotations(
194 @NotNull ClassOrPackageFragmentDescriptor container,
195 @NotNull ProtoBuf.Callable proto,
196 @NotNull NameResolver nameResolver,
197 @NotNull AnnotatedCallableKind kind,
198 @NotNull MemberSignature signature
199 ) {
200 KotlinJvmBinaryClass kotlinClass = findClassWithAnnotationsAndInitializers(container, proto, nameResolver, kind);
201 if (kotlinClass == null) {
202 errorReporter.reportLoadingError("Kotlin class for loading member annotations is not found: " + container, null);
203 return Annotations.EMPTY;
204 }
205
206 List<AnnotationDescriptor> annotations = storage.getStorage().invoke(kotlinClass).getMemberAnnotations().get(signature);
207 return annotations == null ? Annotations.EMPTY : new AnnotationsImpl(annotations);
208 }
209
210 @NotNull
211 @Override
212 public Annotations loadValueParameterAnnotations(
213 @NotNull ClassOrPackageFragmentDescriptor container,
214 @NotNull ProtoBuf.Callable callable,
215 @NotNull NameResolver nameResolver,
216 @NotNull AnnotatedCallableKind kind,
217 @NotNull ProtoBuf.Callable.ValueParameter proto
218 ) {
219 MemberSignature methodSignature = getCallableSignature(callable, nameResolver, kind);
220 if (methodSignature != null) {
221 if (proto.hasExtension(JavaProtoBuf.index)) {
222 MemberSignature paramSignature =
223 MemberSignature.fromMethodSignatureAndParameterIndex(methodSignature, proto.getExtension(JavaProtoBuf.index));
224 return findClassAndLoadMemberAnnotations(container, callable, nameResolver, kind, paramSignature);
225 }
226 }
227
228 return Annotations.EMPTY;
229 }
230 }