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 }