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 if (descriptor instanceof PropertyDescriptor) {
164 PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
165
166 int propertyFlags = Flags.getAccessorFlags(
167 hasAnnotations(propertyDescriptor),
168 propertyDescriptor.getVisibility(),
169 propertyDescriptor.getModality(),
170 false
171 );
172
173 PropertyGetterDescriptor getter = propertyDescriptor.getGetter();
174 if (getter != null) {
175 hasGetter = true;
176 int accessorFlags = getAccessorFlags(getter);
177 if (accessorFlags != propertyFlags) {
178 builder.setGetterFlags(accessorFlags);
179 }
180 }
181
182 PropertySetterDescriptor setter = propertyDescriptor.getSetter();
183 if (setter != null) {
184 hasSetter = true;
185 int accessorFlags = getAccessorFlags(setter);
186 if (accessorFlags != propertyFlags) {
187 builder.setSetterFlags(accessorFlags);
188 }
189
190 if (!setter.isDefault()) {
191 for (ValueParameterDescriptor valueParameterDescriptor : setter.getValueParameters()) {
192 builder.addValueParameter(local.valueParameter(valueParameterDescriptor));
193 }
194 }
195 }
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 ));
207 //TODO builder.setExtraVisibility()
208
209 for (TypeParameterDescriptor typeParameterDescriptor : descriptor.getTypeParameters()) {
210 builder.addTypeParameter(local.typeParameter(typeParameterDescriptor));
211 }
212
213 ReceiverParameterDescriptor receiverParameter = descriptor.getReceiverParameter();
214 if (receiverParameter != null) {
215 builder.setReceiverType(local.type(receiverParameter.getType()));
216 }
217
218 builder.setName(nameTable.getSimpleNameIndex(descriptor.getName()));
219
220 for (ValueParameterDescriptor valueParameterDescriptor : descriptor.getValueParameters()) {
221 builder.addValueParameter(local.valueParameter(valueParameterDescriptor));
222 }
223
224 builder.setReturnType(local.type(getSerializableReturnType(descriptor.getReturnType())));
225
226 extension.serializeCallable(descriptor, builder, nameTable);
227
228 return builder;
229 }
230
231 @NotNull
232 private static JetType getSerializableReturnType(@NotNull JetType type) {
233 return isSerializableType(type) ? type : KotlinBuiltIns.getInstance().getAnyType();
234 }
235
236 /**
237 * @return true iff this type can be serialized. Types which correspond to type parameters, top-level classes, inner classes, and
238 * generic classes with serializable arguments are serializable. For other types (local classes, inner of local, etc.) it may be
239 * problematical to construct a FQ name for serialization
240 */
241 private static boolean isSerializableType(@NotNull JetType type) {
242 ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor();
243 if (descriptor instanceof TypeParameterDescriptor) {
244 return true;
245 }
246 else if (descriptor instanceof ClassDescriptor) {
247 for (TypeProjection projection : type.getArguments()) {
248 if (!isSerializableType(projection.getType())) {
249 return false;
250 }
251 }
252
253 return isTopLevelOrInnerClass((ClassDescriptor) descriptor);
254 }
255 else {
256 throw new IllegalStateException("Unknown type constructor: " + type);
257 }
258 }
259
260 private static int getAccessorFlags(@NotNull PropertyAccessorDescriptor accessor) {
261 return Flags.getAccessorFlags(
262 hasAnnotations(accessor),
263 accessor.getVisibility(),
264 accessor.getModality(),
265 !accessor.isDefault()
266 );
267 }
268
269 @NotNull
270 private static ProtoBuf.Callable.CallableKind callableKind(@NotNull CallableMemberDescriptor descriptor) {
271 if (descriptor instanceof PropertyDescriptor) {
272 return ((PropertyDescriptor) descriptor).isVar() ? ProtoBuf.Callable.CallableKind.VAR : ProtoBuf.Callable.CallableKind.VAL;
273 }
274 if (descriptor instanceof ConstructorDescriptor) {
275 return ProtoBuf.Callable.CallableKind.CONSTRUCTOR;
276 }
277 assert descriptor instanceof FunctionDescriptor : "Unknown descriptor class: " + descriptor.getClass();
278 return ProtoBuf.Callable.CallableKind.FUN;
279 }
280
281 @NotNull
282 private ProtoBuf.Callable.ValueParameter.Builder valueParameter(@NotNull ValueParameterDescriptor descriptor) {
283 ProtoBuf.Callable.ValueParameter.Builder builder = ProtoBuf.Callable.ValueParameter.newBuilder();
284
285 builder.setFlags(Flags.getValueParameterFlags(hasAnnotations(descriptor), descriptor.declaresDefaultValue()));
286
287 builder.setName(nameTable.getSimpleNameIndex(descriptor.getName()));
288
289 builder.setType(type(descriptor.getType()));
290
291 JetType varargElementType = descriptor.getVarargElementType();
292 if (varargElementType != null) {
293 builder.setVarargElementType(type(varargElementType));
294 }
295
296 extension.serializeValueParameter(descriptor, builder, nameTable);
297
298 return builder;
299 }
300
301 private ProtoBuf.TypeParameter.Builder typeParameter(TypeParameterDescriptor typeParameter) {
302 ProtoBuf.TypeParameter.Builder builder = ProtoBuf.TypeParameter.newBuilder();
303
304 builder.setId(getTypeParameterId(typeParameter));
305
306 builder.setName(nameTable.getSimpleNameIndex(typeParameter.getName()));
307
308 // to avoid storing a default
309 if (typeParameter.isReified()) {
310 builder.setReified(true);
311 }
312
313 // to avoid storing a default
314 ProtoBuf.TypeParameter.Variance variance = variance(typeParameter.getVariance());
315 if (variance != ProtoBuf.TypeParameter.Variance.INV) {
316 builder.setVariance(variance);
317 }
318
319 for (JetType upperBound : typeParameter.getUpperBounds()) {
320 builder.addUpperBound(type(upperBound));
321 }
322
323 return builder;
324 }
325
326 private static ProtoBuf.TypeParameter.Variance variance(Variance variance) {
327 switch (variance) {
328 case INVARIANT:
329 return ProtoBuf.TypeParameter.Variance.INV;
330 case IN_VARIANCE:
331 return ProtoBuf.TypeParameter.Variance.IN;
332 case OUT_VARIANCE:
333 return ProtoBuf.TypeParameter.Variance.OUT;
334 }
335 throw new IllegalStateException("Unknown variance: " + variance);
336 }
337
338 @NotNull
339 public ProtoBuf.Type.Builder type(@NotNull JetType type) {
340 assert !type.isError() : "Can't serialize error types: " + type; // TODO
341
342 ProtoBuf.Type.Builder builder = ProtoBuf.Type.newBuilder();
343
344 builder.setConstructor(typeConstructor(type.getConstructor()));
345
346 for (TypeProjection projection : type.getArguments()) {
347 builder.addArgument(typeArgument(projection));
348 }
349
350 // to avoid storing a default
351 if (type.isNullable()) {
352 builder.setNullable(true);
353 }
354
355 return builder;
356 }
357
358 @NotNull
359 private ProtoBuf.Type.Argument.Builder typeArgument(@NotNull TypeProjection typeProjection) {
360 ProtoBuf.Type.Argument.Builder builder = ProtoBuf.Type.Argument.newBuilder();
361 ProtoBuf.Type.Argument.Projection projection = projection(typeProjection.getProjectionKind());
362
363 // to avoid storing a default
364 if (projection != ProtoBuf.Type.Argument.Projection.INV) {
365 builder.setProjection(projection);
366 }
367
368 builder.setType(type(typeProjection.getType()));
369 return builder;
370 }
371
372 @NotNull
373 private ProtoBuf.Type.Constructor.Builder typeConstructor(@NotNull TypeConstructor typeConstructor) {
374 ProtoBuf.Type.Constructor.Builder builder = ProtoBuf.Type.Constructor.newBuilder();
375
376 ClassifierDescriptor declarationDescriptor = typeConstructor.getDeclarationDescriptor();
377
378 assert declarationDescriptor instanceof TypeParameterDescriptor || declarationDescriptor instanceof ClassDescriptor
379 : "Unknown declaration descriptor: " + typeConstructor;
380 if (declarationDescriptor instanceof TypeParameterDescriptor) {
381 TypeParameterDescriptor typeParameterDescriptor = (TypeParameterDescriptor) declarationDescriptor;
382 builder.setKind(ProtoBuf.Type.Constructor.Kind.TYPE_PARAMETER);
383 builder.setId(getTypeParameterId(typeParameterDescriptor));
384 }
385 else {
386 ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor;
387 //default: builder.setKind(Type.Constructor.Kind.CLASS);
388 builder.setId(getClassId(classDescriptor));
389 }
390 return builder;
391 }
392
393 @NotNull
394 public ProtoBuf.Package.Builder packageProto(@NotNull NamespaceDescriptor descriptor) {
395 ProtoBuf.Package.Builder builder = ProtoBuf.Package.newBuilder();
396
397 for (DeclarationDescriptor declaration : sort(descriptor.getMemberScope().getAllDescriptors())) {
398 if (declaration instanceof PropertyDescriptor || declaration instanceof FunctionDescriptor) {
399 builder.addMember(callableProto((CallableMemberDescriptor) declaration));
400 }
401 }
402
403 return builder;
404 }
405
406 @NotNull
407 private static ProtoBuf.Type.Argument.Projection projection(@NotNull Variance projectionKind) {
408 switch (projectionKind) {
409 case INVARIANT:
410 return ProtoBuf.Type.Argument.Projection.INV;
411 case IN_VARIANCE:
412 return ProtoBuf.Type.Argument.Projection.IN;
413 case OUT_VARIANCE:
414 return ProtoBuf.Type.Argument.Projection.OUT;
415 }
416 throw new IllegalStateException("Unknown projectionKind: " + projectionKind);
417 }
418
419 private int getClassId(@NotNull ClassDescriptor descriptor) {
420 return nameTable.getFqNameIndex(descriptor);
421 }
422
423 private int getTypeParameterId(@NotNull TypeParameterDescriptor descriptor) {
424 return typeParameters.intern(descriptor);
425 }
426
427 private static boolean hasAnnotations(Annotated descriptor) {
428 return !descriptor.getAnnotations().isEmpty();
429 }
430
431 @NotNull
432 public static <T extends DeclarationDescriptor> List<T> sort(@NotNull Collection<T> descriptors) {
433 List<T> result = new ArrayList<T>(descriptors);
434 Collections.sort(result, DESCRIPTOR_COMPARATOR);
435 return result;
436
437 }
438 }