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