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.descriptors.serialization;
018    
019    import org.jetbrains.annotations.NotNull;
020    import org.jetbrains.jet.lang.descriptors.*;
021    import org.jetbrains.jet.lang.descriptors.annotations.Annotated;
022    import org.jetbrains.jet.lang.types.*;
023    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
024    import org.jetbrains.jet.renderer.DescriptorRenderer;
025    
026    import java.util.*;
027    
028    import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getEnumEntriesScope;
029    import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isTopLevelOrInnerClass;
030    
031    public class DescriptorSerializer {
032    
033        private static final DescriptorRenderer RENDERER = DescriptorRenderer.STARTS_FROM_NAME;
034        private static final Comparator<DeclarationDescriptor> DESCRIPTOR_COMPARATOR = new Comparator<DeclarationDescriptor>() {
035            @Override
036            public int compare(
037                    DeclarationDescriptor o1, DeclarationDescriptor o2
038            ) {
039                int names = o1.getName().compareTo(o2.getName());
040                if (names != 0) return names;
041    
042                String o1String = RENDERER.render(o1);
043                String o2String = RENDERER.render(o2);
044                return o1String.compareTo(o2String);
045            }
046        };
047        private final NameTable nameTable;
048        private final Interner<TypeParameterDescriptor> typeParameters;
049        private final SerializerExtension extension;
050    
051        public DescriptorSerializer() {
052            this(SerializerExtension.DEFAULT);
053        }
054    
055        public DescriptorSerializer(@NotNull SerializerExtension extension) {
056            this(new NameTable(), new Interner<TypeParameterDescriptor>(), extension);
057        }
058    
059        private DescriptorSerializer(NameTable nameTable, Interner<TypeParameterDescriptor> typeParameters, SerializerExtension extension) {
060            this.nameTable = nameTable;
061            this.typeParameters = typeParameters;
062            this.extension = extension;
063        }
064    
065        private DescriptorSerializer createChildSerializer() {
066            return new DescriptorSerializer(nameTable, new Interner<TypeParameterDescriptor>(typeParameters), extension);
067        }
068    
069        @NotNull
070        public NameTable getNameTable() {
071            return nameTable;
072        }
073    
074        @NotNull
075        public ProtoBuf.Class.Builder classProto(@NotNull ClassDescriptor classDescriptor) {
076            ProtoBuf.Class.Builder builder = ProtoBuf.Class.newBuilder();
077    
078            int flags = Flags.getClassFlags(hasAnnotations(classDescriptor), classDescriptor.getVisibility(),
079                                            classDescriptor.getModality(), classDescriptor.getKind(), classDescriptor.isInner());
080            builder.setFlags(flags);
081    
082            // TODO extra visibility
083    
084            builder.setName(nameTable.getSimpleNameIndex(classDescriptor.getName()));
085    
086            DescriptorSerializer local = createChildSerializer();
087    
088            for (TypeParameterDescriptor typeParameterDescriptor : classDescriptor.getTypeConstructor().getParameters()) {
089                builder.addTypeParameter(local.typeParameter(typeParameterDescriptor));
090            }
091    
092            if (extension.hasSupertypes(classDescriptor)) {
093                // Special classes (Any, Nothing) have no supertypes
094                for (JetType supertype : classDescriptor.getTypeConstructor().getSupertypes()) {
095                    builder.addSupertype(local.type(supertype));
096                }
097            }
098    
099            ConstructorDescriptor primaryConstructor = classDescriptor.getUnsubstitutedPrimaryConstructor();
100            if (primaryConstructor != null) {
101                builder.setPrimaryConstructor(local.callableProto(primaryConstructor));
102            }
103            // TODO: other constructors
104    
105            for (DeclarationDescriptor descriptor : sort(classDescriptor.getDefaultType().getMemberScope().getAllDescriptors())) {
106                if (descriptor instanceof CallableMemberDescriptor) {
107                    CallableMemberDescriptor member = (CallableMemberDescriptor) descriptor;
108                    if (member.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) continue;
109                    builder.addMember(local.callableProto(member));
110                }
111            }
112    
113            Collection<DeclarationDescriptor> nestedClasses = classDescriptor.getUnsubstitutedInnerClassesScope().getAllDescriptors();
114            for (DeclarationDescriptor descriptor : sort(nestedClasses)) {
115                ClassDescriptor nestedClass = (ClassDescriptor) descriptor;
116                int nameIndex = nameTable.getSimpleNameIndex(nestedClass.getName());
117                builder.addNestedClassName(nameIndex);
118            }
119    
120            for (ClassDescriptor descriptor : sort(classDescriptor.getUnsubstitutedInnerClassesScope().getObjectDescriptors())) {
121                int nameIndex = nameTable.getSimpleNameIndex(descriptor.getName());
122                builder.addNestedObjectName(nameIndex);
123            }
124    
125            if (classDescriptor.getClassObjectDescriptor() != null) {
126                // false is default
127                builder.setClassObjectPresent(true);
128            }
129    
130            if (classDescriptor.getKind() == ClassKind.ENUM_CLASS) {
131                for (ClassDescriptor descriptor : getEnumEntriesScope(classDescriptor).getObjectDescriptors()) {
132                    if (descriptor.getKind() == ClassKind.ENUM_ENTRY) {
133                        builder.addEnumEntry(nameTable.getSimpleNameIndex(descriptor.getName()));
134                    }
135                }
136            }
137    
138            return builder;
139        }
140    
141        @NotNull
142        public ProtoBuf.Callable.Builder callableProto(@NotNull CallableMemberDescriptor descriptor) {
143            ProtoBuf.Callable.Builder builder = ProtoBuf.Callable.newBuilder();
144    
145            boolean hasGetter = false;
146            boolean hasSetter = false;
147            if (descriptor instanceof PropertyDescriptor) {
148                PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
149    
150                int propertyFlags = Flags.getAccessorFlags(
151                        hasAnnotations(propertyDescriptor),
152                        propertyDescriptor.getVisibility(),
153                        propertyDescriptor.getModality(),
154                        false
155                );
156    
157                PropertyGetterDescriptor getter = propertyDescriptor.getGetter();
158                if (getter != null) {
159                    hasGetter = true;
160                    int accessorFlags = getAccessorFlags(getter);
161                    if (accessorFlags != propertyFlags) {
162                        builder.setGetterFlags(accessorFlags);
163                    }
164                }
165    
166                PropertySetterDescriptor setter = propertyDescriptor.getSetter();
167                if (setter != null) {
168                    hasSetter = true;
169                    int accessorFlags = getAccessorFlags(setter);
170                    if (accessorFlags != propertyFlags) {
171                        builder.setSetterFlags(accessorFlags);
172                    }
173    
174                    if (!setter.isDefault()) {
175                        List<ValueParameterDescriptor> parameters = setter.getValueParameters();
176                        assert parameters.size() == 1 : "Unexpected number of setter parameters: " + setter;
177                        builder.setSetterParameterName(nameTable.getSimpleNameIndex(parameters.get(0).getName()));
178                    }
179                }
180            }
181    
182            builder.setFlags(Flags.getCallableFlags(
183                    hasAnnotations(descriptor),
184                    descriptor.getVisibility(),
185                    descriptor.getModality(),
186                    descriptor.getKind(),
187                    callableKind(descriptor),
188                    descriptor instanceof SimpleFunctionDescriptor &&
189                    ((SimpleFunctionDescriptor) descriptor).isInline(),
190                    hasGetter,
191                    hasSetter
192            ));
193            //TODO builder.setExtraVisibility()
194    
195            DescriptorSerializer local = createChildSerializer();
196    
197            for (TypeParameterDescriptor typeParameterDescriptor : descriptor.getTypeParameters()) {
198                builder.addTypeParameter(local.typeParameter(typeParameterDescriptor));
199            }
200    
201            ReceiverParameterDescriptor receiverParameter = descriptor.getReceiverParameter();
202            if (receiverParameter != null) {
203                builder.setReceiverType(local.type(receiverParameter.getType()));
204            }
205    
206            builder.setName(nameTable.getSimpleNameIndex(descriptor.getName()));
207    
208            for (ValueParameterDescriptor valueParameterDescriptor : descriptor.getValueParameters()) {
209                builder.addValueParameter(local.valueParameter(valueParameterDescriptor));
210            }
211    
212            builder.setReturnType(local.type(getSerializableReturnType(descriptor.getReturnType())));
213    
214            extension.serializeCallable(descriptor, builder, nameTable);
215    
216            return builder;
217        }
218    
219        @NotNull
220        private static JetType getSerializableReturnType(@NotNull JetType type) {
221            return isSerializableType(type) ? type : KotlinBuiltIns.getInstance().getAnyType();
222        }
223    
224        /**
225         * @return true iff this type can be serialized. Types which correspond to type parameters, top-level classes, inner classes, and
226         * generic classes with serializable arguments are serializable. For other types (local classes, inner of local, etc.) it may be
227         * problematical to construct a FQ name for serialization
228         */
229        private static boolean isSerializableType(@NotNull JetType type) {
230            ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor();
231            if (descriptor instanceof TypeParameterDescriptor) {
232                return true;
233            }
234            else if (descriptor instanceof ClassDescriptor) {
235                for (TypeProjection projection : type.getArguments()) {
236                    if (!isSerializableType(projection.getType())) {
237                        return false;
238                    }
239                }
240    
241                return isTopLevelOrInnerClass((ClassDescriptor) descriptor);
242            }
243            else {
244                throw new IllegalStateException("Unknown type constructor: " + type);
245            }
246        }
247    
248        private static int getAccessorFlags(@NotNull PropertyAccessorDescriptor accessor) {
249            return Flags.getAccessorFlags(
250                    hasAnnotations(accessor),
251                    accessor.getVisibility(),
252                    accessor.getModality(),
253                    !accessor.isDefault()
254            );
255        }
256    
257        private static ProtoBuf.Callable.CallableKind callableKind(CallableMemberDescriptor descriptor) {
258            if (descriptor instanceof VariableDescriptorForObject) {
259                return ProtoBuf.Callable.CallableKind.OBJECT_PROPERTY;
260            }
261            else if (descriptor instanceof PropertyDescriptor) {
262                PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
263                return propertyDescriptor.isVar() ? ProtoBuf.Callable.CallableKind.VAR : ProtoBuf.Callable.CallableKind.VAL;
264            }
265            if (descriptor instanceof ConstructorDescriptor) {
266                return ProtoBuf.Callable.CallableKind.CONSTRUCTOR;
267            }
268            assert descriptor instanceof FunctionDescriptor : "Unknown descriptor class: " + descriptor.getClass();
269            return ProtoBuf.Callable.CallableKind.FUN;
270        }
271    
272        private ProtoBuf.Callable.ValueParameter.Builder valueParameter(ValueParameterDescriptor descriptor) {
273            ProtoBuf.Callable.ValueParameter.Builder builder = ProtoBuf.Callable.ValueParameter.newBuilder();
274    
275            builder.setFlags(Flags.getValueParameterFlags(hasAnnotations(descriptor), descriptor.declaresDefaultValue()));
276    
277            builder.setName(nameTable.getSimpleNameIndex(descriptor.getName()));
278    
279            builder.setType(type(descriptor.getType()));
280    
281            JetType varargElementType = descriptor.getVarargElementType();
282            if (varargElementType != null) {
283                builder.setVarargElementType(type(varargElementType));
284            }
285    
286            return builder;
287        }
288    
289        private ProtoBuf.TypeParameter.Builder typeParameter(TypeParameterDescriptor typeParameter) {
290            ProtoBuf.TypeParameter.Builder builder = ProtoBuf.TypeParameter.newBuilder();
291    
292            builder.setId(getTypeParameterId(typeParameter));
293    
294            builder.setName(nameTable.getSimpleNameIndex(typeParameter.getName()));
295    
296            // to avoid storing a default
297            if (typeParameter.isReified()) {
298                builder.setReified(true);
299            }
300    
301            // to avoid storing a default
302            ProtoBuf.TypeParameter.Variance variance = variance(typeParameter.getVariance());
303            if (variance != ProtoBuf.TypeParameter.Variance.INV) {
304                builder.setVariance(variance);
305            }
306    
307            for (JetType upperBound : typeParameter.getUpperBounds()) {
308                builder.addUpperBound(type(upperBound));
309            }
310    
311            return builder;
312        }
313    
314        private static ProtoBuf.TypeParameter.Variance variance(Variance variance) {
315            switch (variance) {
316                case INVARIANT:
317                    return ProtoBuf.TypeParameter.Variance.INV;
318                case IN_VARIANCE:
319                    return ProtoBuf.TypeParameter.Variance.IN;
320                case OUT_VARIANCE:
321                    return  ProtoBuf.TypeParameter.Variance.OUT;
322            }
323            throw new IllegalStateException("Unknown variance: " + variance);
324        }
325    
326        @NotNull
327        public ProtoBuf.Type.Builder type(@NotNull JetType type) {
328            assert !ErrorUtils.isErrorType(type) : "Can't serialize error types: " + type; // TODO
329    
330            ProtoBuf.Type.Builder builder = ProtoBuf.Type.newBuilder();
331    
332            builder.setConstructor(typeConstructor(type.getConstructor()));
333    
334            for (TypeProjection projection : type.getArguments()) {
335                builder.addArgument(typeArgument(projection));
336            }
337    
338            // to avoid storing a default
339            if (type.isNullable()) {
340                builder.setNullable(true);
341            }
342    
343            return builder;
344        }
345    
346        @NotNull
347        private ProtoBuf.Type.Argument.Builder typeArgument(@NotNull TypeProjection typeProjection) {
348            ProtoBuf.Type.Argument.Builder builder = ProtoBuf.Type.Argument.newBuilder();
349            ProtoBuf.Type.Argument.Projection projection = projection(typeProjection.getProjectionKind());
350    
351            // to avoid storing a default
352            if (projection != ProtoBuf.Type.Argument.Projection.INV) {
353                builder.setProjection(projection);
354            }
355    
356            builder.setType(type(typeProjection.getType()));
357            return builder;
358        }
359    
360        @NotNull
361        private ProtoBuf.Type.Constructor.Builder typeConstructor(@NotNull TypeConstructor typeConstructor) {
362            ProtoBuf.Type.Constructor.Builder builder = ProtoBuf.Type.Constructor.newBuilder();
363    
364            ClassifierDescriptor declarationDescriptor = typeConstructor.getDeclarationDescriptor();
365    
366            assert declarationDescriptor instanceof TypeParameterDescriptor || declarationDescriptor instanceof ClassDescriptor
367                    : "Unknown declaration descriptor: " + typeConstructor;
368            if (declarationDescriptor instanceof TypeParameterDescriptor) {
369                TypeParameterDescriptor typeParameterDescriptor = (TypeParameterDescriptor) declarationDescriptor;
370                builder.setKind(ProtoBuf.Type.Constructor.Kind.TYPE_PARAMETER);
371                builder.setId(getTypeParameterId(typeParameterDescriptor));
372            }
373            else {
374                ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor;
375                //default: builder.setKind(Type.Constructor.Kind.CLASS);
376                builder.setId(getClassId(classDescriptor));
377            }
378            return builder;
379        }
380    
381        @NotNull
382        public ProtoBuf.Package.Builder packageProto(@NotNull NamespaceDescriptor descriptor) {
383            ProtoBuf.Package.Builder builder = ProtoBuf.Package.newBuilder();
384    
385            for (DeclarationDescriptor declaration : sort(descriptor.getMemberScope().getAllDescriptors())) {
386                if (declaration instanceof PropertyDescriptor || declaration instanceof FunctionDescriptor) {
387                    builder.addMember(callableProto((CallableMemberDescriptor) declaration));
388                }
389            }
390    
391            return builder;
392        }
393    
394        @NotNull
395        private static ProtoBuf.Type.Argument.Projection projection(@NotNull Variance projectionKind) {
396            switch (projectionKind) {
397                case INVARIANT:
398                    return ProtoBuf.Type.Argument.Projection.INV;
399                case IN_VARIANCE:
400                    return ProtoBuf.Type.Argument.Projection.IN;
401                case OUT_VARIANCE:
402                    return ProtoBuf.Type.Argument.Projection.OUT;
403            }
404            throw new IllegalStateException("Unknown projectionKind: " + projectionKind);
405        }
406    
407        private int getClassId(@NotNull ClassDescriptor descriptor) {
408            return nameTable.getFqNameIndex(descriptor);
409        }
410    
411        private int getTypeParameterId(@NotNull TypeParameterDescriptor descriptor) {
412            return typeParameters.intern(descriptor);
413        }
414    
415        private static boolean hasAnnotations(Annotated descriptor) {
416            return !descriptor.getAnnotations().isEmpty();
417        }
418    
419        @NotNull
420        public static <T extends DeclarationDescriptor> List<T> sort(@NotNull Collection<T> descriptors) {
421            List<T> result = new ArrayList<T>(descriptors);
422            Collections.sort(result, DESCRIPTOR_COMPARATOR);
423            return result;
424    
425        }
426    }