001 /*
002 * Copyright 2010-2013 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.jet.descriptors.serialization;
018
019 import org.jetbrains.annotations.NotNull;
020 import org.jetbrains.jet.lang.descriptors.*;
021 import org.jetbrains.jet.lang.descriptors.annotations.Annotated;
022 import org.jetbrains.jet.lang.resolve.DescriptorFactory;
023 import org.jetbrains.jet.lang.types.*;
024 import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
025 import org.jetbrains.jet.renderer.DescriptorRenderer;
026
027 import java.util.*;
028
029 import static org.jetbrains.jet.lang.resolve.DescriptorUtils.*;
030
031 public class DescriptorSerializer {
032
033 private static final DescriptorRenderer RENDERER = DescriptorRenderer.STARTS_FROM_NAME;
034 private static final Comparator<DeclarationDescriptor> DESCRIPTOR_COMPARATOR = new Comparator<DeclarationDescriptor>() {
035 @Override
036 public int compare(@NotNull DeclarationDescriptor o1, @NotNull DeclarationDescriptor o2) {
037 int names = o1.getName().compareTo(o2.getName());
038 if (names != 0) return names;
039
040 String o1String = RENDERER.render(o1);
041 String o2String = RENDERER.render(o2);
042 return o1String.compareTo(o2String);
043 }
044 };
045 private final NameTable nameTable;
046 private final Interner<TypeParameterDescriptor> typeParameters;
047 private final SerializerExtension extension;
048
049 public DescriptorSerializer() {
050 this(SerializerExtension.DEFAULT);
051 }
052
053 public DescriptorSerializer(@NotNull SerializerExtension extension) {
054 this(new NameTable(), new Interner<TypeParameterDescriptor>(), extension);
055 }
056
057 private DescriptorSerializer(NameTable nameTable, Interner<TypeParameterDescriptor> typeParameters, SerializerExtension extension) {
058 this.nameTable = nameTable;
059 this.typeParameters = typeParameters;
060 this.extension = extension;
061 }
062
063 private DescriptorSerializer createChildSerializer() {
064 return new DescriptorSerializer(nameTable, new Interner<TypeParameterDescriptor>(typeParameters), extension);
065 }
066
067 @NotNull
068 public NameTable getNameTable() {
069 return nameTable;
070 }
071
072 @NotNull
073 public ProtoBuf.Class.Builder classProto(@NotNull ClassDescriptor classDescriptor) {
074 ProtoBuf.Class.Builder builder = ProtoBuf.Class.newBuilder();
075
076 int flags = Flags.getClassFlags(hasAnnotations(classDescriptor), classDescriptor.getVisibility(),
077 classDescriptor.getModality(), classDescriptor.getKind(), classDescriptor.isInner());
078 builder.setFlags(flags);
079
080 // TODO extra visibility
081
082 builder.setFqName(getClassId(classDescriptor));
083
084 DescriptorSerializer local = createChildSerializer();
085
086 for (TypeParameterDescriptor typeParameterDescriptor : classDescriptor.getTypeConstructor().getParameters()) {
087 builder.addTypeParameter(local.typeParameter(typeParameterDescriptor));
088 }
089
090 if (extension.hasSupertypes(classDescriptor)) {
091 // Special classes (Any, Nothing) have no supertypes
092 for (JetType supertype : classDescriptor.getTypeConstructor().getSupertypes()) {
093 builder.addSupertype(local.type(supertype));
094 }
095 }
096
097 ConstructorDescriptor primaryConstructor = classDescriptor.getUnsubstitutedPrimaryConstructor();
098 if (primaryConstructor != null) {
099 if (DescriptorFactory.isDefaultPrimaryConstructor(primaryConstructor)) {
100 builder.setPrimaryConstructor(ProtoBuf.Class.PrimaryConstructor.getDefaultInstance());
101 }
102 else {
103 ProtoBuf.Class.PrimaryConstructor.Builder constructorBuilder = ProtoBuf.Class.PrimaryConstructor.newBuilder();
104 constructorBuilder.setData(local.callableProto(primaryConstructor));
105 builder.setPrimaryConstructor(constructorBuilder);
106 }
107 }
108
109 // TODO: other constructors
110
111 for (DeclarationDescriptor descriptor : sort(classDescriptor.getDefaultType().getMemberScope().getAllDescriptors())) {
112 if (descriptor instanceof CallableMemberDescriptor) {
113 CallableMemberDescriptor member = (CallableMemberDescriptor) descriptor;
114 if (member.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) continue;
115 builder.addMember(local.callableProto(member));
116 }
117 }
118
119 Collection<DeclarationDescriptor> nestedClasses = classDescriptor.getUnsubstitutedInnerClassesScope().getAllDescriptors();
120 for (DeclarationDescriptor descriptor : sort(nestedClasses)) {
121 if (!isEnumEntry(descriptor)) {
122 builder.addNestedClassName(nameTable.getSimpleNameIndex(descriptor.getName()));
123 }
124 }
125
126 ClassDescriptor classObject = classDescriptor.getClassObjectDescriptor();
127 if (classObject != null) {
128 builder.setClassObject(classObjectProto(classObject));
129 }
130
131 if (classDescriptor.getKind() == ClassKind.ENUM_CLASS) {
132 // Not calling sort() here, because the order of enum entries matters
133 for (DeclarationDescriptor descriptor : nestedClasses) {
134 if (isEnumEntry(descriptor)) {
135 builder.addEnumEntry(nameTable.getSimpleNameIndex(descriptor.getName()));
136 }
137 }
138 }
139
140 return builder;
141 }
142
143 @NotNull
144 private ProtoBuf.Class.ClassObject classObjectProto(@NotNull ClassDescriptor classObject) {
145 if (isObject(classObject.getContainingDeclaration())) {
146 return ProtoBuf.Class.ClassObject.newBuilder().setData(classProto(classObject)).build();
147 }
148
149 return ProtoBuf.Class.ClassObject.getDefaultInstance();
150 }
151
152 @NotNull
153 public ProtoBuf.Callable.Builder callableProto(@NotNull CallableMemberDescriptor descriptor) {
154 ProtoBuf.Callable.Builder builder = ProtoBuf.Callable.newBuilder();
155
156 DescriptorSerializer local = createChildSerializer();
157
158 boolean hasGetter = false;
159 boolean hasSetter = false;
160 boolean hasConstant = false;
161 if (descriptor instanceof PropertyDescriptor) {
162 PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
163
164 int propertyFlags = Flags.getAccessorFlags(
165 hasAnnotations(propertyDescriptor),
166 propertyDescriptor.getVisibility(),
167 propertyDescriptor.getModality(),
168 false
169 );
170
171 PropertyGetterDescriptor getter = propertyDescriptor.getGetter();
172 if (getter != null) {
173 hasGetter = true;
174 int accessorFlags = getAccessorFlags(getter);
175 if (accessorFlags != propertyFlags) {
176 builder.setGetterFlags(accessorFlags);
177 }
178 }
179
180 PropertySetterDescriptor setter = propertyDescriptor.getSetter();
181 if (setter != null) {
182 hasSetter = true;
183 int accessorFlags = getAccessorFlags(setter);
184 if (accessorFlags != propertyFlags) {
185 builder.setSetterFlags(accessorFlags);
186 }
187
188 if (!setter.isDefault()) {
189 for (ValueParameterDescriptor valueParameterDescriptor : setter.getValueParameters()) {
190 builder.addValueParameter(local.valueParameter(valueParameterDescriptor));
191 }
192 }
193 }
194
195 hasConstant = propertyDescriptor.getCompileTimeInitializer() != null;
196 }
197
198 builder.setFlags(Flags.getCallableFlags(
199 hasAnnotations(descriptor),
200 descriptor.getVisibility(),
201 descriptor.getModality(),
202 descriptor.getKind(),
203 callableKind(descriptor),
204 hasGetter,
205 hasSetter,
206 hasConstant
207 ));
208 //TODO builder.setExtraVisibility()
209
210 for (TypeParameterDescriptor typeParameterDescriptor : descriptor.getTypeParameters()) {
211 builder.addTypeParameter(local.typeParameter(typeParameterDescriptor));
212 }
213
214 ReceiverParameterDescriptor receiverParameter = descriptor.getExtensionReceiverParameter();
215 if (receiverParameter != null) {
216 builder.setReceiverType(local.type(receiverParameter.getType()));
217 }
218
219 builder.setName(nameTable.getSimpleNameIndex(descriptor.getName()));
220
221 for (ValueParameterDescriptor valueParameterDescriptor : descriptor.getValueParameters()) {
222 builder.addValueParameter(local.valueParameter(valueParameterDescriptor));
223 }
224
225 builder.setReturnType(local.type(getSerializableReturnType(descriptor.getReturnType())));
226
227 extension.serializeCallable(descriptor, builder, nameTable);
228
229 return builder;
230 }
231
232 @NotNull
233 private static JetType getSerializableReturnType(@NotNull JetType type) {
234 return isSerializableType(type) ? type : KotlinBuiltIns.getInstance().getAnyType();
235 }
236
237 /**
238 * @return true iff this type can be serialized. Types which correspond to type parameters, top-level classes, inner classes, and
239 * generic classes with serializable arguments are serializable. For other types (local classes, inner of local, etc.) it may be
240 * problematical to construct a FQ name for serialization
241 */
242 private static boolean isSerializableType(@NotNull JetType type) {
243 ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor();
244 if (descriptor instanceof TypeParameterDescriptor) {
245 return true;
246 }
247 else if (descriptor instanceof ClassDescriptor) {
248 for (TypeProjection projection : type.getArguments()) {
249 if (!isSerializableType(projection.getType())) {
250 return false;
251 }
252 }
253
254 return isTopLevelOrInnerClass((ClassDescriptor) descriptor);
255 }
256 else {
257 throw new IllegalStateException("Unknown type constructor: " + type);
258 }
259 }
260
261 private static int getAccessorFlags(@NotNull PropertyAccessorDescriptor accessor) {
262 return Flags.getAccessorFlags(
263 hasAnnotations(accessor),
264 accessor.getVisibility(),
265 accessor.getModality(),
266 !accessor.isDefault()
267 );
268 }
269
270 @NotNull
271 private static ProtoBuf.Callable.CallableKind callableKind(@NotNull CallableMemberDescriptor descriptor) {
272 if (descriptor instanceof PropertyDescriptor) {
273 return ((PropertyDescriptor) descriptor).isVar() ? ProtoBuf.Callable.CallableKind.VAR : ProtoBuf.Callable.CallableKind.VAL;
274 }
275 if (descriptor instanceof ConstructorDescriptor) {
276 return ProtoBuf.Callable.CallableKind.CONSTRUCTOR;
277 }
278 assert descriptor instanceof FunctionDescriptor : "Unknown descriptor class: " + descriptor.getClass();
279 return ProtoBuf.Callable.CallableKind.FUN;
280 }
281
282 @NotNull
283 private ProtoBuf.Callable.ValueParameter.Builder valueParameter(@NotNull ValueParameterDescriptor descriptor) {
284 ProtoBuf.Callable.ValueParameter.Builder builder = ProtoBuf.Callable.ValueParameter.newBuilder();
285
286 builder.setFlags(Flags.getValueParameterFlags(hasAnnotations(descriptor), descriptor.declaresDefaultValue()));
287
288 builder.setName(nameTable.getSimpleNameIndex(descriptor.getName()));
289
290 builder.setType(type(descriptor.getType()));
291
292 JetType varargElementType = descriptor.getVarargElementType();
293 if (varargElementType != null) {
294 builder.setVarargElementType(type(varargElementType));
295 }
296
297 extension.serializeValueParameter(descriptor, builder, nameTable);
298
299 return builder;
300 }
301
302 private ProtoBuf.TypeParameter.Builder typeParameter(TypeParameterDescriptor typeParameter) {
303 ProtoBuf.TypeParameter.Builder builder = ProtoBuf.TypeParameter.newBuilder();
304
305 builder.setId(getTypeParameterId(typeParameter));
306
307 builder.setName(nameTable.getSimpleNameIndex(typeParameter.getName()));
308
309 // to avoid storing a default
310 if (typeParameter.isReified()) {
311 builder.setReified(true);
312 }
313
314 // to avoid storing a default
315 ProtoBuf.TypeParameter.Variance variance = variance(typeParameter.getVariance());
316 if (variance != ProtoBuf.TypeParameter.Variance.INV) {
317 builder.setVariance(variance);
318 }
319
320 for (JetType upperBound : typeParameter.getUpperBounds()) {
321 builder.addUpperBound(type(upperBound));
322 }
323
324 return builder;
325 }
326
327 private static ProtoBuf.TypeParameter.Variance variance(Variance variance) {
328 switch (variance) {
329 case INVARIANT:
330 return ProtoBuf.TypeParameter.Variance.INV;
331 case IN_VARIANCE:
332 return ProtoBuf.TypeParameter.Variance.IN;
333 case OUT_VARIANCE:
334 return ProtoBuf.TypeParameter.Variance.OUT;
335 }
336 throw new IllegalStateException("Unknown variance: " + variance);
337 }
338
339 @NotNull
340 public ProtoBuf.Type.Builder type(@NotNull JetType type) {
341 assert !type.isError() : "Can't serialize error types: " + type; // TODO
342
343 if (TypesPackage.isFlexible(type)) return flexibleType(type);
344
345 ProtoBuf.Type.Builder builder = ProtoBuf.Type.newBuilder();
346
347 builder.setConstructor(typeConstructor(type.getConstructor()));
348
349 for (TypeProjection projection : type.getArguments()) {
350 builder.addArgument(typeArgument(projection));
351 }
352
353 // to avoid storing a default
354 if (type.isNullable()) {
355 builder.setNullable(true);
356 }
357
358 return builder;
359 }
360
361 private ProtoBuf.Type.Builder flexibleType(@NotNull JetType type) {
362 Flexibility flexibility = TypesPackage.flexibility(type);
363
364 ProtoBuf.Type.Builder builder = type(flexibility.getLowerBound());
365
366 builder.setFlexibleTypeCapabilitiesId(nameTable.getStringIndex(flexibility.getExtraCapabilities().getId()));
367
368 builder.setFlexibleUpperBound(type(flexibility.getUpperBound()));
369
370 return builder;
371 }
372
373 @NotNull
374 private ProtoBuf.Type.Argument.Builder typeArgument(@NotNull TypeProjection typeProjection) {
375 ProtoBuf.Type.Argument.Builder builder = ProtoBuf.Type.Argument.newBuilder();
376 ProtoBuf.Type.Argument.Projection projection = projection(typeProjection.getProjectionKind());
377
378 // to avoid storing a default
379 if (projection != ProtoBuf.Type.Argument.Projection.INV) {
380 builder.setProjection(projection);
381 }
382
383 builder.setType(type(typeProjection.getType()));
384 return builder;
385 }
386
387 @NotNull
388 private ProtoBuf.Type.Constructor.Builder typeConstructor(@NotNull TypeConstructor typeConstructor) {
389 ProtoBuf.Type.Constructor.Builder builder = ProtoBuf.Type.Constructor.newBuilder();
390
391 ClassifierDescriptor declarationDescriptor = typeConstructor.getDeclarationDescriptor();
392
393 assert declarationDescriptor instanceof TypeParameterDescriptor || declarationDescriptor instanceof ClassDescriptor
394 : "Unknown declaration descriptor: " + typeConstructor;
395 if (declarationDescriptor instanceof TypeParameterDescriptor) {
396 TypeParameterDescriptor typeParameterDescriptor = (TypeParameterDescriptor) declarationDescriptor;
397 builder.setKind(ProtoBuf.Type.Constructor.Kind.TYPE_PARAMETER);
398 builder.setId(getTypeParameterId(typeParameterDescriptor));
399 }
400 else {
401 ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor;
402 //default: builder.setKind(Type.Constructor.Kind.CLASS);
403 builder.setId(getClassId(classDescriptor));
404 }
405 return builder;
406 }
407
408 @NotNull
409 public ProtoBuf.Package.Builder packageProto(@NotNull Collection<PackageFragmentDescriptor> fragments) {
410 ProtoBuf.Package.Builder builder = ProtoBuf.Package.newBuilder();
411
412 Collection<DeclarationDescriptor> members = new ArrayList<DeclarationDescriptor>();
413 for (PackageFragmentDescriptor fragment : fragments) {
414 members.addAll(fragment.getMemberScope().getAllDescriptors());
415 }
416
417 for (DeclarationDescriptor declaration : sort(members)) {
418 if (declaration instanceof PropertyDescriptor || declaration instanceof FunctionDescriptor) {
419 builder.addMember(callableProto((CallableMemberDescriptor) declaration));
420 }
421 }
422
423 return builder;
424 }
425
426 @NotNull
427 private static ProtoBuf.Type.Argument.Projection projection(@NotNull Variance projectionKind) {
428 switch (projectionKind) {
429 case INVARIANT:
430 return ProtoBuf.Type.Argument.Projection.INV;
431 case IN_VARIANCE:
432 return ProtoBuf.Type.Argument.Projection.IN;
433 case OUT_VARIANCE:
434 return ProtoBuf.Type.Argument.Projection.OUT;
435 }
436 throw new IllegalStateException("Unknown projectionKind: " + projectionKind);
437 }
438
439 private int getClassId(@NotNull ClassDescriptor descriptor) {
440 return nameTable.getFqNameIndex(descriptor);
441 }
442
443 private int getTypeParameterId(@NotNull TypeParameterDescriptor descriptor) {
444 return typeParameters.intern(descriptor);
445 }
446
447 private static boolean hasAnnotations(Annotated descriptor) {
448 return !descriptor.getAnnotations().isEmpty();
449 }
450
451 @NotNull
452 public static <T extends DeclarationDescriptor> List<T> sort(@NotNull Collection<T> descriptors) {
453 List<T> result = new ArrayList<T>(descriptors);
454 Collections.sort(result, DESCRIPTOR_COMPARATOR);
455 return result;
456
457 }
458 }