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.serialization;
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.GenerationState;
023 import org.jetbrains.kotlin.descriptors.*;
024 import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
025 import org.jetbrains.kotlin.load.java.JvmAbi;
026 import org.jetbrains.kotlin.load.java.lazy.types.RawTypeCapabilities;
027 import org.jetbrains.kotlin.name.ClassId;
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.jvm.ClassMapperLite;
033 import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf;
034 import org.jetbrains.kotlin.types.KotlinType;
035 import org.jetbrains.org.objectweb.asm.Type;
036 import org.jetbrains.org.objectweb.asm.commons.Method;
037
038 import static org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings.*;
039
040 public class JvmSerializerExtension extends SerializerExtension {
041 private final JvmSerializationBindings bindings;
042 private final StringTable stringTable;
043 private final AnnotationSerializer annotationSerializer;
044 private final boolean useTypeTable;
045 private final String moduleName;
046
047 public JvmSerializerExtension(@NotNull JvmSerializationBindings bindings, @NotNull GenerationState state) {
048 this.bindings = bindings;
049 this.stringTable = new JvmStringTable(state.getTypeMapper());
050 this.annotationSerializer = new AnnotationSerializer(stringTable);
051 this.useTypeTable = state.getUseTypeTableInSerializer();
052 this.moduleName = state.getModuleName();
053 }
054
055 @NotNull
056 @Override
057 public StringTable getStringTable() {
058 return stringTable;
059 }
060
061 @Override
062 public boolean shouldUseTypeTable() {
063 return useTypeTable;
064 }
065
066 @Override
067 public void serializeClass(@NotNull ClassDescriptor descriptor, @NotNull ProtoBuf.Class.Builder proto) {
068 if (!moduleName.equals(JvmAbi.DEFAULT_MODULE_NAME)) {
069 proto.setExtension(JvmProtoBuf.classModuleName, stringTable.getStringIndex(moduleName));
070 }
071 }
072
073 @Override
074 public void serializePackage(@NotNull ProtoBuf.Package.Builder proto) {
075 if (!moduleName.equals(JvmAbi.DEFAULT_MODULE_NAME)) {
076 proto.setExtension(JvmProtoBuf.packageModuleName, stringTable.getStringIndex(moduleName));
077 }
078 }
079
080 @Override
081 public void serializeType(@NotNull KotlinType type, @NotNull ProtoBuf.Type.Builder proto) {
082 // TODO: don't store type annotations in our binary metadata on Java 8, use *TypeAnnotations attributes instead
083 for (AnnotationDescriptor annotation : type.getAnnotations()) {
084 proto.addExtension(JvmProtoBuf.typeAnnotation, annotationSerializer.serializeAnnotation(annotation));
085 }
086
087 if (type.getCapabilities() instanceof RawTypeCapabilities) {
088 proto.setExtension(JvmProtoBuf.isRaw, true);
089 }
090 }
091
092 @Override
093 public void serializeTypeParameter(
094 @NotNull TypeParameterDescriptor typeParameter, @NotNull ProtoBuf.TypeParameter.Builder proto
095 ) {
096 for (AnnotationDescriptor annotation : typeParameter.getAnnotations()) {
097 proto.addExtension(JvmProtoBuf.typeParameterAnnotation, annotationSerializer.serializeAnnotation(annotation));
098 }
099 }
100
101 @Override
102 public void serializeConstructor(@NotNull ConstructorDescriptor descriptor, @NotNull ProtoBuf.Constructor.Builder proto) {
103 Method method = bindings.get(METHOD_FOR_FUNCTION, descriptor);
104 if (method != null) {
105 JvmProtoBuf.JvmMethodSignature signature = new SignatureSerializer().methodSignature(descriptor, method);
106 if (signature != null) {
107 proto.setExtension(JvmProtoBuf.constructorSignature, signature);
108 }
109 }
110 }
111
112 @Override
113 public void serializeFunction(@NotNull FunctionDescriptor descriptor, @NotNull ProtoBuf.Function.Builder proto) {
114 Method method = bindings.get(METHOD_FOR_FUNCTION, descriptor);
115 if (method != null) {
116 JvmProtoBuf.JvmMethodSignature signature = new SignatureSerializer().methodSignature(descriptor, method);
117 if (signature != null) {
118 proto.setExtension(JvmProtoBuf.methodSignature, signature);
119 }
120 }
121 }
122
123 @Override
124 public void serializeProperty(@NotNull PropertyDescriptor descriptor, @NotNull ProtoBuf.Property.Builder proto) {
125 SignatureSerializer signatureSerializer = new SignatureSerializer();
126
127 PropertyGetterDescriptor getter = descriptor.getGetter();
128 PropertySetterDescriptor setter = descriptor.getSetter();
129 Method getterMethod = getter == null ? null : bindings.get(METHOD_FOR_FUNCTION, getter);
130 Method setterMethod = setter == null ? null : bindings.get(METHOD_FOR_FUNCTION, setter);
131
132 Pair<Type, String> field = bindings.get(FIELD_FOR_PROPERTY, descriptor);
133 Method syntheticMethod = bindings.get(SYNTHETIC_METHOD_FOR_PROPERTY, descriptor);
134
135 JvmProtoBuf.JvmPropertySignature signature = signatureSerializer.propertySignature(
136 descriptor,
137 field != null ? field.second : null,
138 field != null ? field.first.getDescriptor() : null,
139 syntheticMethod != null ? signatureSerializer.methodSignature(null, syntheticMethod) : null,
140 getterMethod != null ? signatureSerializer.methodSignature(null, getterMethod) : null,
141 setterMethod != null ? signatureSerializer.methodSignature(null, setterMethod) : null
142 );
143
144 proto.setExtension(JvmProtoBuf.propertySignature, signature);
145 }
146
147 private class SignatureSerializer {
148 @Nullable
149 public JvmProtoBuf.JvmMethodSignature methodSignature(@Nullable FunctionDescriptor descriptor, @NotNull Method method) {
150 JvmProtoBuf.JvmMethodSignature.Builder builder = JvmProtoBuf.JvmMethodSignature.newBuilder();
151 if (descriptor == null || !descriptor.getName().asString().equals(method.getName())) {
152 builder.setName(stringTable.getStringIndex(method.getName()));
153 }
154 if (descriptor == null || requiresSignature(descriptor, method.getDescriptor())) {
155 builder.setDesc(stringTable.getStringIndex(method.getDescriptor()));
156 }
157 return builder.hasName() || builder.hasDesc() ? builder.build() : null;
158 }
159
160 // We don't write those signatures which can be trivially reconstructed from already serialized data
161 // TODO: make JvmStringTable implement NameResolver and use JvmProtoBufUtil#getJvmMethodSignature instead
162 private boolean requiresSignature(@NotNull FunctionDescriptor descriptor, @NotNull String desc) {
163 StringBuilder sb = new StringBuilder();
164 sb.append("(");
165 ReceiverParameterDescriptor receiverParameter = descriptor.getExtensionReceiverParameter();
166 if (receiverParameter != null) {
167 String receiverDesc = mapTypeDefault(receiverParameter.getValue().getType());
168 if (receiverDesc == null) return true;
169 sb.append(receiverDesc);
170 }
171
172 for (ValueParameterDescriptor valueParameter : descriptor.getValueParameters()) {
173 String paramDesc = mapTypeDefault(valueParameter.getType());
174 if (paramDesc == null) return true;
175 sb.append(paramDesc);
176 }
177
178 sb.append(")");
179
180 KotlinType returnType = descriptor.getReturnType();
181 String returnTypeDesc = returnType == null ? "V" : mapTypeDefault(returnType);
182 if (returnTypeDesc == null) return true;
183 sb.append(returnTypeDesc);
184
185 return !sb.toString().equals(desc);
186 }
187
188 private boolean requiresSignature(@NotNull PropertyDescriptor descriptor, @NotNull String desc) {
189 return !desc.equals(mapTypeDefault(descriptor.getType()));
190 }
191
192 @Nullable
193 private String mapTypeDefault(@NotNull KotlinType type) {
194 ClassifierDescriptor classifier = type.getConstructor().getDeclarationDescriptor();
195 if (!(classifier instanceof ClassDescriptor)) return null;
196 ClassId classId = classId((ClassDescriptor) classifier);
197 return classId == null ? null : ClassMapperLite.mapClass(classId);
198 }
199
200 @Nullable
201 private ClassId classId(@NotNull ClassDescriptor descriptor) {
202 DeclarationDescriptor container = descriptor.getContainingDeclaration();
203 if (container instanceof PackageFragmentDescriptor) {
204 return ClassId.topLevel(((PackageFragmentDescriptor) container).getFqName().child(descriptor.getName()));
205 }
206 else if (container instanceof ClassDescriptor) {
207 ClassId outerClassId = classId((ClassDescriptor) container);
208 return outerClassId == null ? null : outerClassId.createNestedClassId(descriptor.getName());
209 }
210 else {
211 return null;
212 }
213 }
214
215 @NotNull
216 public JvmProtoBuf.JvmPropertySignature propertySignature(
217 @NotNull PropertyDescriptor descriptor,
218 @Nullable String fieldName,
219 @Nullable String fieldDesc,
220 @Nullable JvmProtoBuf.JvmMethodSignature syntheticMethod,
221 @Nullable JvmProtoBuf.JvmMethodSignature getter,
222 @Nullable JvmProtoBuf.JvmMethodSignature setter
223 ) {
224 JvmProtoBuf.JvmPropertySignature.Builder signature = JvmProtoBuf.JvmPropertySignature.newBuilder();
225
226 if (fieldDesc != null) {
227 assert fieldName != null : "Field name shouldn't be null when there's a field type: " + fieldDesc;
228 signature.setField(fieldSignature(descriptor, fieldName, fieldDesc));
229 }
230
231 if (syntheticMethod != null) {
232 signature.setSyntheticMethod(syntheticMethod);
233 }
234
235 if (getter != null) {
236 signature.setGetter(getter);
237 }
238 if (setter != null) {
239 signature.setSetter(setter);
240 }
241
242 return signature.build();
243 }
244
245 @NotNull
246 public JvmProtoBuf.JvmFieldSignature fieldSignature(
247 @NotNull PropertyDescriptor descriptor,
248 @NotNull String name,
249 @NotNull String desc
250 ) {
251 JvmProtoBuf.JvmFieldSignature.Builder builder = JvmProtoBuf.JvmFieldSignature.newBuilder();
252 if (!descriptor.getName().asString().equals(name)) {
253 builder.setName(stringTable.getStringIndex(name));
254 }
255 if (requiresSignature(descriptor, desc)) {
256 builder.setDesc(stringTable.getStringIndex(desc));
257 }
258 return builder.build();
259 }
260 }
261 }