org.jetbrains.kotlin.serialization.DescriptorSerializer.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kotlin-compiler-embeddable Show documentation
Show all versions of kotlin-compiler-embeddable Show documentation
the Kotlin compiler embeddable
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.serialization
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotated
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.protobuf.MessageLite
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumEntry
import org.jetbrains.kotlin.resolve.MemberComparator
import org.jetbrains.kotlin.resolve.constants.NullValue
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.utils.Interner
import java.io.ByteArrayOutputStream
import java.util.*
class DescriptorSerializer private constructor(
private val containingDeclaration: DeclarationDescriptor?,
private val typeParameters: Interner,
private val extension: SerializerExtension,
private val typeTable: MutableTypeTable,
private val serializeTypeTableToFunction: Boolean
) {
fun serialize(message: MessageLite): ByteArray {
return ByteArrayOutputStream().apply {
stringTable.serializeTo(this)
message.writeTo(this)
}.toByteArray()
}
private fun createChildSerializer(callable: CallableDescriptor): DescriptorSerializer =
DescriptorSerializer(callable, Interner(typeParameters), extension, typeTable, serializeTypeTableToFunction = false)
val stringTable: StringTable
get() = extension.stringTable
private fun useTypeTable(): Boolean = extension.shouldUseTypeTable()
fun classProto(classDescriptor: ClassDescriptor): ProtoBuf.Class.Builder {
val builder = ProtoBuf.Class.newBuilder()
val flags = Flags.getClassFlags(
hasAnnotations(classDescriptor), classDescriptor.visibility, classDescriptor.modality, classDescriptor.kind,
classDescriptor.isInner, classDescriptor.isCompanionObject, classDescriptor.isData
)
if (flags != builder.flags) {
builder.flags = flags
}
builder.fqName = getClassId(classDescriptor)
for (typeParameterDescriptor in classDescriptor.declaredTypeParameters) {
builder.addTypeParameter(typeParameter(typeParameterDescriptor))
}
if (!KotlinBuiltIns.isSpecialClassWithNoSupertypes(classDescriptor)) {
// Special classes (Any, Nothing) have no supertypes
for (supertype in classDescriptor.typeConstructor.supertypes) {
if (useTypeTable()) {
builder.addSupertypeId(typeId(supertype))
}
else {
builder.addSupertype(type(supertype))
}
}
}
for (descriptor in classDescriptor.constructors) {
builder.addConstructor(constructorProto(descriptor))
}
for (descriptor in sort(DescriptorUtils.getAllDescriptors(classDescriptor.defaultType.memberScope))) {
if (descriptor is CallableMemberDescriptor) {
if (descriptor.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) continue
when (descriptor) {
is PropertyDescriptor -> builder.addProperty(propertyProto(descriptor))
is FunctionDescriptor -> builder.addFunction(functionProto(descriptor))
}
}
}
for (descriptor in sort(DescriptorUtils.getAllDescriptors(classDescriptor.unsubstitutedInnerClassesScope))) {
val name = getSimpleNameIndex(descriptor.name)
if (isEnumEntry(descriptor)) {
builder.addEnumEntry(enumEntryProto(descriptor as ClassDescriptor))
}
else {
builder.addNestedClassName(name)
}
}
val companionObjectDescriptor = classDescriptor.companionObjectDescriptor
if (companionObjectDescriptor != null) {
builder.companionObjectName = getSimpleNameIndex(companionObjectDescriptor.name)
}
val typeTableProto = typeTable.serialize()
if (typeTableProto != null) {
builder.typeTable = typeTableProto
}
extension.serializeClass(classDescriptor, builder)
return builder
}
fun propertyProto(descriptor: PropertyDescriptor): ProtoBuf.Property.Builder {
val builder = ProtoBuf.Property.newBuilder()
val local = createChildSerializer(descriptor)
var hasGetter = false
var hasSetter = false
val lateInit = descriptor.isLateInit
val isConst = descriptor.isConst
val compileTimeConstant = descriptor.compileTimeInitializer
val hasConstant = compileTimeConstant != null && compileTimeConstant !is NullValue
val hasAnnotations = descriptor.annotations.getAllAnnotations().isNotEmpty()
val propertyFlags = Flags.getAccessorFlags(hasAnnotations, descriptor.visibility, descriptor.modality, false, false)
val getter = descriptor.getter
if (getter != null) {
hasGetter = true
val accessorFlags = getAccessorFlags(getter)
if (accessorFlags != propertyFlags) {
builder.getterFlags = accessorFlags
}
}
val setter = descriptor.setter
if (setter != null) {
hasSetter = true
val accessorFlags = getAccessorFlags(setter)
if (accessorFlags != propertyFlags) {
builder.setterFlags = accessorFlags
}
if (!setter.isDefault) {
val setterLocal = local.createChildSerializer(setter)
for (valueParameterDescriptor in setter.valueParameters) {
builder.setSetterValueParameter(setterLocal.valueParameter(valueParameterDescriptor))
}
}
}
val flags = Flags.getPropertyFlags(
hasAnnotations, descriptor.visibility, descriptor.modality, descriptor.kind, descriptor.isVar,
hasGetter, hasSetter, hasConstant, isConst, lateInit
)
if (flags != builder.flags) {
builder.flags = flags
}
builder.name = getSimpleNameIndex(descriptor.name)
if (useTypeTable()) {
builder.returnTypeId = local.typeId(descriptor.type)
}
else {
builder.setReturnType(local.type(descriptor.type))
}
for (typeParameterDescriptor in descriptor.typeParameters) {
builder.addTypeParameter(local.typeParameter(typeParameterDescriptor))
}
val receiverParameter = descriptor.extensionReceiverParameter
if (receiverParameter != null) {
if (useTypeTable()) {
builder.receiverTypeId = local.typeId(receiverParameter.type)
}
else {
builder.setReceiverType(local.type(receiverParameter.type))
}
}
extension.serializeProperty(descriptor, builder)
return builder
}
fun functionProto(descriptor: FunctionDescriptor): ProtoBuf.Function.Builder {
val builder = ProtoBuf.Function.newBuilder()
val local = createChildSerializer(descriptor)
val flags = Flags.getFunctionFlags(
hasAnnotations(descriptor), descriptor.visibility, descriptor.modality, descriptor.kind, descriptor.isOperator,
descriptor.isInfix, descriptor.isInline, descriptor.isTailrec, descriptor.isExternal
)
if (flags != builder.flags) {
builder.flags = flags
}
builder.name = getSimpleNameIndex(descriptor.name)
if (useTypeTable()) {
builder.returnTypeId = local.typeId(descriptor.returnType!!)
}
else {
builder.setReturnType(local.type(descriptor.returnType!!))
}
for (typeParameterDescriptor in descriptor.typeParameters) {
builder.addTypeParameter(local.typeParameter(typeParameterDescriptor))
}
val receiverParameter = descriptor.extensionReceiverParameter
if (receiverParameter != null) {
if (useTypeTable()) {
builder.receiverTypeId = local.typeId(receiverParameter.type)
}
else {
builder.setReceiverType(local.type(receiverParameter.type))
}
}
for (valueParameterDescriptor in descriptor.valueParameters) {
builder.addValueParameter(local.valueParameter(valueParameterDescriptor))
}
if (serializeTypeTableToFunction) {
val typeTableProto = typeTable.serialize()
if (typeTableProto != null) {
builder.typeTable = typeTableProto
}
}
extension.serializeFunction(descriptor, builder)
return builder
}
fun constructorProto(descriptor: ConstructorDescriptor): ProtoBuf.Constructor.Builder {
val builder = ProtoBuf.Constructor.newBuilder()
val local = createChildSerializer(descriptor)
val flags = Flags.getConstructorFlags(hasAnnotations(descriptor), descriptor.visibility, !descriptor.isPrimary)
if (flags != builder.flags) {
builder.flags = flags
}
for (valueParameterDescriptor in descriptor.valueParameters) {
builder.addValueParameter(local.valueParameter(valueParameterDescriptor))
}
extension.serializeConstructor(descriptor, builder)
return builder
}
fun enumEntryProto(descriptor: ClassDescriptor): ProtoBuf.EnumEntry.Builder {
val builder = ProtoBuf.EnumEntry.newBuilder()
builder.name = getSimpleNameIndex(descriptor.name)
extension.serializeEnumEntry(descriptor, builder)
return builder
}
private fun valueParameter(descriptor: ValueParameterDescriptor): ProtoBuf.ValueParameter.Builder {
val builder = ProtoBuf.ValueParameter.newBuilder()
val flags = Flags.getValueParameterFlags(
hasAnnotations(descriptor), descriptor.declaresDefaultValue(),
descriptor.isCrossinline, descriptor.isNoinline
)
if (flags != builder.flags) {
builder.flags = flags
}
builder.name = getSimpleNameIndex(descriptor.name)
if (useTypeTable()) {
builder.typeId = typeId(descriptor.type)
}
else {
builder.setType(type(descriptor.type))
}
val varargElementType = descriptor.varargElementType
if (varargElementType != null) {
if (useTypeTable()) {
builder.varargElementTypeId = typeId(varargElementType)
}
else {
builder.setVarargElementType(type(varargElementType))
}
}
extension.serializeValueParameter(descriptor, builder)
return builder
}
private fun typeParameter(typeParameter: TypeParameterDescriptor): ProtoBuf.TypeParameter.Builder {
val builder = ProtoBuf.TypeParameter.newBuilder()
builder.id = getTypeParameterId(typeParameter)
builder.name = getSimpleNameIndex(typeParameter.name)
if (typeParameter.isReified != builder.reified) {
builder.reified = typeParameter.isReified
}
val variance = variance(typeParameter.variance)
if (variance != builder.variance) {
builder.variance = variance
}
extension.serializeTypeParameter(typeParameter, builder)
val upperBounds = typeParameter.upperBounds
if (upperBounds.size == 1 && KotlinBuiltIns.isDefaultBound(upperBounds.single())) return builder
for (upperBound in upperBounds) {
if (useTypeTable()) {
builder.addUpperBoundId(typeId(upperBound))
}
else {
builder.addUpperBound(type(upperBound))
}
}
return builder
}
private fun typeId(type: KotlinType): Int = typeTable[type(type)]
private fun type(type: KotlinType): ProtoBuf.Type.Builder {
val builder = ProtoBuf.Type.newBuilder()
if (type.isError) {
extension.serializeErrorType(type, builder)
return builder
}
if (type.isFlexible()) {
val flexibility = type.flexibility()
val lowerBound = type(flexibility.lowerBound)
lowerBound.flexibleTypeCapabilitiesId = stringTable.getStringIndex(flexibility.factory.id)
if (useTypeTable()) {
lowerBound.flexibleUpperBoundId = typeId(flexibility.upperBound)
}
else {
lowerBound.setFlexibleUpperBound(type(flexibility.upperBound))
}
return lowerBound
}
val descriptor = type.constructor.declarationDescriptor
when (descriptor) {
is ClassDescriptor -> {
val possiblyInnerType = type.buildPossiblyInnerType() ?: error("possiblyInnerType should not be null: $type")
fillFromPossiblyInnerType(builder, possiblyInnerType)
}
is TypeParameterDescriptor -> {
if (descriptor.containingDeclaration === containingDeclaration) {
builder.typeParameterName = getSimpleNameIndex(descriptor.name)
}
else {
builder.typeParameter = getTypeParameterId(descriptor)
}
assert(type.arguments.isEmpty()) { "Found arguments for type constructor build on type parameter: $descriptor" }
}
}
if (type.isMarkedNullable != builder.nullable) {
builder.nullable = type.isMarkedNullable
}
extension.serializeType(type, builder)
return builder
}
private fun fillFromPossiblyInnerType(builder: ProtoBuf.Type.Builder, type: PossiblyInnerType) {
builder.className = getClassId(type.classDescriptor)
for (projection in type.arguments) {
builder.addArgument(typeArgument(projection))
}
if (type.outerType != null) {
val outerBuilder = ProtoBuf.Type.newBuilder()
fillFromPossiblyInnerType(outerBuilder, type.outerType!!)
if (useTypeTable()) {
builder.outerTypeId = typeTable[outerBuilder]
}
else {
builder.setOuterType(outerBuilder)
}
}
}
private fun typeArgument(typeProjection: TypeProjection): ProtoBuf.Type.Argument.Builder {
val builder = ProtoBuf.Type.Argument.newBuilder()
if (typeProjection.isStarProjection) {
builder.projection = ProtoBuf.Type.Argument.Projection.STAR
}
else {
val projection = projection(typeProjection.projectionKind)
if (projection != builder.projection) {
builder.projection = projection
}
if (useTypeTable()) {
builder.typeId = typeId(typeProjection.type)
}
else {
builder.setType(type(typeProjection.type))
}
}
return builder
}
@JvmOverloads
fun packageProto(
fragments: Collection, skip: ((DeclarationDescriptor) -> Boolean)? = null
): ProtoBuf.Package.Builder {
val builder = ProtoBuf.Package.newBuilder()
val members = fragments.flatMap { fragment ->
DescriptorUtils.getAllDescriptors(fragment.getMemberScope())
}
for (declaration in sort(members)) {
if (skip?.invoke(declaration) == true) continue
when (declaration) {
is PropertyDescriptor -> builder.addProperty(propertyProto(declaration))
is FunctionDescriptor -> builder.addFunction(functionProto(declaration))
}
}
val typeTableProto = typeTable.serialize()
if (typeTableProto != null) {
builder.typeTable = typeTableProto
}
extension.serializePackage(builder)
return builder
}
fun packagePartProto(members: Collection): ProtoBuf.Package.Builder {
val builder = ProtoBuf.Package.newBuilder()
for (declaration in sort(members)) {
when (declaration) {
is PropertyDescriptor -> builder.addProperty(propertyProto(declaration))
is FunctionDescriptor -> builder.addFunction(functionProto(declaration))
}
}
val typeTableProto = typeTable.serialize()
if (typeTableProto != null) {
builder.typeTable = typeTableProto
}
extension.serializePackage(builder)
return builder
}
private fun getClassId(descriptor: ClassDescriptor): Int =
stringTable.getFqNameIndex(descriptor)
private fun getSimpleNameIndex(name: Name): Int =
stringTable.getStringIndex(name.asString())
private fun getTypeParameterId(descriptor: TypeParameterDescriptor): Int =
typeParameters.intern(descriptor)
companion object {
@JvmStatic
fun createTopLevel(extension: SerializerExtension): DescriptorSerializer {
return DescriptorSerializer(null, Interner(), extension, MutableTypeTable(), serializeTypeTableToFunction = false)
}
@JvmStatic
fun createForLambda(extension: SerializerExtension): DescriptorSerializer {
return DescriptorSerializer(null, Interner(), extension, MutableTypeTable(), serializeTypeTableToFunction = true)
}
@JvmStatic
fun create(descriptor: ClassDescriptor, extension: SerializerExtension): DescriptorSerializer {
val container = descriptor.containingDeclaration
val parentSerializer = if (container is ClassDescriptor)
create(container, extension)
else
createTopLevel(extension)
// Calculate type parameter ids for the outer class beforehand, as it would've had happened if we were always
// serializing outer classes before nested classes.
// Otherwise our interner can get wrong ids because we may serialize classes in any order.
val serializer = DescriptorSerializer(
descriptor,
Interner(parentSerializer.typeParameters),
parentSerializer.extension,
MutableTypeTable(),
serializeTypeTableToFunction = false
)
for (typeParameter in descriptor.declaredTypeParameters) {
serializer.typeParameters.intern(typeParameter)
}
return serializer
}
private fun getAccessorFlags(accessor: PropertyAccessorDescriptor): Int {
return Flags.getAccessorFlags(
hasAnnotations(accessor),
accessor.visibility,
accessor.modality,
!accessor.isDefault,
accessor.isExternal
)
}
private fun variance(variance: Variance): ProtoBuf.TypeParameter.Variance = when (variance) {
Variance.INVARIANT -> ProtoBuf.TypeParameter.Variance.INV
Variance.IN_VARIANCE -> ProtoBuf.TypeParameter.Variance.IN
Variance.OUT_VARIANCE -> ProtoBuf.TypeParameter.Variance.OUT
}
private fun projection(projectionKind: Variance): ProtoBuf.Type.Argument.Projection = when (projectionKind) {
Variance.INVARIANT -> ProtoBuf.Type.Argument.Projection.INV
Variance.IN_VARIANCE -> ProtoBuf.Type.Argument.Projection.IN
Variance.OUT_VARIANCE -> ProtoBuf.Type.Argument.Projection.OUT
}
private fun hasAnnotations(descriptor: Annotated): Boolean = !descriptor.annotations.isEmpty()
fun sort(descriptors: Collection): List =
ArrayList(descriptors).apply {
//NOTE: the exact comparator does matter here
Collections.sort(this, MemberComparator.INSTANCE)
}
}
}