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.ModuleDescriptor;
023 import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
024 import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
025 import org.jetbrains.jet.lang.resolve.constants.ConstantsPackage;
026 import org.jetbrains.jet.lang.resolve.java.JvmClassName;
027 import org.jetbrains.jet.lang.resolve.java.resolver.ErrorReporter;
028 import org.jetbrains.jet.lang.resolve.name.Name;
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 DescriptorLoadersStorage {
037 private ErrorReporter errorReporter;
038 private ModuleDescriptor module;
039
040 private final MemoizedFunctionToNotNull<KotlinJvmBinaryClass, Storage> storage;
041
042 public DescriptorLoadersStorage(@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 setModule(ModuleDescriptor module) {
061 this.module = module;
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, module);
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, module);
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 }