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.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 extension.serializeType(type, builder, stringTable);
327
328 return builder;
329 }
330
331 private ProtoBuf.Type.Builder flexibleType(@NotNull JetType type) {
332 Flexibility flexibility = TypesPackage.flexibility(type);
333
334 ProtoBuf.Type.Builder builder = type(flexibility.getLowerBound());
335
336 builder.setFlexibleTypeCapabilitiesId(stringTable.getStringIndex(flexibility.getExtraCapabilities().getId()));
337
338 builder.setFlexibleUpperBound(type(flexibility.getUpperBound()));
339
340 return builder;
341 }
342
343 @NotNull
344 private ProtoBuf.Type.Argument.Builder typeArgument(@NotNull TypeProjection typeProjection) {
345 ProtoBuf.Type.Argument.Builder builder = ProtoBuf.Type.Argument.newBuilder();
346
347 if (typeProjection.isStarProjection()) {
348 builder.setProjection(ProtoBuf.Type.Argument.Projection.STAR);
349 }
350 else {
351 ProtoBuf.Type.Argument.Projection projection = projection(typeProjection.getProjectionKind());
352
353 // to avoid storing a default
354 if (projection != ProtoBuf.Type.Argument.Projection.INV) {
355 builder.setProjection(projection);
356 }
357 builder.setType(type(typeProjection.getType()));
358 }
359
360 return builder;
361 }
362
363 @NotNull
364 private ProtoBuf.Type.Constructor.Builder typeConstructor(@NotNull TypeConstructor typeConstructor) {
365 ProtoBuf.Type.Constructor.Builder builder = ProtoBuf.Type.Constructor.newBuilder();
366
367 ClassifierDescriptor declarationDescriptor = typeConstructor.getDeclarationDescriptor();
368
369 assert declarationDescriptor instanceof TypeParameterDescriptor || declarationDescriptor instanceof ClassDescriptor
370 : "Unknown declaration descriptor: " + typeConstructor;
371 if (declarationDescriptor instanceof TypeParameterDescriptor) {
372 TypeParameterDescriptor typeParameterDescriptor = (TypeParameterDescriptor) declarationDescriptor;
373 builder.setKind(ProtoBuf.Type.Constructor.Kind.TYPE_PARAMETER);
374 builder.setId(getTypeParameterId(typeParameterDescriptor));
375 }
376 else {
377 ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor;
378 //default: builder.setKind(Type.Constructor.Kind.CLASS);
379 builder.setId(getClassId(classDescriptor));
380 }
381 return builder;
382 }
383
384 @NotNull
385 public ProtoBuf.Package.Builder packageProto(@NotNull Collection<PackageFragmentDescriptor> fragments) {
386 return packageProto(fragments, null);
387 }
388
389 @NotNull
390 public ProtoBuf.Package.Builder packageProto(@NotNull Collection<PackageFragmentDescriptor> fragments, @Nullable Function1<DeclarationDescriptor, Boolean> skip) {
391 ProtoBuf.Package.Builder builder = ProtoBuf.Package.newBuilder();
392
393 Collection<DeclarationDescriptor> members = new ArrayList<DeclarationDescriptor>();
394 for (PackageFragmentDescriptor fragment : fragments) {
395 members.addAll(fragment.getMemberScope().getAllDescriptors());
396 }
397
398 for (DeclarationDescriptor declaration : sort(members)) {
399 if (skip != null && skip.invoke(declaration)) continue;
400
401 if (declaration instanceof PropertyDescriptor || declaration instanceof FunctionDescriptor) {
402 builder.addMember(callableProto((CallableMemberDescriptor) declaration));
403 }
404 }
405
406 extension.serializePackage(fragments, builder, stringTable);
407
408 return builder;
409 }
410
411 @NotNull
412 private static ProtoBuf.Type.Argument.Projection projection(@NotNull Variance projectionKind) {
413 switch (projectionKind) {
414 case INVARIANT:
415 return ProtoBuf.Type.Argument.Projection.INV;
416 case IN_VARIANCE:
417 return ProtoBuf.Type.Argument.Projection.IN;
418 case OUT_VARIANCE:
419 return ProtoBuf.Type.Argument.Projection.OUT;
420 }
421 throw new IllegalStateException("Unknown projectionKind: " + projectionKind);
422 }
423
424 private int getClassId(@NotNull ClassDescriptor descriptor) {
425 return stringTable.getFqNameIndex(descriptor);
426 }
427
428 private int getTypeParameterId(@NotNull TypeParameterDescriptor descriptor) {
429 return typeParameters.intern(descriptor);
430 }
431
432 private static boolean hasAnnotations(Annotated descriptor) {
433 return !descriptor.getAnnotations().isEmpty();
434 }
435
436 @NotNull
437 public static <T extends DeclarationDescriptor> List<T> sort(@NotNull Collection<T> descriptors) {
438 List<T> result = new ArrayList<T>(descriptors);
439 //NOTE: the exact comparator does matter here
440 Collections.sort(result, MemberComparator.INSTANCE);
441 return result;
442
443 }
444 }