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 com.google.common.collect.Lists;
020 import org.jetbrains.annotations.NotNull;
021 import org.jetbrains.jet.lang.descriptors.*;
022 import org.jetbrains.jet.lang.descriptors.annotations.Annotated;
023 import org.jetbrains.jet.lang.resolve.DescriptorFactory;
024 import org.jetbrains.jet.lang.types.JetType;
025 import org.jetbrains.jet.lang.types.TypeConstructor;
026 import org.jetbrains.jet.lang.types.TypeProjection;
027 import org.jetbrains.jet.lang.types.Variance;
028 import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
029 import org.jetbrains.jet.renderer.DescriptorRenderer;
030
031 import java.util.*;
032
033 import static org.jetbrains.jet.lang.resolve.DescriptorUtils.*;
034
035 public class DescriptorSerializer {
036
037 private static final DescriptorRenderer RENDERER = DescriptorRenderer.STARTS_FROM_NAME;
038 private static final Comparator<DeclarationDescriptor> DESCRIPTOR_COMPARATOR = new Comparator<DeclarationDescriptor>() {
039 @Override
040 public int compare(@NotNull DeclarationDescriptor o1, @NotNull DeclarationDescriptor o2) {
041 int names = o1.getName().compareTo(o2.getName());
042 if (names != 0) return names;
043
044 String o1String = RENDERER.render(o1);
045 String o2String = RENDERER.render(o2);
046 return o1String.compareTo(o2String);
047 }
048 };
049 private final NameTable nameTable;
050 private final Interner<TypeParameterDescriptor> typeParameters;
051 private final SerializerExtension extension;
052
053 public DescriptorSerializer() {
054 this(SerializerExtension.DEFAULT);
055 }
056
057 public DescriptorSerializer(@NotNull SerializerExtension extension) {
058 this(new NameTable(), new Interner<TypeParameterDescriptor>(), extension);
059 }
060
061 private DescriptorSerializer(NameTable nameTable, Interner<TypeParameterDescriptor> typeParameters, SerializerExtension extension) {
062 this.nameTable = nameTable;
063 this.typeParameters = typeParameters;
064 this.extension = extension;
065 }
066
067 private DescriptorSerializer createChildSerializer() {
068 return new DescriptorSerializer(nameTable, new Interner<TypeParameterDescriptor>(typeParameters), extension);
069 }
070
071 @NotNull
072 public NameTable getNameTable() {
073 return nameTable;
074 }
075
076 @NotNull
077 public ProtoBuf.Class.Builder classProto(@NotNull ClassDescriptor classDescriptor) {
078 ProtoBuf.Class.Builder builder = ProtoBuf.Class.newBuilder();
079
080 int flags = Flags.getClassFlags(hasAnnotations(classDescriptor), classDescriptor.getVisibility(),
081 classDescriptor.getModality(), classDescriptor.getKind(), classDescriptor.isInner());
082 builder.setFlags(flags);
083
084 // TODO extra visibility
085
086 builder.setFqName(getClassId(classDescriptor));
087
088 DescriptorSerializer local = createChildSerializer();
089
090 for (TypeParameterDescriptor typeParameterDescriptor : classDescriptor.getTypeConstructor().getParameters()) {
091 builder.addTypeParameter(local.typeParameter(typeParameterDescriptor));
092 }
093
094 if (extension.hasSupertypes(classDescriptor)) {
095 // Special classes (Any, Nothing) have no supertypes
096 for (JetType supertype : classDescriptor.getTypeConstructor().getSupertypes()) {
097 builder.addSupertype(local.type(supertype));
098 }
099 }
100
101 ConstructorDescriptor primaryConstructor = classDescriptor.getUnsubstitutedPrimaryConstructor();
102 if (primaryConstructor != null) {
103 if (DescriptorFactory.isDefaultPrimaryConstructor(primaryConstructor)) {
104 builder.setPrimaryConstructor(ProtoBuf.Class.PrimaryConstructor.getDefaultInstance());
105 }
106 else {
107 ProtoBuf.Class.PrimaryConstructor.Builder constructorBuilder = ProtoBuf.Class.PrimaryConstructor.newBuilder();
108 constructorBuilder.setData(local.callableProto(primaryConstructor));
109 builder.setPrimaryConstructor(constructorBuilder);
110 }
111 }
112
113 // TODO: other constructors
114
115 for (DeclarationDescriptor descriptor : sort(classDescriptor.getDefaultType().getMemberScope().getAllDescriptors())) {
116 if (descriptor instanceof CallableMemberDescriptor) {
117 CallableMemberDescriptor member = (CallableMemberDescriptor) descriptor;
118 if (member.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) continue;
119 builder.addMember(local.callableProto(member));
120 }
121 }
122
123 Collection<DeclarationDescriptor> nestedClasses = classDescriptor.getUnsubstitutedInnerClassesScope().getAllDescriptors();
124 for (DeclarationDescriptor descriptor : sort(nestedClasses)) {
125 if (!isEnumEntry(descriptor)) {
126 builder.addNestedClassName(nameTable.getSimpleNameIndex(descriptor.getName()));
127 }
128 }
129
130 ClassDescriptor classObject = classDescriptor.getClassObjectDescriptor();
131 if (classObject != null) {
132 builder.setClassObject(classObjectProto(classObject));
133 }
134
135 if (classDescriptor.getKind() == ClassKind.ENUM_CLASS) {
136 // Not calling sort() here, because the order of enum entries matters
137 for (DeclarationDescriptor descriptor : nestedClasses) {
138 if (isEnumEntry(descriptor)) {
139 builder.addEnumEntry(nameTable.getSimpleNameIndex(descriptor.getName()));
140 }
141 }
142 }
143
144 return builder;
145 }
146
147 @NotNull
148 private ProtoBuf.Class.ClassObject classObjectProto(@NotNull ClassDescriptor classObject) {
149 if (isObject(classObject.getContainingDeclaration())) {
150 return ProtoBuf.Class.ClassObject.newBuilder().setData(classProto(classObject)).build();
151 }
152
153 return ProtoBuf.Class.ClassObject.getDefaultInstance();
154 }
155
156 @NotNull
157 public ProtoBuf.Callable.Builder callableProto(@NotNull CallableMemberDescriptor descriptor) {
158 ProtoBuf.Callable.Builder builder = ProtoBuf.Callable.newBuilder();
159
160 DescriptorSerializer local = createChildSerializer();
161
162 boolean hasGetter = false;
163 boolean hasSetter = 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
199 builder.setFlags(Flags.getCallableFlags(
200 hasAnnotations(descriptor),
201 descriptor.getVisibility(),
202 descriptor.getModality(),
203 descriptor.getKind(),
204 callableKind(descriptor),
205 hasGetter,
206 hasSetter
207 ));
208 //TODO builder.setExtraVisibility()
209
210 for (TypeParameterDescriptor typeParameterDescriptor : descriptor.getTypeParameters()) {
211 builder.addTypeParameter(local.typeParameter(typeParameterDescriptor));
212 }
213
214 ReceiverParameterDescriptor receiverParameter = descriptor.getReceiverParameter();
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 ProtoBuf.Type.Builder builder = ProtoBuf.Type.newBuilder();
344
345 builder.setConstructor(typeConstructor(type.getConstructor()));
346
347 for (TypeProjection projection : type.getArguments()) {
348 builder.addArgument(typeArgument(projection));
349 }
350
351 // to avoid storing a default
352 if (type.isNullable()) {
353 builder.setNullable(true);
354 }
355
356 return builder;
357 }
358
359 @NotNull
360 private ProtoBuf.Type.Argument.Builder typeArgument(@NotNull TypeProjection typeProjection) {
361 ProtoBuf.Type.Argument.Builder builder = ProtoBuf.Type.Argument.newBuilder();
362 ProtoBuf.Type.Argument.Projection projection = projection(typeProjection.getProjectionKind());
363
364 // to avoid storing a default
365 if (projection != ProtoBuf.Type.Argument.Projection.INV) {
366 builder.setProjection(projection);
367 }
368
369 builder.setType(type(typeProjection.getType()));
370 return builder;
371 }
372
373 @NotNull
374 private ProtoBuf.Type.Constructor.Builder typeConstructor(@NotNull TypeConstructor typeConstructor) {
375 ProtoBuf.Type.Constructor.Builder builder = ProtoBuf.Type.Constructor.newBuilder();
376
377 ClassifierDescriptor declarationDescriptor = typeConstructor.getDeclarationDescriptor();
378
379 assert declarationDescriptor instanceof TypeParameterDescriptor || declarationDescriptor instanceof ClassDescriptor
380 : "Unknown declaration descriptor: " + typeConstructor;
381 if (declarationDescriptor instanceof TypeParameterDescriptor) {
382 TypeParameterDescriptor typeParameterDescriptor = (TypeParameterDescriptor) declarationDescriptor;
383 builder.setKind(ProtoBuf.Type.Constructor.Kind.TYPE_PARAMETER);
384 builder.setId(getTypeParameterId(typeParameterDescriptor));
385 }
386 else {
387 ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor;
388 //default: builder.setKind(Type.Constructor.Kind.CLASS);
389 builder.setId(getClassId(classDescriptor));
390 }
391 return builder;
392 }
393
394 @NotNull
395 public ProtoBuf.Package.Builder packageProto(@NotNull Collection<PackageFragmentDescriptor> fragments) {
396 ProtoBuf.Package.Builder builder = ProtoBuf.Package.newBuilder();
397
398 Collection<DeclarationDescriptor> members = Lists.newArrayList();
399 for (PackageFragmentDescriptor fragment : fragments) {
400 members.addAll(fragment.getMemberScope().getAllDescriptors());
401 }
402
403 for (DeclarationDescriptor declaration : sort(members)) {
404 if (declaration instanceof PropertyDescriptor || declaration instanceof FunctionDescriptor) {
405 builder.addMember(callableProto((CallableMemberDescriptor) declaration));
406 }
407 }
408
409 return builder;
410 }
411
412 @NotNull
413 private static ProtoBuf.Type.Argument.Projection projection(@NotNull Variance projectionKind) {
414 switch (projectionKind) {
415 case INVARIANT:
416 return ProtoBuf.Type.Argument.Projection.INV;
417 case IN_VARIANCE:
418 return ProtoBuf.Type.Argument.Projection.IN;
419 case OUT_VARIANCE:
420 return ProtoBuf.Type.Argument.Projection.OUT;
421 }
422 throw new IllegalStateException("Unknown projectionKind: " + projectionKind);
423 }
424
425 private int getClassId(@NotNull ClassDescriptor descriptor) {
426 return nameTable.getFqNameIndex(descriptor);
427 }
428
429 private int getTypeParameterId(@NotNull TypeParameterDescriptor descriptor) {
430 return typeParameters.intern(descriptor);
431 }
432
433 private static boolean hasAnnotations(Annotated descriptor) {
434 return !descriptor.getAnnotations().isEmpty();
435 }
436
437 @NotNull
438 public static <T extends DeclarationDescriptor> List<T> sort(@NotNull Collection<T> descriptors) {
439 List<T> result = new ArrayList<T>(descriptors);
440 Collections.sort(result, DESCRIPTOR_COMPARATOR);
441 return result;
442
443 }
444 }