JS IR: serialize declarations without mutable state
This commit is contained in:
committed by
TeamCityServer
parent
1e822aa28a
commit
8fe8419ad4
+2
@@ -567,6 +567,8 @@ class IrBodyDeserializer(
|
|||||||
IrTypeOperator.SAM_CONVERSION
|
IrTypeOperator.SAM_CONVERSION
|
||||||
ProtoTypeOperator.IMPLICIT_DYNAMIC_CAST ->
|
ProtoTypeOperator.IMPLICIT_DYNAMIC_CAST ->
|
||||||
IrTypeOperator.IMPLICIT_DYNAMIC_CAST
|
IrTypeOperator.IMPLICIT_DYNAMIC_CAST
|
||||||
|
ProtoTypeOperator.REINTERPRET_CAST ->
|
||||||
|
IrTypeOperator.REINTERPRET_CAST
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun deserializeTypeOp(proto: ProtoTypeOp, start: Int, end: Int, type: IrType): IrTypeOperatorCall {
|
private fun deserializeTypeOp(proto: ProtoTypeOp, start: Int, end: Int, type: IrType): IrTypeOperatorCall {
|
||||||
|
|||||||
+62
-49
@@ -62,12 +62,13 @@ class IrDeclarationDeserializer(
|
|||||||
private val allowErrorNodes: Boolean,
|
private val allowErrorNodes: Boolean,
|
||||||
private val deserializeInlineFunctions: Boolean,
|
private val deserializeInlineFunctions: Boolean,
|
||||||
private var deserializeBodies: Boolean,
|
private var deserializeBodies: Boolean,
|
||||||
private val symbolDeserializer: IrSymbolDeserializer,
|
val symbolDeserializer: IrSymbolDeserializer,
|
||||||
private val platformFakeOverrideClassFilter: FakeOverrideClassFilter,
|
private val platformFakeOverrideClassFilter: FakeOverrideClassFilter,
|
||||||
private val fakeOverrideBuilder: FakeOverrideBuilder,
|
private val fakeOverrideBuilder: FakeOverrideBuilder,
|
||||||
|
private val skipMutableState: Boolean = false,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val bodyDeserializer = IrBodyDeserializer(builtIns, allowErrorNodes, irFactory, fileReader, this)
|
val bodyDeserializer = IrBodyDeserializer(builtIns, allowErrorNodes, irFactory, fileReader, this)
|
||||||
|
|
||||||
private fun deserializeName(index: Int): Name {
|
private fun deserializeName(index: Int): Name {
|
||||||
val name = fileReader.deserializeString(index)
|
val name = fileReader.deserializeString(index)
|
||||||
@@ -83,7 +84,7 @@ class IrDeclarationDeserializer(
|
|||||||
return ProtoType.parseFrom(readType(index), ExtensionRegistryLite.newInstance())
|
return ProtoType.parseFrom(readType(index), ExtensionRegistryLite.newInstance())
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun deserializeIrType(index: Int): IrType {
|
fun deserializeIrType(index: Int): IrType {
|
||||||
return irTypeCache.getOrPut(index) {
|
return irTypeCache.getOrPut(index) {
|
||||||
val typeData = loadTypeProto(index)
|
val typeData = loadTypeProto(index)
|
||||||
deserializeIrTypeData(typeData)
|
deserializeIrTypeData(typeData)
|
||||||
@@ -216,7 +217,9 @@ class IrDeclarationDeserializer(
|
|||||||
deserializeIrDeclarationOrigin(proto.originName), proto.flags
|
deserializeIrDeclarationOrigin(proto.originName), proto.flags
|
||||||
)
|
)
|
||||||
result.annotations += deserializeAnnotations(proto.annotationList)
|
result.annotations += deserializeAnnotations(proto.annotationList)
|
||||||
result.parent = currentParent
|
if (!skipMutableState) {
|
||||||
|
result.parent = currentParent
|
||||||
|
}
|
||||||
return result
|
return result
|
||||||
} finally {
|
} finally {
|
||||||
eraseDelegatedSymbol(s)
|
eraseDelegatedSymbol(s)
|
||||||
@@ -305,19 +308,19 @@ class IrDeclarationDeserializer(
|
|||||||
flags.isFun,
|
flags.isFun,
|
||||||
)
|
)
|
||||||
}.usingParent {
|
}.usingParent {
|
||||||
typeParameters = deserializeTypeParameters(proto.typeParameterList, true)
|
if (!skipMutableState) {
|
||||||
|
typeParameters = deserializeTypeParameters(proto.typeParameterList, true)
|
||||||
|
|
||||||
superTypes = proto.superTypeList.map { deserializeIrType(it) }
|
superTypes = proto.superTypeList.map { deserializeIrType(it) }
|
||||||
|
|
||||||
withExternalValue(isExternal) {
|
withExternalValue(isExternal) {proto.declarationList
|
||||||
proto.declarationList
|
|
||||||
.filterNot { isSkippableFakeOverride(it, this) }
|
.filterNot { isSkippableFakeOverride(it, this) }
|
||||||
.mapTo(declarations) { deserializeDeclaration(it) }
|
.mapTo(declarations) { deserializeDeclaration(it) }}
|
||||||
|
|
||||||
|
thisReceiver = deserializeIrValueParameter(proto.thisReceiver, -1)
|
||||||
|
|
||||||
|
fakeOverrideBuilder.enqueueClass(this, signature)
|
||||||
}
|
}
|
||||||
|
|
||||||
thisReceiver = deserializeIrValueParameter(proto.thisReceiver, -1)
|
|
||||||
|
|
||||||
fakeOverrideBuilder.enqueueClass(this, signature)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,7 +340,9 @@ class IrDeclarationDeserializer(
|
|||||||
origin
|
origin
|
||||||
)
|
)
|
||||||
}.usingParent {
|
}.usingParent {
|
||||||
typeParameters = deserializeTypeParameters(proto.typeParameterList, true)
|
if (!skipMutableState) {
|
||||||
|
typeParameters = deserializeTypeParameters(proto.typeParameterList, true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -437,7 +442,7 @@ class IrDeclarationDeserializer(
|
|||||||
return ProtoExpression.parseFrom(readBody(index), ExtensionRegistryLite.newInstance())
|
return ProtoExpression.parseFrom(readBody(index), ExtensionRegistryLite.newInstance())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun deserializeExpressionBody(index: Int): IrExpressionBody {
|
fun deserializeExpressionBody(index: Int): IrExpressionBody {
|
||||||
return irFactory.createExpressionBody(
|
return irFactory.createExpressionBody(
|
||||||
if (deserializeBodies) {
|
if (deserializeBodies) {
|
||||||
val bodyData = loadExpressionBodyProto(index)
|
val bodyData = loadExpressionBodyProto(index)
|
||||||
@@ -449,7 +454,7 @@ class IrDeclarationDeserializer(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun deserializeStatementBody(index: Int): IrElement {
|
fun deserializeStatementBody(index: Int): IrElement {
|
||||||
return if (deserializeBodies) {
|
return if (deserializeBodies) {
|
||||||
val bodyData = loadStatementBodyProto(index)
|
val bodyData = loadStatementBodyProto(index)
|
||||||
bodyDeserializer.deserializeStatement(bodyData)
|
bodyDeserializer.deserializeStatement(bodyData)
|
||||||
@@ -467,18 +472,20 @@ class IrDeclarationDeserializer(
|
|||||||
): T = withDeserializedIrDeclarationBase(proto.base) { symbol, idSig, startOffset, endOffset, origin, fcode ->
|
): T = withDeserializedIrDeclarationBase(proto.base) { symbol, idSig, startOffset, endOffset, origin, fcode ->
|
||||||
symbolTable.withScope(symbol) {
|
symbolTable.withScope(symbol) {
|
||||||
block(symbol as IrFunctionSymbol, idSig, startOffset, endOffset, origin, fcode).usingParent {
|
block(symbol as IrFunctionSymbol, idSig, startOffset, endOffset, origin, fcode).usingParent {
|
||||||
typeParameters = deserializeTypeParameters(proto.typeParameterList, false)
|
if (!skipMutableState) {
|
||||||
val nameType = BinaryNameAndType.decode(proto.nameType)
|
typeParameters = deserializeTypeParameters(proto.typeParameterList, false)
|
||||||
returnType = deserializeIrType(nameType.typeIndex)
|
val nameType = BinaryNameAndType.decode(proto.nameType)
|
||||||
|
returnType = deserializeIrType(nameType.typeIndex)
|
||||||
|
|
||||||
withBodyGuard {
|
withBodyGuard {
|
||||||
valueParameters = deserializeValueParameters(proto.valueParameterList)
|
valueParameters = deserializeValueParameters(proto.valueParameterList)
|
||||||
if (proto.hasDispatchReceiver())
|
if (proto.hasDispatchReceiver())
|
||||||
dispatchReceiverParameter = deserializeIrValueParameter(proto.dispatchReceiver, -1)
|
dispatchReceiverParameter = deserializeIrValueParameter(proto.dispatchReceiver, -1)
|
||||||
if (proto.hasExtensionReceiver())
|
if (proto.hasExtensionReceiver())
|
||||||
extensionReceiverParameter = deserializeIrValueParameter(proto.extensionReceiver, -1)
|
extensionReceiverParameter = deserializeIrValueParameter(proto.extensionReceiver, -1)
|
||||||
if (proto.hasBody()) {
|
if (proto.hasBody()) {
|
||||||
body = deserializeStatementBody(proto.body) as IrBody
|
body = deserializeStatementBody(proto.body) as IrBody
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -512,7 +519,7 @@ class IrDeclarationDeserializer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun deserializeIrVariable(proto: ProtoVariable): IrVariable =
|
fun deserializeIrVariable(proto: ProtoVariable): IrVariable =
|
||||||
withDeserializedIrDeclarationBase(proto.base) { symbol, _, startOffset, endOffset, origin, fcode ->
|
withDeserializedIrDeclarationBase(proto.base) { symbol, _, startOffset, endOffset, origin, fcode ->
|
||||||
val flags = LocalVariableFlags.decode(fcode)
|
val flags = LocalVariableFlags.decode(fcode)
|
||||||
val nameType = BinaryNameAndType.decode(proto.nameType)
|
val nameType = BinaryNameAndType.decode(proto.nameType)
|
||||||
@@ -535,10 +542,12 @@ class IrDeclarationDeserializer(
|
|||||||
symbolTable.declareEnumEntry(uniqId, { symbol as IrEnumEntrySymbol }) {
|
symbolTable.declareEnumEntry(uniqId, { symbol as IrEnumEntrySymbol }) {
|
||||||
irFactory.createEnumEntry(startOffset, endOffset, origin, it, deserializeName(proto.name))
|
irFactory.createEnumEntry(startOffset, endOffset, origin, it, deserializeName(proto.name))
|
||||||
}.apply {
|
}.apply {
|
||||||
if (proto.hasCorrespondingClass())
|
if (!skipMutableState) {
|
||||||
correspondingClass = deserializeIrClass(proto.correspondingClass)
|
if (proto.hasCorrespondingClass())
|
||||||
if (proto.hasInitializer())
|
correspondingClass = deserializeIrClass(proto.correspondingClass)
|
||||||
initializerExpression = deserializeExpressionBody(proto.initializer)
|
if (proto.hasInitializer())
|
||||||
|
initializerExpression = deserializeExpressionBody(proto.initializer)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -607,10 +616,12 @@ class IrDeclarationDeserializer(
|
|||||||
deserializeIrType(nameAndType.typeIndex),
|
deserializeIrType(nameAndType.typeIndex),
|
||||||
flags.isVar
|
flags.isVar
|
||||||
).apply {
|
).apply {
|
||||||
delegate = deserializeIrVariable(proto.delegate)
|
if (!skipMutableState) {
|
||||||
getter = deserializeIrFunction(proto.getter)
|
delegate = deserializeIrVariable(proto.delegate)
|
||||||
if (proto.hasSetter())
|
getter = deserializeIrFunction(proto.getter)
|
||||||
setter = deserializeIrFunction(proto.setter)
|
if (proto.hasSetter())
|
||||||
|
setter = deserializeIrFunction(proto.setter)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -634,20 +645,22 @@ class IrDeclarationDeserializer(
|
|||||||
flags.isFakeOverride
|
flags.isFakeOverride
|
||||||
)
|
)
|
||||||
}.apply {
|
}.apply {
|
||||||
withExternalValue(isExternal) {
|
if (!skipMutableState) {
|
||||||
if (proto.hasGetter()) {
|
withExternalValue(isExternal) {
|
||||||
getter = deserializeIrFunction(proto.getter).also {
|
if (proto.hasGetter()) {
|
||||||
it.correspondingPropertySymbol = symbol
|
getter = deserializeIrFunction(proto.getter).also {
|
||||||
|
it.correspondingPropertySymbol = symbol
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if (proto.hasSetter()) {
|
||||||
if (proto.hasSetter()) {
|
setter = deserializeIrFunction(proto.setter).also {
|
||||||
setter = deserializeIrFunction(proto.setter).also {
|
it.correspondingPropertySymbol = symbol
|
||||||
it.correspondingPropertySymbol = symbol
|
}
|
||||||
}
|
}
|
||||||
}
|
if (proto.hasBackingField()) {
|
||||||
if (proto.hasBackingField()) {
|
backingField = deserializeIrField(proto.backingField).also {
|
||||||
backingField = deserializeIrField(proto.backingField).also {
|
it.correspondingPropertySymbol = symbol
|
||||||
it.correspondingPropertySymbol = symbol
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -661,7 +674,7 @@ class IrDeclarationDeserializer(
|
|||||||
allKnownDeclarationOrigins.map { it.objectInstance as IrDeclarationOriginImpl }.associateBy { it.name }
|
allKnownDeclarationOrigins.map { it.objectInstance as IrDeclarationOriginImpl }.associateBy { it.name }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun deserializeIrDeclarationOrigin(protoName: Int): IrDeclarationOriginImpl {
|
fun deserializeIrDeclarationOrigin(protoName: Int): IrDeclarationOriginImpl {
|
||||||
val originName = fileReader.deserializeString(protoName)
|
val originName = fileReader.deserializeString(protoName)
|
||||||
return declarationOriginIndex[originName] ?: object : IrDeclarationOriginImpl(originName) {}
|
return declarationOriginIndex[originName] ?: object : IrDeclarationOriginImpl(originName) {}
|
||||||
}
|
}
|
||||||
|
|||||||
+148
-94
@@ -24,7 +24,6 @@ import org.jetbrains.kotlin.library.impl.IrMemoryDeclarationWriter
|
|||||||
import org.jetbrains.kotlin.library.impl.IrMemoryStringWriter
|
import org.jetbrains.kotlin.library.impl.IrMemoryStringWriter
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.types.Variance
|
import org.jetbrains.kotlin.types.Variance
|
||||||
import org.jetbrains.kotlin.backend.common.serialization.proto.AccessorIdSignature as ProtoAccessorIdSignature
|
|
||||||
import org.jetbrains.kotlin.backend.common.serialization.proto.Actual as ProtoActual
|
import org.jetbrains.kotlin.backend.common.serialization.proto.Actual as ProtoActual
|
||||||
import org.jetbrains.kotlin.backend.common.serialization.proto.FieldAccessCommon as ProtoFieldAccessCommon
|
import org.jetbrains.kotlin.backend.common.serialization.proto.FieldAccessCommon as ProtoFieldAccessCommon
|
||||||
import org.jetbrains.kotlin.backend.common.serialization.proto.FileEntry as ProtoFileEntry
|
import org.jetbrains.kotlin.backend.common.serialization.proto.FileEntry as ProtoFileEntry
|
||||||
@@ -53,9 +52,9 @@ import org.jetbrains.kotlin.backend.common.serialization.proto.IrDynamicOperator
|
|||||||
import org.jetbrains.kotlin.backend.common.serialization.proto.IrDynamicType as ProtoDynamicType
|
import org.jetbrains.kotlin.backend.common.serialization.proto.IrDynamicType as ProtoDynamicType
|
||||||
import org.jetbrains.kotlin.backend.common.serialization.proto.IrEnumConstructorCall as ProtoEnumConstructorCall
|
import org.jetbrains.kotlin.backend.common.serialization.proto.IrEnumConstructorCall as ProtoEnumConstructorCall
|
||||||
import org.jetbrains.kotlin.backend.common.serialization.proto.IrEnumEntry as ProtoEnumEntry
|
import org.jetbrains.kotlin.backend.common.serialization.proto.IrEnumEntry as ProtoEnumEntry
|
||||||
import org.jetbrains.kotlin.backend.common.serialization.proto.IrErrorCallExpression as ProtoErrorCallExpression
|
|
||||||
import org.jetbrains.kotlin.backend.common.serialization.proto.IrErrorDeclaration as ProtoErrorDeclaration
|
import org.jetbrains.kotlin.backend.common.serialization.proto.IrErrorDeclaration as ProtoErrorDeclaration
|
||||||
import org.jetbrains.kotlin.backend.common.serialization.proto.IrErrorExpression as ProtoErrorExpression
|
import org.jetbrains.kotlin.backend.common.serialization.proto.IrErrorExpression as ProtoErrorExpression
|
||||||
|
import org.jetbrains.kotlin.backend.common.serialization.proto.IrErrorCallExpression as ProtoErrorCallExpression
|
||||||
import org.jetbrains.kotlin.backend.common.serialization.proto.IrErrorType as ProtoErrorType
|
import org.jetbrains.kotlin.backend.common.serialization.proto.IrErrorType as ProtoErrorType
|
||||||
import org.jetbrains.kotlin.backend.common.serialization.proto.IrExpression as ProtoExpression
|
import org.jetbrains.kotlin.backend.common.serialization.proto.IrExpression as ProtoExpression
|
||||||
import org.jetbrains.kotlin.backend.common.serialization.proto.IrField as ProtoField
|
import org.jetbrains.kotlin.backend.common.serialization.proto.IrField as ProtoField
|
||||||
@@ -102,13 +101,15 @@ import org.jetbrains.kotlin.backend.common.serialization.proto.Loop as ProtoLoop
|
|||||||
import org.jetbrains.kotlin.backend.common.serialization.proto.MemberAccessCommon as ProtoMemberAccessCommon
|
import org.jetbrains.kotlin.backend.common.serialization.proto.MemberAccessCommon as ProtoMemberAccessCommon
|
||||||
import org.jetbrains.kotlin.backend.common.serialization.proto.NullableIrExpression as ProtoNullableIrExpression
|
import org.jetbrains.kotlin.backend.common.serialization.proto.NullableIrExpression as ProtoNullableIrExpression
|
||||||
import org.jetbrains.kotlin.backend.common.serialization.proto.PublicIdSignature as ProtoPublicIdSignature
|
import org.jetbrains.kotlin.backend.common.serialization.proto.PublicIdSignature as ProtoPublicIdSignature
|
||||||
|
import org.jetbrains.kotlin.backend.common.serialization.proto.AccessorIdSignature as ProtoAccessorIdSignature
|
||||||
|
|
||||||
open class IrFileSerializer(
|
open class IrFileSerializer(
|
||||||
val messageLogger: IrMessageLogger,
|
val messageLogger: IrMessageLogger,
|
||||||
private val declarationTable: DeclarationTable,
|
private val declarationTable: DeclarationTable,
|
||||||
private val expectDescriptorToSymbol: MutableMap<DeclarationDescriptor, IrSymbol>,
|
private val expectDescriptorToSymbol: MutableMap<DeclarationDescriptor, IrSymbol>,
|
||||||
private val bodiesOnlyForInlines: Boolean = false,
|
private val bodiesOnlyForInlines: Boolean = false,
|
||||||
private val skipExpects: Boolean = false
|
private val skipExpects: Boolean = false,
|
||||||
|
private val skipMutableState: Boolean = false, // required for JS IC caches
|
||||||
) {
|
) {
|
||||||
private val loopIndex = mutableMapOf<IrLoop, Int>()
|
private val loopIndex = mutableMapOf<IrLoop, Int>()
|
||||||
private var currentLoopIndex = 0
|
private var currentLoopIndex = 0
|
||||||
@@ -154,19 +155,19 @@ open class IrFileSerializer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun serializeIrExpressionBody(expression: IrExpression): Int {
|
fun serializeIrExpressionBody(expression: IrExpression): Int {
|
||||||
protoBodyArray.add(XStatementOrExpression.XExpression(serializeExpression(expression)))
|
protoBodyArray.add(XStatementOrExpression.XExpression(serializeExpression(expression)))
|
||||||
return protoBodyArray.size - 1
|
return protoBodyArray.size - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun serializeIrStatementBody(statement: IrElement): Int {
|
fun serializeIrStatementBody(statement: IrElement): Int {
|
||||||
protoBodyArray.add(XStatementOrExpression.XStatement(serializeStatement(statement)))
|
protoBodyArray.add(XStatementOrExpression.XStatement(serializeStatement(statement)))
|
||||||
return protoBodyArray.size - 1
|
return protoBodyArray.size - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------- Common fields ---------------------------------------------------- */
|
/* ------- Common fields ---------------------------------------------------- */
|
||||||
|
|
||||||
private fun serializeIrDeclarationOrigin(origin: IrDeclarationOrigin): Int = serializeString((origin as IrDeclarationOriginImpl).name)
|
fun serializeIrDeclarationOrigin(origin: IrDeclarationOrigin): Int = serializeString((origin as IrDeclarationOriginImpl).name)
|
||||||
|
|
||||||
private fun serializeIrStatementOrigin(origin: IrStatementOrigin): Int = serializeString((origin as IrStatementOriginImpl).debugName)
|
private fun serializeIrStatementOrigin(origin: IrStatementOrigin): Int = serializeString((origin as IrStatementOriginImpl).debugName)
|
||||||
|
|
||||||
@@ -238,7 +239,7 @@ open class IrFileSerializer(
|
|||||||
return protoIdSignature(idSig)
|
return protoIdSignature(idSig)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun protoIdSignature(idSig: IdSignature): Int {
|
fun protoIdSignature(idSig: IdSignature): Int {
|
||||||
return protoIdSignatureMap.getOrPut(idSig) {
|
return protoIdSignatureMap.getOrPut(idSig) {
|
||||||
protoIdSignatureArray.add(serializeIdSignature(idSig))
|
protoIdSignatureArray.add(serializeIdSignature(idSig))
|
||||||
protoIdSignatureArray.size - 1
|
protoIdSignatureArray.size - 1
|
||||||
@@ -247,41 +248,43 @@ open class IrFileSerializer(
|
|||||||
|
|
||||||
/* ------- IrSymbols -------------------------------------------------------- */
|
/* ------- IrSymbols -------------------------------------------------------- */
|
||||||
|
|
||||||
private fun protoSymbolKind(symbol: IrSymbol): BinarySymbolData.SymbolKind = when (symbol) {
|
companion object {
|
||||||
is IrAnonymousInitializerSymbol ->
|
fun protoSymbolKind(symbol: IrSymbol): BinarySymbolData.SymbolKind = when (symbol) {
|
||||||
BinarySymbolData.SymbolKind.ANONYMOUS_INIT_SYMBOL
|
is IrAnonymousInitializerSymbol ->
|
||||||
is IrClassSymbol ->
|
BinarySymbolData.SymbolKind.ANONYMOUS_INIT_SYMBOL
|
||||||
BinarySymbolData.SymbolKind.CLASS_SYMBOL
|
is IrClassSymbol ->
|
||||||
is IrConstructorSymbol ->
|
BinarySymbolData.SymbolKind.CLASS_SYMBOL
|
||||||
BinarySymbolData.SymbolKind.CONSTRUCTOR_SYMBOL
|
is IrConstructorSymbol ->
|
||||||
is IrTypeParameterSymbol ->
|
BinarySymbolData.SymbolKind.CONSTRUCTOR_SYMBOL
|
||||||
BinarySymbolData.SymbolKind.TYPE_PARAMETER_SYMBOL
|
is IrTypeParameterSymbol ->
|
||||||
is IrEnumEntrySymbol ->
|
BinarySymbolData.SymbolKind.TYPE_PARAMETER_SYMBOL
|
||||||
BinarySymbolData.SymbolKind.ENUM_ENTRY_SYMBOL
|
is IrEnumEntrySymbol ->
|
||||||
is IrVariableSymbol ->
|
BinarySymbolData.SymbolKind.ENUM_ENTRY_SYMBOL
|
||||||
BinarySymbolData.SymbolKind.VARIABLE_SYMBOL
|
is IrVariableSymbol ->
|
||||||
is IrValueParameterSymbol ->
|
BinarySymbolData.SymbolKind.VARIABLE_SYMBOL
|
||||||
if (symbol.descriptor is ReceiverParameterDescriptor) // TODO: we use descriptor here.
|
is IrValueParameterSymbol ->
|
||||||
BinarySymbolData.SymbolKind.RECEIVER_PARAMETER_SYMBOL
|
if (symbol.descriptor is ReceiverParameterDescriptor) // TODO: we use descriptor here.
|
||||||
else
|
BinarySymbolData.SymbolKind.RECEIVER_PARAMETER_SYMBOL
|
||||||
BinarySymbolData.SymbolKind.VALUE_PARAMETER_SYMBOL
|
else
|
||||||
is IrSimpleFunctionSymbol ->
|
BinarySymbolData.SymbolKind.VALUE_PARAMETER_SYMBOL
|
||||||
BinarySymbolData.SymbolKind.FUNCTION_SYMBOL
|
is IrSimpleFunctionSymbol ->
|
||||||
is IrReturnableBlockSymbol ->
|
BinarySymbolData.SymbolKind.FUNCTION_SYMBOL
|
||||||
BinarySymbolData.SymbolKind.RETURNABLE_BLOCK_SYMBOL
|
is IrReturnableBlockSymbol ->
|
||||||
is IrFieldSymbol ->
|
BinarySymbolData.SymbolKind.RETURNABLE_BLOCK_SYMBOL
|
||||||
if (symbol.owner.correspondingPropertySymbol?.owner.let { it == null || it.isDelegated })
|
is IrFieldSymbol ->
|
||||||
BinarySymbolData.SymbolKind.STANDALONE_FIELD_SYMBOL
|
if (symbol.owner.correspondingPropertySymbol?.owner.let { it == null || it.isDelegated })
|
||||||
else
|
BinarySymbolData.SymbolKind.STANDALONE_FIELD_SYMBOL
|
||||||
BinarySymbolData.SymbolKind.FIELD_SYMBOL
|
else
|
||||||
is IrPropertySymbol ->
|
BinarySymbolData.SymbolKind.FIELD_SYMBOL
|
||||||
BinarySymbolData.SymbolKind.PROPERTY_SYMBOL
|
is IrPropertySymbol ->
|
||||||
is IrLocalDelegatedPropertySymbol ->
|
BinarySymbolData.SymbolKind.PROPERTY_SYMBOL
|
||||||
BinarySymbolData.SymbolKind.LOCAL_DELEGATED_PROPERTY_SYMBOL
|
is IrLocalDelegatedPropertySymbol ->
|
||||||
is IrTypeAliasSymbol ->
|
BinarySymbolData.SymbolKind.LOCAL_DELEGATED_PROPERTY_SYMBOL
|
||||||
BinarySymbolData.SymbolKind.TYPEALIAS_SYMBOL
|
is IrTypeAliasSymbol ->
|
||||||
else ->
|
BinarySymbolData.SymbolKind.TYPEALIAS_SYMBOL
|
||||||
TODO("Unexpected symbol kind: $symbol")
|
else ->
|
||||||
|
TODO("Unexpected symbol kind: $symbol")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun serializeIrSymbol(symbol: IrSymbol): Long {
|
fun serializeIrSymbol(symbol: IrSymbol): Long {
|
||||||
@@ -412,7 +415,7 @@ open class IrFileSerializer(
|
|||||||
type = (this as? IrTypeProjection)?.type?.toIrTypeKey
|
type = (this as? IrTypeProjection)?.type?.toIrTypeKey
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun serializeIrType(type: IrType) = protoTypeMap.getOrPut(type.toIrTypeKey) {
|
fun serializeIrType(type: IrType) = protoTypeMap.getOrPut(type.toIrTypeKey) {
|
||||||
protoTypeArray.add(serializeIrTypeData(type))
|
protoTypeArray.add(serializeIrTypeData(type))
|
||||||
protoTypeArray.size - 1
|
protoTypeArray.size - 1
|
||||||
}
|
}
|
||||||
@@ -518,7 +521,7 @@ open class IrFileSerializer(
|
|||||||
return proto.build()
|
return proto.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun serializeConstructorCall(call: IrConstructorCall): ProtoConstructorCall =
|
fun serializeConstructorCall(call: IrConstructorCall): ProtoConstructorCall =
|
||||||
ProtoConstructorCall.newBuilder().apply {
|
ProtoConstructorCall.newBuilder().apply {
|
||||||
symbol = serializeIrSymbol(call.symbol)
|
symbol = serializeIrSymbol(call.symbol)
|
||||||
constructorTypeArgumentsCount = call.constructorTypeArgumentsCount
|
constructorTypeArgumentsCount = call.constructorTypeArgumentsCount
|
||||||
@@ -741,7 +744,7 @@ open class IrFileSerializer(
|
|||||||
IrTypeOperator.IMPLICIT_DYNAMIC_CAST ->
|
IrTypeOperator.IMPLICIT_DYNAMIC_CAST ->
|
||||||
ProtoTypeOperator.IMPLICIT_DYNAMIC_CAST
|
ProtoTypeOperator.IMPLICIT_DYNAMIC_CAST
|
||||||
IrTypeOperator.REINTERPRET_CAST ->
|
IrTypeOperator.REINTERPRET_CAST ->
|
||||||
error("Unreachable execution")
|
ProtoTypeOperator.REINTERPRET_CAST
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun serializeTypeOp(expression: IrTypeOperatorCall): ProtoTypeOp {
|
private fun serializeTypeOp(expression: IrTypeOperatorCall): ProtoTypeOp {
|
||||||
@@ -1024,8 +1027,10 @@ open class IrFileSerializer(
|
|||||||
.setBase(serializeIrDeclarationBase(parameter, ValueParameterFlags.encode(parameter)))
|
.setBase(serializeIrDeclarationBase(parameter, ValueParameterFlags.encode(parameter)))
|
||||||
.setNameType(serializeNameAndType(parameter.name, parameter.type))
|
.setNameType(serializeNameAndType(parameter.name, parameter.type))
|
||||||
|
|
||||||
parameter.varargElementType?.let { proto.setVarargElementType(serializeIrType(it)) }
|
if (!skipMutableState) {
|
||||||
parameter.defaultValue?.let { proto.setDefaultValue(serializeIrExpressionBody(it.expression)) }
|
parameter.varargElementType?.let { proto.setVarargElementType(serializeIrType(it)) }
|
||||||
|
parameter.defaultValue?.let { proto.setDefaultValue(serializeIrExpressionBody(it.expression)) }
|
||||||
|
}
|
||||||
|
|
||||||
return proto.build()
|
return proto.build()
|
||||||
}
|
}
|
||||||
@@ -1045,18 +1050,21 @@ open class IrFileSerializer(
|
|||||||
.setBase(serializeIrDeclarationBase(function, flags))
|
.setBase(serializeIrDeclarationBase(function, flags))
|
||||||
.setNameType(serializeNameAndType(function.name, function.returnType))
|
.setNameType(serializeNameAndType(function.name, function.returnType))
|
||||||
|
|
||||||
function.typeParameters.forEach {
|
if (!skipMutableState) {
|
||||||
proto.addTypeParameter(serializeIrTypeParameter(it))
|
function.typeParameters.forEach {
|
||||||
|
proto.addTypeParameter(serializeIrTypeParameter(it))
|
||||||
|
}
|
||||||
|
function.dispatchReceiverParameter?.let { proto.setDispatchReceiver(serializeIrValueParameter(it)) }
|
||||||
|
function.extensionReceiverParameter?.let { proto.setExtensionReceiver(serializeIrValueParameter(it)) }
|
||||||
|
function.valueParameters.forEach {
|
||||||
|
proto.addValueParameter(serializeIrValueParameter(it))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bodiesOnlyForInlines || function.isInline) {
|
||||||
|
function.body?.let { proto.body = serializeIrStatementBody(it) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function.dispatchReceiverParameter?.let { proto.setDispatchReceiver(serializeIrValueParameter(it)) }
|
|
||||||
function.extensionReceiverParameter?.let { proto.setExtensionReceiver(serializeIrValueParameter(it)) }
|
|
||||||
function.valueParameters.forEach {
|
|
||||||
proto.addValueParameter(serializeIrValueParameter(it))
|
|
||||||
}
|
|
||||||
if (!bodiesOnlyForInlines || function.isInline) {
|
|
||||||
function.body?.let { proto.body = serializeIrStatementBody(it) }
|
|
||||||
}
|
|
||||||
return proto.build()
|
return proto.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1076,20 +1084,27 @@ open class IrFileSerializer(
|
|||||||
return proto.build()
|
return proto.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun serializeIrAnonymousInit(declaration: IrAnonymousInitializer) =
|
private fun serializeIrAnonymousInit(declaration: IrAnonymousInitializer): ProtoAnonymousInit {
|
||||||
ProtoAnonymousInit.newBuilder()
|
val proto = ProtoAnonymousInit.newBuilder()
|
||||||
.setBase(serializeIrDeclarationBase(declaration, null))
|
.setBase(serializeIrDeclarationBase(declaration, null))
|
||||||
.setBody(serializeIrStatementBody(declaration.body))
|
|
||||||
.build()
|
if (!skipMutableState) {
|
||||||
|
proto.setBody(serializeIrStatementBody(declaration.body))
|
||||||
|
}
|
||||||
|
|
||||||
|
return proto.build()
|
||||||
|
}
|
||||||
|
|
||||||
private fun serializeIrLocalDelegatedProperty(variable: IrLocalDelegatedProperty): ProtoLocalDelegatedProperty {
|
private fun serializeIrLocalDelegatedProperty(variable: IrLocalDelegatedProperty): ProtoLocalDelegatedProperty {
|
||||||
val proto = ProtoLocalDelegatedProperty.newBuilder()
|
val proto = ProtoLocalDelegatedProperty.newBuilder()
|
||||||
.setBase(serializeIrDeclarationBase(variable, LocalVariableFlags.encode(variable)))
|
.setBase(serializeIrDeclarationBase(variable, LocalVariableFlags.encode(variable)))
|
||||||
.setNameType(serializeNameAndType(variable.name, variable.type))
|
.setNameType(serializeNameAndType(variable.name, variable.type))
|
||||||
.setDelegate(serializeIrVariable(variable.delegate))
|
|
||||||
.setGetter(serializeIrFunction(variable.getter))
|
|
||||||
|
|
||||||
variable.setter?.let { proto.setSetter(serializeIrFunction(it)) }
|
if (!skipMutableState) {
|
||||||
|
proto.delegate = serializeIrVariable(variable.delegate)
|
||||||
|
proto.getter = serializeIrFunction(variable.getter)
|
||||||
|
variable.setter?.let { proto.setSetter(serializeIrFunction(it)) }
|
||||||
|
}
|
||||||
|
|
||||||
return proto.build()
|
return proto.build()
|
||||||
}
|
}
|
||||||
@@ -1099,15 +1114,11 @@ open class IrFileSerializer(
|
|||||||
.setBase(serializeIrDeclarationBase(property, PropertyFlags.encode(property)))
|
.setBase(serializeIrDeclarationBase(property, PropertyFlags.encode(property)))
|
||||||
.setName(serializeName(property.name))
|
.setName(serializeName(property.name))
|
||||||
|
|
||||||
val backingField = property.backingField
|
if (!skipMutableState) {
|
||||||
val getter = property.getter
|
property.backingField?.let { proto.backingField = serializeIrField(it) }
|
||||||
val setter = property.setter
|
property.getter?.let { proto.getter = serializeIrFunction(it) }
|
||||||
if (backingField != null)
|
property.setter?.let { proto.setter = serializeIrFunction(it) }
|
||||||
proto.backingField = serializeIrField(backingField)
|
}
|
||||||
if (getter != null)
|
|
||||||
proto.getter = serializeIrFunction(getter)
|
|
||||||
if (setter != null)
|
|
||||||
proto.setter = serializeIrFunction(setter)
|
|
||||||
|
|
||||||
return proto.build()
|
return proto.build()
|
||||||
}
|
}
|
||||||
@@ -1116,14 +1127,16 @@ open class IrFileSerializer(
|
|||||||
val proto = ProtoField.newBuilder()
|
val proto = ProtoField.newBuilder()
|
||||||
.setBase(serializeIrDeclarationBase(field, FieldFlags.encode(field)))
|
.setBase(serializeIrDeclarationBase(field, FieldFlags.encode(field)))
|
||||||
.setNameType(serializeNameAndType(field.name, field.type))
|
.setNameType(serializeNameAndType(field.name, field.type))
|
||||||
val initializer = field.initializer?.expression
|
if (!skipMutableState) {
|
||||||
if (initializer != null) {
|
val initializer = field.initializer?.expression
|
||||||
proto.initializer = serializeIrExpressionBody(initializer)
|
if (initializer != null) {
|
||||||
|
proto.initializer = serializeIrExpressionBody(initializer)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return proto.build()
|
return proto.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun serializeIrVariable(variable: IrVariable): ProtoVariable {
|
fun serializeIrVariable(variable: IrVariable): ProtoVariable {
|
||||||
val proto = ProtoVariable.newBuilder()
|
val proto = ProtoVariable.newBuilder()
|
||||||
.setBase(serializeIrDeclarationBase(variable, LocalVariableFlags.encode(variable)))
|
.setBase(serializeIrDeclarationBase(variable, LocalVariableFlags.encode(variable)))
|
||||||
.setNameType(serializeNameAndType(variable.name, variable.type))
|
.setNameType(serializeNameAndType(variable.name, variable.type))
|
||||||
@@ -1136,19 +1149,21 @@ open class IrFileSerializer(
|
|||||||
.setBase(serializeIrDeclarationBase(clazz, ClassFlags.encode(clazz)))
|
.setBase(serializeIrDeclarationBase(clazz, ClassFlags.encode(clazz)))
|
||||||
.setName(serializeName(clazz.name))
|
.setName(serializeName(clazz.name))
|
||||||
|
|
||||||
clazz.declarations.forEach {
|
if (!skipMutableState) {
|
||||||
if (memberNeedsSerialization(it)) proto.addDeclaration(serializeDeclaration(it))
|
clazz.declarations.forEach {
|
||||||
}
|
if (memberNeedsSerialization(it)) proto.addDeclaration(serializeDeclaration(it))
|
||||||
|
}
|
||||||
|
|
||||||
clazz.typeParameters.forEach {
|
clazz.typeParameters.forEach {
|
||||||
proto.addTypeParameter(serializeIrTypeParameter(it))
|
proto.addTypeParameter(serializeIrTypeParameter(it))
|
||||||
}
|
}
|
||||||
|
|
||||||
clazz.superTypes.forEach {
|
clazz.thisReceiver?.let { proto.thisReceiver = serializeIrValueParameter(it) }
|
||||||
proto.addSuperType(serializeIrType(it))
|
|
||||||
}
|
|
||||||
|
|
||||||
clazz.thisReceiver?.let { proto.thisReceiver = serializeIrValueParameter(it) }
|
clazz.superTypes.forEach {
|
||||||
|
proto.addSuperType(serializeIrType(it))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return proto.build()
|
return proto.build()
|
||||||
}
|
}
|
||||||
@@ -1160,7 +1175,9 @@ open class IrFileSerializer(
|
|||||||
.setNameType(serializeNameAndType(typeAlias.name, typeAlias.expandedType))
|
.setNameType(serializeNameAndType(typeAlias.name, typeAlias.expandedType))
|
||||||
|
|
||||||
typeAlias.typeParameters.forEach {
|
typeAlias.typeParameters.forEach {
|
||||||
proto.addTypeParameter(serializeIrTypeParameter(it))
|
if (!skipMutableState) {
|
||||||
|
proto.addTypeParameter(serializeIrTypeParameter(it))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return proto.build()
|
return proto.build()
|
||||||
@@ -1177,16 +1194,18 @@ open class IrFileSerializer(
|
|||||||
.setBase(serializeIrDeclarationBase(enumEntry, null))
|
.setBase(serializeIrDeclarationBase(enumEntry, null))
|
||||||
.setName(serializeName(enumEntry.name))
|
.setName(serializeName(enumEntry.name))
|
||||||
|
|
||||||
enumEntry.initializerExpression?.let {
|
if (!skipMutableState) {
|
||||||
proto.initializer = serializeIrExpressionBody(it.expression)
|
enumEntry.initializerExpression?.let {
|
||||||
}
|
proto.initializer = serializeIrExpressionBody(it.expression)
|
||||||
enumEntry.correspondingClass?.let {
|
}
|
||||||
proto.correspondingClass = serializeIrClass(it)
|
enumEntry.correspondingClass?.let {
|
||||||
|
proto.correspondingClass = serializeIrClass(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return proto.build()
|
return proto.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun serializeDeclaration(declaration: IrDeclaration): ProtoDeclaration {
|
fun serializeDeclaration(declaration: IrDeclaration): ProtoDeclaration {
|
||||||
val proto = ProtoDeclaration.newBuilder()
|
val proto = ProtoDeclaration.newBuilder()
|
||||||
|
|
||||||
when (declaration) {
|
when (declaration) {
|
||||||
@@ -1279,6 +1298,41 @@ open class IrFileSerializer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun serializeDeclarationsForIC(file: IrFile, declarations: Iterable<IrDeclaration>): SerializedIrFile {
|
||||||
|
val proto = ProtoFile.newBuilder()
|
||||||
|
.setFileEntry(serializeFileEntry(file.fileEntry))
|
||||||
|
.addAllFqName(serializeFqName(file.fqName.asString()))
|
||||||
|
|
||||||
|
val topLevelDeclarations = mutableListOf<SerializedDeclaration>()
|
||||||
|
|
||||||
|
for (declaration in declarations) {
|
||||||
|
val byteArray = serializeDeclaration(declaration).toByteArray()
|
||||||
|
val idSig = declarationTable.signatureByDeclaration(declaration)
|
||||||
|
|
||||||
|
// TODO: keep order similar
|
||||||
|
// ^ TODO what does that mean?
|
||||||
|
val sigIndex = protoIdSignatureMap[idSig]
|
||||||
|
?: if (declaration is IrErrorDeclaration) protoIdSignature(idSig) else error("Not found ID for $idSig (${declaration.render()})")
|
||||||
|
|
||||||
|
topLevelDeclarations.add(TopLevelDeclaration(sigIndex, idSig.toString(), byteArray))
|
||||||
|
}
|
||||||
|
|
||||||
|
topLevelDeclarations.forEach {
|
||||||
|
proto.addDeclarationId(it.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return SerializedIrFile(
|
||||||
|
proto.build().toByteArray(),
|
||||||
|
file.fqName.asString(),
|
||||||
|
file.path,
|
||||||
|
IrMemoryArrayWriter(protoTypeArray.map { it.toByteArray() }).writeIntoMemory(),
|
||||||
|
IrMemoryArrayWriter(protoIdSignatureArray.map { it.toByteArray() }).writeIntoMemory(),
|
||||||
|
IrMemoryArrayWriter(protoStringArray.map { it.toByteArray() }).writeIntoMemory(),
|
||||||
|
IrMemoryArrayWriter(protoBodyArray.map { it.toByteArray() }).writeIntoMemory(),
|
||||||
|
IrMemoryDeclarationWriter(topLevelDeclarations).writeIntoMemory()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fun serializeIrFile(file: IrFile): SerializedIrFile {
|
fun serializeIrFile(file: IrFile): SerializedIrFile {
|
||||||
val topLevelDeclarations = mutableListOf<SerializedDeclaration>()
|
val topLevelDeclarations = mutableListOf<SerializedDeclaration>()
|
||||||
|
|
||||||
|
|||||||
+5
-2
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.common.serialization.DeclarationTable
|
|||||||
import org.jetbrains.kotlin.backend.common.serialization.IrFileSerializer
|
import org.jetbrains.kotlin.backend.common.serialization.IrFileSerializer
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrAnnotationContainer
|
import org.jetbrains.kotlin.ir.declarations.IrAnnotationContainer
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||||
import org.jetbrains.kotlin.ir.util.IrMessageLogger
|
import org.jetbrains.kotlin.ir.util.IrMessageLogger
|
||||||
import org.jetbrains.kotlin.ir.util.hasAnnotation
|
import org.jetbrains.kotlin.ir.util.hasAnnotation
|
||||||
@@ -19,13 +20,15 @@ class JsIrFileSerializer(
|
|||||||
declarationTable: DeclarationTable,
|
declarationTable: DeclarationTable,
|
||||||
expectDescriptorToSymbol: MutableMap<DeclarationDescriptor, IrSymbol>,
|
expectDescriptorToSymbol: MutableMap<DeclarationDescriptor, IrSymbol>,
|
||||||
skipExpects: Boolean,
|
skipExpects: Boolean,
|
||||||
bodiesOnlyForInlines: Boolean = false
|
bodiesOnlyForInlines: Boolean = false,
|
||||||
|
icMode: Boolean = false,
|
||||||
) : IrFileSerializer(
|
) : IrFileSerializer(
|
||||||
messageLogger,
|
messageLogger,
|
||||||
declarationTable,
|
declarationTable,
|
||||||
expectDescriptorToSymbol,
|
expectDescriptorToSymbol,
|
||||||
bodiesOnlyForInlines = bodiesOnlyForInlines,
|
bodiesOnlyForInlines = bodiesOnlyForInlines,
|
||||||
skipExpects = skipExpects
|
skipExpects = skipExpects,
|
||||||
|
skipMutableState = icMode,
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
private val JS_EXPORT_FQN = FqName("kotlin.js.JsExport")
|
private val JS_EXPORT_FQN = FqName("kotlin.js.JsExport")
|
||||||
|
|||||||
Reference in New Issue
Block a user