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 com.google.protobuf.MessageLite;
020    import kotlin.CollectionsKt;
021    import kotlin.jvm.functions.Function1;
022    import org.jetbrains.annotations.NotNull;
023    import org.jetbrains.annotations.Nullable;
024    import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
025    import org.jetbrains.kotlin.descriptors.*;
026    import org.jetbrains.kotlin.descriptors.annotations.Annotated;
027    import org.jetbrains.kotlin.name.Name;
028    import org.jetbrains.kotlin.resolve.MemberComparator;
029    import org.jetbrains.kotlin.resolve.constants.ConstantValue;
030    import org.jetbrains.kotlin.resolve.constants.NullValue;
031    import org.jetbrains.kotlin.types.*;
032    import org.jetbrains.kotlin.utils.ExceptionUtilsKt;
033    import org.jetbrains.kotlin.utils.Interner;
034    
035    import java.io.ByteArrayOutputStream;
036    import java.io.IOException;
037    import java.util.*;
038    
039    import static org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumEntry;
040    
041    public class DescriptorSerializer {
042        private final DeclarationDescriptor containingDeclaration;
043        private final Interner<TypeParameterDescriptor> typeParameters;
044        private final SerializerExtension extension;
045        private final MutableTypeTable typeTable;
046        private final boolean serializeTypeTableToFunction;
047    
048        private DescriptorSerializer(
049                @Nullable DeclarationDescriptor containingDeclaration,
050                @NotNull Interner<TypeParameterDescriptor> typeParameters,
051                @NotNull SerializerExtension extension,
052                @NotNull MutableTypeTable typeTable,
053                boolean serializeTypeTableToFunction
054        ) {
055            this.containingDeclaration = containingDeclaration;
056            this.typeParameters = typeParameters;
057            this.extension = extension;
058            this.typeTable = typeTable;
059            this.serializeTypeTableToFunction = serializeTypeTableToFunction;
060        }
061    
062        @NotNull
063        public byte[] serialize(@NotNull MessageLite message) {
064            try {
065                ByteArrayOutputStream result = new ByteArrayOutputStream();
066                getStringTable().serializeTo(result);
067                message.writeTo(result);
068                return result.toByteArray();
069            }
070            catch (IOException e) {
071                throw ExceptionUtilsKt.rethrow(e);
072            }
073        }
074    
075        @NotNull
076        public static DescriptorSerializer createTopLevel(@NotNull SerializerExtension extension) {
077            return new DescriptorSerializer(null, new Interner<TypeParameterDescriptor>(), extension, new MutableTypeTable(), false);
078        }
079    
080        @NotNull
081        public static DescriptorSerializer createForLambda(@NotNull SerializerExtension extension) {
082            return new DescriptorSerializer(null, new Interner<TypeParameterDescriptor>(), extension, new MutableTypeTable(), true);
083        }
084    
085        @NotNull
086        public static DescriptorSerializer create(@NotNull ClassDescriptor descriptor, @NotNull SerializerExtension extension) {
087            DeclarationDescriptor container = descriptor.getContainingDeclaration();
088            DescriptorSerializer parentSerializer =
089                    container instanceof ClassDescriptor
090                    ? create((ClassDescriptor) container, extension)
091                    : createTopLevel(extension);
092    
093            // Calculate type parameter ids for the outer class beforehand, as it would've had happened if we were always
094            // serializing outer classes before nested classes.
095            // Otherwise our interner can get wrong ids because we may serialize classes in any order.
096            DescriptorSerializer serializer = new DescriptorSerializer(
097                    descriptor,
098                    new Interner<TypeParameterDescriptor>(parentSerializer.typeParameters),
099                    parentSerializer.extension,
100                    new MutableTypeTable(),
101                    false
102            );
103            for (TypeParameterDescriptor typeParameter : descriptor.getTypeConstructor().getParameters()) {
104                serializer.typeParameters.intern(typeParameter);
105            }
106            return serializer;
107        }
108    
109        @NotNull
110        private DescriptorSerializer createChildSerializer(@NotNull CallableDescriptor callable) {
111            return new DescriptorSerializer(callable, new Interner<TypeParameterDescriptor>(typeParameters), extension, typeTable, false);
112        }
113    
114        @NotNull
115        public StringTable getStringTable() {
116            return extension.getStringTable();
117        }
118    
119        private boolean useTypeTable() {
120            return extension.shouldUseTypeTable();
121        }
122    
123        @NotNull
124        public ProtoBuf.Class.Builder classProto(@NotNull ClassDescriptor classDescriptor) {
125            ProtoBuf.Class.Builder builder = ProtoBuf.Class.newBuilder();
126    
127            int flags = Flags.getClassFlags(hasAnnotations(classDescriptor), classDescriptor.getVisibility(), classDescriptor.getModality(),
128                                            classDescriptor.getKind(), classDescriptor.isInner(), classDescriptor.isCompanionObject(),
129                                            classDescriptor.isData());
130            if (flags != builder.getFlags()) {
131                builder.setFlags(flags);
132            }
133    
134            builder.setFqName(getClassId(classDescriptor));
135    
136            for (TypeParameterDescriptor typeParameterDescriptor : classDescriptor.getTypeConstructor().getParameters()) {
137                builder.addTypeParameter(typeParameter(typeParameterDescriptor));
138            }
139    
140            if (!KotlinBuiltIns.isSpecialClassWithNoSupertypes(classDescriptor)) {
141                // Special classes (Any, Nothing) have no supertypes
142                for (KotlinType supertype : classDescriptor.getTypeConstructor().getSupertypes()) {
143                    if (useTypeTable()) {
144                        builder.addSupertypeId(typeId(supertype));
145                    }
146                    else {
147                        builder.addSupertype(type(supertype));
148                    }
149                }
150            }
151    
152            for (ConstructorDescriptor descriptor : classDescriptor.getConstructors()) {
153                builder.addConstructor(constructorProto(descriptor));
154            }
155    
156            for (DeclarationDescriptor descriptor : sort(classDescriptor.getDefaultType().getMemberScope().getAllDescriptors())) {
157                if (descriptor instanceof CallableMemberDescriptor) {
158                    CallableMemberDescriptor member = (CallableMemberDescriptor) descriptor;
159                    if (member.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) continue;
160    
161                    if (descriptor instanceof PropertyDescriptor) {
162                        builder.addProperty(propertyProto((PropertyDescriptor) descriptor));
163                    }
164                    else if (descriptor instanceof FunctionDescriptor) {
165                        builder.addFunction(functionProto((FunctionDescriptor) descriptor));
166                    }
167                }
168            }
169    
170            for (DeclarationDescriptor descriptor : sort(classDescriptor.getUnsubstitutedInnerClassesScope().getAllDescriptors())) {
171                int name = getSimpleNameIndex(descriptor.getName());
172                if (isEnumEntry(descriptor)) {
173                    builder.addEnumEntry(name);
174                }
175                else {
176                    builder.addNestedClassName(name);
177                }
178            }
179    
180            ClassDescriptor companionObjectDescriptor = classDescriptor.getCompanionObjectDescriptor();
181            if (companionObjectDescriptor != null) {
182                builder.setCompanionObjectName(getSimpleNameIndex(companionObjectDescriptor.getName()));
183            }
184    
185            ProtoBuf.TypeTable typeTableProto = typeTable.serialize();
186            if (typeTableProto != null) {
187                builder.setTypeTable(typeTableProto);
188            }
189    
190            extension.serializeClass(classDescriptor, builder);
191    
192            return builder;
193        }
194    
195        @NotNull
196        public ProtoBuf.Property.Builder propertyProto(@NotNull PropertyDescriptor descriptor) {
197            ProtoBuf.Property.Builder builder = ProtoBuf.Property.newBuilder();
198    
199            DescriptorSerializer local = createChildSerializer(descriptor);
200    
201            boolean hasGetter = false;
202            boolean hasSetter = false;
203            boolean lateInit = descriptor.isLateInit();
204            boolean isConst = descriptor.isConst();
205    
206            ConstantValue<?> compileTimeConstant = descriptor.getCompileTimeInitializer();
207            boolean hasConstant = !(compileTimeConstant == null || compileTimeConstant instanceof NullValue);
208    
209            boolean hasAnnotations = !descriptor.getAnnotations().getAllAnnotations().isEmpty();
210    
211            int propertyFlags = Flags.getAccessorFlags(
212                    hasAnnotations,
213                    descriptor.getVisibility(),
214                    descriptor.getModality(),
215                    false,
216                    false
217            );
218    
219            PropertyGetterDescriptor getter = descriptor.getGetter();
220            if (getter != null) {
221                hasGetter = true;
222                int accessorFlags = getAccessorFlags(getter);
223                if (accessorFlags != propertyFlags) {
224                    builder.setGetterFlags(accessorFlags);
225                }
226            }
227    
228            PropertySetterDescriptor setter = descriptor.getSetter();
229            if (setter != null) {
230                hasSetter = true;
231                int accessorFlags = getAccessorFlags(setter);
232                if (accessorFlags != propertyFlags) {
233                    builder.setSetterFlags(accessorFlags);
234                }
235    
236                if (!setter.isDefault()) {
237                    DescriptorSerializer setterLocal = local.createChildSerializer(setter);
238                    for (ValueParameterDescriptor valueParameterDescriptor : setter.getValueParameters()) {
239                        builder.setSetterValueParameter(setterLocal.valueParameter(valueParameterDescriptor));
240                    }
241                }
242            }
243    
244            int flags = Flags.getPropertyFlags(
245                    hasAnnotations, descriptor.getVisibility(), descriptor.getModality(), descriptor.getKind(), descriptor.isVar(),
246                    hasGetter, hasSetter, hasConstant, isConst, lateInit
247            );
248            if (flags != builder.getFlags()) {
249                builder.setFlags(flags);
250            }
251    
252            builder.setName(getSimpleNameIndex(descriptor.getName()));
253    
254            if (useTypeTable()) {
255                builder.setReturnTypeId(local.typeId(descriptor.getType()));
256            }
257            else {
258                builder.setReturnType(local.type(descriptor.getType()));
259            }
260    
261            for (TypeParameterDescriptor typeParameterDescriptor : descriptor.getTypeParameters()) {
262                builder.addTypeParameter(local.typeParameter(typeParameterDescriptor));
263            }
264    
265            ReceiverParameterDescriptor receiverParameter = descriptor.getExtensionReceiverParameter();
266            if (receiverParameter != null) {
267                if (useTypeTable()) {
268                    builder.setReceiverTypeId(local.typeId(receiverParameter.getType()));
269                }
270                else {
271                    builder.setReceiverType(local.type(receiverParameter.getType()));
272                }
273            }
274    
275            extension.serializeProperty(descriptor, builder);
276    
277            return builder;
278        }
279    
280        @NotNull
281        public ProtoBuf.Function.Builder functionProto(@NotNull FunctionDescriptor descriptor) {
282            ProtoBuf.Function.Builder builder = ProtoBuf.Function.newBuilder();
283    
284            DescriptorSerializer local = createChildSerializer(descriptor);
285    
286            int flags = Flags.getFunctionFlags(
287                    hasAnnotations(descriptor), descriptor.getVisibility(), descriptor.getModality(), descriptor.getKind(),
288                    descriptor.isOperator(), descriptor.isInfix(), descriptor.isInline(), descriptor.isTailrec(),
289                    descriptor.isExternal()
290            );
291            if (flags != builder.getFlags()) {
292                builder.setFlags(flags);
293            }
294    
295            builder.setName(getSimpleNameIndex(descriptor.getName()));
296    
297            if (useTypeTable()) {
298                //noinspection ConstantConditions
299                builder.setReturnTypeId(local.typeId(descriptor.getReturnType()));
300            }
301            else {
302                //noinspection ConstantConditions
303                builder.setReturnType(local.type(descriptor.getReturnType()));
304            }
305    
306            for (TypeParameterDescriptor typeParameterDescriptor : descriptor.getTypeParameters()) {
307                builder.addTypeParameter(local.typeParameter(typeParameterDescriptor));
308            }
309    
310            ReceiverParameterDescriptor receiverParameter = descriptor.getExtensionReceiverParameter();
311            if (receiverParameter != null) {
312                if (useTypeTable()) {
313                    builder.setReceiverTypeId(local.typeId(receiverParameter.getType()));
314                }
315                else {
316                    builder.setReceiverType(local.type(receiverParameter.getType()));
317                }
318            }
319    
320            for (ValueParameterDescriptor valueParameterDescriptor : descriptor.getValueParameters()) {
321                builder.addValueParameter(local.valueParameter(valueParameterDescriptor));
322            }
323    
324            if (serializeTypeTableToFunction) {
325                ProtoBuf.TypeTable typeTableProto = typeTable.serialize();
326                if (typeTableProto != null) {
327                    builder.setTypeTable(typeTableProto);
328                }
329            }
330    
331            extension.serializeFunction(descriptor, builder);
332    
333            return builder;
334        }
335    
336        @NotNull
337        public ProtoBuf.Constructor.Builder constructorProto(@NotNull ConstructorDescriptor descriptor) {
338            ProtoBuf.Constructor.Builder builder = ProtoBuf.Constructor.newBuilder();
339    
340            DescriptorSerializer local = createChildSerializer(descriptor);
341    
342            int flags = Flags.getConstructorFlags(hasAnnotations(descriptor), descriptor.getVisibility(), !descriptor.isPrimary());
343            if (flags != builder.getFlags()) {
344                builder.setFlags(flags);
345            }
346    
347            for (ValueParameterDescriptor valueParameterDescriptor : descriptor.getValueParameters()) {
348                builder.addValueParameter(local.valueParameter(valueParameterDescriptor));
349            }
350    
351            extension.serializeConstructor(descriptor, builder);
352    
353            return builder;
354        }
355    
356        private static int getAccessorFlags(@NotNull PropertyAccessorDescriptor accessor) {
357            return Flags.getAccessorFlags(
358                    hasAnnotations(accessor),
359                    accessor.getVisibility(),
360                    accessor.getModality(),
361                    !accessor.isDefault(),
362                    accessor.isExternal()
363            );
364        }
365    
366        @NotNull
367        private ProtoBuf.ValueParameter.Builder valueParameter(@NotNull ValueParameterDescriptor descriptor) {
368            ProtoBuf.ValueParameter.Builder builder = ProtoBuf.ValueParameter.newBuilder();
369    
370            int flags = Flags.getValueParameterFlags(hasAnnotations(descriptor), descriptor.declaresDefaultValue(),
371                                                     descriptor.isCrossinline(), descriptor.isNoinline());
372            if (flags != builder.getFlags()) {
373                builder.setFlags(flags);
374            }
375    
376            builder.setName(getSimpleNameIndex(descriptor.getName()));
377    
378            if (useTypeTable()) {
379                builder.setTypeId(typeId(descriptor.getType()));
380            }
381            else {
382                builder.setType(type(descriptor.getType()));
383            }
384    
385            KotlinType varargElementType = descriptor.getVarargElementType();
386            if (varargElementType != null) {
387                if (useTypeTable()) {
388                    builder.setVarargElementTypeId(typeId(varargElementType));
389                }
390                else {
391                    builder.setVarargElementType(type(varargElementType));
392                }
393            }
394    
395            extension.serializeValueParameter(descriptor, builder);
396    
397            return builder;
398        }
399    
400        private ProtoBuf.TypeParameter.Builder typeParameter(TypeParameterDescriptor typeParameter) {
401            ProtoBuf.TypeParameter.Builder builder = ProtoBuf.TypeParameter.newBuilder();
402    
403            builder.setId(getTypeParameterId(typeParameter));
404    
405            builder.setName(getSimpleNameIndex(typeParameter.getName()));
406    
407            if (typeParameter.isReified() != builder.getReified()) {
408                builder.setReified(typeParameter.isReified());
409            }
410    
411            ProtoBuf.TypeParameter.Variance variance = variance(typeParameter.getVariance());
412            if (variance != builder.getVariance()) {
413                builder.setVariance(variance);
414            }
415            extension.serializeTypeParameter(typeParameter, builder);
416    
417            Set<KotlinType> upperBounds = typeParameter.getUpperBounds();
418            if (upperBounds.size() == 1 && KotlinBuiltIns.isDefaultBound(CollectionsKt.single(upperBounds))) return builder;
419    
420            for (KotlinType upperBound : upperBounds) {
421                if (useTypeTable()) {
422                    builder.addUpperBoundId(typeId(upperBound));
423                }
424                else {
425                    builder.addUpperBound(type(upperBound));
426                }
427            }
428    
429            return builder;
430        }
431    
432        private static ProtoBuf.TypeParameter.Variance variance(Variance variance) {
433            switch (variance) {
434                case INVARIANT:
435                    return ProtoBuf.TypeParameter.Variance.INV;
436                case IN_VARIANCE:
437                    return ProtoBuf.TypeParameter.Variance.IN;
438                case OUT_VARIANCE:
439                    return ProtoBuf.TypeParameter.Variance.OUT;
440            }
441            throw new IllegalStateException("Unknown variance: " + variance);
442        }
443    
444        private int typeId(@NotNull KotlinType type) {
445            return typeTable.get(type(type));
446        }
447    
448        @NotNull
449        private ProtoBuf.Type.Builder type(@NotNull KotlinType type) {
450            assert !type.isError() : "Can't serialize error types: " + type; // TODO
451    
452            if (FlexibleTypesKt.isFlexible(type)) {
453                Flexibility flexibility = FlexibleTypesKt.flexibility(type);
454    
455                ProtoBuf.Type.Builder lowerBound = type(flexibility.getLowerBound());
456                lowerBound.setFlexibleTypeCapabilitiesId(getStringTable().getStringIndex(flexibility.getExtraCapabilities().getId()));
457                if (useTypeTable()) {
458                    lowerBound.setFlexibleUpperBoundId(typeId(flexibility.getUpperBound()));
459                }
460                else {
461                    lowerBound.setFlexibleUpperBound(type(flexibility.getUpperBound()));
462                }
463                return lowerBound;
464            }
465    
466            ProtoBuf.Type.Builder builder = ProtoBuf.Type.newBuilder();
467    
468            ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor();
469            if (descriptor instanceof ClassDescriptor) {
470                builder.setClassName(getClassId((ClassDescriptor) descriptor));
471            }
472            if (descriptor instanceof TypeParameterDescriptor) {
473                TypeParameterDescriptor typeParameter = (TypeParameterDescriptor) descriptor;
474                if (typeParameter.getContainingDeclaration() == containingDeclaration) {
475                    builder.setTypeParameterName(getSimpleNameIndex(typeParameter.getName()));
476                }
477                else {
478                    builder.setTypeParameter(getTypeParameterId(typeParameter));
479                }
480            }
481    
482            for (TypeProjection projection : type.getArguments()) {
483                builder.addArgument(typeArgument(projection));
484            }
485    
486            if (type.isMarkedNullable() != builder.getNullable()) {
487                builder.setNullable(type.isMarkedNullable());
488            }
489    
490            extension.serializeType(type, builder);
491    
492            return builder;
493        }
494    
495        @NotNull
496        private ProtoBuf.Type.Argument.Builder typeArgument(@NotNull TypeProjection typeProjection) {
497            ProtoBuf.Type.Argument.Builder builder = ProtoBuf.Type.Argument.newBuilder();
498    
499            if (typeProjection.isStarProjection()) {
500                builder.setProjection(ProtoBuf.Type.Argument.Projection.STAR);
501            }
502            else {
503                ProtoBuf.Type.Argument.Projection projection = projection(typeProjection.getProjectionKind());
504    
505                if (projection != builder.getProjection()) {
506                    builder.setProjection(projection);
507                }
508    
509                if (useTypeTable()) {
510                    builder.setTypeId(typeId(typeProjection.getType()));
511                }
512                else {
513                    builder.setType(type(typeProjection.getType()));
514                }
515            }
516    
517            return builder;
518        }
519    
520        @NotNull
521        public ProtoBuf.Package.Builder packageProto(@NotNull Collection<PackageFragmentDescriptor> fragments) {
522            return packageProto(fragments, null);
523        }
524    
525        @NotNull
526        public ProtoBuf.Package.Builder packageProtoWithoutDescriptors() {
527            ProtoBuf.Package.Builder builder = ProtoBuf.Package.newBuilder();
528    
529            extension.serializePackage(Collections.<PackageFragmentDescriptor>emptyList(), builder);
530    
531            return builder;
532        }
533    
534        @NotNull
535        public ProtoBuf.Package.Builder packageProto(@NotNull Collection<PackageFragmentDescriptor> fragments, @Nullable Function1<DeclarationDescriptor, Boolean> skip) {
536            ProtoBuf.Package.Builder builder = ProtoBuf.Package.newBuilder();
537    
538            Collection<DeclarationDescriptor> members = new ArrayList<DeclarationDescriptor>();
539            for (PackageFragmentDescriptor fragment : fragments) {
540                members.addAll(fragment.getMemberScope().getAllDescriptors());
541            }
542    
543            for (DeclarationDescriptor declaration : sort(members)) {
544                if (skip != null && skip.invoke(declaration)) continue;
545    
546                if (declaration instanceof PropertyDescriptor) {
547                    builder.addProperty(propertyProto((PropertyDescriptor) declaration));
548                }
549                else if (declaration instanceof FunctionDescriptor) {
550                    builder.addFunction(functionProto((FunctionDescriptor) declaration));
551                }
552            }
553    
554            ProtoBuf.TypeTable typeTableProto = typeTable.serialize();
555            if (typeTableProto != null) {
556                builder.setTypeTable(typeTableProto);
557            }
558    
559            extension.serializePackage(fragments, builder);
560    
561            return builder;
562        }
563    
564        @NotNull
565        public ProtoBuf.Package.Builder packagePartProto(@NotNull Collection<DeclarationDescriptor> members) {
566            ProtoBuf.Package.Builder builder = ProtoBuf.Package.newBuilder();
567    
568            for (DeclarationDescriptor declaration : sort(members)) {
569                if (declaration instanceof PropertyDescriptor) {
570                    builder.addProperty(propertyProto((PropertyDescriptor) declaration));
571                }
572                else if (declaration instanceof FunctionDescriptor) {
573                    builder.addFunction(functionProto((FunctionDescriptor) declaration));
574                }
575            }
576    
577            ProtoBuf.TypeTable typeTableProto = typeTable.serialize();
578            if (typeTableProto != null) {
579                builder.setTypeTable(typeTableProto);
580            }
581    
582            return builder;
583        }
584    
585        @NotNull
586        private static ProtoBuf.Type.Argument.Projection projection(@NotNull Variance projectionKind) {
587            switch (projectionKind) {
588                case INVARIANT:
589                    return ProtoBuf.Type.Argument.Projection.INV;
590                case IN_VARIANCE:
591                    return ProtoBuf.Type.Argument.Projection.IN;
592                case OUT_VARIANCE:
593                    return ProtoBuf.Type.Argument.Projection.OUT;
594            }
595            throw new IllegalStateException("Unknown projectionKind: " + projectionKind);
596        }
597    
598        private int getClassId(@NotNull ClassDescriptor descriptor) {
599            return getStringTable().getFqNameIndex(descriptor);
600        }
601    
602        private int getSimpleNameIndex(@NotNull Name name) {
603            return getStringTable().getStringIndex(name.asString());
604        }
605    
606        private int getTypeParameterId(@NotNull TypeParameterDescriptor descriptor) {
607            return typeParameters.intern(descriptor);
608        }
609    
610        private static boolean hasAnnotations(Annotated descriptor) {
611            return !descriptor.getAnnotations().isEmpty();
612        }
613    
614        @NotNull
615        public static <T extends DeclarationDescriptor> List<T> sort(@NotNull Collection<T> descriptors) {
616            List<T> result = new ArrayList<T>(descriptors);
617            //NOTE: the exact comparator does matter here
618            Collections.sort(result, MemberComparator.INSTANCE);
619            return result;
620    
621        }
622    }