backend: implement autoboxing as IR transformation
This commit is contained in:
committed by
SvyatoslavScherbina
parent
3b7321ef94
commit
44f2036506
+233
@@ -0,0 +1,233 @@
|
||||
package org.jetbrains.kotlin.backend.common
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.declarations.IrField
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
|
||||
|
||||
/**
|
||||
* Transforms expressions depending on the context they are used in.
|
||||
*
|
||||
* The transformations are defined with `IrExpression.use*` methods in this class,
|
||||
* the most common are [useAs], [useAsStatement], [useInTypeOperator].
|
||||
*
|
||||
* TODO: the implementation is originally based on [org.jetbrains.kotlin.psi2ir.transformations.InsertImplicitCasts]
|
||||
* and should probably be used as its base.
|
||||
*
|
||||
* TODO: consider making this visitor non-recursive to make it more general.
|
||||
*/
|
||||
abstract class AbstractValueUsageTransformer(val builtIns: KotlinBuiltIns): IrElementTransformerVoid() {
|
||||
|
||||
protected open fun IrExpression.useAs(type: KotlinType): IrExpression = this
|
||||
|
||||
protected open fun IrExpression.useAsStatement(): IrExpression = this
|
||||
|
||||
protected open fun IrExpression.useInTypeOperator(operator: IrTypeOperator, typeOperand: KotlinType): IrExpression =
|
||||
this
|
||||
|
||||
protected open fun IrExpression.useAsValue(value: ValueDescriptor): IrExpression = this.useAs(value.type)
|
||||
|
||||
protected open fun IrExpression.useAsArgument(parameter: ParameterDescriptor): IrExpression =
|
||||
this.useAsValue(parameter)
|
||||
|
||||
protected open fun IrExpression.useForVariable(variable: VariableDescriptor): IrExpression =
|
||||
this.useAsValue(variable)
|
||||
|
||||
protected open fun IrExpression.useForField(field: PropertyDescriptor): IrExpression =
|
||||
this.useForVariable(field)
|
||||
|
||||
protected open fun IrExpression.useAsReturnValue(returnTarget: CallableDescriptor): IrExpression {
|
||||
val returnType = returnTarget.returnType ?: return this
|
||||
return this.useAs(returnType)
|
||||
}
|
||||
|
||||
protected open fun IrExpression.useAsResult(enclosing: IrExpression): IrExpression =
|
||||
this.useAs(enclosing.type)
|
||||
|
||||
override fun visitMemberAccess(expression: IrMemberAccessExpression): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
with(expression) {
|
||||
dispatchReceiver = dispatchReceiver?.useAsArgument(descriptor.dispatchReceiverParameter!!)
|
||||
extensionReceiver = extensionReceiver?.useAsArgument(descriptor.extensionReceiverParameter!!)
|
||||
for (index in descriptor.valueParameters.indices) {
|
||||
val argument = getValueArgument(index) ?: continue
|
||||
val parameter = descriptor.valueParameters[index]
|
||||
putValueArgument(index, argument.useAsArgument(parameter))
|
||||
}
|
||||
}
|
||||
|
||||
return expression
|
||||
}
|
||||
|
||||
override fun visitBlockBody(body: IrBlockBody): IrBody {
|
||||
body.transformChildrenVoid(this)
|
||||
|
||||
body.statements.forEachIndexed { i, irStatement ->
|
||||
if (irStatement is IrExpression) {
|
||||
body.statements[i] = irStatement.useAsStatement()
|
||||
}
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
override fun visitContainerExpression(expression: IrContainerExpression): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
if (expression.statements.isEmpty()) {
|
||||
return expression
|
||||
}
|
||||
|
||||
val lastIndex = expression.statements.lastIndex
|
||||
expression.statements.forEachIndexed { i, irStatement ->
|
||||
if (irStatement is IrExpression) {
|
||||
expression.statements[i] =
|
||||
if (i == lastIndex)
|
||||
irStatement.useAsResult(expression)
|
||||
else
|
||||
irStatement.useAsStatement()
|
||||
}
|
||||
}
|
||||
|
||||
return expression
|
||||
}
|
||||
|
||||
override fun visitReturn(expression: IrReturn): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
expression.value = expression.value.useAsReturnValue(expression.returnTarget)
|
||||
|
||||
return expression
|
||||
}
|
||||
|
||||
override fun visitSetVariable(expression: IrSetVariable): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
expression.value = expression.value.useForVariable(expression.descriptor)
|
||||
|
||||
return expression
|
||||
}
|
||||
|
||||
override fun visitSetField(expression: IrSetField): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
expression.value = expression.value.useForField(expression.descriptor)
|
||||
|
||||
return expression
|
||||
}
|
||||
|
||||
override fun visitField(declaration: IrField): IrStatement {
|
||||
declaration.transformChildrenVoid(this)
|
||||
|
||||
declaration.initializer?.let {
|
||||
it.expression = it.expression.useForField(declaration.descriptor)
|
||||
}
|
||||
|
||||
return declaration
|
||||
}
|
||||
|
||||
override fun visitVariable(declaration: IrVariable): IrVariable {
|
||||
declaration.transformChildrenVoid(this)
|
||||
|
||||
declaration.initializer = declaration.initializer?.useForVariable(declaration.descriptor)
|
||||
|
||||
return declaration
|
||||
}
|
||||
|
||||
override fun visitWhen(expression: IrWhen): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
for (irBranch in expression.branches) {
|
||||
irBranch.condition = irBranch.condition.useAs(builtIns.booleanType)
|
||||
irBranch.result = irBranch.result.useAsResult(expression)
|
||||
}
|
||||
|
||||
return expression
|
||||
}
|
||||
|
||||
override fun visitLoop(loop: IrLoop): IrExpression {
|
||||
loop.transformChildrenVoid(this)
|
||||
|
||||
loop.condition = loop.condition.useAs(builtIns.booleanType)
|
||||
|
||||
loop.body = loop.body?.useAsStatement()
|
||||
|
||||
return loop
|
||||
}
|
||||
|
||||
override fun visitThrow(expression: IrThrow): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
expression.value = expression.value.useAs(builtIns.throwable.defaultType)
|
||||
|
||||
return expression
|
||||
}
|
||||
|
||||
override fun visitTry(aTry: IrTry): IrExpression {
|
||||
aTry.transformChildrenVoid(this)
|
||||
|
||||
aTry.tryResult = aTry.tryResult.useAsResult(aTry)
|
||||
|
||||
for (aCatch in aTry.catches) {
|
||||
aCatch.result = aCatch.result.useAsResult(aTry)
|
||||
}
|
||||
|
||||
aTry.finallyExpression = aTry.finallyExpression?.useAsStatement()
|
||||
|
||||
return aTry
|
||||
}
|
||||
|
||||
override fun visitVararg(expression: IrVararg): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
expression.elements.forEachIndexed { i, element ->
|
||||
when (element) {
|
||||
is IrSpreadElement ->
|
||||
element.expression = element.expression.useAs(expression.type)
|
||||
is IrExpression ->
|
||||
expression.putElement(i, element.useAs(expression.varargElementType))
|
||||
}
|
||||
}
|
||||
|
||||
return expression
|
||||
}
|
||||
|
||||
override fun visitTypeOperator(expression: IrTypeOperatorCall): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
expression.argument = expression.argument.useInTypeOperator(expression.operator, expression.typeOperand)
|
||||
|
||||
return expression
|
||||
}
|
||||
|
||||
override fun visitFunction(declaration: IrFunction): IrStatement {
|
||||
declaration.transformChildrenVoid(this)
|
||||
|
||||
declaration.descriptor.valueParameters.forEach { parameter ->
|
||||
val defaultValue = declaration.getDefault(parameter)
|
||||
if (defaultValue is IrExpressionBody) {
|
||||
defaultValue.expression = defaultValue.expression.useAsArgument(parameter)
|
||||
}
|
||||
}
|
||||
|
||||
declaration.body?.let {
|
||||
if (it is IrExpressionBody) {
|
||||
it.expression = it.expression.useAsReturnValue(declaration.descriptor)
|
||||
}
|
||||
}
|
||||
|
||||
return declaration
|
||||
}
|
||||
|
||||
// TODO: IrStringConcatenation, IrEnumEntry?
|
||||
|
||||
}
|
||||
|
||||
+28
-9
@@ -8,6 +8,7 @@ import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.OverridingUtil
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.*
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
@@ -45,7 +46,6 @@ internal val FunctionDescriptor.implementation: FunctionDescriptor
|
||||
private val intrinsicTypes = setOf(
|
||||
"kotlin.Unit",
|
||||
"kotlin.Boolean", "kotlin.Char",
|
||||
"kotlin.Number",
|
||||
"kotlin.Byte", "kotlin.Short",
|
||||
"kotlin.Int", "kotlin.Long",
|
||||
"kotlin.Float", "kotlin.Double"
|
||||
@@ -74,23 +74,42 @@ internal val ClassDescriptor.isArray: Boolean
|
||||
internal val ClassDescriptor.isInterface: Boolean
|
||||
get() = (this.kind == ClassKind.INTERFACE)
|
||||
|
||||
private val konanInternal = FqName.fromSegments(listOf("konan", "internal"))
|
||||
private val konanInternalPackageName = FqName.fromSegments(listOf("konan", "internal"))
|
||||
|
||||
/**
|
||||
* @return `konan.internal` member scope
|
||||
*/
|
||||
internal val KonanBuiltIns.konanInternal: MemberScope
|
||||
get() = this.builtInsModule.getPackage(konanInternalPackageName).memberScope
|
||||
|
||||
/**
|
||||
* @return built-in class `konan.internal.$name` or
|
||||
* `null` if no such class is available (e.g. when compiling `link` test without stdlib).
|
||||
*
|
||||
* TODO: remove this workaround after removing compilation without stdlib.
|
||||
*/
|
||||
internal fun KonanBuiltIns.getKonanInternalClassOrNull(name: String): ClassDescriptor? {
|
||||
val classifier = konanInternal.getContributedClassifier(Name.identifier(name), NoLookupLocation.FROM_BACKEND)
|
||||
return classifier as? ClassDescriptor
|
||||
}
|
||||
|
||||
/**
|
||||
* @return built-in class `konan.internal.$name`
|
||||
*/
|
||||
internal fun KonanBuiltIns.getKonanInternalClass(name: String): ClassDescriptor {
|
||||
val classifier = this.builtInsModule
|
||||
.getPackage(konanInternal).memberScope
|
||||
.getContributedClassifier(Name.identifier(name), NoLookupLocation.FROM_BACKEND)
|
||||
internal fun KonanBuiltIns.getKonanInternalClass(name: String): ClassDescriptor =
|
||||
getKonanInternalClassOrNull(name)!!
|
||||
|
||||
return classifier as ClassDescriptor
|
||||
internal fun KonanBuiltIns.getKonanInternalFunctions(name: String): List<FunctionDescriptor> {
|
||||
return konanInternal.getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_BACKEND).toList()
|
||||
}
|
||||
|
||||
private val UNBOUND_CALLABLE_REFERENCE = "UnboundCallableReference"
|
||||
|
||||
internal val KonanBuiltIns.unboundCallableReferenceType: KotlinType
|
||||
get() = this.getKonanInternalClass(UNBOUND_CALLABLE_REFERENCE).defaultType
|
||||
/**
|
||||
* `konan.internal.UnboundCallableReference` type or `null` if it is not available.
|
||||
*/
|
||||
internal val KonanBuiltIns.unboundCallableReferenceTypeOrNull: KotlinType?
|
||||
get() = this.getKonanInternalClassOrNull(UNBOUND_CALLABLE_REFERENCE)?.defaultType
|
||||
|
||||
internal fun KotlinType.isUnboundCallableReference(): Boolean {
|
||||
val classDescriptor = TypeUtils.getClassDescriptor(this) ?: return false
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ internal class KonanLower(val context: Context) {
|
||||
CallableReferenceLowering(context).runOnFilePostfix(irFile)
|
||||
}
|
||||
phaser.phase("Autobox") {
|
||||
Autoboxing(context).runOnFilePostfix(irFile)
|
||||
Autoboxing(context).lower(irFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -1310,9 +1310,10 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
|
||||
private fun evaluateCast(tmpVariableName: String, value: IrTypeOperatorCall): LLVMValueRef {
|
||||
logger.log("evaluateCast : ${ir2string(value)}")
|
||||
assert(!KotlinBuiltIns.isPrimitiveType(value.type) && !KotlinBuiltIns.isPrimitiveType(value.argument.type))
|
||||
val type = value.typeOperand
|
||||
assert(!KotlinBuiltIns.isPrimitiveType(type) && !KotlinBuiltIns.isPrimitiveType(value.argument.type))
|
||||
|
||||
val dstDescriptor = TypeUtils.getClassDescriptor(value.type) // Get class descriptor for dst type.
|
||||
val dstDescriptor = TypeUtils.getClassDescriptor(type) // Get class descriptor for dst type.
|
||||
val dstTypeInfo = codegen.typeInfoValue(dstDescriptor!!) // Get TypeInfo for dst type.
|
||||
val srcArg = evaluateExpression(codegen.newVar(), value.argument)!! // Evaluate src expression.
|
||||
val srcObjInfoPtr = codegen.bitcast(codegen.kObjHeaderPtr, srcArg, codegen.newVar()) // Cast src to ObjInfoPtr.
|
||||
|
||||
+183
-15
@@ -1,22 +1,190 @@
|
||||
package org.jetbrains.kotlin.backend.konan.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.*
|
||||
import org.jetbrains.kotlin.backend.konan.KonanBackendContext
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.ir2string
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrBody
|
||||
import org.jetbrains.kotlin.backend.common.AbstractValueUsageTransformer
|
||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.getKonanInternalClass
|
||||
import org.jetbrains.kotlin.backend.konan.getKonanInternalFunctions
|
||||
import org.jetbrains.kotlin.backend.konan.unboundCallableReferenceTypeOrNull
|
||||
import org.jetbrains.kotlin.descriptors.ParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrTypeOperator
|
||||
import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.SimpleType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNullable
|
||||
import org.jetbrains.kotlin.utils.singletonOrEmptyList
|
||||
|
||||
internal class Autoboxing(val context: KonanBackendContext) : FunctionLoweringPass {
|
||||
/**
|
||||
* Boxes and unboxes values of primitive types when necessary.
|
||||
*/
|
||||
internal class Autoboxing(val context: Context) : FileLoweringPass {
|
||||
|
||||
var boxedCount = 0
|
||||
|
||||
/*
|
||||
override fun lower(irBody: IrBody) {
|
||||
println("Boxer body " + ir2string(irBody))
|
||||
} */
|
||||
|
||||
override fun lower(irFunction: IrFunction) {
|
||||
println("Boxer function " + ir2string(irFunction))
|
||||
private val transformer = AutoboxingTransformer(context)
|
||||
|
||||
override fun lower(irFile: IrFile) {
|
||||
irFile.transformChildrenVoid(transformer)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class AutoboxingTransformer(val context: Context) : AbstractValueUsageTransformer(context.builtIns) {
|
||||
|
||||
// TODO: should we handle the cases when expression type
|
||||
// is not equal to e.g. called function return type?
|
||||
|
||||
|
||||
private val irBuiltins = context.irModule!!.irBuiltins
|
||||
|
||||
/**
|
||||
* The list of primitive types to box and unbox.
|
||||
*/
|
||||
private val primitiveTypes = with(context.builtIns) {
|
||||
listOf(booleanType, byteType, shortType, charType, intType, longType, floatType, doubleType) +
|
||||
unboundCallableReferenceTypeOrNull.singletonOrEmptyList()
|
||||
}
|
||||
|
||||
/**
|
||||
* @return type to use for runtime type checks instead of given one (e.g. `IntBox` instead of `Int`)
|
||||
*/
|
||||
private fun getRuntimeReferenceType(type: KotlinType): KotlinType {
|
||||
if (type.isSubtypeOf(builtIns.nullableNothingType)) return type
|
||||
|
||||
primitiveTypes.forEach {
|
||||
listOf(it, it.makeNullable()).forEach { superType ->
|
||||
if (type.isSubtypeOf(superType)) {
|
||||
return getBoxType(superType).makeNullableAsSpecified(superType.isMarkedNullable)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return type
|
||||
}
|
||||
|
||||
override fun IrExpression.useInTypeOperator(operator: IrTypeOperator, typeOperand: KotlinType): IrExpression {
|
||||
return if (operator == IrTypeOperator.IMPLICIT_COERCION_TO_UNIT) {
|
||||
this
|
||||
} else {
|
||||
// Codegen expects the argument of type-checking operator to be object reference:
|
||||
this.useAs(builtIns.nullableAnyType)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitTypeOperator(expression: IrTypeOperatorCall): IrExpression {
|
||||
super.visitTypeOperator(expression).let {
|
||||
// Assume that the transformer doesn't replace the entire expression for simplicity:
|
||||
assert (it === expression)
|
||||
}
|
||||
|
||||
val newTypeOperand = getRuntimeReferenceType(expression.typeOperand)
|
||||
|
||||
return when (expression.operator) {
|
||||
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> expression
|
||||
|
||||
IrTypeOperator.CAST, IrTypeOperator.IMPLICIT_CAST,
|
||||
IrTypeOperator.IMPLICIT_NOTNULL, IrTypeOperator.SAFE_CAST -> {
|
||||
|
||||
// Codegen produces the object reference:
|
||||
val newExpressionType = builtIns.nullableAnyType
|
||||
|
||||
IrTypeOperatorCallImpl(expression.startOffset, expression.endOffset,
|
||||
newExpressionType, expression.operator, newTypeOperand,
|
||||
expression.argument).useAs(expression.type)
|
||||
}
|
||||
|
||||
IrTypeOperator.INSTANCEOF, IrTypeOperator.NOT_INSTANCEOF -> if (newTypeOperand == expression.typeOperand) {
|
||||
// Do not create new expression if nothing changes:
|
||||
expression
|
||||
} else {
|
||||
IrTypeOperatorCallImpl(expression.startOffset, expression.endOffset,
|
||||
expression.type, expression.operator, newTypeOperand, expression.argument)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun IrExpression.useAsArgument(parameter: ParameterDescriptor): IrExpression {
|
||||
return if (parameter.containingDeclaration == irBuiltins.eqeq) {
|
||||
this // Do not box arguments of `==` because codegen expects them to be unboxed.
|
||||
// TODO: implement `==` lowering at IR level and remove this hack.
|
||||
} else {
|
||||
this.useAsValue(parameter)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the element of [primitiveTypes] if given type is represented as primitive type in generated code,
|
||||
* or `null` if represented as object reference.
|
||||
*/
|
||||
private fun getCustomRepresentation(type: KotlinType): KotlinType? {
|
||||
if (type.isSubtypeOf(builtIns.nothingType)) return null
|
||||
|
||||
return primitiveTypes.firstOrNull { type.isSubtypeOf(it) }
|
||||
}
|
||||
|
||||
override fun IrExpression.useAs(type: KotlinType): IrExpression {
|
||||
return this.adaptIfNecessary(type)
|
||||
}
|
||||
|
||||
private fun IrExpression.adaptIfNecessary(expectedType: KotlinType): IrExpression {
|
||||
val thisRepresentation = getCustomRepresentation(this.type)
|
||||
val expectedRepresentation = getCustomRepresentation(expectedType)
|
||||
|
||||
return when {
|
||||
thisRepresentation == expectedRepresentation -> this
|
||||
|
||||
thisRepresentation == null && expectedRepresentation != null -> {
|
||||
// This may happen in the following cases:
|
||||
// 1. `this.type` is `Nothing`;
|
||||
// 2. `this` has the incompatible type.
|
||||
|
||||
this.unbox(expectedRepresentation)
|
||||
}
|
||||
|
||||
thisRepresentation != null && expectedRepresentation == null -> this.box(thisRepresentation)
|
||||
|
||||
else -> throw IllegalArgumentException("this is ${this.type}, expected $expectedType")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Casts this expression to `type` without changing its representation in generated code.
|
||||
*/
|
||||
private fun IrExpression.uncheckedCast(type: KotlinType): IrExpression {
|
||||
// TODO: apply some cast if types are incompatible; not required currently.
|
||||
return this
|
||||
}
|
||||
|
||||
private fun getBoxType(primitiveType: KotlinType): SimpleType {
|
||||
val primitiveTypeClass = TypeUtils.getClassDescriptor(primitiveType)!!
|
||||
return context.builtIns.getKonanInternalClass("${primitiveTypeClass.name}Box").defaultType
|
||||
}
|
||||
|
||||
private fun IrExpression.box(primitiveType: KotlinType): IrExpression {
|
||||
val primitiveTypeClass = TypeUtils.getClassDescriptor(primitiveType)!!
|
||||
val boxFunction = context.builtIns.getKonanInternalFunctions("box${primitiveTypeClass.name}").single()
|
||||
|
||||
return IrCallImpl(startOffset, endOffset, boxFunction).apply {
|
||||
putValueArgument(0, this@box)
|
||||
}.uncheckedCast(this.type) // Try not to bring new type incompatibilities.
|
||||
}
|
||||
|
||||
private fun IrExpression.unbox(primitiveType: KotlinType): IrExpression {
|
||||
val boxGetter = getBoxType(primitiveType)
|
||||
.memberScope.getContributedDescriptors()
|
||||
.filterIsInstance<PropertyDescriptor>()
|
||||
.single { it.name.asString() == "value" }
|
||||
.getter!!
|
||||
|
||||
return IrCallImpl(startOffset, endOffset, boxGetter).apply {
|
||||
dispatchReceiver = this@unbox.uncheckedCast(boxGetter.dispatchReceiverParameter!!.type)
|
||||
}.uncheckedCast(this.type) // Try not to bring new type incompatibilities.
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package konan.internal
|
||||
|
||||
// TODO: cache some boxes.
|
||||
|
||||
class BooleanBox(val value: Boolean) : Comparable<Boolean> {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is BooleanBox) {
|
||||
return false
|
||||
}
|
||||
|
||||
return this.value == other.value
|
||||
}
|
||||
|
||||
override fun hashCode() = value.hashCode()
|
||||
|
||||
override fun toString() = value.toString()
|
||||
|
||||
override fun compareTo(other: Boolean): Int = value.compareTo(other)
|
||||
}
|
||||
|
||||
fun boxBoolean(value: Boolean) = BooleanBox(value)
|
||||
|
||||
class IntBox(val value: Int) : Number(), Comparable<Int> {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is IntBox) {
|
||||
return false
|
||||
}
|
||||
|
||||
return this.value == other.value
|
||||
}
|
||||
|
||||
override fun hashCode() = value.hashCode()
|
||||
|
||||
override fun toString() = value.toString()
|
||||
|
||||
override fun compareTo(other: Int): Int = value.compareTo(other)
|
||||
|
||||
override fun toByte() = value.toByte()
|
||||
override fun toChar() = value.toChar()
|
||||
override fun toShort() = value.toShort()
|
||||
override fun toInt() = value.toInt()
|
||||
override fun toLong() = value.toLong()
|
||||
override fun toFloat() = value.toFloat()
|
||||
override fun toDouble() = value.toDouble()
|
||||
}
|
||||
|
||||
fun boxInt(value: Int) = IntBox(value)
|
||||
|
||||
// TODO: support other boxes.
|
||||
Reference in New Issue
Block a user