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