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