001    /*
002     * Copyright 2010-2015 JetBrains s.r.o.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.jetbrains.kotlin.serialization;
018    
019    import org.jetbrains.annotations.NotNull;
020    import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
021    import org.jetbrains.kotlin.descriptors.*;
022    import org.jetbrains.kotlin.descriptors.annotations.Annotated;
023    import org.jetbrains.kotlin.resolve.DescriptorFactory;
024    import org.jetbrains.kotlin.resolve.MemberComparator;
025    import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
026    import org.jetbrains.kotlin.resolve.constants.NullValue;
027    import org.jetbrains.kotlin.types.*;
028    
029    import java.util.ArrayList;
030    import java.util.Collection;
031    import java.util.Collections;
032    import java.util.List;
033    
034    import static org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumEntry;
035    
036    public class DescriptorSerializer {
037    
038        private final StringTable stringTable;
039        private final Interner<TypeParameterDescriptor> typeParameters;
040        private final SerializerExtension extension;
041    
042        private DescriptorSerializer(StringTable stringTable, Interner<TypeParameterDescriptor> typeParameters, SerializerExtension extension) {
043            this.stringTable = stringTable;
044            this.typeParameters = typeParameters;
045            this.extension = extension;
046        }
047    
048        @NotNull
049        public static DescriptorSerializer createTopLevel(@NotNull SerializerExtension extension) {
050            return new DescriptorSerializer(new StringTable(extension), new Interner<TypeParameterDescriptor>(), extension);
051        }
052    
053        @NotNull
054        public static DescriptorSerializer create(@NotNull ClassDescriptor descriptor, @NotNull SerializerExtension extension) {
055            DeclarationDescriptor container = descriptor.getContainingDeclaration();
056            DescriptorSerializer parentSerializer =
057                    container instanceof ClassDescriptor
058                    ? create((ClassDescriptor) container, extension)
059                    : createTopLevel(extension);
060    
061            // Calculate type parameter ids for the outer class beforehand, as it would've had happened if we were always
062            // serializing outer classes before nested classes.
063            // Otherwise our interner can get wrong ids because we may serialize classes in any order.
064            DescriptorSerializer serializer = parentSerializer.createChildSerializer();
065            for (TypeParameterDescriptor typeParameter : descriptor.getTypeConstructor().getParameters()) {
066                serializer.typeParameters.intern(typeParameter);
067            }
068            return serializer;
069        }
070    
071        private DescriptorSerializer createChildSerializer() {
072            return new DescriptorSerializer(stringTable, new Interner<TypeParameterDescriptor>(typeParameters), extension);
073        }
074    
075        @NotNull
076        public StringTable getStringTable() {
077            return stringTable;
078        }
079    
080        @NotNull
081        public ProtoBuf.Class.Builder classProto(@NotNull ClassDescriptor classDescriptor) {
082            ProtoBuf.Class.Builder builder = ProtoBuf.Class.newBuilder();
083    
084            int flags = Flags.getClassFlags(hasAnnotations(classDescriptor), classDescriptor.getVisibility(), classDescriptor.getModality(),
085                                            classDescriptor.getKind(), classDescriptor.isInner(), classDescriptor.isDefaultObject());
086            builder.setFlags(flags);
087    
088            builder.setFqName(getClassId(classDescriptor));
089    
090            for (TypeParameterDescriptor typeParameterDescriptor : classDescriptor.getTypeConstructor().getParameters()) {
091                builder.addTypeParameter(typeParameter(typeParameterDescriptor));
092            }
093    
094            if (!KotlinBuiltIns.isSpecialClassWithNoSupertypes(classDescriptor)) {
095                // Special classes (Any, Nothing) have no supertypes
096                for (JetType supertype : classDescriptor.getTypeConstructor().getSupertypes()) {
097                    builder.addSupertype(type(supertype));
098                }
099            }
100    
101            ConstructorDescriptor primaryConstructor = classDescriptor.getUnsubstitutedPrimaryConstructor();
102            if (primaryConstructor != null) {
103                if (DescriptorFactory.isDefaultPrimaryConstructor(primaryConstructor)) {
104                    builder.setPrimaryConstructor(ProtoBuf.Class.PrimaryConstructor.getDefaultInstance());
105                }
106                else {
107                    ProtoBuf.Class.PrimaryConstructor.Builder constructorBuilder = ProtoBuf.Class.PrimaryConstructor.newBuilder();
108                    constructorBuilder.setData(callableProto(primaryConstructor));
109                    builder.setPrimaryConstructor(constructorBuilder);
110                }
111            }
112    
113            // TODO: other constructors
114    
115            for (DeclarationDescriptor descriptor : sort(classDescriptor.getDefaultType().getMemberScope().getAllDescriptors())) {
116                if (descriptor instanceof CallableMemberDescriptor) {
117                    CallableMemberDescriptor member = (CallableMemberDescriptor) descriptor;
118                    if (member.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) continue;
119                    builder.addMember(callableProto(member));
120                }
121            }
122    
123            for (DeclarationDescriptor descriptor : sort(classDescriptor.getUnsubstitutedInnerClassesScope().getAllDescriptors())) {
124                int name = stringTable.getSimpleNameIndex(descriptor.getName());
125                if (isEnumEntry(descriptor)) {
126                    builder.addEnumEntry(name);
127                }
128                else {
129                    builder.addNestedClassName(name);
130                }
131            }
132    
133            ClassDescriptor defaultObjectDescriptor = classDescriptor.getDefaultObjectDescriptor();
134            if (defaultObjectDescriptor != null) {
135                builder.setDefaultObjectName(stringTable.getSimpleNameIndex(defaultObjectDescriptor.getName()));
136            }
137    
138            extension.serializeClass(classDescriptor, builder, stringTable);
139    
140            return builder;
141        }
142    
143        @NotNull
144        public ProtoBuf.Callable.Builder callableProto(@NotNull CallableMemberDescriptor descriptor) {
145            ProtoBuf.Callable.Builder builder = ProtoBuf.Callable.newBuilder();
146    
147            DescriptorSerializer local = createChildSerializer();
148    
149            boolean hasGetter = false;
150            boolean hasSetter = false;
151            boolean hasConstant = false;
152            if (descriptor instanceof PropertyDescriptor) {
153                PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
154    
155                int propertyFlags = Flags.getAccessorFlags(
156                        hasAnnotations(propertyDescriptor),
157                        propertyDescriptor.getVisibility(),
158                        propertyDescriptor.getModality(),
159                        false
160                );
161    
162                PropertyGetterDescriptor getter = propertyDescriptor.getGetter();
163                if (getter != null) {
164                    hasGetter = true;
165                    int accessorFlags = getAccessorFlags(getter);
166                    if (accessorFlags != propertyFlags) {
167                        builder.setGetterFlags(accessorFlags);
168                    }
169                }
170    
171                PropertySetterDescriptor setter = propertyDescriptor.getSetter();
172                if (setter != null) {
173                    hasSetter = true;
174                    int accessorFlags = getAccessorFlags(setter);
175                    if (accessorFlags != propertyFlags) {
176                        builder.setSetterFlags(accessorFlags);
177                    }
178    
179                    if (!setter.isDefault()) {
180                        for (ValueParameterDescriptor valueParameterDescriptor : setter.getValueParameters()) {
181                            builder.addValueParameter(local.valueParameter(valueParameterDescriptor));
182                        }
183                    }
184                }
185    
186                CompileTimeConstant<?> compileTimeConstant = propertyDescriptor.getCompileTimeInitializer();
187                hasConstant = !(compileTimeConstant == null || compileTimeConstant instanceof NullValue);
188            }
189    
190            builder.setFlags(Flags.getCallableFlags(
191                    hasAnnotations(descriptor),
192                    descriptor.getVisibility(),
193                    descriptor.getModality(),
194                    descriptor.getKind(),
195                    callableKind(descriptor),
196                    hasGetter,
197                    hasSetter,
198                    hasConstant
199            ));
200    
201            for (TypeParameterDescriptor typeParameterDescriptor : descriptor.getTypeParameters()) {
202                builder.addTypeParameter(local.typeParameter(typeParameterDescriptor));
203            }
204    
205            ReceiverParameterDescriptor receiverParameter = descriptor.getExtensionReceiverParameter();
206            if (receiverParameter != null) {
207                builder.setReceiverType(local.type(receiverParameter.getType()));
208            }
209    
210            builder.setName(stringTable.getSimpleNameIndex(descriptor.getName()));
211    
212            for (ValueParameterDescriptor valueParameterDescriptor : descriptor.getValueParameters()) {
213                builder.addValueParameter(local.valueParameter(valueParameterDescriptor));
214            }
215    
216            //noinspection ConstantConditions
217            builder.setReturnType(local.type(descriptor.getReturnType()));
218    
219            extension.serializeCallable(descriptor, builder, stringTable);
220    
221            return builder;
222        }
223    
224        private static int getAccessorFlags(@NotNull PropertyAccessorDescriptor accessor) {
225            return Flags.getAccessorFlags(
226                    hasAnnotations(accessor),
227                    accessor.getVisibility(),
228                    accessor.getModality(),
229                    !accessor.isDefault()
230            );
231        }
232    
233        @NotNull
234        private static ProtoBuf.Callable.CallableKind callableKind(@NotNull CallableMemberDescriptor descriptor) {
235            if (descriptor instanceof PropertyDescriptor) {
236                return ((PropertyDescriptor) descriptor).isVar() ? ProtoBuf.Callable.CallableKind.VAR : ProtoBuf.Callable.CallableKind.VAL;
237            }
238            if (descriptor instanceof ConstructorDescriptor) {
239                return ProtoBuf.Callable.CallableKind.CONSTRUCTOR;
240            }
241            assert descriptor instanceof FunctionDescriptor : "Unknown descriptor class: " + descriptor.getClass();
242            return ProtoBuf.Callable.CallableKind.FUN;
243        }
244    
245        @NotNull
246        private ProtoBuf.Callable.ValueParameter.Builder valueParameter(@NotNull ValueParameterDescriptor descriptor) {
247            ProtoBuf.Callable.ValueParameter.Builder builder = ProtoBuf.Callable.ValueParameter.newBuilder();
248    
249            builder.setFlags(Flags.getValueParameterFlags(hasAnnotations(descriptor), descriptor.declaresDefaultValue()));
250    
251            builder.setName(stringTable.getSimpleNameIndex(descriptor.getName()));
252    
253            builder.setType(type(descriptor.getType()));
254    
255            JetType varargElementType = descriptor.getVarargElementType();
256            if (varargElementType != null) {
257                builder.setVarargElementType(type(varargElementType));
258            }
259    
260            extension.serializeValueParameter(descriptor, builder, stringTable);
261    
262            return builder;
263        }
264    
265        private ProtoBuf.TypeParameter.Builder typeParameter(TypeParameterDescriptor typeParameter) {
266            ProtoBuf.TypeParameter.Builder builder = ProtoBuf.TypeParameter.newBuilder();
267    
268            builder.setId(getTypeParameterId(typeParameter));
269    
270            builder.setName(stringTable.getSimpleNameIndex(typeParameter.getName()));
271    
272            // to avoid storing a default
273            if (typeParameter.isReified()) {
274                builder.setReified(true);
275            }
276    
277            // to avoid storing a default
278            ProtoBuf.TypeParameter.Variance variance = variance(typeParameter.getVariance());
279            if (variance != ProtoBuf.TypeParameter.Variance.INV) {
280                builder.setVariance(variance);
281            }
282    
283            for (JetType upperBound : typeParameter.getUpperBounds()) {
284                builder.addUpperBound(type(upperBound));
285            }
286    
287            return builder;
288        }
289    
290        private static ProtoBuf.TypeParameter.Variance variance(Variance variance) {
291            switch (variance) {
292                case INVARIANT:
293                    return ProtoBuf.TypeParameter.Variance.INV;
294                case IN_VARIANCE:
295                    return ProtoBuf.TypeParameter.Variance.IN;
296                case OUT_VARIANCE:
297                    return  ProtoBuf.TypeParameter.Variance.OUT;
298            }
299            throw new IllegalStateException("Unknown variance: " + variance);
300        }
301    
302        @NotNull
303        public ProtoBuf.Type.Builder type(@NotNull JetType type) {
304            assert !type.isError() : "Can't serialize error types: " + type; // TODO
305    
306            if (TypesPackage.isFlexible(type)) return flexibleType(type);
307    
308            ProtoBuf.Type.Builder builder = ProtoBuf.Type.newBuilder();
309    
310            builder.setConstructor(typeConstructor(type.getConstructor()));
311    
312            for (TypeProjection projection : type.getArguments()) {
313                builder.addArgument(typeArgument(projection));
314            }
315    
316            // to avoid storing a default
317            if (type.isMarkedNullable()) {
318                builder.setNullable(true);
319            }
320    
321            return builder;
322        }
323    
324        private ProtoBuf.Type.Builder flexibleType(@NotNull JetType type) {
325            Flexibility flexibility = TypesPackage.flexibility(type);
326    
327            ProtoBuf.Type.Builder builder = type(flexibility.getLowerBound());
328    
329            builder.setFlexibleTypeCapabilitiesId(stringTable.getStringIndex(flexibility.getExtraCapabilities().getId()));
330    
331            builder.setFlexibleUpperBound(type(flexibility.getUpperBound()));
332    
333            return builder;
334        }
335    
336        @NotNull
337        private ProtoBuf.Type.Argument.Builder typeArgument(@NotNull TypeProjection typeProjection) {
338            ProtoBuf.Type.Argument.Builder builder = ProtoBuf.Type.Argument.newBuilder();
339    
340            if (typeProjection.isStarProjection()) {
341                builder.setProjection(ProtoBuf.Type.Argument.Projection.STAR);
342            }
343            else {
344                ProtoBuf.Type.Argument.Projection projection = projection(typeProjection.getProjectionKind());
345    
346                // to avoid storing a default
347                if (projection != ProtoBuf.Type.Argument.Projection.INV) {
348                    builder.setProjection(projection);
349                }
350            }
351    
352            builder.setType(type(typeProjection.getType()));
353            return builder;
354        }
355    
356        @NotNull
357        private ProtoBuf.Type.Constructor.Builder typeConstructor(@NotNull TypeConstructor typeConstructor) {
358            ProtoBuf.Type.Constructor.Builder builder = ProtoBuf.Type.Constructor.newBuilder();
359    
360            ClassifierDescriptor declarationDescriptor = typeConstructor.getDeclarationDescriptor();
361    
362            assert declarationDescriptor instanceof TypeParameterDescriptor || declarationDescriptor instanceof ClassDescriptor
363                    : "Unknown declaration descriptor: " + typeConstructor;
364            if (declarationDescriptor instanceof TypeParameterDescriptor) {
365                TypeParameterDescriptor typeParameterDescriptor = (TypeParameterDescriptor) declarationDescriptor;
366                builder.setKind(ProtoBuf.Type.Constructor.Kind.TYPE_PARAMETER);
367                builder.setId(getTypeParameterId(typeParameterDescriptor));
368            }
369            else {
370                ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor;
371                //default: builder.setKind(Type.Constructor.Kind.CLASS);
372                builder.setId(getClassId(classDescriptor));
373            }
374            return builder;
375        }
376    
377        @NotNull
378        public ProtoBuf.Package.Builder packageProto(@NotNull Collection<PackageFragmentDescriptor> fragments) {
379            ProtoBuf.Package.Builder builder = ProtoBuf.Package.newBuilder();
380    
381            Collection<DeclarationDescriptor> members = new ArrayList<DeclarationDescriptor>();
382            for (PackageFragmentDescriptor fragment : fragments) {
383                members.addAll(fragment.getMemberScope().getAllDescriptors());
384            }
385    
386            for (DeclarationDescriptor declaration : sort(members)) {
387                if (declaration instanceof PropertyDescriptor || declaration instanceof FunctionDescriptor) {
388                    builder.addMember(callableProto((CallableMemberDescriptor) declaration));
389                }
390            }
391    
392            extension.serializePackage(fragments, builder, stringTable);
393    
394            return builder;
395        }
396    
397        @NotNull
398        private static ProtoBuf.Type.Argument.Projection projection(@NotNull Variance projectionKind) {
399            switch (projectionKind) {
400                case INVARIANT:
401                    return ProtoBuf.Type.Argument.Projection.INV;
402                case IN_VARIANCE:
403                    return ProtoBuf.Type.Argument.Projection.IN;
404                case OUT_VARIANCE:
405                    return ProtoBuf.Type.Argument.Projection.OUT;
406            }
407            throw new IllegalStateException("Unknown projectionKind: " + projectionKind);
408        }
409    
410        private int getClassId(@NotNull ClassDescriptor descriptor) {
411            return stringTable.getFqNameIndex(descriptor);
412        }
413    
414        private int getTypeParameterId(@NotNull TypeParameterDescriptor descriptor) {
415            return typeParameters.intern(descriptor);
416        }
417    
418        private static boolean hasAnnotations(Annotated descriptor) {
419            return !descriptor.getAnnotations().isEmpty();
420        }
421    
422        @NotNull
423        public static <T extends DeclarationDescriptor> List<T> sort(@NotNull Collection<T> descriptors) {
424            List<T> result = new ArrayList<T>(descriptors);
425            //NOTE: the exact comparator does matter here
426            Collections.sort(result, MemberComparator.INSTANCE);
427            return result;
428    
429        }
430    }