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