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.*;
024    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
025    import org.jetbrains.jet.renderer.DescriptorRenderer;
026    
027    import java.util.*;
028    
029    import static org.jetbrains.jet.lang.resolve.DescriptorUtils.*;
030    
031    public class DescriptorSerializer {
032    
033        private static final DescriptorRenderer RENDERER = DescriptorRenderer.STARTS_FROM_NAME;
034        private static final Comparator<DeclarationDescriptor> DESCRIPTOR_COMPARATOR = new Comparator<DeclarationDescriptor>() {
035            @Override
036            public int compare(@NotNull DeclarationDescriptor o1, @NotNull DeclarationDescriptor o2) {
037                int names = o1.getName().compareTo(o2.getName());
038                if (names != 0) return names;
039    
040                String o1String = RENDERER.render(o1);
041                String o2String = RENDERER.render(o2);
042                return o1String.compareTo(o2String);
043            }
044        };
045        private final NameTable nameTable;
046        private final Interner<TypeParameterDescriptor> typeParameters;
047        private final SerializerExtension extension;
048    
049        public DescriptorSerializer() {
050            this(SerializerExtension.DEFAULT);
051        }
052    
053        public DescriptorSerializer(@NotNull SerializerExtension extension) {
054            this(new NameTable(), new Interner<TypeParameterDescriptor>(), extension);
055        }
056    
057        private DescriptorSerializer(NameTable nameTable, Interner<TypeParameterDescriptor> typeParameters, SerializerExtension extension) {
058            this.nameTable = nameTable;
059            this.typeParameters = typeParameters;
060            this.extension = extension;
061        }
062    
063        private DescriptorSerializer createChildSerializer() {
064            return new DescriptorSerializer(nameTable, new Interner<TypeParameterDescriptor>(typeParameters), extension);
065        }
066    
067        @NotNull
068        public NameTable getNameTable() {
069            return nameTable;
070        }
071    
072        @NotNull
073        public ProtoBuf.Class.Builder classProto(@NotNull ClassDescriptor classDescriptor) {
074            ProtoBuf.Class.Builder builder = ProtoBuf.Class.newBuilder();
075    
076            int flags = Flags.getClassFlags(hasAnnotations(classDescriptor), classDescriptor.getVisibility(),
077                                            classDescriptor.getModality(), classDescriptor.getKind(), classDescriptor.isInner());
078            builder.setFlags(flags);
079    
080            // TODO extra visibility
081    
082            builder.setFqName(getClassId(classDescriptor));
083    
084            DescriptorSerializer local = createChildSerializer();
085    
086            for (TypeParameterDescriptor typeParameterDescriptor : classDescriptor.getTypeConstructor().getParameters()) {
087                builder.addTypeParameter(local.typeParameter(typeParameterDescriptor));
088            }
089    
090            if (extension.hasSupertypes(classDescriptor)) {
091                // Special classes (Any, Nothing) have no supertypes
092                for (JetType supertype : classDescriptor.getTypeConstructor().getSupertypes()) {
093                    builder.addSupertype(local.type(supertype));
094                }
095            }
096    
097            ConstructorDescriptor primaryConstructor = classDescriptor.getUnsubstitutedPrimaryConstructor();
098            if (primaryConstructor != null) {
099                if (DescriptorFactory.isDefaultPrimaryConstructor(primaryConstructor)) {
100                    builder.setPrimaryConstructor(ProtoBuf.Class.PrimaryConstructor.getDefaultInstance());
101                }
102                else {
103                    ProtoBuf.Class.PrimaryConstructor.Builder constructorBuilder = ProtoBuf.Class.PrimaryConstructor.newBuilder();
104                    constructorBuilder.setData(local.callableProto(primaryConstructor));
105                    builder.setPrimaryConstructor(constructorBuilder);
106                }
107            }
108    
109            // TODO: other constructors
110    
111            for (DeclarationDescriptor descriptor : sort(classDescriptor.getDefaultType().getMemberScope().getAllDescriptors())) {
112                if (descriptor instanceof CallableMemberDescriptor) {
113                    CallableMemberDescriptor member = (CallableMemberDescriptor) descriptor;
114                    if (member.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) continue;
115                    builder.addMember(local.callableProto(member));
116                }
117            }
118    
119            Collection<DeclarationDescriptor> nestedClasses = classDescriptor.getUnsubstitutedInnerClassesScope().getAllDescriptors();
120            for (DeclarationDescriptor descriptor : sort(nestedClasses)) {
121                if (!isEnumEntry(descriptor)) {
122                    builder.addNestedClassName(nameTable.getSimpleNameIndex(descriptor.getName()));
123                }
124            }
125    
126            ClassDescriptor classObject = classDescriptor.getClassObjectDescriptor();
127            if (classObject != null) {
128                builder.setClassObject(classObjectProto(classObject));
129            }
130    
131            if (classDescriptor.getKind() == ClassKind.ENUM_CLASS) {
132                // Not calling sort() here, because the order of enum entries matters
133                for (DeclarationDescriptor descriptor : nestedClasses) {
134                    if (isEnumEntry(descriptor)) {
135                        builder.addEnumEntry(nameTable.getSimpleNameIndex(descriptor.getName()));
136                    }
137                }
138            }
139    
140            return builder;
141        }
142    
143        @NotNull
144        private ProtoBuf.Class.ClassObject classObjectProto(@NotNull ClassDescriptor classObject) {
145            if (isObject(classObject.getContainingDeclaration())) {
146                return ProtoBuf.Class.ClassObject.newBuilder().setData(classProto(classObject)).build();
147            }
148    
149            return ProtoBuf.Class.ClassObject.getDefaultInstance();
150        }
151    
152        @NotNull
153        public ProtoBuf.Callable.Builder callableProto(@NotNull CallableMemberDescriptor descriptor) {
154            ProtoBuf.Callable.Builder builder = ProtoBuf.Callable.newBuilder();
155    
156            DescriptorSerializer local = createChildSerializer();
157    
158            boolean hasGetter = false;
159            boolean hasSetter = false;
160            boolean hasConstant = false;
161            if (descriptor instanceof PropertyDescriptor) {
162                PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
163    
164                int propertyFlags = Flags.getAccessorFlags(
165                        hasAnnotations(propertyDescriptor),
166                        propertyDescriptor.getVisibility(),
167                        propertyDescriptor.getModality(),
168                        false
169                );
170    
171                PropertyGetterDescriptor getter = propertyDescriptor.getGetter();
172                if (getter != null) {
173                    hasGetter = true;
174                    int accessorFlags = getAccessorFlags(getter);
175                    if (accessorFlags != propertyFlags) {
176                        builder.setGetterFlags(accessorFlags);
177                    }
178                }
179    
180                PropertySetterDescriptor setter = propertyDescriptor.getSetter();
181                if (setter != null) {
182                    hasSetter = true;
183                    int accessorFlags = getAccessorFlags(setter);
184                    if (accessorFlags != propertyFlags) {
185                        builder.setSetterFlags(accessorFlags);
186                    }
187    
188                    if (!setter.isDefault()) {
189                        for (ValueParameterDescriptor valueParameterDescriptor : setter.getValueParameters()) {
190                            builder.addValueParameter(local.valueParameter(valueParameterDescriptor));
191                        }
192                    }
193                }
194    
195                hasConstant = propertyDescriptor.getCompileTimeInitializer() != null;
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                    hasConstant
207            ));
208            //TODO builder.setExtraVisibility()
209    
210            for (TypeParameterDescriptor typeParameterDescriptor : descriptor.getTypeParameters()) {
211                builder.addTypeParameter(local.typeParameter(typeParameterDescriptor));
212            }
213    
214            ReceiverParameterDescriptor receiverParameter = descriptor.getExtensionReceiverParameter();
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            if (TypesPackage.isFlexible(type)) return flexibleType(type);
344    
345            ProtoBuf.Type.Builder builder = ProtoBuf.Type.newBuilder();
346    
347            builder.setConstructor(typeConstructor(type.getConstructor()));
348    
349            for (TypeProjection projection : type.getArguments()) {
350                builder.addArgument(typeArgument(projection));
351            }
352    
353            // to avoid storing a default
354            if (type.isNullable()) {
355                builder.setNullable(true);
356            }
357    
358            return builder;
359        }
360    
361        private ProtoBuf.Type.Builder flexibleType(@NotNull JetType type) {
362            Flexibility flexibility = TypesPackage.flexibility(type);
363    
364            ProtoBuf.Type.Builder builder = type(flexibility.getLowerBound());
365    
366            builder.setFlexibleTypeCapabilitiesId(nameTable.getStringIndex(flexibility.getExtraCapabilities().getId()));
367    
368            builder.setFlexibleUpperBound(type(flexibility.getUpperBound()));
369    
370            return builder;
371        }
372    
373        @NotNull
374        private ProtoBuf.Type.Argument.Builder typeArgument(@NotNull TypeProjection typeProjection) {
375            ProtoBuf.Type.Argument.Builder builder = ProtoBuf.Type.Argument.newBuilder();
376            ProtoBuf.Type.Argument.Projection projection = projection(typeProjection.getProjectionKind());
377    
378            // to avoid storing a default
379            if (projection != ProtoBuf.Type.Argument.Projection.INV) {
380                builder.setProjection(projection);
381            }
382    
383            builder.setType(type(typeProjection.getType()));
384            return builder;
385        }
386    
387        @NotNull
388        private ProtoBuf.Type.Constructor.Builder typeConstructor(@NotNull TypeConstructor typeConstructor) {
389            ProtoBuf.Type.Constructor.Builder builder = ProtoBuf.Type.Constructor.newBuilder();
390    
391            ClassifierDescriptor declarationDescriptor = typeConstructor.getDeclarationDescriptor();
392    
393            assert declarationDescriptor instanceof TypeParameterDescriptor || declarationDescriptor instanceof ClassDescriptor
394                    : "Unknown declaration descriptor: " + typeConstructor;
395            if (declarationDescriptor instanceof TypeParameterDescriptor) {
396                TypeParameterDescriptor typeParameterDescriptor = (TypeParameterDescriptor) declarationDescriptor;
397                builder.setKind(ProtoBuf.Type.Constructor.Kind.TYPE_PARAMETER);
398                builder.setId(getTypeParameterId(typeParameterDescriptor));
399            }
400            else {
401                ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor;
402                //default: builder.setKind(Type.Constructor.Kind.CLASS);
403                builder.setId(getClassId(classDescriptor));
404            }
405            return builder;
406        }
407    
408        @NotNull
409        public ProtoBuf.Package.Builder packageProto(@NotNull Collection<PackageFragmentDescriptor> fragments) {
410            ProtoBuf.Package.Builder builder = ProtoBuf.Package.newBuilder();
411    
412            Collection<DeclarationDescriptor> members = new ArrayList<DeclarationDescriptor>();
413            for (PackageFragmentDescriptor fragment : fragments) {
414                members.addAll(fragment.getMemberScope().getAllDescriptors());
415            }
416    
417            for (DeclarationDescriptor declaration : sort(members)) {
418                if (declaration instanceof PropertyDescriptor || declaration instanceof FunctionDescriptor) {
419                    builder.addMember(callableProto((CallableMemberDescriptor) declaration));
420                }
421            }
422    
423            return builder;
424        }
425    
426        @NotNull
427        private static ProtoBuf.Type.Argument.Projection projection(@NotNull Variance projectionKind) {
428            switch (projectionKind) {
429                case INVARIANT:
430                    return ProtoBuf.Type.Argument.Projection.INV;
431                case IN_VARIANCE:
432                    return ProtoBuf.Type.Argument.Projection.IN;
433                case OUT_VARIANCE:
434                    return ProtoBuf.Type.Argument.Projection.OUT;
435            }
436            throw new IllegalStateException("Unknown projectionKind: " + projectionKind);
437        }
438    
439        private int getClassId(@NotNull ClassDescriptor descriptor) {
440            return nameTable.getFqNameIndex(descriptor);
441        }
442    
443        private int getTypeParameterId(@NotNull TypeParameterDescriptor descriptor) {
444            return typeParameters.intern(descriptor);
445        }
446    
447        private static boolean hasAnnotations(Annotated descriptor) {
448            return !descriptor.getAnnotations().isEmpty();
449        }
450    
451        @NotNull
452        public static <T extends DeclarationDescriptor> List<T> sort(@NotNull Collection<T> descriptors) {
453            List<T> result = new ArrayList<T>(descriptors);
454            Collections.sort(result, DESCRIPTOR_COMPARATOR);
455            return result;
456    
457        }
458    }