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