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.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.CompileTimeConstant;
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            if (descriptor instanceof PropertyDescriptor) {
158                PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
159    
160                int propertyFlags = Flags.getAccessorFlags(
161                        hasAnnotations(propertyDescriptor),
162                        propertyDescriptor.getVisibility(),
163                        propertyDescriptor.getModality(),
164                        false
165                );
166    
167                PropertyGetterDescriptor getter = propertyDescriptor.getGetter();
168                if (getter != null) {
169                    hasGetter = true;
170                    int accessorFlags = getAccessorFlags(getter);
171                    if (accessorFlags != propertyFlags) {
172                        builder.setGetterFlags(accessorFlags);
173                    }
174                }
175    
176                PropertySetterDescriptor setter = propertyDescriptor.getSetter();
177                if (setter != null) {
178                    hasSetter = true;
179                    int accessorFlags = getAccessorFlags(setter);
180                    if (accessorFlags != propertyFlags) {
181                        builder.setSetterFlags(accessorFlags);
182                    }
183    
184                    if (!setter.isDefault()) {
185                        for (ValueParameterDescriptor valueParameterDescriptor : setter.getValueParameters()) {
186                            builder.addValueParameter(local.valueParameter(valueParameterDescriptor));
187                        }
188                    }
189                }
190    
191                CompileTimeConstant<?> compileTimeConstant = propertyDescriptor.getCompileTimeInitializer();
192                hasConstant = !(compileTimeConstant == null || compileTimeConstant instanceof NullValue);
193            }
194    
195            builder.setFlags(Flags.getCallableFlags(
196                    hasAnnotations(descriptor),
197                    descriptor.getVisibility(),
198                    descriptor.getModality(),
199                    descriptor.getKind(),
200                    callableKind(descriptor),
201                    hasGetter,
202                    hasSetter,
203                    hasConstant
204            ));
205    
206            for (TypeParameterDescriptor typeParameterDescriptor : descriptor.getTypeParameters()) {
207                builder.addTypeParameter(local.typeParameter(typeParameterDescriptor));
208            }
209    
210            ReceiverParameterDescriptor receiverParameter = descriptor.getExtensionReceiverParameter();
211            if (receiverParameter != null) {
212                builder.setReceiverType(local.type(receiverParameter.getType()));
213            }
214    
215            builder.setName(stringTable.getSimpleNameIndex(descriptor.getName()));
216    
217            for (ValueParameterDescriptor valueParameterDescriptor : descriptor.getValueParameters()) {
218                builder.addValueParameter(local.valueParameter(valueParameterDescriptor));
219            }
220    
221            //noinspection ConstantConditions
222            builder.setReturnType(local.type(descriptor.getReturnType()));
223    
224            extension.serializeCallable(descriptor, builder, stringTable);
225    
226            return builder;
227        }
228    
229        private static int getAccessorFlags(@NotNull PropertyAccessorDescriptor accessor) {
230            return Flags.getAccessorFlags(
231                    hasAnnotations(accessor),
232                    accessor.getVisibility(),
233                    accessor.getModality(),
234                    !accessor.isDefault()
235            );
236        }
237    
238        @NotNull
239        private static ProtoBuf.Callable.CallableKind callableKind(@NotNull CallableMemberDescriptor descriptor) {
240            if (descriptor instanceof PropertyDescriptor) {
241                return ((PropertyDescriptor) descriptor).isVar() ? ProtoBuf.Callable.CallableKind.VAR : ProtoBuf.Callable.CallableKind.VAL;
242            }
243            if (descriptor instanceof ConstructorDescriptor) {
244                return ProtoBuf.Callable.CallableKind.CONSTRUCTOR;
245            }
246            assert descriptor instanceof FunctionDescriptor : "Unknown descriptor class: " + descriptor.getClass();
247            return ProtoBuf.Callable.CallableKind.FUN;
248        }
249    
250        @NotNull
251        private ProtoBuf.Callable.ValueParameter.Builder valueParameter(@NotNull ValueParameterDescriptor descriptor) {
252            ProtoBuf.Callable.ValueParameter.Builder builder = ProtoBuf.Callable.ValueParameter.newBuilder();
253    
254            builder.setFlags(Flags.getValueParameterFlags(hasAnnotations(descriptor), descriptor.declaresDefaultValue()));
255    
256            builder.setName(stringTable.getSimpleNameIndex(descriptor.getName()));
257    
258            builder.setType(type(descriptor.getType()));
259    
260            JetType varargElementType = descriptor.getVarargElementType();
261            if (varargElementType != null) {
262                builder.setVarargElementType(type(varargElementType));
263            }
264    
265            extension.serializeValueParameter(descriptor, builder, stringTable);
266    
267            return builder;
268        }
269    
270        private ProtoBuf.TypeParameter.Builder typeParameter(TypeParameterDescriptor typeParameter) {
271            ProtoBuf.TypeParameter.Builder builder = ProtoBuf.TypeParameter.newBuilder();
272    
273            builder.setId(getTypeParameterId(typeParameter));
274    
275            builder.setName(stringTable.getSimpleNameIndex(typeParameter.getName()));
276    
277            // to avoid storing a default
278            if (typeParameter.isReified()) {
279                builder.setReified(true);
280            }
281    
282            // to avoid storing a default
283            ProtoBuf.TypeParameter.Variance variance = variance(typeParameter.getVariance());
284            if (variance != ProtoBuf.TypeParameter.Variance.INV) {
285                builder.setVariance(variance);
286            }
287    
288            for (JetType upperBound : typeParameter.getUpperBounds()) {
289                builder.addUpperBound(type(upperBound));
290            }
291    
292            return builder;
293        }
294    
295        private static ProtoBuf.TypeParameter.Variance variance(Variance variance) {
296            switch (variance) {
297                case INVARIANT:
298                    return ProtoBuf.TypeParameter.Variance.INV;
299                case IN_VARIANCE:
300                    return ProtoBuf.TypeParameter.Variance.IN;
301                case OUT_VARIANCE:
302                    return  ProtoBuf.TypeParameter.Variance.OUT;
303            }
304            throw new IllegalStateException("Unknown variance: " + variance);
305        }
306    
307        @NotNull
308        public ProtoBuf.Type.Builder type(@NotNull JetType type) {
309            assert !type.isError() : "Can't serialize error types: " + type; // TODO
310    
311            if (TypesPackage.isFlexible(type)) return flexibleType(type);
312    
313            ProtoBuf.Type.Builder builder = ProtoBuf.Type.newBuilder();
314    
315            builder.setConstructor(typeConstructor(type.getConstructor()));
316    
317            for (TypeProjection projection : type.getArguments()) {
318                builder.addArgument(typeArgument(projection));
319            }
320    
321            // to avoid storing a default
322            if (type.isMarkedNullable()) {
323                builder.setNullable(true);
324            }
325    
326            return builder;
327        }
328    
329        private ProtoBuf.Type.Builder flexibleType(@NotNull JetType type) {
330            Flexibility flexibility = TypesPackage.flexibility(type);
331    
332            ProtoBuf.Type.Builder builder = type(flexibility.getLowerBound());
333    
334            builder.setFlexibleTypeCapabilitiesId(stringTable.getStringIndex(flexibility.getExtraCapabilities().getId()));
335    
336            builder.setFlexibleUpperBound(type(flexibility.getUpperBound()));
337    
338            return builder;
339        }
340    
341        @NotNull
342        private ProtoBuf.Type.Argument.Builder typeArgument(@NotNull TypeProjection typeProjection) {
343            ProtoBuf.Type.Argument.Builder builder = ProtoBuf.Type.Argument.newBuilder();
344    
345            if (typeProjection.isStarProjection()) {
346                builder.setProjection(ProtoBuf.Type.Argument.Projection.STAR);
347            }
348            else {
349                ProtoBuf.Type.Argument.Projection projection = projection(typeProjection.getProjectionKind());
350    
351                // to avoid storing a default
352                if (projection != ProtoBuf.Type.Argument.Projection.INV) {
353                    builder.setProjection(projection);
354                }
355                builder.setType(type(typeProjection.getType()));
356            }
357    
358            return builder;
359        }
360    
361        @NotNull
362        private ProtoBuf.Type.Constructor.Builder typeConstructor(@NotNull TypeConstructor typeConstructor) {
363            ProtoBuf.Type.Constructor.Builder builder = ProtoBuf.Type.Constructor.newBuilder();
364    
365            ClassifierDescriptor declarationDescriptor = typeConstructor.getDeclarationDescriptor();
366    
367            assert declarationDescriptor instanceof TypeParameterDescriptor || declarationDescriptor instanceof ClassDescriptor
368                    : "Unknown declaration descriptor: " + typeConstructor;
369            if (declarationDescriptor instanceof TypeParameterDescriptor) {
370                TypeParameterDescriptor typeParameterDescriptor = (TypeParameterDescriptor) declarationDescriptor;
371                builder.setKind(ProtoBuf.Type.Constructor.Kind.TYPE_PARAMETER);
372                builder.setId(getTypeParameterId(typeParameterDescriptor));
373            }
374            else {
375                ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor;
376                //default: builder.setKind(Type.Constructor.Kind.CLASS);
377                builder.setId(getClassId(classDescriptor));
378            }
379            return builder;
380        }
381    
382        @NotNull
383        public ProtoBuf.Package.Builder packageProto(@NotNull Collection<PackageFragmentDescriptor> fragments) {
384            return packageProto(fragments, null);
385        }
386    
387        @NotNull
388        public ProtoBuf.Package.Builder packageProto(@NotNull Collection<PackageFragmentDescriptor> fragments, @Nullable Function1<DeclarationDescriptor, Boolean> skip) {
389            ProtoBuf.Package.Builder builder = ProtoBuf.Package.newBuilder();
390    
391            Collection<DeclarationDescriptor> members = new ArrayList<DeclarationDescriptor>();
392            for (PackageFragmentDescriptor fragment : fragments) {
393                members.addAll(fragment.getMemberScope().getAllDescriptors());
394            }
395    
396            for (DeclarationDescriptor declaration : sort(members)) {
397                if (skip != null && skip.invoke(declaration)) continue;
398    
399                if (declaration instanceof PropertyDescriptor || declaration instanceof FunctionDescriptor) {
400                    builder.addMember(callableProto((CallableMemberDescriptor) declaration));
401                }
402            }
403    
404            extension.serializePackage(fragments, builder, stringTable);
405    
406            return builder;
407        }
408    
409        @NotNull
410        private static ProtoBuf.Type.Argument.Projection projection(@NotNull Variance projectionKind) {
411            switch (projectionKind) {
412                case INVARIANT:
413                    return ProtoBuf.Type.Argument.Projection.INV;
414                case IN_VARIANCE:
415                    return ProtoBuf.Type.Argument.Projection.IN;
416                case OUT_VARIANCE:
417                    return ProtoBuf.Type.Argument.Projection.OUT;
418            }
419            throw new IllegalStateException("Unknown projectionKind: " + projectionKind);
420        }
421    
422        private int getClassId(@NotNull ClassDescriptor descriptor) {
423            return stringTable.getFqNameIndex(descriptor);
424        }
425    
426        private int getTypeParameterId(@NotNull TypeParameterDescriptor descriptor) {
427            return typeParameters.intern(descriptor);
428        }
429    
430        private static boolean hasAnnotations(Annotated descriptor) {
431            return !descriptor.getAnnotations().isEmpty();
432        }
433    
434        @NotNull
435        public static <T extends DeclarationDescriptor> List<T> sort(@NotNull Collection<T> descriptors) {
436            List<T> result = new ArrayList<T>(descriptors);
437            //NOTE: the exact comparator does matter here
438            Collections.sort(result, MemberComparator.INSTANCE);
439            return result;
440    
441        }
442    }