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 }