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