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