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