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