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    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            if (callable instanceof FunctionDescriptor) {
071                Method method = bindings.get(METHOD_FOR_FUNCTION, (FunctionDescriptor) callable);
072                if (method != null) {
073                    proto.setExtension(JavaProtoBuf.methodSignature, new SignatureSerializer(nameTable).methodSignature(method));
074                }
075            }
076            else if (callable instanceof PropertyDescriptor) {
077                PropertyDescriptor property = (PropertyDescriptor) callable;
078    
079                PropertyGetterDescriptor getter = property.getGetter();
080                PropertySetterDescriptor setter = property.getSetter();
081                Method getterMethod = getter == null ? null : bindings.get(METHOD_FOR_FUNCTION, getter);
082                Method setterMethod = setter == null ? null : bindings.get(METHOD_FOR_FUNCTION, setter);
083    
084                Pair<Type, String> field = bindings.get(FIELD_FOR_PROPERTY, property);
085                Type fieldType;
086                String fieldName;
087                boolean isStaticInOuter;
088                Method syntheticMethod;
089                if (field != null) {
090                    fieldType = field.first;
091                    fieldName = field.second;
092                    isStaticInOuter = bindings.get(STATIC_FIELD_IN_OUTER_CLASS, property);
093                    syntheticMethod = null;
094                }
095                else {
096                    fieldType = null;
097                    fieldName = null;
098                    isStaticInOuter = false;
099                    syntheticMethod = bindings.get(SYNTHETIC_METHOD_FOR_PROPERTY, property);
100                }
101    
102                JavaProtoBuf.JavaPropertySignature signature = new SignatureSerializer(nameTable)
103                                .propertySignature(fieldType, fieldName, isStaticInOuter, syntheticMethod, getterMethod, setterMethod);
104                proto.setExtension(JavaProtoBuf.propertySignature, signature);
105            }
106        }
107    
108        private void saveImplClassName(
109                @NotNull CallableMemberDescriptor callable,
110                @NotNull ProtoBuf.Callable.Builder proto,
111                @NotNull NameTable nameTable
112        ) {
113            String name = bindings.get(IMPL_CLASS_NAME_FOR_CALLABLE, callable);
114            if (name != null) {
115                proto.setExtension(JavaProtoBuf.implClassName, nameTable.getSimpleNameIndex(Name.identifier(name)));
116            }
117        }
118    
119        private static class SignatureSerializer {
120            private final NameTable nameTable;
121    
122            public SignatureSerializer(@NotNull NameTable nameTable) {
123                this.nameTable = nameTable;
124            }
125    
126            @NotNull
127            public JavaProtoBuf.JavaMethodSignature methodSignature(@NotNull Method method) {
128                JavaProtoBuf.JavaMethodSignature.Builder signature = JavaProtoBuf.JavaMethodSignature.newBuilder();
129    
130                signature.setName(nameTable.getSimpleNameIndex(Name.guess(method.getName())));
131    
132                signature.setReturnType(type(method.getReturnType()));
133    
134                for (Type type : method.getArgumentTypes()) {
135                    signature.addParameterType(type(type));
136                }
137    
138                return signature.build();
139            }
140    
141            @NotNull
142            public JavaProtoBuf.JavaPropertySignature propertySignature(
143                    @Nullable Type fieldType,
144                    @Nullable String fieldName,
145                    boolean isStaticInOuter,
146                    @Nullable Method syntheticMethod,
147                    @Nullable Method getter,
148                    @Nullable Method setter
149            ) {
150                JavaProtoBuf.JavaPropertySignature.Builder signature = JavaProtoBuf.JavaPropertySignature.newBuilder();
151    
152                if (fieldType != null) {
153                    assert fieldName != null : "Field name shouldn't be null when there's a field type: " + fieldType;
154                    signature.setField(fieldSignature(fieldType, fieldName, isStaticInOuter));
155                }
156    
157                if (syntheticMethod != null) {
158                    signature.setSyntheticMethod(methodSignature(syntheticMethod));
159                }
160    
161                if (getter != null) {
162                    signature.setGetter(methodSignature(getter));
163                }
164                if (setter != null) {
165                    signature.setSetter(methodSignature(setter));
166                }
167    
168                return signature.build();
169            }
170    
171            @NotNull
172            public JavaProtoBuf.JavaFieldSignature fieldSignature(@NotNull Type type, @NotNull String name, boolean isStaticInOuter) {
173                JavaProtoBuf.JavaFieldSignature.Builder signature = JavaProtoBuf.JavaFieldSignature.newBuilder();
174                signature.setName(nameTable.getSimpleNameIndex(Name.guess(name)));
175                signature.setType(type(type));
176                if (isStaticInOuter) {
177                    signature.setIsStaticInOuter(true);
178                }
179                return signature.build();
180            }
181    
182            @NotNull
183            public JavaProtoBuf.JavaType type(@NotNull Type givenType) {
184                JavaProtoBuf.JavaType.Builder builder = JavaProtoBuf.JavaType.newBuilder();
185    
186                int arrayDimension = 0;
187                Type type = givenType;
188                while (type.getSort() == Type.ARRAY) {
189                    arrayDimension++;
190                    type = type.getElementType();
191                }
192                if (arrayDimension != 0) {
193                    builder.setArrayDimension(arrayDimension);
194                }
195    
196                if (type.getSort() == Type.OBJECT) {
197                    FqName fqName = internalNameToFqName(type.getInternalName());
198                    builder.setClassFqName(nameTable.getFqNameIndex(fqName));
199                }
200                else {
201                    builder.setPrimitiveType(JavaProtoBuf.JavaType.PrimitiveType.valueOf(type.getSort()));
202                }
203    
204                return builder.build();
205            }
206    
207            @NotNull
208            private static FqName internalNameToFqName(@NotNull String internalName) {
209                return FqName.fromSegments(Arrays.asList(internalName.split("/")));
210            }
211        }
212    }