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