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