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.asm4.Type;
023    import org.jetbrains.asm4.commons.Method;
024    import org.jetbrains.jet.descriptors.serialization.*;
025    import org.jetbrains.jet.lang.descriptors.*;
026    import org.jetbrains.jet.lang.resolve.name.FqName;
027    import org.jetbrains.jet.lang.resolve.name.Name;
028    
029    import java.util.Arrays;
030    
031    public class JavaSerializerExtension extends SerializerExtension {
032        private final MemberMap memberMap;
033    
034        public JavaSerializerExtension(@NotNull MemberMap memberMap) {
035            this.memberMap = memberMap;
036        }
037    
038        @Override
039        public void serializeCallable(
040                @NotNull CallableMemberDescriptor callable,
041                @NotNull ProtoBuf.Callable.Builder proto,
042                @NotNull NameTable nameTable
043        ) {
044            saveSignature(callable, proto, nameTable);
045            saveSrcClassName(callable, proto, nameTable);
046        }
047    
048        private void saveSignature(
049                @NotNull CallableMemberDescriptor callable,
050                @NotNull ProtoBuf.Callable.Builder proto,
051                @NotNull NameTable nameTable
052        ) {
053            if (callable instanceof FunctionDescriptor) {
054                Method method = memberMap.getMethodOfDescriptor((FunctionDescriptor) callable);
055                if (method != null) {
056                    proto.setExtension(JavaProtoBuf.methodSignature, new SignatureSerializer(nameTable).methodSignature(method));
057                }
058            }
059            else if (callable instanceof PropertyDescriptor) {
060                PropertyDescriptor property = (PropertyDescriptor) callable;
061    
062                PropertyGetterDescriptor getter = property.getGetter();
063                PropertySetterDescriptor setter = property.getSetter();
064                Method getterMethod = getter == null ? null : memberMap.getMethodOfDescriptor(getter);
065                Method setterMethod = setter == null ? null : memberMap.getMethodOfDescriptor(setter);
066    
067                Pair<Type, String> field = memberMap.getFieldOfProperty(property);
068                Type fieldType;
069                String fieldName;
070                boolean isStaticInOuter;
071                String syntheticMethodName;
072                if (field != null) {
073                    fieldType = field.first;
074                    fieldName = field.second;
075                    isStaticInOuter = memberMap.isStaticFieldInOuterClass(property);
076                    syntheticMethodName = null;
077                }
078                else {
079                    fieldType = null;
080                    fieldName = null;
081                    isStaticInOuter = false;
082                    syntheticMethodName = memberMap.getSyntheticMethodNameOfProperty(property);
083                }
084    
085                JavaProtoBuf.JavaPropertySignature signature = new SignatureSerializer(nameTable)
086                                .propertySignature(fieldType, fieldName, isStaticInOuter, syntheticMethodName, getterMethod, setterMethod);
087                proto.setExtension(JavaProtoBuf.propertySignature, signature);
088            }
089        }
090    
091        private void saveSrcClassName(
092                @NotNull CallableMemberDescriptor callable,
093                @NotNull ProtoBuf.Callable.Builder proto,
094                @NotNull NameTable nameTable
095        ) {
096            Name name = memberMap.getSrcClassNameOfCallable(callable);
097            if (name != null) {
098                proto.setExtension(JavaProtoBuf.srcClassName, nameTable.getSimpleNameIndex(name));
099            }
100        }
101    
102        private static class SignatureSerializer {
103            private final NameTable nameTable;
104    
105            public SignatureSerializer(@NotNull NameTable nameTable) {
106                this.nameTable = nameTable;
107            }
108    
109            @NotNull
110            public JavaProtoBuf.JavaMethodSignature methodSignature(@NotNull Method method) {
111                JavaProtoBuf.JavaMethodSignature.Builder signature = JavaProtoBuf.JavaMethodSignature.newBuilder();
112    
113                signature.setName(nameTable.getSimpleNameIndex(Name.guess(method.getName())));
114    
115                signature.setReturnType(type(method.getReturnType()));
116    
117                for (Type type : method.getArgumentTypes()) {
118                    signature.addParameterType(type(type));
119                }
120    
121                return signature.build();
122            }
123    
124            @NotNull
125            public JavaProtoBuf.JavaPropertySignature propertySignature(
126                    @Nullable Type fieldType,
127                    @Nullable String fieldName,
128                    boolean isStaticInOuter,
129                    @Nullable String syntheticMethodName,
130                    @Nullable Method getter,
131                    @Nullable Method setter
132            ) {
133                JavaProtoBuf.JavaPropertySignature.Builder signature = JavaProtoBuf.JavaPropertySignature.newBuilder();
134    
135                if (fieldType != null) {
136                    assert fieldName != null : "Field name shouldn't be null when there's a field type: " + fieldType;
137                    signature.setField(fieldSignature(fieldType, fieldName, isStaticInOuter));
138                }
139    
140                if (syntheticMethodName != null) {
141                    signature.setSyntheticMethodName(nameTable.getSimpleNameIndex(Name.guess(syntheticMethodName)));
142                }
143    
144                if (getter != null) {
145                    signature.setGetter(methodSignature(getter));
146                }
147                if (setter != null) {
148                    signature.setSetter(methodSignature(setter));
149                }
150    
151                return signature.build();
152            }
153    
154            @NotNull
155            public JavaProtoBuf.JavaFieldSignature fieldSignature(@NotNull Type type, @NotNull String name, boolean isStaticInOuter) {
156                JavaProtoBuf.JavaFieldSignature.Builder signature = JavaProtoBuf.JavaFieldSignature.newBuilder();
157                signature.setName(nameTable.getSimpleNameIndex(Name.guess(name)));
158                signature.setType(type(type));
159                if (isStaticInOuter) {
160                    signature.setIsStaticInOuter(true);
161                }
162                return signature.build();
163            }
164    
165            @NotNull
166            public JavaProtoBuf.JavaType type(@NotNull Type givenType) {
167                JavaProtoBuf.JavaType.Builder builder = JavaProtoBuf.JavaType.newBuilder();
168    
169                int arrayDimension = 0;
170                Type type = givenType;
171                while (type.getSort() == Type.ARRAY) {
172                    arrayDimension++;
173                    type = type.getElementType();
174                }
175                if (arrayDimension != 0) {
176                    builder.setArrayDimension(arrayDimension);
177                }
178    
179                if (type.getSort() == Type.OBJECT) {
180                    FqName fqName = internalNameToFqName(type.getInternalName());
181                    builder.setClassFqName(nameTable.getFqNameIndex(fqName));
182                }
183                else {
184                    builder.setPrimitiveType(JavaProtoBuf.JavaType.PrimitiveType.valueOf(type.getSort()));
185                }
186    
187                return builder.build();
188            }
189    
190            @NotNull
191            private static FqName internalNameToFqName(@NotNull String internalName) {
192                return FqName.fromSegments(Arrays.asList(internalName.split("/")));
193            }
194        }
195    }