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            boolean hasConstant = false;
164            if (descriptor instanceof PropertyDescriptor) {
165                PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
166    
167                int propertyFlags = Flags.getAccessorFlags(
168                        hasAnnotations(propertyDescriptor),
169                        propertyDescriptor.getVisibility(),
170                        propertyDescriptor.getModality(),
171                        false
172                );
173    
174                PropertyGetterDescriptor getter = propertyDescriptor.getGetter();
175                if (getter != null) {
176                    hasGetter = true;
177                    int accessorFlags = getAccessorFlags(getter);
178                    if (accessorFlags != propertyFlags) {
179                        builder.setGetterFlags(accessorFlags);
180                    }
181                }
182    
183                PropertySetterDescriptor setter = propertyDescriptor.getSetter();
184                if (setter != null) {
185                    hasSetter = true;
186                    int accessorFlags = getAccessorFlags(setter);
187                    if (accessorFlags != propertyFlags) {
188                        builder.setSetterFlags(accessorFlags);
189                    }
190    
191                    if (!setter.isDefault()) {
192                        for (ValueParameterDescriptor valueParameterDescriptor : setter.getValueParameters()) {
193                            builder.addValueParameter(local.valueParameter(valueParameterDescriptor));
194                        }
195                    }
196                }
197    
198                hasConstant = propertyDescriptor.getCompileTimeInitializer() != null;
199            }
200    
201            builder.setFlags(Flags.getCallableFlags(
202                    hasAnnotations(descriptor),
203                    descriptor.getVisibility(),
204                    descriptor.getModality(),
205                    descriptor.getKind(),
206                    callableKind(descriptor),
207                    hasGetter,
208                    hasSetter,
209                    hasConstant
210            ));
211            //TODO builder.setExtraVisibility()
212    
213            for (TypeParameterDescriptor typeParameterDescriptor : descriptor.getTypeParameters()) {
214                builder.addTypeParameter(local.typeParameter(typeParameterDescriptor));
215            }
216    
217            ReceiverParameterDescriptor receiverParameter = descriptor.getReceiverParameter();
218            if (receiverParameter != null) {
219                builder.setReceiverType(local.type(receiverParameter.getType()));
220            }
221    
222            builder.setName(nameTable.getSimpleNameIndex(descriptor.getName()));
223    
224            for (ValueParameterDescriptor valueParameterDescriptor : descriptor.getValueParameters()) {
225                builder.addValueParameter(local.valueParameter(valueParameterDescriptor));
226            }
227    
228            builder.setReturnType(local.type(getSerializableReturnType(descriptor.getReturnType())));
229    
230            extension.serializeCallable(descriptor, builder, nameTable);
231    
232            return builder;
233        }
234    
235        @NotNull
236        private static JetType getSerializableReturnType(@NotNull JetType type) {
237            return isSerializableType(type) ? type : KotlinBuiltIns.getInstance().getAnyType();
238        }
239    
240        /**
241         * @return true iff this type can be serialized. Types which correspond to type parameters, top-level classes, inner classes, and
242         * generic classes with serializable arguments are serializable. For other types (local classes, inner of local, etc.) it may be
243         * problematical to construct a FQ name for serialization
244         */
245        private static boolean isSerializableType(@NotNull JetType type) {
246            ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor();
247            if (descriptor instanceof TypeParameterDescriptor) {
248                return true;
249            }
250            else if (descriptor instanceof ClassDescriptor) {
251                for (TypeProjection projection : type.getArguments()) {
252                    if (!isSerializableType(projection.getType())) {
253                        return false;
254                    }
255                }
256    
257                return isTopLevelOrInnerClass((ClassDescriptor) descriptor);
258            }
259            else {
260                throw new IllegalStateException("Unknown type constructor: " + type);
261            }
262        }
263    
264        private static int getAccessorFlags(@NotNull PropertyAccessorDescriptor accessor) {
265            return Flags.getAccessorFlags(
266                    hasAnnotations(accessor),
267                    accessor.getVisibility(),
268                    accessor.getModality(),
269                    !accessor.isDefault()
270            );
271        }
272    
273        @NotNull
274        private static ProtoBuf.Callable.CallableKind callableKind(@NotNull CallableMemberDescriptor descriptor) {
275            if (descriptor instanceof PropertyDescriptor) {
276                return ((PropertyDescriptor) descriptor).isVar() ? ProtoBuf.Callable.CallableKind.VAR : ProtoBuf.Callable.CallableKind.VAL;
277            }
278            if (descriptor instanceof ConstructorDescriptor) {
279                return ProtoBuf.Callable.CallableKind.CONSTRUCTOR;
280            }
281            assert descriptor instanceof FunctionDescriptor : "Unknown descriptor class: " + descriptor.getClass();
282            return ProtoBuf.Callable.CallableKind.FUN;
283        }
284    
285        @NotNull
286        private ProtoBuf.Callable.ValueParameter.Builder valueParameter(@NotNull ValueParameterDescriptor descriptor) {
287            ProtoBuf.Callable.ValueParameter.Builder builder = ProtoBuf.Callable.ValueParameter.newBuilder();
288    
289            builder.setFlags(Flags.getValueParameterFlags(hasAnnotations(descriptor), descriptor.declaresDefaultValue()));
290    
291            builder.setName(nameTable.getSimpleNameIndex(descriptor.getName()));
292    
293            builder.setType(type(descriptor.getType()));
294    
295            JetType varargElementType = descriptor.getVarargElementType();
296            if (varargElementType != null) {
297                builder.setVarargElementType(type(varargElementType));
298            }
299    
300            extension.serializeValueParameter(descriptor, builder, nameTable);
301    
302            return builder;
303        }
304    
305        private ProtoBuf.TypeParameter.Builder typeParameter(TypeParameterDescriptor typeParameter) {
306            ProtoBuf.TypeParameter.Builder builder = ProtoBuf.TypeParameter.newBuilder();
307    
308            builder.setId(getTypeParameterId(typeParameter));
309    
310            builder.setName(nameTable.getSimpleNameIndex(typeParameter.getName()));
311    
312            // to avoid storing a default
313            if (typeParameter.isReified()) {
314                builder.setReified(true);
315            }
316    
317            // to avoid storing a default
318            ProtoBuf.TypeParameter.Variance variance = variance(typeParameter.getVariance());
319            if (variance != ProtoBuf.TypeParameter.Variance.INV) {
320                builder.setVariance(variance);
321            }
322    
323            for (JetType upperBound : typeParameter.getUpperBounds()) {
324                builder.addUpperBound(type(upperBound));
325            }
326    
327            return builder;
328        }
329    
330        private static ProtoBuf.TypeParameter.Variance variance(Variance variance) {
331            switch (variance) {
332                case INVARIANT:
333                    return ProtoBuf.TypeParameter.Variance.INV;
334                case IN_VARIANCE:
335                    return ProtoBuf.TypeParameter.Variance.IN;
336                case OUT_VARIANCE:
337                    return  ProtoBuf.TypeParameter.Variance.OUT;
338            }
339            throw new IllegalStateException("Unknown variance: " + variance);
340        }
341    
342        @NotNull
343        public ProtoBuf.Type.Builder type(@NotNull JetType type) {
344            assert !type.isError() : "Can't serialize error types: " + type; // TODO
345    
346            ProtoBuf.Type.Builder builder = ProtoBuf.Type.newBuilder();
347    
348            builder.setConstructor(typeConstructor(type.getConstructor()));
349    
350            for (TypeProjection projection : type.getArguments()) {
351                builder.addArgument(typeArgument(projection));
352            }
353    
354            // to avoid storing a default
355            if (type.isNullable()) {
356                builder.setNullable(true);
357            }
358    
359            return builder;
360        }
361    
362        @NotNull
363        private ProtoBuf.Type.Argument.Builder typeArgument(@NotNull TypeProjection typeProjection) {
364            ProtoBuf.Type.Argument.Builder builder = ProtoBuf.Type.Argument.newBuilder();
365            ProtoBuf.Type.Argument.Projection projection = projection(typeProjection.getProjectionKind());
366    
367            // to avoid storing a default
368            if (projection != ProtoBuf.Type.Argument.Projection.INV) {
369                builder.setProjection(projection);
370            }
371    
372            builder.setType(type(typeProjection.getType()));
373            return builder;
374        }
375    
376        @NotNull
377        private ProtoBuf.Type.Constructor.Builder typeConstructor(@NotNull TypeConstructor typeConstructor) {
378            ProtoBuf.Type.Constructor.Builder builder = ProtoBuf.Type.Constructor.newBuilder();
379    
380            ClassifierDescriptor declarationDescriptor = typeConstructor.getDeclarationDescriptor();
381    
382            assert declarationDescriptor instanceof TypeParameterDescriptor || declarationDescriptor instanceof ClassDescriptor
383                    : "Unknown declaration descriptor: " + typeConstructor;
384            if (declarationDescriptor instanceof TypeParameterDescriptor) {
385                TypeParameterDescriptor typeParameterDescriptor = (TypeParameterDescriptor) declarationDescriptor;
386                builder.setKind(ProtoBuf.Type.Constructor.Kind.TYPE_PARAMETER);
387                builder.setId(getTypeParameterId(typeParameterDescriptor));
388            }
389            else {
390                ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor;
391                //default: builder.setKind(Type.Constructor.Kind.CLASS);
392                builder.setId(getClassId(classDescriptor));
393            }
394            return builder;
395        }
396    
397        @NotNull
398        public ProtoBuf.Package.Builder packageProto(@NotNull Collection<PackageFragmentDescriptor> fragments) {
399            ProtoBuf.Package.Builder builder = ProtoBuf.Package.newBuilder();
400    
401            Collection<DeclarationDescriptor> members = new ArrayList<DeclarationDescriptor>();
402            for (PackageFragmentDescriptor fragment : fragments) {
403                members.addAll(fragment.getMemberScope().getAllDescriptors());
404            }
405    
406            for (DeclarationDescriptor declaration : sort(members)) {
407                if (declaration instanceof PropertyDescriptor || declaration instanceof FunctionDescriptor) {
408                    builder.addMember(callableProto((CallableMemberDescriptor) declaration));
409                }
410            }
411    
412            return builder;
413        }
414    
415        @NotNull
416        private static ProtoBuf.Type.Argument.Projection projection(@NotNull Variance projectionKind) {
417            switch (projectionKind) {
418                case INVARIANT:
419                    return ProtoBuf.Type.Argument.Projection.INV;
420                case IN_VARIANCE:
421                    return ProtoBuf.Type.Argument.Projection.IN;
422                case OUT_VARIANCE:
423                    return ProtoBuf.Type.Argument.Projection.OUT;
424            }
425            throw new IllegalStateException("Unknown projectionKind: " + projectionKind);
426        }
427    
428        private int getClassId(@NotNull ClassDescriptor descriptor) {
429            return nameTable.getFqNameIndex(descriptor);
430        }
431    
432        private int getTypeParameterId(@NotNull TypeParameterDescriptor descriptor) {
433            return typeParameters.intern(descriptor);
434        }
435    
436        private static boolean hasAnnotations(Annotated descriptor) {
437            return !descriptor.getAnnotations().isEmpty();
438        }
439    
440        @NotNull
441        public static <T extends DeclarationDescriptor> List<T> sort(@NotNull Collection<T> descriptors) {
442            List<T> result = new ArrayList<T>(descriptors);
443            Collections.sort(result, DESCRIPTOR_COMPARATOR);
444            return result;
445    
446        }
447    }