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