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