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