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.CompileTimeConstant;
024    import org.jetbrains.jet.lang.resolve.constants.ConstantsPackage;
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.asString() + 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    
091                    if (initializer != null) {
092                        Object normalizedValue;
093                        if ("ZBCS".contains(desc)) {
094                            int intValue = ((Integer) initializer).intValue();
095                            if ("Z".equals(desc)) {
096                                normalizedValue = intValue != 0;
097                            }
098                            else if ("B".equals(desc)) {
099                                normalizedValue = ((byte) intValue);
100                            }
101                            else if ("C".equals(desc)) {
102                                normalizedValue = ((char) intValue);
103                            }
104                            else if ("S".equals(desc)) {
105                                normalizedValue = ((short) intValue);
106                            }
107                            else {
108                                throw new AssertionError(desc);
109                            }
110                        }
111                        else {
112                            normalizedValue = initializer;
113                        }
114    
115                        propertyConstants.put(signature, ConstantsPackage.createCompileTimeConstant(
116                                normalizedValue,
117                                /* canBeUsedInAnnotation */ true,
118                                /* isPureIntConstant */ true,
119                                /* usesVariableAsConstant */ true,
120                                /* expectedType */ null
121                        ));
122                    }
123                    return new MemberAnnotationVisitor(signature);
124                }
125    
126                class AnnotationVisitorForMethod extends MemberAnnotationVisitor implements KotlinJvmBinaryClass.MethodAnnotationVisitor {
127                    public AnnotationVisitorForMethod(@NotNull MemberSignature signature) {
128                        super(signature);
129                    }
130    
131                    @Nullable
132                    @Override
133                    public KotlinJvmBinaryClass.AnnotationArgumentVisitor visitParameterAnnotation(int index, @NotNull JvmClassName className) {
134                        MemberSignature paramSignature = MemberSignature.fromMethodSignatureAndParameterIndex(signature, index);
135                        List<AnnotationDescriptor> result = memberAnnotations.get(paramSignature);
136                        if (result == null) {
137                            result = new ArrayList<AnnotationDescriptor>();
138                            memberAnnotations.put(paramSignature, result);
139                        }
140                        return AnnotationDescriptorLoader.resolveAnnotation(className, result, classResolver);
141                    }
142                }
143    
144                class MemberAnnotationVisitor implements KotlinJvmBinaryClass.AnnotationVisitor {
145                    private final List<AnnotationDescriptor> result = new ArrayList<AnnotationDescriptor>();
146                    protected final MemberSignature signature;
147    
148                    public MemberAnnotationVisitor(@NotNull MemberSignature signature) {
149                        this.signature = signature;
150                    }
151    
152                    @Nullable
153                    @Override
154                    public KotlinJvmBinaryClass.AnnotationArgumentVisitor visitAnnotation(@NotNull JvmClassName className) {
155                        return AnnotationDescriptorLoader.resolveAnnotation(className, result, classResolver);
156                    }
157    
158                    @Override
159                    public void visitEnd() {
160                        if (!result.isEmpty()) {
161                            memberAnnotations.put(signature, result);
162                        }
163                    }
164                }
165            });
166    
167            return new Storage(memberAnnotations, propertyConstants);
168        }
169    
170        // 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
171        // into a map indexed by these signatures
172        public static final class MemberSignature {
173            private final String signature;
174    
175            private MemberSignature(@NotNull String signature) {
176                this.signature = signature;
177            }
178    
179            @NotNull
180            public static MemberSignature fromMethodNameAndDesc(@NotNull String nameAndDesc) {
181                return new MemberSignature(nameAndDesc);
182            }
183    
184            @NotNull
185            public static MemberSignature fromFieldNameAndDesc(@NotNull Name name, @NotNull String desc) {
186                return new MemberSignature(name.asString() + "#" + desc);
187            }
188    
189            @NotNull
190            public static MemberSignature fromMethodSignatureAndParameterIndex(@NotNull MemberSignature signature, int index) {
191                return new MemberSignature(signature.signature + "@" + index);
192            }
193    
194            @Override
195            public int hashCode() {
196                return signature.hashCode();
197            }
198    
199            @Override
200            public boolean equals(Object o) {
201                return o instanceof MemberSignature && signature.equals(((MemberSignature) o).signature);
202            }
203    
204            @Override
205            public String toString() {
206                return signature;
207            }
208        }
209    
210        protected static class Storage {
211            private final Map<MemberSignature, List<AnnotationDescriptor>> memberAnnotations;
212            private final Map<MemberSignature, CompileTimeConstant<?>> propertyConstants;
213    
214            public static final Storage EMPTY = new Storage(
215                    Collections.<MemberSignature, List<AnnotationDescriptor>>emptyMap(),
216                    Collections.<MemberSignature, CompileTimeConstant<?>>emptyMap()
217            );
218    
219            public Storage(
220                    @NotNull Map<MemberSignature, List<AnnotationDescriptor>> annotations,
221                    @NotNull Map<MemberSignature, CompileTimeConstant<?>> constants
222            ) {
223                this.memberAnnotations = annotations;
224                this.propertyConstants = constants;
225            }
226    
227            public Map<MemberSignature, List<AnnotationDescriptor>> getMemberAnnotations() {
228                return memberAnnotations;
229            }
230    
231            public Map<MemberSignature, CompileTimeConstant<?>> getPropertyConstants() {
232                return propertyConstants;
233            }
234        }
235    }