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