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    }