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.codegen;
018
019 import com.intellij.openapi.util.Pair;
020 import org.jetbrains.annotations.NotNull;
021 import org.jetbrains.annotations.Nullable;
022 import org.jetbrains.org.objectweb.asm.Type;
023 import org.jetbrains.org.objectweb.asm.commons.Method;
024 import org.jetbrains.jet.descriptors.serialization.*;
025 import org.jetbrains.jet.descriptors.serialization.descriptors.DeserializedPropertyDescriptor;
026 import org.jetbrains.jet.descriptors.serialization.descriptors.DeserializedSimpleFunctionDescriptor;
027 import org.jetbrains.jet.lang.descriptors.*;
028 import org.jetbrains.jet.lang.resolve.kotlin.SignatureDeserializer;
029 import org.jetbrains.jet.lang.resolve.name.FqName;
030 import org.jetbrains.jet.lang.resolve.name.Name;
031
032 import java.util.Arrays;
033
034 import static org.jetbrains.jet.codegen.JvmSerializationBindings.*;
035
036 public class JavaSerializerExtension extends SerializerExtension {
037 private final JvmSerializationBindings bindings;
038
039 public JavaSerializerExtension(@NotNull JvmSerializationBindings bindings) {
040 this.bindings = bindings;
041 }
042
043 @Override
044 public void serializeCallable(
045 @NotNull CallableMemberDescriptor callable,
046 @NotNull ProtoBuf.Callable.Builder proto,
047 @NotNull NameTable nameTable
048 ) {
049 saveSignature(callable, proto, nameTable);
050 saveImplClassName(callable, proto, nameTable);
051 }
052
053 @Override
054 public void serializeValueParameter(
055 @NotNull ValueParameterDescriptor descriptor,
056 @NotNull ProtoBuf.Callable.ValueParameter.Builder proto,
057 @NotNull NameTable nameTable
058 ) {
059 Integer index = bindings.get(INDEX_FOR_VALUE_PARAMETER, descriptor);
060 if (index != null) {
061 proto.setExtension(JavaProtoBuf.index, index);
062 }
063 }
064
065 private void saveSignature(
066 @NotNull CallableMemberDescriptor callable,
067 @NotNull ProtoBuf.Callable.Builder proto,
068 @NotNull NameTable nameTable
069 ) {
070 SignatureSerializer signatureSerializer = new SignatureSerializer(nameTable);
071 if (callable instanceof FunctionDescriptor) {
072 JavaProtoBuf.JavaMethodSignature signature;
073 if (callable instanceof DeserializedSimpleFunctionDescriptor) {
074 DeserializedSimpleFunctionDescriptor deserialized = (DeserializedSimpleFunctionDescriptor) callable;
075 signature = signatureSerializer.copyMethodSignature(
076 deserialized.getProto().getExtension(JavaProtoBuf.methodSignature), deserialized.getNameResolver());
077 }
078 else {
079 Method method = bindings.get(METHOD_FOR_FUNCTION, (FunctionDescriptor) callable);
080 signature = method != null ? signatureSerializer.methodSignature(method) : null;
081 }
082 if (signature != null) {
083 proto.setExtension(JavaProtoBuf.methodSignature, signature);
084 }
085 }
086 else if (callable instanceof PropertyDescriptor) {
087 PropertyDescriptor property = (PropertyDescriptor) callable;
088
089 PropertyGetterDescriptor getter = property.getGetter();
090 PropertySetterDescriptor setter = property.getSetter();
091 Method getterMethod = getter == null ? null : bindings.get(METHOD_FOR_FUNCTION, getter);
092 Method setterMethod = setter == null ? null : bindings.get(METHOD_FOR_FUNCTION, setter);
093
094 Pair<Type, String> field = bindings.get(FIELD_FOR_PROPERTY, property);
095 Type fieldType;
096 String fieldName;
097 boolean isStaticInOuter;
098 Method syntheticMethod;
099 if (field != null) {
100 fieldType = field.first;
101 fieldName = field.second;
102 isStaticInOuter = bindings.get(STATIC_FIELD_IN_OUTER_CLASS, property);
103 syntheticMethod = null;
104 }
105 else {
106 fieldType = null;
107 fieldName = null;
108 isStaticInOuter = false;
109 syntheticMethod = bindings.get(SYNTHETIC_METHOD_FOR_PROPERTY, property);
110 }
111
112 JavaProtoBuf.JavaPropertySignature signature;
113 if (callable instanceof DeserializedPropertyDescriptor) {
114 DeserializedPropertyDescriptor deserializedCallable = (DeserializedPropertyDescriptor) callable;
115 signature = signatureSerializer.copyPropertySignature(
116 deserializedCallable.getProto().getExtension(JavaProtoBuf.propertySignature),
117 deserializedCallable.getNameResolver()
118 );
119 }
120 else {
121 signature = signatureSerializer
122 .propertySignature(fieldType, fieldName, isStaticInOuter, syntheticMethod, getterMethod, setterMethod);
123 }
124 proto.setExtension(JavaProtoBuf.propertySignature, signature);
125 }
126 }
127
128 private void saveImplClassName(
129 @NotNull CallableMemberDescriptor callable,
130 @NotNull ProtoBuf.Callable.Builder proto,
131 @NotNull NameTable nameTable
132 ) {
133 String name = bindings.get(IMPL_CLASS_NAME_FOR_CALLABLE, callable);
134 if (name != null) {
135 proto.setExtension(JavaProtoBuf.implClassName, nameTable.getSimpleNameIndex(Name.identifier(name)));
136 }
137 }
138
139 private static class SignatureSerializer {
140 private final NameTable nameTable;
141
142 public SignatureSerializer(@NotNull NameTable nameTable) {
143 this.nameTable = nameTable;
144 }
145
146 @NotNull
147 public JavaProtoBuf.JavaMethodSignature copyMethodSignature(
148 @NotNull JavaProtoBuf.JavaMethodSignature signature,
149 @NotNull NameResolver nameResolver
150 ) {
151 String method = new SignatureDeserializer(nameResolver).methodSignatureString(signature);
152 return methodSignature(getAsmMethod(method));
153 }
154
155 @NotNull
156 public JavaProtoBuf.JavaMethodSignature methodSignature(@NotNull Method method) {
157 JavaProtoBuf.JavaMethodSignature.Builder signature = JavaProtoBuf.JavaMethodSignature.newBuilder();
158
159 signature.setName(nameTable.getSimpleNameIndex(Name.guess(method.getName())));
160
161 signature.setReturnType(type(method.getReturnType()));
162
163 for (Type type : method.getArgumentTypes()) {
164 signature.addParameterType(type(type));
165 }
166
167 return signature.build();
168 }
169
170 @NotNull
171 public JavaProtoBuf.JavaPropertySignature copyPropertySignature(
172 @NotNull JavaProtoBuf.JavaPropertySignature signature,
173 @NotNull NameResolver nameResolver
174 ) {
175 Type fieldType;
176 String fieldName;
177 boolean isStaticInOuter;
178 SignatureDeserializer signatureDeserializer = new SignatureDeserializer(nameResolver);
179 if (signature.hasField()) {
180 JavaProtoBuf.JavaFieldSignature field = signature.getField();
181 fieldType = Type.getType(signatureDeserializer.typeDescriptor(field.getType()));
182 fieldName = nameResolver.getName(field.getName()).asString();
183 isStaticInOuter = field.getIsStaticInOuter();
184 }
185 else {
186 fieldType = null;
187 fieldName = null;
188 isStaticInOuter = false;
189 }
190
191 Method syntheticMethod = signature.hasSyntheticMethod()
192 ? getAsmMethod(signatureDeserializer.methodSignatureString(signature.getSyntheticMethod()))
193 : null;
194
195 Method getter = signature.hasGetter() ? getAsmMethod(signatureDeserializer.methodSignatureString(signature.getGetter())) : null;
196 Method setter = signature.hasSetter() ? getAsmMethod(signatureDeserializer.methodSignatureString(signature.getSetter())) : null;
197
198 return propertySignature(fieldType, fieldName, isStaticInOuter, syntheticMethod, getter, setter);
199 }
200
201 @NotNull
202 public JavaProtoBuf.JavaPropertySignature propertySignature(
203 @Nullable Type fieldType,
204 @Nullable String fieldName,
205 boolean isStaticInOuter,
206 @Nullable Method syntheticMethod,
207 @Nullable Method getter,
208 @Nullable Method setter
209 ) {
210 JavaProtoBuf.JavaPropertySignature.Builder signature = JavaProtoBuf.JavaPropertySignature.newBuilder();
211
212 if (fieldType != null) {
213 assert fieldName != null : "Field name shouldn't be null when there's a field type: " + fieldType;
214 signature.setField(fieldSignature(fieldType, fieldName, isStaticInOuter));
215 }
216
217 if (syntheticMethod != null) {
218 signature.setSyntheticMethod(methodSignature(syntheticMethod));
219 }
220
221 if (getter != null) {
222 signature.setGetter(methodSignature(getter));
223 }
224 if (setter != null) {
225 signature.setSetter(methodSignature(setter));
226 }
227
228 return signature.build();
229 }
230
231 @NotNull
232 public JavaProtoBuf.JavaFieldSignature fieldSignature(@NotNull Type type, @NotNull String name, boolean isStaticInOuter) {
233 JavaProtoBuf.JavaFieldSignature.Builder signature = JavaProtoBuf.JavaFieldSignature.newBuilder();
234 signature.setName(nameTable.getSimpleNameIndex(Name.guess(name)));
235 signature.setType(type(type));
236 if (isStaticInOuter) {
237 signature.setIsStaticInOuter(true);
238 }
239 return signature.build();
240 }
241
242 @NotNull
243 public JavaProtoBuf.JavaType type(@NotNull Type givenType) {
244 JavaProtoBuf.JavaType.Builder builder = JavaProtoBuf.JavaType.newBuilder();
245
246 int arrayDimension = 0;
247 Type type = givenType;
248 while (type.getSort() == Type.ARRAY) {
249 arrayDimension++;
250 type = type.getElementType();
251 }
252 if (arrayDimension != 0) {
253 builder.setArrayDimension(arrayDimension);
254 }
255
256 if (type.getSort() == Type.OBJECT) {
257 FqName fqName = internalNameToFqName(type.getInternalName());
258 builder.setClassFqName(nameTable.getFqNameIndex(fqName));
259 }
260 else {
261 builder.setPrimitiveType(JavaProtoBuf.JavaType.PrimitiveType.valueOf(type.getSort()));
262 }
263
264 return builder.build();
265 }
266
267 @NotNull
268 private static FqName internalNameToFqName(@NotNull String internalName) {
269 return FqName.fromSegments(Arrays.asList(internalName.split("/")));
270 }
271 }
272
273 @NotNull
274 private static Method getAsmMethod(@NotNull String nameAndDesc) {
275 int indexOf = nameAndDesc.indexOf('(');
276 return new Method(nameAndDesc.substring(0, indexOf), nameAndDesc.substring(indexOf));
277 }
278 }