001    /*
002    * Copyright 2010-2014 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.lang.descriptors.annotations.AnnotationDescriptor;
023    import org.jetbrains.jet.lang.resolve.constants.*;
024    import org.jetbrains.jet.lang.resolve.java.JavaDescriptorResolver;
025    import org.jetbrains.jet.lang.resolve.java.JvmClassName;
026    import org.jetbrains.jet.lang.resolve.java.resolver.ErrorReporter;
027    import org.jetbrains.jet.lang.resolve.name.Name;
028    import org.jetbrains.jet.lang.types.DependencyClassByQualifiedNameResolver;
029    import org.jetbrains.jet.storage.MemoizedFunctionToNotNull;
030    import org.jetbrains.jet.storage.StorageManager;
031    
032    import javax.inject.Inject;
033    import java.io.IOException;
034    import java.util.*;
035    
036    public class DescriptorDeserializersStorage {
037        private DependencyClassByQualifiedNameResolver classResolver;
038        private ErrorReporter errorReporter;
039    
040        private final MemoizedFunctionToNotNull<KotlinJvmBinaryClass, Storage> storage;
041    
042        public DescriptorDeserializersStorage(@NotNull StorageManager storageManager) {
043            this.storage = storageManager.createMemoizedFunction(
044                    new Function1<KotlinJvmBinaryClass, Storage>() {
045                        @NotNull
046                        @Override
047                        public Storage invoke(@NotNull KotlinJvmBinaryClass kotlinClass) {
048                            try {
049                                return loadAnnotationsAndInitializers(kotlinClass);
050                            }
051                            catch (IOException e) {
052                                errorReporter.reportLoadingError("Error loading member information from Kotlin class: " + kotlinClass, e);
053                                return Storage.EMPTY;
054                            }
055                        }
056                    });
057        }
058    
059        @Inject
060        public void setClassResolver(DependencyClassByQualifiedNameResolver classResolver) {
061            this.classResolver = classResolver;
062        }
063    
064        @Inject
065        public void setErrorReporter(ErrorReporter errorReporter) {
066            this.errorReporter = errorReporter;
067        }
068    
069        @NotNull
070        protected MemoizedFunctionToNotNull<KotlinJvmBinaryClass, Storage> getStorage() {
071            return storage;
072        }
073    
074        @NotNull
075        private Storage loadAnnotationsAndInitializers(@NotNull KotlinJvmBinaryClass kotlinClass) throws IOException {
076            final Map<MemberSignature, List<AnnotationDescriptor>> memberAnnotations = new HashMap<MemberSignature, List<AnnotationDescriptor>>();
077            final Map<MemberSignature, CompileTimeConstant<?>> propertyConstants = new HashMap<MemberSignature, CompileTimeConstant<?>>();
078    
079            kotlinClass.visitMembers(new KotlinJvmBinaryClass.MemberVisitor() {
080                @Nullable
081                @Override
082                public KotlinJvmBinaryClass.MethodAnnotationVisitor visitMethod(@NotNull Name name, @NotNull String desc) {
083                    return new AnnotationVisitorForMethod(MemberSignature.fromMethodNameAndDesc(name, desc));
084                }
085    
086                @Nullable
087                @Override
088                public KotlinJvmBinaryClass.AnnotationVisitor visitField(@NotNull Name name, @NotNull String desc, @Nullable Object initializer) {
089                    MemberSignature signature = MemberSignature.fromFieldNameAndDesc(name, desc);
090                    if (initializer != null) {
091                        propertyConstants.put(signature, ConstantsPackage.createCompileTimeConstant(
092                                initializer, /* canBeUsedInAnnotation */ true, /* isPureIntConstant */ true, /* expectedType */ null));
093                    }
094                    return new MemberAnnotationVisitor(signature);
095                }
096    
097                class AnnotationVisitorForMethod extends MemberAnnotationVisitor implements KotlinJvmBinaryClass.MethodAnnotationVisitor {
098                    public AnnotationVisitorForMethod(@NotNull MemberSignature signature) {
099                        super(signature);
100                    }
101    
102                    @Nullable
103                    @Override
104                    public KotlinJvmBinaryClass.AnnotationArgumentVisitor visitParameterAnnotation(int index, @NotNull JvmClassName className) {
105                        MemberSignature paramSignature = MemberSignature.fromMethodSignatureAndParameterIndex(signature, index);
106                        List<AnnotationDescriptor> result = memberAnnotations.get(paramSignature);
107                        if (result == null) {
108                            result = new ArrayList<AnnotationDescriptor>();
109                            memberAnnotations.put(paramSignature, result);
110                        }
111                        return AnnotationDescriptorDeserializer.resolveAnnotation(className, result, classResolver);
112                    }
113                }
114    
115                class MemberAnnotationVisitor implements KotlinJvmBinaryClass.AnnotationVisitor {
116                    private final List<AnnotationDescriptor> result = new ArrayList<AnnotationDescriptor>();
117                    protected final MemberSignature signature;
118    
119                    public MemberAnnotationVisitor(@NotNull MemberSignature signature) {
120                        this.signature = signature;
121                    }
122    
123                    @Nullable
124                    @Override
125                    public KotlinJvmBinaryClass.AnnotationArgumentVisitor visitAnnotation(@NotNull JvmClassName className) {
126                        return AnnotationDescriptorDeserializer.resolveAnnotation(className, result, classResolver);
127                    }
128    
129                    @Override
130                    public void visitEnd() {
131                        if (!result.isEmpty()) {
132                            memberAnnotations.put(signature, result);
133                        }
134                    }
135                }
136            });
137    
138            return new Storage(memberAnnotations, propertyConstants);
139        }
140    
141        // 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
142        // into a map indexed by these signatures
143        protected static final class MemberSignature {
144            private final String signature;
145    
146            private MemberSignature(@NotNull String signature) {
147                this.signature = signature;
148            }
149    
150            @NotNull
151            public static MemberSignature fromMethodNameAndDesc(@NotNull Name name, @NotNull String desc) {
152                return new MemberSignature(name.asString() + desc);
153            }
154    
155            @NotNull
156            public static MemberSignature fromFieldNameAndDesc(@NotNull Name name, @NotNull String desc) {
157                return new MemberSignature(name.asString() + "#" + desc);
158            }
159    
160            @NotNull
161            public static MemberSignature fromMethodSignatureAndParameterIndex(@NotNull MemberSignature signature, int index) {
162                return new MemberSignature(signature.signature + "@" + index);
163            }
164    
165            @Override
166            public int hashCode() {
167                return signature.hashCode();
168            }
169    
170            @Override
171            public boolean equals(Object o) {
172                return o instanceof MemberSignature && signature.equals(((MemberSignature) o).signature);
173            }
174    
175            @Override
176            public String toString() {
177                return signature;
178            }
179        }
180    
181        protected static class Storage {
182            private final Map<MemberSignature, List<AnnotationDescriptor>> memberAnnotations;
183            private final Map<MemberSignature, CompileTimeConstant<?>> propertyConstants;
184    
185            public static final Storage EMPTY = new Storage(
186                    Collections.<MemberSignature, List<AnnotationDescriptor>>emptyMap(),
187                    Collections.<MemberSignature, CompileTimeConstant<?>>emptyMap()
188            );
189    
190            public Storage(
191                    @NotNull Map<MemberSignature, List<AnnotationDescriptor>> annotations,
192                    @NotNull Map<MemberSignature, CompileTimeConstant<?>> constants
193            ) {
194                this.memberAnnotations = annotations;
195                this.propertyConstants = constants;
196            }
197    
198            public Map<MemberSignature, List<AnnotationDescriptor>> getMemberAnnotations() {
199                return memberAnnotations;
200            }
201    
202            public Map<MemberSignature, CompileTimeConstant<?>> getPropertyConstants() {
203                return propertyConstants;
204            }
205        }
206    }