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; 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.builtins.KotlinBuiltIns; 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.load.kotlin.SignatureDeserializer; 028 import org.jetbrains.kotlin.name.FqName; 029 import org.jetbrains.kotlin.name.Name; 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.deserialization.NameResolver; 035 import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPropertyDescriptor; 036 import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedSimpleFunctionDescriptor; 037 import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf; 038 import org.jetbrains.kotlin.types.JetType; 039 import org.jetbrains.org.objectweb.asm.Type; 040 import org.jetbrains.org.objectweb.asm.commons.Method; 041 042 import java.util.Arrays; 043 044 import static org.jetbrains.kotlin.codegen.AsmUtil.shortNameByAsmType; 045 import static org.jetbrains.kotlin.codegen.JvmSerializationBindings.*; 046 047 public class JvmSerializerExtension extends SerializerExtension { 048 private final JvmSerializationBindings bindings; 049 private final JetTypeMapper typeMapper; 050 private final AnnotationSerializer annotationSerializer = new AnnotationSerializer(); 051 052 public JvmSerializerExtension(@NotNull JvmSerializationBindings bindings, @NotNull JetTypeMapper typeMapper) { 053 this.bindings = bindings; 054 this.typeMapper = typeMapper; 055 } 056 057 @Override 058 public void serializeClass(@NotNull ClassDescriptor descriptor, @NotNull ProtoBuf.Class.Builder proto, @NotNull StringTable stringTable) { 059 AnnotationDescriptor annotation = descriptor.getAnnotations().findAnnotation(KotlinBuiltIns.FQ_NAMES.annotation); 060 if (annotation != null) { 061 proto.addExtension(JvmProtoBuf.classAnnotation, annotationSerializer.serializeAnnotation(annotation, stringTable)); 062 } 063 } 064 065 @Override 066 public void serializeCallable( 067 @NotNull CallableMemberDescriptor callable, 068 @NotNull ProtoBuf.Callable.Builder proto, 069 @NotNull StringTable stringTable 070 ) { 071 saveSignature(callable, proto, stringTable); 072 saveImplClassName(callable, proto, stringTable); 073 } 074 075 @Override 076 public void serializeValueParameter( 077 @NotNull ValueParameterDescriptor descriptor, 078 @NotNull ProtoBuf.Callable.ValueParameter.Builder proto, 079 @NotNull StringTable stringTable 080 ) { 081 Integer index = bindings.get(INDEX_FOR_VALUE_PARAMETER, descriptor); 082 if (index != null) { 083 proto.setExtension(JvmProtoBuf.index, index); 084 } 085 } 086 087 @Override 088 public void serializeType(@NotNull JetType type, @NotNull ProtoBuf.Type.Builder proto, @NotNull StringTable stringTable) { 089 // TODO: don't store type annotations in our binary metadata on Java 8, use *TypeAnnotations attributes instead 090 for (AnnotationDescriptor annotation : type.getAnnotations()) { 091 proto.addExtension(JvmProtoBuf.typeAnnotation, annotationSerializer.serializeAnnotation(annotation, stringTable)); 092 } 093 094 if (type.getCapabilities() instanceof RawTypeCapabilities) { 095 proto.setExtension(JvmProtoBuf.isRaw, true); 096 } 097 } 098 099 @Override 100 @NotNull 101 public String getLocalClassName(@NotNull ClassDescriptor descriptor) { 102 return shortNameByAsmType(typeMapper.mapClass(descriptor)); 103 } 104 105 private void saveSignature( 106 @NotNull CallableMemberDescriptor callable, 107 @NotNull ProtoBuf.Callable.Builder proto, 108 @NotNull StringTable stringTable 109 ) { 110 SignatureSerializer signatureSerializer = new SignatureSerializer(stringTable); 111 if (callable instanceof FunctionDescriptor) { 112 JvmProtoBuf.JvmMethodSignature signature; 113 if (callable instanceof DeserializedSimpleFunctionDescriptor) { 114 DeserializedSimpleFunctionDescriptor deserialized = (DeserializedSimpleFunctionDescriptor) callable; 115 signature = signatureSerializer.copyMethodSignature( 116 deserialized.getProto().getExtension(JvmProtoBuf.methodSignature), deserialized.getNameResolver()); 117 } 118 else { 119 Method method = bindings.get(METHOD_FOR_FUNCTION, (FunctionDescriptor) callable); 120 signature = method != null ? signatureSerializer.methodSignature(method) : null; 121 } 122 if (signature != null) { 123 proto.setExtension(JvmProtoBuf.methodSignature, signature); 124 } 125 } 126 else if (callable instanceof PropertyDescriptor) { 127 PropertyDescriptor property = (PropertyDescriptor) callable; 128 129 PropertyGetterDescriptor getter = property.getGetter(); 130 PropertySetterDescriptor setter = property.getSetter(); 131 Method getterMethod = getter == null ? null : bindings.get(METHOD_FOR_FUNCTION, getter); 132 Method setterMethod = setter == null ? null : bindings.get(METHOD_FOR_FUNCTION, setter); 133 134 Pair<Type, String> field = bindings.get(FIELD_FOR_PROPERTY, property); 135 Type fieldType; 136 String fieldName; 137 boolean isStaticInOuter; 138 Method syntheticMethod; 139 if (field != null) { 140 fieldType = field.first; 141 fieldName = field.second; 142 isStaticInOuter = bindings.get(STATIC_FIELD_IN_OUTER_CLASS, property); 143 syntheticMethod = bindings.get(SYNTHETIC_METHOD_FOR_PROPERTY, property); 144 } 145 else { 146 fieldType = null; 147 fieldName = null; 148 isStaticInOuter = false; 149 syntheticMethod = bindings.get(SYNTHETIC_METHOD_FOR_PROPERTY, property); 150 } 151 152 JvmProtoBuf.JvmPropertySignature signature; 153 if (callable instanceof DeserializedPropertyDescriptor) { 154 DeserializedPropertyDescriptor deserializedCallable = (DeserializedPropertyDescriptor) callable; 155 signature = signatureSerializer.copyPropertySignature( 156 deserializedCallable.getProto().getExtension(JvmProtoBuf.propertySignature), 157 deserializedCallable.getNameResolver() 158 ); 159 } 160 else { 161 signature = signatureSerializer 162 .propertySignature(fieldType, fieldName, isStaticInOuter, syntheticMethod, getterMethod, setterMethod); 163 } 164 proto.setExtension(JvmProtoBuf.propertySignature, signature); 165 } 166 } 167 168 private void saveImplClassName( 169 @NotNull CallableMemberDescriptor callable, 170 @NotNull ProtoBuf.Callable.Builder proto, 171 @NotNull StringTable stringTable 172 ) { 173 String name = bindings.get(IMPL_CLASS_NAME_FOR_CALLABLE, callable); 174 if (name != null) { 175 proto.setExtension(JvmProtoBuf.implClassName, stringTable.getSimpleNameIndex(Name.identifier(name))); 176 } 177 } 178 179 private static class SignatureSerializer { 180 private final StringTable stringTable; 181 182 public SignatureSerializer(@NotNull StringTable stringTable) { 183 this.stringTable = stringTable; 184 } 185 186 @NotNull 187 public JvmProtoBuf.JvmMethodSignature copyMethodSignature( 188 @NotNull JvmProtoBuf.JvmMethodSignature signature, 189 @NotNull NameResolver nameResolver 190 ) { 191 String method = new SignatureDeserializer(nameResolver).methodSignatureString(signature); 192 return methodSignature(getAsmMethod(method)); 193 } 194 195 @NotNull 196 public JvmProtoBuf.JvmMethodSignature methodSignature(@NotNull Method method) { 197 JvmProtoBuf.JvmMethodSignature.Builder signature = JvmProtoBuf.JvmMethodSignature.newBuilder(); 198 199 signature.setName(stringTable.getStringIndex(method.getName())); 200 201 signature.setReturnType(type(method.getReturnType())); 202 203 for (Type type : method.getArgumentTypes()) { 204 signature.addParameterType(type(type)); 205 } 206 207 return signature.build(); 208 } 209 210 @NotNull 211 public JvmProtoBuf.JvmPropertySignature copyPropertySignature( 212 @NotNull JvmProtoBuf.JvmPropertySignature signature, 213 @NotNull NameResolver nameResolver 214 ) { 215 Type fieldType; 216 String fieldName; 217 boolean isStaticInOuter; 218 SignatureDeserializer signatureDeserializer = new SignatureDeserializer(nameResolver); 219 if (signature.hasField()) { 220 JvmProtoBuf.JvmFieldSignature field = signature.getField(); 221 fieldType = Type.getType(signatureDeserializer.typeDescriptor(field.getType())); 222 fieldName = nameResolver.getName(field.getName()).asString(); 223 isStaticInOuter = field.getIsStaticInOuter(); 224 } 225 else { 226 fieldType = null; 227 fieldName = null; 228 isStaticInOuter = false; 229 } 230 231 Method syntheticMethod = signature.hasSyntheticMethod() 232 ? getAsmMethod(signatureDeserializer.methodSignatureString(signature.getSyntheticMethod())) 233 : null; 234 235 Method getter = signature.hasGetter() ? getAsmMethod(signatureDeserializer.methodSignatureString(signature.getGetter())) : null; 236 Method setter = signature.hasSetter() ? getAsmMethod(signatureDeserializer.methodSignatureString(signature.getSetter())) : null; 237 238 return propertySignature(fieldType, fieldName, isStaticInOuter, syntheticMethod, getter, setter); 239 } 240 241 @NotNull 242 public JvmProtoBuf.JvmPropertySignature propertySignature( 243 @Nullable Type fieldType, 244 @Nullable String fieldName, 245 boolean isStaticInOuter, 246 @Nullable Method syntheticMethod, 247 @Nullable Method getter, 248 @Nullable Method setter 249 ) { 250 JvmProtoBuf.JvmPropertySignature.Builder signature = JvmProtoBuf.JvmPropertySignature.newBuilder(); 251 252 if (fieldType != null) { 253 assert fieldName != null : "Field name shouldn't be null when there's a field type: " + fieldType; 254 signature.setField(fieldSignature(fieldType, fieldName, isStaticInOuter)); 255 } 256 257 if (syntheticMethod != null) { 258 signature.setSyntheticMethod(methodSignature(syntheticMethod)); 259 } 260 261 if (getter != null) { 262 signature.setGetter(methodSignature(getter)); 263 } 264 if (setter != null) { 265 signature.setSetter(methodSignature(setter)); 266 } 267 268 return signature.build(); 269 } 270 271 @NotNull 272 public JvmProtoBuf.JvmFieldSignature fieldSignature(@NotNull Type type, @NotNull String name, boolean isStaticInOuter) { 273 JvmProtoBuf.JvmFieldSignature.Builder signature = JvmProtoBuf.JvmFieldSignature.newBuilder(); 274 signature.setName(stringTable.getStringIndex(name)); 275 signature.setType(type(type)); 276 if (isStaticInOuter) { 277 signature.setIsStaticInOuter(true); 278 } 279 return signature.build(); 280 } 281 282 @NotNull 283 public JvmProtoBuf.JvmType type(@NotNull Type givenType) { 284 JvmProtoBuf.JvmType.Builder builder = JvmProtoBuf.JvmType.newBuilder(); 285 286 Type type = givenType; 287 if (type.getSort() == Type.ARRAY) { 288 builder.setArrayDimension(type.getDimensions()); 289 type = type.getElementType(); 290 } 291 292 if (type.getSort() == Type.OBJECT) { 293 FqName fqName = internalNameToFqName(type.getInternalName()); 294 builder.setClassFqName(stringTable.getFqNameIndex(fqName)); 295 } 296 else { 297 builder.setPrimitiveType(JvmProtoBuf.JvmType.PrimitiveType.valueOf(type.getSort())); 298 } 299 300 return builder.build(); 301 } 302 303 @NotNull 304 private static FqName internalNameToFqName(@NotNull String internalName) { 305 return FqName.fromSegments(Arrays.asList(internalName.split("/"))); 306 } 307 } 308 309 @NotNull 310 private static Method getAsmMethod(@NotNull String nameAndDesc) { 311 int indexOf = nameAndDesc.indexOf('('); 312 return new Method(nameAndDesc.substring(0, indexOf), nameAndDesc.substring(indexOf)); 313 } 314 }