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