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