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 }