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.getErrorClass();
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    }