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 }