001    /*
002     * Copyright 2010-2015 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.kotlin.serialization;
018    
019    import org.jetbrains.annotations.NotNull;
020    import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
021    import org.jetbrains.kotlin.descriptors.*;
022    import org.jetbrains.kotlin.descriptors.annotations.Annotated;
023    import org.jetbrains.kotlin.resolve.DescriptorFactory;
024    import org.jetbrains.kotlin.resolve.MemberComparator;
025    import org.jetbrains.kotlin.types.*;
026    
027    import java.util.ArrayList;
028    import java.util.Collection;
029    import java.util.Collections;
030    import java.util.List;
031    
032    import static org.jetbrains.kotlin.resolve.DescriptorUtils.*;
033    
034    public class DescriptorSerializer {
035    
036        private final StringTable stringTable;
037        private final Interner<TypeParameterDescriptor> typeParameters;
038        private final SerializerExtension extension;
039    
040        private DescriptorSerializer(StringTable stringTable, Interner<TypeParameterDescriptor> typeParameters, SerializerExtension extension) {
041            this.stringTable = stringTable;
042            this.typeParameters = typeParameters;
043            this.extension = extension;
044        }
045    
046        @NotNull
047        public static DescriptorSerializer createTopLevel(@NotNull SerializerExtension extension) {
048            return new DescriptorSerializer(new StringTable(), new Interner<TypeParameterDescriptor>(), extension);
049        }
050    
051        @NotNull
052        public static DescriptorSerializer create(@NotNull ClassDescriptor descriptor, @NotNull SerializerExtension extension) {
053            DeclarationDescriptor container = descriptor.getContainingDeclaration();
054            DescriptorSerializer parentSerializer =
055                    container instanceof ClassDescriptor
056                    ? create((ClassDescriptor) container, extension)
057                    : createTopLevel(extension);
058    
059            // Calculate type parameter ids for the outer class beforehand, as it would've had happened if we were always
060            // serializing outer classes before nested classes.
061            // Otherwise our interner can get wrong ids because we may serialize classes in any order.
062            DescriptorSerializer serializer = parentSerializer.createChildSerializer();
063            for (TypeParameterDescriptor typeParameter : descriptor.getTypeConstructor().getParameters()) {
064                serializer.typeParameters.intern(typeParameter);
065            }
066            return serializer;
067        }
068    
069        private DescriptorSerializer createChildSerializer() {
070            return new DescriptorSerializer(stringTable, new Interner<TypeParameterDescriptor>(typeParameters), extension);
071        }
072    
073        @NotNull
074        public StringTable getStringTable() {
075            return stringTable;
076        }
077    
078        @NotNull
079        public ProtoBuf.Class.Builder classProto(@NotNull ClassDescriptor classDescriptor) {
080            ProtoBuf.Class.Builder builder = ProtoBuf.Class.newBuilder();
081    
082            int flags = Flags.getClassFlags(hasAnnotations(classDescriptor), classDescriptor.getVisibility(),
083                                            classDescriptor.getModality(), classDescriptor.getKind(), classDescriptor.isInner());
084            builder.setFlags(flags);
085    
086            // TODO extra visibility
087    
088            builder.setFqName(getClassId(classDescriptor));
089    
090            for (TypeParameterDescriptor typeParameterDescriptor : classDescriptor.getTypeConstructor().getParameters()) {
091                builder.addTypeParameter(typeParameter(typeParameterDescriptor));
092            }
093    
094            if (!KotlinBuiltIns.isSpecialClassWithNoSupertypes(classDescriptor)) {
095                // Special classes (Any, Nothing) have no supertypes
096                for (JetType supertype : classDescriptor.getTypeConstructor().getSupertypes()) {
097                    builder.addSupertype(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(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(callableProto(member));
120                }
121            }
122    
123            for (DeclarationDescriptor descriptor : sort(classDescriptor.getUnsubstitutedInnerClassesScope().getAllDescriptors())) {
124                int name = stringTable.getSimpleNameIndex(descriptor.getName());
125                if (isEnumEntry(descriptor)) {
126                    builder.addEnumEntry(name);
127                }
128                else {
129                    builder.addNestedClassName(name);
130                }
131            }
132    
133            ClassDescriptor classObject = classDescriptor.getClassObjectDescriptor();
134            if (classObject != null) {
135                builder.setClassObject(classObjectProto(classObject));
136            }
137    
138            extension.serializeClass(classDescriptor, builder, stringTable);
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(stringTable.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, stringTable);
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(stringTable.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, stringTable);
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(stringTable.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.isMarkedNullable()) {
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(stringTable.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    
377            if (typeProjection.isStarProjection()) {
378                builder.setProjection(ProtoBuf.Type.Argument.Projection.STAR);
379            }
380            else {
381                ProtoBuf.Type.Argument.Projection projection = projection(typeProjection.getProjectionKind());
382    
383                // to avoid storing a default
384                if (projection != ProtoBuf.Type.Argument.Projection.INV) {
385                    builder.setProjection(projection);
386                }
387            }
388    
389            builder.setType(type(typeProjection.getType()));
390            return builder;
391        }
392    
393        @NotNull
394        private ProtoBuf.Type.Constructor.Builder typeConstructor(@NotNull TypeConstructor typeConstructor) {
395            ProtoBuf.Type.Constructor.Builder builder = ProtoBuf.Type.Constructor.newBuilder();
396    
397            ClassifierDescriptor declarationDescriptor = typeConstructor.getDeclarationDescriptor();
398    
399            assert declarationDescriptor instanceof TypeParameterDescriptor || declarationDescriptor instanceof ClassDescriptor
400                    : "Unknown declaration descriptor: " + typeConstructor;
401            if (declarationDescriptor instanceof TypeParameterDescriptor) {
402                TypeParameterDescriptor typeParameterDescriptor = (TypeParameterDescriptor) declarationDescriptor;
403                builder.setKind(ProtoBuf.Type.Constructor.Kind.TYPE_PARAMETER);
404                builder.setId(getTypeParameterId(typeParameterDescriptor));
405            }
406            else {
407                ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor;
408                //default: builder.setKind(Type.Constructor.Kind.CLASS);
409                builder.setId(getClassId(classDescriptor));
410            }
411            return builder;
412        }
413    
414        @NotNull
415        public ProtoBuf.Package.Builder packageProto(@NotNull Collection<PackageFragmentDescriptor> fragments) {
416            ProtoBuf.Package.Builder builder = ProtoBuf.Package.newBuilder();
417    
418            Collection<DeclarationDescriptor> members = new ArrayList<DeclarationDescriptor>();
419            for (PackageFragmentDescriptor fragment : fragments) {
420                members.addAll(fragment.getMemberScope().getAllDescriptors());
421            }
422    
423            for (DeclarationDescriptor declaration : sort(members)) {
424                if (declaration instanceof PropertyDescriptor || declaration instanceof FunctionDescriptor) {
425                    builder.addMember(callableProto((CallableMemberDescriptor) declaration));
426                }
427            }
428    
429            extension.serializePackage(fragments, builder, stringTable);
430    
431            return builder;
432        }
433    
434        @NotNull
435        private static ProtoBuf.Type.Argument.Projection projection(@NotNull Variance projectionKind) {
436            switch (projectionKind) {
437                case INVARIANT:
438                    return ProtoBuf.Type.Argument.Projection.INV;
439                case IN_VARIANCE:
440                    return ProtoBuf.Type.Argument.Projection.IN;
441                case OUT_VARIANCE:
442                    return ProtoBuf.Type.Argument.Projection.OUT;
443            }
444            throw new IllegalStateException("Unknown projectionKind: " + projectionKind);
445        }
446    
447        private int getClassId(@NotNull ClassDescriptor descriptor) {
448            return stringTable.getFqNameIndex(descriptor);
449        }
450    
451        private int getTypeParameterId(@NotNull TypeParameterDescriptor descriptor) {
452            return typeParameters.intern(descriptor);
453        }
454    
455        private static boolean hasAnnotations(Annotated descriptor) {
456            return !descriptor.getAnnotations().isEmpty();
457        }
458    
459        @NotNull
460        public static <T extends DeclarationDescriptor> List<T> sort(@NotNull Collection<T> descriptors) {
461            List<T> result = new ArrayList<T>(descriptors);
462            //NOTE: the exact comparator does matter here
463            Collections.sort(result, MemberComparator.INSTANCE);
464            return result;
465    
466        }
467    }