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