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.types.JetType;
023 import org.jetbrains.jet.lang.types.TypeConstructor;
024 import org.jetbrains.jet.lang.types.TypeProjection;
025 import org.jetbrains.jet.lang.types.Variance;
026 import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
027 import org.jetbrains.jet.renderer.DescriptorRenderer;
028
029 import java.util.*;
030
031 import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getEnumEntriesScope;
032 import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isTopLevelOrInnerClass;
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(
040 DeclarationDescriptor o1, DeclarationDescriptor o2
041 ) {
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 builder.setPrimaryConstructor(local.callableProto(primaryConstructor));
105 }
106 // TODO: other constructors
107
108 for (DeclarationDescriptor descriptor : sort(classDescriptor.getDefaultType().getMemberScope().getAllDescriptors())) {
109 if (descriptor instanceof CallableMemberDescriptor) {
110 CallableMemberDescriptor member = (CallableMemberDescriptor) descriptor;
111 if (member.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) continue;
112 builder.addMember(local.callableProto(member));
113 }
114 }
115
116 Collection<DeclarationDescriptor> nestedClasses = classDescriptor.getUnsubstitutedInnerClassesScope().getAllDescriptors();
117 for (DeclarationDescriptor descriptor : sort(nestedClasses)) {
118 ClassDescriptor nestedClass = (ClassDescriptor) descriptor;
119 int nameIndex = nameTable.getSimpleNameIndex(nestedClass.getName());
120 builder.addNestedClassName(nameIndex);
121 }
122
123 for (ClassDescriptor descriptor : sort(classDescriptor.getUnsubstitutedInnerClassesScope().getObjectDescriptors())) {
124 int nameIndex = nameTable.getSimpleNameIndex(descriptor.getName());
125 builder.addNestedObjectName(nameIndex);
126 }
127
128 if (classDescriptor.getClassObjectDescriptor() != null) {
129 // false is default
130 builder.setClassObjectPresent(true);
131 }
132
133 if (classDescriptor.getKind() == ClassKind.ENUM_CLASS) {
134 for (ClassDescriptor descriptor : getEnumEntriesScope(classDescriptor).getObjectDescriptors()) {
135 if (descriptor.getKind() == ClassKind.ENUM_ENTRY) {
136 builder.addEnumEntry(nameTable.getSimpleNameIndex(descriptor.getName()));
137 }
138 }
139 }
140
141 return builder;
142 }
143
144 @NotNull
145 public ProtoBuf.Callable.Builder callableProto(@NotNull CallableMemberDescriptor descriptor) {
146 ProtoBuf.Callable.Builder builder = ProtoBuf.Callable.newBuilder();
147
148 DescriptorSerializer local = createChildSerializer();
149
150 boolean hasGetter = false;
151 boolean hasSetter = false;
152 if (descriptor instanceof PropertyDescriptor) {
153 PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
154
155 int propertyFlags = Flags.getAccessorFlags(
156 hasAnnotations(propertyDescriptor),
157 propertyDescriptor.getVisibility(),
158 propertyDescriptor.getModality(),
159 false
160 );
161
162 PropertyGetterDescriptor getter = propertyDescriptor.getGetter();
163 if (getter != null) {
164 hasGetter = true;
165 int accessorFlags = getAccessorFlags(getter);
166 if (accessorFlags != propertyFlags) {
167 builder.setGetterFlags(accessorFlags);
168 }
169 }
170
171 PropertySetterDescriptor setter = propertyDescriptor.getSetter();
172 if (setter != null) {
173 hasSetter = true;
174 int accessorFlags = getAccessorFlags(setter);
175 if (accessorFlags != propertyFlags) {
176 builder.setSetterFlags(accessorFlags);
177 }
178
179 if (!setter.isDefault()) {
180 for (ValueParameterDescriptor valueParameterDescriptor : setter.getValueParameters()) {
181 builder.addValueParameter(local.valueParameter(valueParameterDescriptor));
182 }
183 }
184 }
185 }
186
187 builder.setFlags(Flags.getCallableFlags(
188 hasAnnotations(descriptor),
189 descriptor.getVisibility(),
190 descriptor.getModality(),
191 descriptor.getKind(),
192 callableKind(descriptor),
193 descriptor instanceof SimpleFunctionDescriptor &&
194 ((SimpleFunctionDescriptor) descriptor).isInline(),
195 hasGetter,
196 hasSetter
197 ));
198 //TODO builder.setExtraVisibility()
199
200 for (TypeParameterDescriptor typeParameterDescriptor : descriptor.getTypeParameters()) {
201 builder.addTypeParameter(local.typeParameter(typeParameterDescriptor));
202 }
203
204 ReceiverParameterDescriptor receiverParameter = descriptor.getReceiverParameter();
205 if (receiverParameter != null) {
206 builder.setReceiverType(local.type(receiverParameter.getType()));
207 }
208
209 builder.setName(nameTable.getSimpleNameIndex(descriptor.getName()));
210
211 for (ValueParameterDescriptor valueParameterDescriptor : descriptor.getValueParameters()) {
212 builder.addValueParameter(local.valueParameter(valueParameterDescriptor));
213 }
214
215 builder.setReturnType(local.type(getSerializableReturnType(descriptor.getReturnType())));
216
217 extension.serializeCallable(descriptor, builder, nameTable);
218
219 return builder;
220 }
221
222 @NotNull
223 private static JetType getSerializableReturnType(@NotNull JetType type) {
224 return isSerializableType(type) ? type : KotlinBuiltIns.getInstance().getAnyType();
225 }
226
227 /**
228 * @return true iff this type can be serialized. Types which correspond to type parameters, top-level classes, inner classes, and
229 * generic classes with serializable arguments are serializable. For other types (local classes, inner of local, etc.) it may be
230 * problematical to construct a FQ name for serialization
231 */
232 private static boolean isSerializableType(@NotNull JetType type) {
233 ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor();
234 if (descriptor instanceof TypeParameterDescriptor) {
235 return true;
236 }
237 else if (descriptor instanceof ClassDescriptor) {
238 for (TypeProjection projection : type.getArguments()) {
239 if (!isSerializableType(projection.getType())) {
240 return false;
241 }
242 }
243
244 return isTopLevelOrInnerClass((ClassDescriptor) descriptor);
245 }
246 else {
247 throw new IllegalStateException("Unknown type constructor: " + type);
248 }
249 }
250
251 private static int getAccessorFlags(@NotNull PropertyAccessorDescriptor accessor) {
252 return Flags.getAccessorFlags(
253 hasAnnotations(accessor),
254 accessor.getVisibility(),
255 accessor.getModality(),
256 !accessor.isDefault()
257 );
258 }
259
260 private static ProtoBuf.Callable.CallableKind callableKind(CallableMemberDescriptor descriptor) {
261 if (descriptor instanceof VariableDescriptorForObject) {
262 return ProtoBuf.Callable.CallableKind.OBJECT_PROPERTY;
263 }
264 else if (descriptor instanceof PropertyDescriptor) {
265 PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
266 return propertyDescriptor.isVar() ? ProtoBuf.Callable.CallableKind.VAR : ProtoBuf.Callable.CallableKind.VAL;
267 }
268 if (descriptor instanceof ConstructorDescriptor) {
269 return ProtoBuf.Callable.CallableKind.CONSTRUCTOR;
270 }
271 assert descriptor instanceof FunctionDescriptor : "Unknown descriptor class: " + descriptor.getClass();
272 return ProtoBuf.Callable.CallableKind.FUN;
273 }
274
275 @NotNull
276 private ProtoBuf.Callable.ValueParameter.Builder valueParameter(@NotNull ValueParameterDescriptor descriptor) {
277 ProtoBuf.Callable.ValueParameter.Builder builder = ProtoBuf.Callable.ValueParameter.newBuilder();
278
279 builder.setFlags(Flags.getValueParameterFlags(hasAnnotations(descriptor), descriptor.declaresDefaultValue()));
280
281 builder.setName(nameTable.getSimpleNameIndex(descriptor.getName()));
282
283 builder.setType(type(descriptor.getType()));
284
285 JetType varargElementType = descriptor.getVarargElementType();
286 if (varargElementType != null) {
287 builder.setVarargElementType(type(varargElementType));
288 }
289
290 extension.serializeValueParameter(descriptor, builder, nameTable);
291
292 return builder;
293 }
294
295 private ProtoBuf.TypeParameter.Builder typeParameter(TypeParameterDescriptor typeParameter) {
296 ProtoBuf.TypeParameter.Builder builder = ProtoBuf.TypeParameter.newBuilder();
297
298 builder.setId(getTypeParameterId(typeParameter));
299
300 builder.setName(nameTable.getSimpleNameIndex(typeParameter.getName()));
301
302 // to avoid storing a default
303 if (typeParameter.isReified()) {
304 builder.setReified(true);
305 }
306
307 // to avoid storing a default
308 ProtoBuf.TypeParameter.Variance variance = variance(typeParameter.getVariance());
309 if (variance != ProtoBuf.TypeParameter.Variance.INV) {
310 builder.setVariance(variance);
311 }
312
313 for (JetType upperBound : typeParameter.getUpperBounds()) {
314 builder.addUpperBound(type(upperBound));
315 }
316
317 return builder;
318 }
319
320 private static ProtoBuf.TypeParameter.Variance variance(Variance variance) {
321 switch (variance) {
322 case INVARIANT:
323 return ProtoBuf.TypeParameter.Variance.INV;
324 case IN_VARIANCE:
325 return ProtoBuf.TypeParameter.Variance.IN;
326 case OUT_VARIANCE:
327 return ProtoBuf.TypeParameter.Variance.OUT;
328 }
329 throw new IllegalStateException("Unknown variance: " + variance);
330 }
331
332 @NotNull
333 public ProtoBuf.Type.Builder type(@NotNull JetType type) {
334 assert !type.isError() : "Can't serialize error types: " + type; // TODO
335
336 ProtoBuf.Type.Builder builder = ProtoBuf.Type.newBuilder();
337
338 builder.setConstructor(typeConstructor(type.getConstructor()));
339
340 for (TypeProjection projection : type.getArguments()) {
341 builder.addArgument(typeArgument(projection));
342 }
343
344 // to avoid storing a default
345 if (type.isNullable()) {
346 builder.setNullable(true);
347 }
348
349 return builder;
350 }
351
352 @NotNull
353 private ProtoBuf.Type.Argument.Builder typeArgument(@NotNull TypeProjection typeProjection) {
354 ProtoBuf.Type.Argument.Builder builder = ProtoBuf.Type.Argument.newBuilder();
355 ProtoBuf.Type.Argument.Projection projection = projection(typeProjection.getProjectionKind());
356
357 // to avoid storing a default
358 if (projection != ProtoBuf.Type.Argument.Projection.INV) {
359 builder.setProjection(projection);
360 }
361
362 builder.setType(type(typeProjection.getType()));
363 return builder;
364 }
365
366 @NotNull
367 private ProtoBuf.Type.Constructor.Builder typeConstructor(@NotNull TypeConstructor typeConstructor) {
368 ProtoBuf.Type.Constructor.Builder builder = ProtoBuf.Type.Constructor.newBuilder();
369
370 ClassifierDescriptor declarationDescriptor = typeConstructor.getDeclarationDescriptor();
371
372 assert declarationDescriptor instanceof TypeParameterDescriptor || declarationDescriptor instanceof ClassDescriptor
373 : "Unknown declaration descriptor: " + typeConstructor;
374 if (declarationDescriptor instanceof TypeParameterDescriptor) {
375 TypeParameterDescriptor typeParameterDescriptor = (TypeParameterDescriptor) declarationDescriptor;
376 builder.setKind(ProtoBuf.Type.Constructor.Kind.TYPE_PARAMETER);
377 builder.setId(getTypeParameterId(typeParameterDescriptor));
378 }
379 else {
380 ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor;
381 //default: builder.setKind(Type.Constructor.Kind.CLASS);
382 builder.setId(getClassId(classDescriptor));
383 }
384 return builder;
385 }
386
387 @NotNull
388 public ProtoBuf.Package.Builder packageProto(@NotNull NamespaceDescriptor descriptor) {
389 ProtoBuf.Package.Builder builder = ProtoBuf.Package.newBuilder();
390
391 for (DeclarationDescriptor declaration : sort(descriptor.getMemberScope().getAllDescriptors())) {
392 if (declaration instanceof PropertyDescriptor || declaration instanceof FunctionDescriptor) {
393 builder.addMember(callableProto((CallableMemberDescriptor) declaration));
394 }
395 }
396
397 return builder;
398 }
399
400 @NotNull
401 private static ProtoBuf.Type.Argument.Projection projection(@NotNull Variance projectionKind) {
402 switch (projectionKind) {
403 case INVARIANT:
404 return ProtoBuf.Type.Argument.Projection.INV;
405 case IN_VARIANCE:
406 return ProtoBuf.Type.Argument.Projection.IN;
407 case OUT_VARIANCE:
408 return ProtoBuf.Type.Argument.Projection.OUT;
409 }
410 throw new IllegalStateException("Unknown projectionKind: " + projectionKind);
411 }
412
413 private int getClassId(@NotNull ClassDescriptor descriptor) {
414 return nameTable.getFqNameIndex(descriptor);
415 }
416
417 private int getTypeParameterId(@NotNull TypeParameterDescriptor descriptor) {
418 return typeParameters.intern(descriptor);
419 }
420
421 private static boolean hasAnnotations(Annotated descriptor) {
422 return !descriptor.getAnnotations().isEmpty();
423 }
424
425 @NotNull
426 public static <T extends DeclarationDescriptor> List<T> sort(@NotNull Collection<T> descriptors) {
427 List<T> result = new ArrayList<T>(descriptors);
428 Collections.sort(result, DESCRIPTOR_COMPARATOR);
429 return result;
430
431 }
432 }