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.jvm.functions.Function1;
021    import org.jetbrains.annotations.NotNull;
022    import org.jetbrains.annotations.Nullable;
023    import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
024    import org.jetbrains.kotlin.descriptors.*;
025    import org.jetbrains.kotlin.descriptors.annotations.Annotated;
026    import org.jetbrains.kotlin.resolve.DescriptorFactory;
027    import org.jetbrains.kotlin.resolve.MemberComparator;
028    import org.jetbrains.kotlin.resolve.constants.ConstantValue;
029    import org.jetbrains.kotlin.resolve.constants.NullValue;
030    import org.jetbrains.kotlin.types.*;
031    import org.jetbrains.kotlin.utils.Interner;
032    import org.jetbrains.kotlin.utils.UtilsPackage;
033    
034    import java.io.ByteArrayOutputStream;
035    import java.io.IOException;
036    import java.io.OutputStream;
037    import java.util.ArrayList;
038    import java.util.Collection;
039    import java.util.Collections;
040    import java.util.List;
041    
042    import static org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumEntry;
043    import static org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilPackage.getSecondaryConstructors;
044    
045    public class DescriptorSerializer {
046    
047        private final StringTable stringTable;
048        private final Interner<TypeParameterDescriptor> typeParameters;
049        private final SerializerExtension extension;
050    
051        private DescriptorSerializer(StringTable stringTable, Interner<TypeParameterDescriptor> typeParameters, SerializerExtension extension) {
052            this.stringTable = stringTable;
053            this.typeParameters = typeParameters;
054            this.extension = extension;
055        }
056    
057        @NotNull
058        public byte[] serialize(@NotNull MessageLite message) {
059            try {
060                ByteArrayOutputStream result = new ByteArrayOutputStream();
061                serializeStringTable(result);
062                message.writeTo(result);
063                return result.toByteArray();
064            }
065            catch (IOException e) {
066                throw UtilsPackage.rethrow(e);
067            }
068        }
069    
070        public void serializeStringTable(@NotNull OutputStream out) throws IOException {
071            stringTable.serializeSimpleNames().writeDelimitedTo(out);
072            stringTable.serializeQualifiedNames().writeDelimitedTo(out);
073        }
074    
075        @NotNull
076        public static DescriptorSerializer createTopLevel(@NotNull SerializerExtension extension) {
077            return new DescriptorSerializer(new StringTable(extension), new Interner<TypeParameterDescriptor>(), extension);
078        }
079    
080        @NotNull
081        public static DescriptorSerializer create(@NotNull ClassDescriptor descriptor, @NotNull SerializerExtension extension) {
082            DeclarationDescriptor container = descriptor.getContainingDeclaration();
083            DescriptorSerializer parentSerializer =
084                    container instanceof ClassDescriptor
085                    ? create((ClassDescriptor) container, extension)
086                    : createTopLevel(extension);
087    
088            // Calculate type parameter ids for the outer class beforehand, as it would've had happened if we were always
089            // serializing outer classes before nested classes.
090            // Otherwise our interner can get wrong ids because we may serialize classes in any order.
091            DescriptorSerializer serializer = parentSerializer.createChildSerializer();
092            for (TypeParameterDescriptor typeParameter : descriptor.getTypeConstructor().getParameters()) {
093                serializer.typeParameters.intern(typeParameter);
094            }
095            return serializer;
096        }
097    
098        private DescriptorSerializer createChildSerializer() {
099            return new DescriptorSerializer(stringTable, new Interner<TypeParameterDescriptor>(typeParameters), extension);
100        }
101    
102        @NotNull
103        public StringTable getStringTable() {
104            return stringTable;
105        }
106    
107        @NotNull
108        public ProtoBuf.Class.Builder classProto(@NotNull ClassDescriptor classDescriptor) {
109            ProtoBuf.Class.Builder builder = ProtoBuf.Class.newBuilder();
110    
111            int flags = Flags.getClassFlags(hasAnnotations(classDescriptor), classDescriptor.getVisibility(), classDescriptor.getModality(),
112                                            classDescriptor.getKind(), classDescriptor.isInner(), classDescriptor.isCompanionObject());
113            builder.setFlags(flags);
114    
115            builder.setFqName(getClassId(classDescriptor));
116    
117            for (TypeParameterDescriptor typeParameterDescriptor : classDescriptor.getTypeConstructor().getParameters()) {
118                builder.addTypeParameter(typeParameter(typeParameterDescriptor));
119            }
120    
121            if (!KotlinBuiltIns.isSpecialClassWithNoSupertypes(classDescriptor)) {
122                // Special classes (Any, Nothing) have no supertypes
123                for (JetType supertype : classDescriptor.getTypeConstructor().getSupertypes()) {
124                    builder.addSupertype(type(supertype));
125                }
126            }
127    
128            ConstructorDescriptor primaryConstructor = classDescriptor.getUnsubstitutedPrimaryConstructor();
129            if (primaryConstructor != null) {
130                if (DescriptorFactory.isDefaultPrimaryConstructor(primaryConstructor)) {
131                    builder.setPrimaryConstructor(ProtoBuf.Class.PrimaryConstructor.getDefaultInstance());
132                }
133                else {
134                    ProtoBuf.Class.PrimaryConstructor.Builder constructorBuilder = ProtoBuf.Class.PrimaryConstructor.newBuilder();
135                    constructorBuilder.setData(callableProto(primaryConstructor));
136                    builder.setPrimaryConstructor(constructorBuilder);
137                }
138            }
139    
140            for (ConstructorDescriptor constructorDescriptor : getSecondaryConstructors(classDescriptor)) {
141                builder.addSecondaryConstructor(callableProto(constructorDescriptor));
142            }
143    
144            for (DeclarationDescriptor descriptor : sort(classDescriptor.getDefaultType().getMemberScope().getAllDescriptors())) {
145                if (descriptor instanceof CallableMemberDescriptor) {
146                    CallableMemberDescriptor member = (CallableMemberDescriptor) descriptor;
147                    if (member.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) continue;
148                    builder.addMember(callableProto(member));
149                }
150            }
151    
152            for (DeclarationDescriptor descriptor : sort(classDescriptor.getUnsubstitutedInnerClassesScope().getAllDescriptors())) {
153                int name = stringTable.getSimpleNameIndex(descriptor.getName());
154                if (isEnumEntry(descriptor)) {
155                    builder.addEnumEntry(name);
156                }
157                else {
158                    builder.addNestedClassName(name);
159                }
160            }
161    
162            ClassDescriptor companionObjectDescriptor = classDescriptor.getCompanionObjectDescriptor();
163            if (companionObjectDescriptor != null) {
164                builder.setCompanionObjectName(stringTable.getSimpleNameIndex(companionObjectDescriptor.getName()));
165            }
166    
167            extension.serializeClass(classDescriptor, builder, stringTable);
168    
169            return builder;
170        }
171    
172        @NotNull
173        public ProtoBuf.Callable.Builder callableProto(@NotNull CallableMemberDescriptor descriptor) {
174            ProtoBuf.Callable.Builder builder = ProtoBuf.Callable.newBuilder();
175    
176            DescriptorSerializer local = createChildSerializer();
177    
178            boolean hasGetter = false;
179            boolean hasSetter = false;
180            boolean hasConstant = false;
181            boolean lateInit = false;
182            boolean isConst = false;
183            boolean isOperator = false;
184    
185            if (descriptor instanceof FunctionDescriptor) {
186                isOperator = ((FunctionDescriptor) descriptor).isOperator();
187            }
188    
189            if (descriptor instanceof PropertyDescriptor) {
190                PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
191                lateInit = propertyDescriptor.isLateInit();
192                isConst = propertyDescriptor.isConst();
193    
194                int propertyFlags = Flags.getAccessorFlags(
195                        hasAnnotations(propertyDescriptor),
196                        propertyDescriptor.getVisibility(),
197                        propertyDescriptor.getModality(),
198                        false
199                );
200    
201                PropertyGetterDescriptor getter = propertyDescriptor.getGetter();
202                if (getter != null) {
203                    hasGetter = true;
204                    int accessorFlags = getAccessorFlags(getter);
205                    if (accessorFlags != propertyFlags) {
206                        builder.setGetterFlags(accessorFlags);
207                    }
208                }
209    
210                PropertySetterDescriptor setter = propertyDescriptor.getSetter();
211                if (setter != null) {
212                    hasSetter = true;
213                    int accessorFlags = getAccessorFlags(setter);
214                    if (accessorFlags != propertyFlags) {
215                        builder.setSetterFlags(accessorFlags);
216                    }
217    
218                    if (!setter.isDefault()) {
219                        for (ValueParameterDescriptor valueParameterDescriptor : setter.getValueParameters()) {
220                            builder.addValueParameter(local.valueParameter(valueParameterDescriptor));
221                        }
222                    }
223                }
224    
225                ConstantValue<?> compileTimeConstant = propertyDescriptor.getCompileTimeInitializer();
226                hasConstant = !(compileTimeConstant == null || compileTimeConstant instanceof NullValue);
227            }
228    
229            boolean hasAnnotations = (descriptor instanceof PropertyDescriptor)
230                                     ? !descriptor.getAnnotations().getAllAnnotations().isEmpty()
231                                     : hasAnnotations(descriptor);
232    
233            builder.setFlags(Flags.getCallableFlags(
234                    hasAnnotations,
235                    descriptor.getVisibility(),
236                    descriptor.getModality(),
237                    descriptor.getKind(),
238                    callableKind(descriptor),
239                    hasGetter,
240                    hasSetter,
241                    hasConstant,
242                    lateInit,
243                    isConst,
244                    isOperator
245            ));
246    
247            for (TypeParameterDescriptor typeParameterDescriptor : descriptor.getTypeParameters()) {
248                builder.addTypeParameter(local.typeParameter(typeParameterDescriptor));
249            }
250    
251            ReceiverParameterDescriptor receiverParameter = descriptor.getExtensionReceiverParameter();
252            if (receiverParameter != null) {
253                builder.setReceiverType(local.type(receiverParameter.getType()));
254            }
255    
256            builder.setName(stringTable.getSimpleNameIndex(descriptor.getName()));
257    
258            for (ValueParameterDescriptor valueParameterDescriptor : descriptor.getValueParameters()) {
259                builder.addValueParameter(local.valueParameter(valueParameterDescriptor));
260            }
261    
262            //noinspection ConstantConditions
263            builder.setReturnType(local.type(descriptor.getReturnType()));
264    
265            extension.serializeCallable(descriptor, builder, stringTable);
266    
267            return builder;
268        }
269    
270        private static int getAccessorFlags(@NotNull PropertyAccessorDescriptor accessor) {
271            return Flags.getAccessorFlags(
272                    hasAnnotations(accessor),
273                    accessor.getVisibility(),
274                    accessor.getModality(),
275                    !accessor.isDefault()
276            );
277        }
278    
279        @NotNull
280        private static ProtoBuf.Callable.CallableKind callableKind(@NotNull CallableMemberDescriptor descriptor) {
281            if (descriptor instanceof PropertyDescriptor) {
282                return ((PropertyDescriptor) descriptor).isVar() ? ProtoBuf.Callable.CallableKind.VAR : ProtoBuf.Callable.CallableKind.VAL;
283            }
284            if (descriptor instanceof ConstructorDescriptor) {
285                return ProtoBuf.Callable.CallableKind.CONSTRUCTOR;
286            }
287            assert descriptor instanceof FunctionDescriptor : "Unknown descriptor class: " + descriptor.getClass();
288            return ProtoBuf.Callable.CallableKind.FUN;
289        }
290    
291        @NotNull
292        private ProtoBuf.Callable.ValueParameter.Builder valueParameter(@NotNull ValueParameterDescriptor descriptor) {
293            ProtoBuf.Callable.ValueParameter.Builder builder = ProtoBuf.Callable.ValueParameter.newBuilder();
294    
295            builder.setFlags(Flags.getValueParameterFlags(hasAnnotations(descriptor), descriptor.declaresDefaultValue()));
296    
297            builder.setName(stringTable.getSimpleNameIndex(descriptor.getName()));
298    
299            builder.setType(type(descriptor.getType()));
300    
301            JetType varargElementType = descriptor.getVarargElementType();
302            if (varargElementType != null) {
303                builder.setVarargElementType(type(varargElementType));
304            }
305    
306            extension.serializeValueParameter(descriptor, builder, stringTable);
307    
308            return builder;
309        }
310    
311        private ProtoBuf.TypeParameter.Builder typeParameter(TypeParameterDescriptor typeParameter) {
312            ProtoBuf.TypeParameter.Builder builder = ProtoBuf.TypeParameter.newBuilder();
313    
314            builder.setId(getTypeParameterId(typeParameter));
315    
316            builder.setName(stringTable.getSimpleNameIndex(typeParameter.getName()));
317    
318            // to avoid storing a default
319            if (typeParameter.isReified()) {
320                builder.setReified(true);
321            }
322    
323            // to avoid storing a default
324            ProtoBuf.TypeParameter.Variance variance = variance(typeParameter.getVariance());
325            if (variance != ProtoBuf.TypeParameter.Variance.INV) {
326                builder.setVariance(variance);
327            }
328    
329            for (JetType upperBound : typeParameter.getUpperBounds()) {
330                builder.addUpperBound(type(upperBound));
331            }
332    
333            return builder;
334        }
335    
336        private static ProtoBuf.TypeParameter.Variance variance(Variance variance) {
337            switch (variance) {
338                case INVARIANT:
339                    return ProtoBuf.TypeParameter.Variance.INV;
340                case IN_VARIANCE:
341                    return ProtoBuf.TypeParameter.Variance.IN;
342                case OUT_VARIANCE:
343                    return ProtoBuf.TypeParameter.Variance.OUT;
344            }
345            throw new IllegalStateException("Unknown variance: " + variance);
346        }
347    
348        @NotNull
349        public ProtoBuf.Type.Builder type(@NotNull JetType type) {
350            assert !type.isError() : "Can't serialize error types: " + type; // TODO
351    
352            if (TypesPackage.isFlexible(type)) return flexibleType(type);
353    
354            ProtoBuf.Type.Builder builder = ProtoBuf.Type.newBuilder();
355    
356            builder.setConstructor(typeConstructor(type.getConstructor()));
357            setTypeConstructorFields(builder, type.getConstructor());
358    
359            for (TypeProjection projection : type.getArguments()) {
360                builder.addArgument(typeArgument(projection));
361            }
362    
363            // to avoid storing a default
364            if (type.isMarkedNullable()) {
365                builder.setNullable(true);
366            }
367    
368            extension.serializeType(type, builder, stringTable);
369    
370            return builder;
371        }
372    
373        private void setTypeConstructorFields(@NotNull ProtoBuf.Type.Builder builder, @NotNull TypeConstructor typeConstructor) {
374            ClassifierDescriptor declarationDescriptor = typeConstructor.getDeclarationDescriptor();
375    
376            assert declarationDescriptor instanceof TypeParameterDescriptor || declarationDescriptor instanceof ClassDescriptor
377                    : "Unknown declaration descriptor: " + typeConstructor;
378            if (declarationDescriptor instanceof TypeParameterDescriptor) {
379                TypeParameterDescriptor typeParameterDescriptor = (TypeParameterDescriptor) declarationDescriptor;
380                builder.setConstructorTypeParameter(getTypeParameterId(typeParameterDescriptor));
381            }
382            else {
383                ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor;
384                builder.setConstructorClassName(getClassId(classDescriptor));
385            }
386        }
387    
388        private ProtoBuf.Type.Builder flexibleType(@NotNull JetType type) {
389            Flexibility flexibility = TypesPackage.flexibility(type);
390    
391            ProtoBuf.Type.Builder builder = type(flexibility.getLowerBound());
392    
393            builder.setFlexibleTypeCapabilitiesId(stringTable.getStringIndex(flexibility.getExtraCapabilities().getId()));
394    
395            builder.setFlexibleUpperBound(type(flexibility.getUpperBound()));
396    
397            return builder;
398        }
399    
400        @NotNull
401        private ProtoBuf.Type.Argument.Builder typeArgument(@NotNull TypeProjection typeProjection) {
402            ProtoBuf.Type.Argument.Builder builder = ProtoBuf.Type.Argument.newBuilder();
403    
404            if (typeProjection.isStarProjection()) {
405                builder.setProjection(ProtoBuf.Type.Argument.Projection.STAR);
406            }
407            else {
408                ProtoBuf.Type.Argument.Projection projection = projection(typeProjection.getProjectionKind());
409    
410                // to avoid storing a default
411                if (projection != ProtoBuf.Type.Argument.Projection.INV) {
412                    builder.setProjection(projection);
413                }
414                builder.setType(type(typeProjection.getType()));
415            }
416    
417            return builder;
418        }
419    
420        @NotNull
421        private ProtoBuf.Type.Constructor.Builder typeConstructor(@NotNull TypeConstructor typeConstructor) {
422            ProtoBuf.Type.Constructor.Builder builder = ProtoBuf.Type.Constructor.newBuilder();
423    
424            ClassifierDescriptor declarationDescriptor = typeConstructor.getDeclarationDescriptor();
425    
426            assert declarationDescriptor instanceof TypeParameterDescriptor || declarationDescriptor instanceof ClassDescriptor
427                    : "Unknown declaration descriptor: " + typeConstructor;
428            if (declarationDescriptor instanceof TypeParameterDescriptor) {
429                TypeParameterDescriptor typeParameterDescriptor = (TypeParameterDescriptor) declarationDescriptor;
430                builder.setKind(ProtoBuf.Type.Constructor.Kind.TYPE_PARAMETER);
431                builder.setId(getTypeParameterId(typeParameterDescriptor));
432            }
433            else {
434                ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor;
435                //default: builder.setKind(Type.Constructor.Kind.CLASS);
436                builder.setId(getClassId(classDescriptor));
437            }
438            return builder;
439        }
440    
441        @NotNull
442        public ProtoBuf.Package.Builder packageProto(@NotNull Collection<PackageFragmentDescriptor> fragments) {
443            return packageProto(fragments, null);
444        }
445    
446        @NotNull
447        public ProtoBuf.Package.Builder packageProtoWithoutDescriptors() {
448            ProtoBuf.Package.Builder builder = ProtoBuf.Package.newBuilder();
449    
450            extension.serializePackage(Collections.<PackageFragmentDescriptor>emptyList(), builder, stringTable);
451    
452            return builder;
453        }
454    
455        @NotNull
456        public ProtoBuf.Package.Builder packageProto(@NotNull Collection<PackageFragmentDescriptor> fragments, @Nullable Function1<DeclarationDescriptor, Boolean> skip) {
457            ProtoBuf.Package.Builder builder = ProtoBuf.Package.newBuilder();
458    
459            Collection<DeclarationDescriptor> members = new ArrayList<DeclarationDescriptor>();
460            for (PackageFragmentDescriptor fragment : fragments) {
461                members.addAll(fragment.getMemberScope().getAllDescriptors());
462            }
463    
464            for (DeclarationDescriptor declaration : sort(members)) {
465                if (skip != null && skip.invoke(declaration)) continue;
466    
467                if (declaration instanceof PropertyDescriptor || declaration instanceof FunctionDescriptor) {
468                    builder.addMember(callableProto((CallableMemberDescriptor) declaration));
469                }
470            }
471    
472            extension.serializePackage(fragments, builder, stringTable);
473    
474            return builder;
475        }
476    
477        @NotNull
478        public ProtoBuf.Package.Builder packagePartProto(@NotNull Collection<DeclarationDescriptor> members) {
479            ProtoBuf.Package.Builder builder = ProtoBuf.Package.newBuilder();
480    
481            for (DeclarationDescriptor declaration : sort(members)) {
482                if (declaration instanceof PropertyDescriptor || declaration instanceof FunctionDescriptor) {
483                    builder.addMember(callableProto((CallableMemberDescriptor) declaration));
484                }
485            }
486    
487            return builder;
488        }
489    
490        @NotNull
491        private static ProtoBuf.Type.Argument.Projection projection(@NotNull Variance projectionKind) {
492            switch (projectionKind) {
493                case INVARIANT:
494                    return ProtoBuf.Type.Argument.Projection.INV;
495                case IN_VARIANCE:
496                    return ProtoBuf.Type.Argument.Projection.IN;
497                case OUT_VARIANCE:
498                    return ProtoBuf.Type.Argument.Projection.OUT;
499            }
500            throw new IllegalStateException("Unknown projectionKind: " + projectionKind);
501        }
502    
503        private int getClassId(@NotNull ClassDescriptor descriptor) {
504            return stringTable.getFqNameIndex(descriptor);
505        }
506    
507        private int getTypeParameterId(@NotNull TypeParameterDescriptor descriptor) {
508            return typeParameters.intern(descriptor);
509        }
510    
511        private static boolean hasAnnotations(Annotated descriptor) {
512            return !descriptor.getAnnotations().isEmpty();
513        }
514    
515        @NotNull
516        public static <T extends DeclarationDescriptor> List<T> sort(@NotNull Collection<T> descriptors) {
517            List<T> result = new ArrayList<T>(descriptors);
518            //NOTE: the exact comparator does matter here
519            Collections.sort(result, MemberComparator.INSTANCE);
520            return result;
521    
522        }
523    }