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