Implement inline classes in the JVM_IR backend
This commit is contained in:
committed by
Alexander Udalov
parent
ad3e03bdbd
commit
9c957edcea
@@ -31,7 +31,7 @@ open class FrameMapBase<T : Any> {
|
||||
var currentSize = 0
|
||||
private set
|
||||
|
||||
fun enter(descriptor: T, type: Type): Int {
|
||||
open fun enter(descriptor: T, type: Type): Int {
|
||||
val index = currentSize
|
||||
myVarIndex.put(descriptor, index)
|
||||
currentSize += type.size
|
||||
@@ -39,7 +39,7 @@ open class FrameMapBase<T : Any> {
|
||||
return index
|
||||
}
|
||||
|
||||
fun leave(descriptor: T): Int {
|
||||
open fun leave(descriptor: T): Int {
|
||||
val size = myVarSizes.get(descriptor)
|
||||
currentSize -= size
|
||||
myVarSizes.remove(descriptor)
|
||||
|
||||
@@ -449,7 +449,7 @@ public abstract class StackValue {
|
||||
v.invokevirtual(methodOwner.getInternalName(), type.getClassName() + "Value", "()" + type.getDescriptor(), false);
|
||||
}
|
||||
|
||||
private static void boxInlineClass(@NotNull KotlinType kotlinType, @NotNull InstructionAdapter v) {
|
||||
public static void boxInlineClass(@NotNull KotlinType kotlinType, @NotNull InstructionAdapter v) {
|
||||
Type boxedType = KotlinTypeMapper.mapInlineClassTypeAsDeclaration(kotlinType);
|
||||
Type underlyingType = KotlinTypeMapper.mapUnderlyingTypeOfInlineClassType(kotlinType);
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ private fun getSignatureElementForMangling(type: KotlinType): String = buildStri
|
||||
}
|
||||
}
|
||||
|
||||
private fun md5base64(signatureForMangling: String): String {
|
||||
fun md5base64(signatureForMangling: String): String {
|
||||
val d = MessageDigest.getInstance("MD5").digest(signatureForMangling.toByteArray()).copyOfRange(0, 5)
|
||||
// base64 URL encoder without padding uses exactly the characters allowed in both JVM bytecode and Dalvik bytecode names
|
||||
return Base64.getUrlEncoder().withoutPadding().encodeToString(d)
|
||||
|
||||
@@ -170,10 +170,7 @@ fun IrTypeParameter.copyToWithoutSuperTypes(
|
||||
}
|
||||
}
|
||||
|
||||
fun IrFunction.copyParameterDeclarationsFrom(from: IrFunction) {
|
||||
assert(typeParameters.isEmpty())
|
||||
copyTypeParametersFrom(from)
|
||||
|
||||
fun IrFunction.copyValueParametersFrom(from: IrFunction) {
|
||||
// TODO: should dispatch receiver be copied?
|
||||
dispatchReceiverParameter = from.dispatchReceiverParameter?.let {
|
||||
IrValueParameterImpl(it.startOffset, it.endOffset, it.origin, it.descriptor, it.type, it.varargElementType).also {
|
||||
@@ -186,23 +183,33 @@ fun IrFunction.copyParameterDeclarationsFrom(from: IrFunction) {
|
||||
valueParameters += from.valueParameters.map { it.copyTo(this, index = it.index + shift) }
|
||||
}
|
||||
|
||||
fun IrTypeParametersContainer.copyTypeParametersFrom(
|
||||
source: IrTypeParametersContainer,
|
||||
fun IrFunction.copyParameterDeclarationsFrom(from: IrFunction) {
|
||||
assert(typeParameters.isEmpty())
|
||||
copyTypeParametersFrom(from)
|
||||
copyValueParametersFrom(from)
|
||||
}
|
||||
|
||||
fun IrTypeParametersContainer.copyTypeParameters(
|
||||
srcTypeParameters: List<IrTypeParameter>,
|
||||
origin: IrDeclarationOrigin? = null
|
||||
) {
|
||||
val target = this
|
||||
val shift = target.typeParameters.size
|
||||
val shift = typeParameters.size
|
||||
// Any type parameter can figure in a boundary type for any other parameter.
|
||||
// Therefore, we first copy the parameters themselves, then set up their supertypes.
|
||||
source.typeParameters.forEachIndexed { i, sourceParameter ->
|
||||
srcTypeParameters.forEachIndexed { i, sourceParameter ->
|
||||
assert(sourceParameter.index == i)
|
||||
target.typeParameters.add(sourceParameter.copyToWithoutSuperTypes(target, shift = shift, origin = origin ?: sourceParameter.origin))
|
||||
typeParameters.add(sourceParameter.copyToWithoutSuperTypes(this, shift = shift, origin = origin ?: sourceParameter.origin))
|
||||
}
|
||||
source.typeParameters.zip(target.typeParameters.drop(shift)).forEach { (srcParameter, dstParameter) ->
|
||||
srcTypeParameters.zip(typeParameters.drop(shift)).forEach { (srcParameter, dstParameter) ->
|
||||
dstParameter.copySuperTypesFrom(srcParameter)
|
||||
}
|
||||
}
|
||||
|
||||
fun IrTypeParametersContainer.copyTypeParametersFrom(
|
||||
source: IrTypeParametersContainer,
|
||||
origin: IrDeclarationOrigin? = null
|
||||
) = copyTypeParameters(source.typeParameters, origin)
|
||||
|
||||
private fun IrTypeParameter.copySuperTypesFrom(source: IrTypeParameter) {
|
||||
val target = this
|
||||
val sourceParent = source.parent as IrTypeParametersContainer
|
||||
@@ -379,6 +386,9 @@ fun IrClass.simpleFunctions() = declarations.flatMap {
|
||||
}
|
||||
}
|
||||
|
||||
val IrClass.primaryConstructor: IrConstructor?
|
||||
get() = constructors.singleOrNull(IrConstructor::isPrimary)
|
||||
|
||||
fun IrClass.createParameterDeclarations() {
|
||||
assert (thisReceiver == null)
|
||||
|
||||
|
||||
+2
-2
@@ -101,7 +101,7 @@ inline fun IrProperty.addSetter(b: IrFunctionBuilder.() -> Unit = {}): IrSimpleF
|
||||
}
|
||||
}
|
||||
|
||||
fun IrFunctionBuilder.buildFun(): IrSimpleFunction {
|
||||
fun IrFunctionBuilder.buildFun(): IrFunctionImpl {
|
||||
val wrappedDescriptor = WrappedSimpleFunctionDescriptor()
|
||||
return IrFunctionImpl(
|
||||
startOffset, endOffset, origin,
|
||||
@@ -126,7 +126,7 @@ fun IrFunctionBuilder.buildConstructor(): IrConstructor {
|
||||
}
|
||||
}
|
||||
|
||||
inline fun buildFun(b: IrFunctionBuilder.() -> Unit): IrSimpleFunction =
|
||||
inline fun buildFun(b: IrFunctionBuilder.() -> Unit): IrFunctionImpl =
|
||||
IrFunctionBuilder().run {
|
||||
b()
|
||||
buildFun()
|
||||
|
||||
@@ -38,6 +38,7 @@ interface JvmLoweredDeclarationOrigin : IrDeclarationOrigin {
|
||||
object GENERATED_PROPERTY_REFERENCE : IrDeclarationOriginImpl("GENERATED_PROPERTY_REFERENCE", isSynthetic = true)
|
||||
object GENERATED_SAM_IMPLEMENTATION : IrDeclarationOriginImpl("GENERATED_SAM_IMPLEMENTATION", isSynthetic = true)
|
||||
object ENUM_MAPPINGS_FOR_WHEN : IrDeclarationOriginImpl("ENUM_MAPPINGS_FOR_WHEN", isSynthetic = true)
|
||||
object SYNTHETIC_INLINE_CLASS_MEMBER : IrDeclarationOriginImpl("SYNTHETIC_INLINE_CLASS_MEMBER", isSynthetic = true)
|
||||
}
|
||||
|
||||
interface JvmLoweredStatementOrigin : IrStatementOrigin {
|
||||
|
||||
@@ -116,6 +116,7 @@ val jvmPhases = namedIrFilePhase<JvmBackendContext>(
|
||||
tailrecPhase then
|
||||
|
||||
jvmDefaultConstructorPhase then
|
||||
jvmInlineClassPhase then
|
||||
defaultArgumentStubPhase then
|
||||
|
||||
interfacePhase then
|
||||
|
||||
@@ -257,4 +257,17 @@ class JvmSymbols(
|
||||
|
||||
val getOrCreateKotlinClasses: IrSimpleFunctionSymbol =
|
||||
reflection.functions.single { it.owner.name.asString() == "getOrCreateKotlinClasses" }
|
||||
|
||||
val unsafeCoerceIntrinsic =
|
||||
buildFun {
|
||||
name = Name.special("<unsafe-coerce>")
|
||||
origin = IrDeclarationOrigin.IR_BUILTINS_STUB
|
||||
}.apply {
|
||||
parent = kotlinJvmInternalPackage
|
||||
val src = addTypeParameter("T", irBuiltIns.anyNType)
|
||||
val dst = addTypeParameter("R", irBuiltIns.anyNType)
|
||||
addValueParameter("v", src.defaultType)
|
||||
returnType = dst.defaultType
|
||||
}
|
||||
val unsafeCoerceIntrinsicSymbol = unsafeCoerceIntrinsic.symbol
|
||||
}
|
||||
|
||||
+1
-1
@@ -66,7 +66,7 @@ open class ClassCodegen protected constructor(
|
||||
|
||||
val type: Type = if (isAnonymous)
|
||||
state.bindingContext.get(ASM_TYPE, descriptor)!!
|
||||
else typeMapper.mapType(irClass)
|
||||
else typeMapper.mapClass(irClass)
|
||||
|
||||
private val sourceManager = context.psiSourceManager
|
||||
|
||||
|
||||
+102
-76
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.backend.jvm.intrinsics.JavaClassProperty
|
||||
import org.jetbrains.kotlin.backend.jvm.intrinsics.Not
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.CrIrType
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.constantValue
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.unboxInlineClass
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil.*
|
||||
import org.jetbrains.kotlin.codegen.inline.*
|
||||
@@ -20,7 +21,10 @@ import org.jetbrains.kotlin.codegen.pseudoInsns.fakeAlwaysFalseIfeq
|
||||
import org.jetbrains.kotlin.codegen.pseudoInsns.fixStackAndJump
|
||||
import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter
|
||||
import org.jetbrains.kotlin.config.isReleaseCoroutines
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
@@ -28,9 +32,6 @@ import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.util.dump
|
||||
import org.jetbrains.kotlin.ir.util.parentAsClass
|
||||
import org.jetbrains.kotlin.ir.util.render
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE
|
||||
@@ -128,7 +129,7 @@ class ExpressionCodegen(
|
||||
// Assume this expression's result has already been materialized on the stack
|
||||
// with the correct type.
|
||||
val IrExpression.onStack: MaterialValue
|
||||
get() = MaterialValue(mv, asmType)
|
||||
get() = MaterialValue(this@ExpressionCodegen, asmType, type)
|
||||
|
||||
private fun markNewLabel() = Label().apply { mv.visitLabel(this) }
|
||||
|
||||
@@ -146,9 +147,9 @@ class ExpressionCodegen(
|
||||
}
|
||||
|
||||
// TODO remove
|
||||
fun gen(expression: IrElement, type: Type, data: BlockInfo): StackValue {
|
||||
expression.accept(this, data).coerce(type).materialize()
|
||||
return StackValue.onStack(type)
|
||||
fun gen(expression: IrExpression, type: Type, irType: IrType, data: BlockInfo): StackValue {
|
||||
expression.accept(this, data).coerce(type, irType).materialize()
|
||||
return StackValue.onStack(type, irType.toKotlinType())
|
||||
}
|
||||
|
||||
fun generate() {
|
||||
@@ -168,7 +169,7 @@ class ExpressionCodegen(
|
||||
irFunction.markLineNumber(startOffset = irFunction is IrConstructor && irFunction.isPrimary)
|
||||
}
|
||||
val returnType = typeMapper.mapReturnType(irFunction)
|
||||
result.coerce(returnType).materialize()
|
||||
result.coerce(returnType, if (irFunction !is IrConstructor) irFunction.returnType else null).materialize()
|
||||
mv.areturn(returnType)
|
||||
}
|
||||
val endLabel = markNewLabel()
|
||||
@@ -190,7 +191,7 @@ class ExpressionCodegen(
|
||||
|
||||
private fun generateNonNullAssertion(param: IrValueParameter) {
|
||||
val asmType = param.type.asmType
|
||||
if (!param.type.isNullable() && !isPrimitive(asmType)) {
|
||||
if (!param.type.unboxInlineClass().isNullable() && !isPrimitive(asmType)) {
|
||||
mv.load(findLocalIndex(param.symbol), asmType)
|
||||
mv.aconst(param.name.asString())
|
||||
mv.invokestatic("kotlin/jvm/internal/Intrinsics", "checkParameterIsNotNull", "(Ljava/lang/Object;Ljava/lang/String;)V", false)
|
||||
@@ -286,22 +287,23 @@ class ExpressionCodegen(
|
||||
visitStatementContainer(body, data).discard()
|
||||
|
||||
override fun visitContainerExpression(expression: IrContainerExpression, data: BlockInfo) =
|
||||
visitStatementContainer(expression, data).coerce(expression.asmType)
|
||||
visitStatementContainer(expression, data).coerce(expression.type)
|
||||
|
||||
override fun visitFunctionAccess(expression: IrFunctionAccessExpression, data: BlockInfo): PromisedValue {
|
||||
classCodegen.context.irIntrinsics.getIntrinsic(expression.symbol)
|
||||
?.invoke(expression, this, data)?.let { return it.coerce(expression.asmType) }
|
||||
?.invoke(expression, this, data)?.let { return it.coerce(expression.type) }
|
||||
|
||||
val isSuperCall = (expression as? IrCall)?.superQualifier != null
|
||||
val callable = resolveToCallable(expression, isSuperCall)
|
||||
val callee = expression.symbol.owner
|
||||
val callGenerator = getOrCreateCallGenerator(expression, data)
|
||||
val asmType = if (expression is IrConstructorCall) typeMapper.mapTypeAsDeclaration(expression.type) else expression.asmType
|
||||
|
||||
when {
|
||||
expression is IrConstructorCall -> {
|
||||
// IR constructors have no receiver and return the new instance, but on JVM they are void-returning
|
||||
// instance methods named <init>.
|
||||
mv.anew(expression.asmType)
|
||||
mv.anew(asmType)
|
||||
mv.dup()
|
||||
}
|
||||
expression is IrDelegatingConstructorCall ->
|
||||
@@ -314,15 +316,23 @@ class ExpressionCodegen(
|
||||
val receiver = expression.dispatchReceiver
|
||||
receiver?.apply {
|
||||
callGenerator.genValueAndPut(
|
||||
null, this,
|
||||
callee.dispatchReceiverParameter!!,
|
||||
this,
|
||||
if (isSuperCall) receiver.asmType else callable.dispatchReceiverType
|
||||
?: throw AssertionError("No dispatch receiver type: ${expression.render()}"),
|
||||
-1, this@ExpressionCodegen, data
|
||||
this@ExpressionCodegen,
|
||||
data
|
||||
)
|
||||
}
|
||||
|
||||
expression.extensionReceiver?.apply {
|
||||
callGenerator.genValueAndPut(null, this, callable.extensionReceiverType!!, -1, this@ExpressionCodegen, data)
|
||||
callGenerator.genValueAndPut(
|
||||
callee.extensionReceiverParameter!!,
|
||||
this,
|
||||
callable.extensionReceiverType!!,
|
||||
this@ExpressionCodegen,
|
||||
data
|
||||
)
|
||||
}
|
||||
|
||||
callGenerator.beforeValueParametersStart()
|
||||
@@ -344,7 +354,7 @@ class ExpressionCodegen(
|
||||
val parameterType = callable.valueParameterTypes[i]
|
||||
when {
|
||||
arg != null -> {
|
||||
callGenerator.genValueAndPut(irParameter, arg, parameterType, i, this@ExpressionCodegen, data)
|
||||
callGenerator.genValueAndPut(irParameter, arg, parameterType, this@ExpressionCodegen, data)
|
||||
}
|
||||
irParameter.hasDefaultValue() -> {
|
||||
callGenerator.putValueIfNeeded(
|
||||
@@ -381,19 +391,22 @@ class ExpressionCodegen(
|
||||
expression
|
||||
)
|
||||
|
||||
val returnType = callee.returnType.substitute(typeSubstitutionMap)
|
||||
val returnType = callee.returnType
|
||||
return when {
|
||||
returnType.isNothing() -> {
|
||||
returnType.substitute(typeSubstitutionMap).isNothing() -> {
|
||||
mv.aconst(null)
|
||||
mv.athrow()
|
||||
voidValue
|
||||
}
|
||||
expression is IrConstructorCall -> expression.onStack
|
||||
expression is IrDelegatingConstructorCall -> voidValue
|
||||
expression is IrConstructorCall ->
|
||||
MaterialValue(this, asmType, expression.type)
|
||||
expression is IrDelegatingConstructorCall ->
|
||||
voidValue
|
||||
expression.type.isUnit() ->
|
||||
// NewInference allows casting `() -> T` to `() -> Unit`. A CHECKCAST here will fail.
|
||||
MaterialValue(mv, callable.returnType).discard().coerce(expression.asmType)
|
||||
else -> MaterialValue(mv, callable.returnType).coerce(expression.asmType)
|
||||
MaterialValue(this, callable.returnType, returnType).discard().coerce(expression.type)
|
||||
else ->
|
||||
MaterialValue(this, callable.returnType, returnType).coerce(expression.type)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -404,7 +417,7 @@ class ExpressionCodegen(
|
||||
declaration.markLineNumber(startOffset = true)
|
||||
|
||||
declaration.initializer?.let {
|
||||
it.accept(this, data).coerce(varType).materialize()
|
||||
it.accept(this, data).coerce(varType, declaration.type).materialize()
|
||||
mv.store(index, varType)
|
||||
it.markLineNumber(startOffset = true)
|
||||
}
|
||||
@@ -418,8 +431,9 @@ class ExpressionCodegen(
|
||||
// temporary variables. They do not correspond to variable loads in user code.
|
||||
if (expression.symbol.owner.origin != IrDeclarationOrigin.IR_TEMPORARY_VARIABLE)
|
||||
expression.markLineNumber(startOffset = true)
|
||||
mv.load(findLocalIndex(expression.symbol), expression.asmType)
|
||||
return expression.onStack
|
||||
val type = frameMap.typeOf(expression.symbol)
|
||||
mv.load(findLocalIndex(expression.symbol), type)
|
||||
return MaterialValue(this, type, expression.type)
|
||||
}
|
||||
|
||||
override fun visitFieldAccess(expression: IrFieldAccessExpression, data: BlockInfo): PromisedValue {
|
||||
@@ -428,7 +442,7 @@ class ExpressionCodegen(
|
||||
assert(expression is IrSetField) { "read of const val ${expression.symbol.owner.name} not inlined by ConstLowering" }
|
||||
// This can only be the field's initializer; JVM implementations are required
|
||||
// to generate those for ConstantValue-marked fields automatically, so this is redundant.
|
||||
return voidValue.coerce(expression.asmType)
|
||||
return voidValue.coerce(expression.type)
|
||||
}
|
||||
|
||||
val realField = expression.symbol.owner.resolveFakeOverride()!!
|
||||
@@ -439,18 +453,20 @@ class ExpressionCodegen(
|
||||
expression.markLineNumber(startOffset = true)
|
||||
expression.receiver?.accept(this, data)?.materialize()
|
||||
return if (expression is IrSetField) {
|
||||
expression.value.accept(this, data).coerce(fieldType).materialize()
|
||||
expression.value.accept(this, data).coerce(fieldType, expression.symbol.owner.type).materialize()
|
||||
when {
|
||||
isStatic -> mv.putstatic(ownerType, fieldName, fieldType.descriptor)
|
||||
else -> mv.putfield(ownerType, fieldName, fieldType.descriptor)
|
||||
}
|
||||
voidValue.coerce(expression.asmType)
|
||||
voidValue.coerce(expression.type)
|
||||
} else {
|
||||
when {
|
||||
isStatic -> mv.getstatic(ownerType, fieldName, fieldType.descriptor)
|
||||
else -> mv.getfield(ownerType, fieldName, fieldType.descriptor)
|
||||
isStatic ->
|
||||
mv.getstatic(ownerType, fieldName, fieldType.descriptor)
|
||||
else ->
|
||||
mv.getfield(ownerType, fieldName, fieldType.descriptor)
|
||||
}
|
||||
MaterialValue(mv, fieldType).coerce(expression.asmType)
|
||||
MaterialValue(this, fieldType, expression.symbol.owner.type).coerce(expression.type)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -464,7 +480,7 @@ class ExpressionCodegen(
|
||||
val isFieldInitializer = expression.origin == null
|
||||
val skip = (inPrimaryConstructor || inClassInit) && isFieldInitializer && expressionValue is IrConst<*> &&
|
||||
isDefaultValueForType(expression.symbol.owner.type.asmType, expressionValue.value)
|
||||
return if (skip) voidValue.coerce(expression.asmType) else super.visitSetField(expression, data)
|
||||
return if (skip) voidValue.coerce(expression.type) else super.visitSetField(expression, data)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -493,15 +509,15 @@ class ExpressionCodegen(
|
||||
override fun visitSetVariable(expression: IrSetVariable, data: BlockInfo): PromisedValue {
|
||||
expression.markLineNumber(startOffset = true)
|
||||
expression.value.markLineNumber(startOffset = true)
|
||||
expression.value.accept(this, data).coerce(expression.symbol.owner.asmType).materialize()
|
||||
expression.value.accept(this, data).coerce(expression.symbol.owner.type).materialize()
|
||||
mv.store(findLocalIndex(expression.symbol), expression.symbol.owner.asmType)
|
||||
return voidValue.coerce(expression.asmType)
|
||||
return voidValue.coerce(expression.type)
|
||||
}
|
||||
|
||||
override fun <T> visitConst(expression: IrConst<T>, data: BlockInfo): PromisedValue {
|
||||
expression.markLineNumber(startOffset = true)
|
||||
when (val value = expression.value) {
|
||||
is Boolean -> return object : BooleanValue(mv) {
|
||||
is Boolean -> return object : BooleanValue(this) {
|
||||
override fun jumpIfFalse(target: Label) = if (value) Unit else mv.goTo(target)
|
||||
override fun jumpIfTrue(target: Label) = if (value) mv.goTo(target) else Unit
|
||||
override fun materialize() = mv.iconst(if (value) 1 else 0)
|
||||
@@ -551,7 +567,7 @@ class ExpressionCodegen(
|
||||
Type.getType("[Ljava/lang/Object;")
|
||||
else
|
||||
Type.getType("[" + elementType.descriptor)
|
||||
argument.accept(this, data).coerce(type).materialize()
|
||||
argument.accept(this, data).coerce(type, argument.type).materialize()
|
||||
mv.dup()
|
||||
mv.arraylength()
|
||||
mv.invokestatic("java/util/Arrays", "copyOf", Type.getMethodDescriptor(arrayType, arrayType, Type.INT_TYPE), false)
|
||||
@@ -568,8 +584,8 @@ class ExpressionCodegen(
|
||||
toArrayDescriptor = "([Ljava/lang/Object;)[Ljava/lang/Object;"
|
||||
} else {
|
||||
val spreadBuilderClassName =
|
||||
AsmUtil.asmPrimitiveTypeToLangPrimitiveType(elementType)!!.typeName.identifier + "SpreadBuilder"
|
||||
owner = "kotlin/jvm/internal/" + spreadBuilderClassName
|
||||
asmPrimitiveTypeToLangPrimitiveType(elementType)!!.typeName.identifier + "SpreadBuilder"
|
||||
owner = "kotlin/jvm/internal/$spreadBuilderClassName"
|
||||
addDescriptor = "(" + elementType.descriptor + ")V"
|
||||
toArrayDescriptor = "()" + type.descriptor
|
||||
}
|
||||
@@ -577,14 +593,14 @@ class ExpressionCodegen(
|
||||
mv.dup()
|
||||
mv.iconst(size)
|
||||
mv.invokespecial(owner, "<init>", "(I)V", false)
|
||||
for (i in 0..size - 1) {
|
||||
for (i in 0 until size) {
|
||||
mv.dup()
|
||||
val argument = arguments[i]
|
||||
if (argument is IrSpreadElement) {
|
||||
argument.expression.accept(this, data).coerce(AsmTypes.OBJECT_TYPE).materialize()
|
||||
argument.expression.accept(this, data).coerce(OBJECT_TYPE, argument.expression.type).materialize()
|
||||
mv.invokevirtual(owner, "addSpread", "(Ljava/lang/Object;)V", false)
|
||||
} else {
|
||||
argument.accept(this, data).coerce(elementType).materialize()
|
||||
argument.accept(this, data).coerce(elementType, expression.varargElementType).materialize()
|
||||
mv.invokevirtual(owner, "add", addDescriptor, false)
|
||||
}
|
||||
}
|
||||
@@ -604,7 +620,7 @@ class ExpressionCodegen(
|
||||
for ((i, element) in expression.elements.withIndex()) {
|
||||
mv.dup()
|
||||
mv.iconst(i)
|
||||
element.accept(this, data).coerce(elementType).materialize()
|
||||
element.accept(this, data).coerce(elementType, expression.varargElementType).materialize()
|
||||
mv.astore(elementType)
|
||||
}
|
||||
}
|
||||
@@ -620,13 +636,21 @@ class ExpressionCodegen(
|
||||
mv,
|
||||
this
|
||||
)
|
||||
mv.newarray(boxType(elementIrType.asmType))
|
||||
mv.newarray(typeMapper.boxType(elementIrType))
|
||||
} else {
|
||||
val type = typeMapper.mapType(arrayType)
|
||||
mv.newarray(correctElementType(type))
|
||||
mv.newarray(correctElementType(arrayType.asmType))
|
||||
}
|
||||
}
|
||||
|
||||
// Copied from FinallyBlocksLowering
|
||||
private val IrReturnTarget.returnType: IrType
|
||||
get() = when (this) {
|
||||
is IrConstructor -> context.irBuiltIns.unitType
|
||||
is IrFunction -> returnType
|
||||
is IrReturnableBlock -> type
|
||||
else -> error("Unknown ReturnTarget: $this")
|
||||
}
|
||||
|
||||
override fun visitReturn(expression: IrReturn, data: BlockInfo): PromisedValue {
|
||||
val owner = expression.returnTargetSymbol.owner
|
||||
//TODO: should be owner != irFunction
|
||||
@@ -644,7 +668,7 @@ class ExpressionCodegen(
|
||||
val target = data.findBlock<ReturnableBlockInfo> { it.returnSymbol == expression.returnTargetSymbol }
|
||||
val returnType = typeMapper.mapReturnType(owner)
|
||||
val afterReturnLabel = Label()
|
||||
expression.value.accept(this, data).coerce(returnType).materialize()
|
||||
expression.value.accept(this, data).coerce(returnType, owner.returnType).materialize()
|
||||
generateFinallyBlocksIfNeeded(returnType, afterReturnLabel, data, target)
|
||||
expression.markLineNumber(startOffset = true)
|
||||
if (target != null) {
|
||||
@@ -680,7 +704,7 @@ class ExpressionCodegen(
|
||||
} else {
|
||||
branch.condition.accept(this, data).coerceToBoolean().jumpIfFalse(elseLabel)
|
||||
}
|
||||
val result = branch.result.accept(this, data).coerce(expression.asmType).materialized
|
||||
val result = branch.result.accept(this, data).coerce(expression.type).materialized
|
||||
if (branch.condition.isTrueConst()) {
|
||||
// The rest of the expression is dead code.
|
||||
mv.mark(endLabel)
|
||||
@@ -691,14 +715,13 @@ class ExpressionCodegen(
|
||||
}
|
||||
// Produce the default value for the type. Doesn't really matter right now, as non-exhaustive
|
||||
// conditionals cannot be used as expressions.
|
||||
val result = voidValue.coerce(expression.asmType).materialized
|
||||
val result = voidValue.coerce(expression.type).materialized
|
||||
mv.mark(endLabel)
|
||||
return result
|
||||
}
|
||||
|
||||
override fun visitTypeOperator(expression: IrTypeOperatorCall, data: BlockInfo): PromisedValue {
|
||||
val typeOperand = expression.typeOperand
|
||||
val asmType = typeOperand.asmType
|
||||
val kotlinType = typeOperand.toKotlinType()
|
||||
return when (expression.operator) {
|
||||
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> {
|
||||
@@ -708,30 +731,32 @@ class ExpressionCodegen(
|
||||
}
|
||||
|
||||
IrTypeOperator.IMPLICIT_CAST, IrTypeOperator.IMPLICIT_INTEGER_COERCION -> {
|
||||
expression.argument.accept(this, data).coerce(expression.asmType)
|
||||
expression.argument.accept(this, data).coerce(expression.type)
|
||||
}
|
||||
|
||||
IrTypeOperator.CAST, IrTypeOperator.SAFE_CAST -> {
|
||||
expression.argument.accept(this, data).coerce(AsmTypes.OBJECT_TYPE).materialize()
|
||||
val boxedType = boxType(asmType)
|
||||
val result = expression.argument.accept(this, data)
|
||||
val boxedLeftType = result.irType?.let { typeMapper.boxType(it) } ?: OBJECT_TYPE
|
||||
result.coerce(boxedLeftType, expression.argument.type).materialize()
|
||||
val boxedRightType = typeMapper.boxType(typeOperand)
|
||||
|
||||
if (typeOperand.isReifiedTypeParameter) {
|
||||
putReifiedOperationMarkerIfTypeIsReifiedParameter(
|
||||
typeOperand, if (IrTypeOperator.SAFE_CAST == expression.operator) SAFE_AS else AS, mv, this
|
||||
)
|
||||
v.checkcast(boxedType)
|
||||
v.checkcast(boxedRightType)
|
||||
} else {
|
||||
generateAsCast(
|
||||
mv, kotlinType, boxedType, expression.operator == IrTypeOperator.SAFE_CAST,
|
||||
mv, kotlinType, boxedRightType, expression.operator == IrTypeOperator.SAFE_CAST,
|
||||
state.languageVersionSettings.isReleaseCoroutines()
|
||||
)
|
||||
}
|
||||
MaterialValue(mv, boxedType).coerce(expression.asmType)
|
||||
MaterialValue(this, boxedRightType, expression.type).coerce(expression.type)
|
||||
}
|
||||
|
||||
IrTypeOperator.INSTANCEOF, IrTypeOperator.NOT_INSTANCEOF -> {
|
||||
expression.argument.accept(this, data).coerce(AsmTypes.OBJECT_TYPE).materialize()
|
||||
val type = boxType(asmType)
|
||||
expression.argument.accept(this, data).coerce(OBJECT_TYPE, context.irBuiltIns.anyNType).materialize()
|
||||
val type = typeMapper.boxType(typeOperand)
|
||||
if (typeOperand.isReifiedTypeParameter) {
|
||||
putReifiedOperationMarkerIfTypeIsReifiedParameter(typeOperand, ReifiedTypeInliner.OperationKind.IS, mv, this)
|
||||
v.instanceOf(type)
|
||||
@@ -754,7 +779,7 @@ class ExpressionCodegen(
|
||||
"(Ljava/lang/Object;Ljava/lang/String;)V", false
|
||||
)
|
||||
// Unbox primitives.
|
||||
value.coerce(expression.asmType)
|
||||
value.coerce(expression.type)
|
||||
}
|
||||
|
||||
else -> throw AssertionError("type operator ${expression.operator} should have been lowered")
|
||||
@@ -781,16 +806,19 @@ class ExpressionCodegen(
|
||||
arity == 0 -> mv.aconst("")
|
||||
arity == 1 -> {
|
||||
// Convert single arg to string.
|
||||
val type = expression.arguments[0].accept(this, data).materialized.type
|
||||
if (!expression.arguments[0].type.isString())
|
||||
AsmUtil.genToString(StackValue.onStack(type), type, null, typeMapper.kotlinTypeMapper).put(expression.asmType, mv)
|
||||
val arg = expression.arguments[0]
|
||||
val result = arg.accept(this, data).boxInlineClasses(arg.type).materialized
|
||||
if (!arg.type.isString())
|
||||
AsmUtil.genToString(StackValue.onStack(result.type), result.type, result.kotlinType, typeMapper.kotlinTypeMapper)
|
||||
.put(expression.asmType, mv)
|
||||
}
|
||||
arity == 2 && expression.arguments[0].type.isStringClassType() -> {
|
||||
// Call the stringPlus intrinsic
|
||||
expression.arguments.forEach {
|
||||
val type = it.accept(this, data).materialized.type
|
||||
val result = it.accept(this, data).boxInlineClasses(it.type).materialized
|
||||
val type = result.type
|
||||
if (type.sort != Type.OBJECT)
|
||||
AsmUtil.genToString(StackValue.onStack(type), type, null, typeMapper.kotlinTypeMapper).put(expression.asmType, mv)
|
||||
AsmUtil.genToString(StackValue.onStack(type), type, result.kotlinType, typeMapper.kotlinTypeMapper).put(expression.asmType, mv)
|
||||
}
|
||||
mv.invokestatic(
|
||||
IrIntrinsicMethods.INTRINSICS_CLASS_NAME,
|
||||
@@ -801,9 +829,9 @@ class ExpressionCodegen(
|
||||
}
|
||||
else -> {
|
||||
// Use StringBuilder to concatenate.
|
||||
AsmUtil.genStringBuilderConstructor(mv)
|
||||
genStringBuilderConstructor(mv)
|
||||
expression.arguments.forEach {
|
||||
AsmUtil.genInvokeAppendMethod(mv, it.accept(this, data).materialized.type, null)
|
||||
genInvokeAppendMethod(mv, it.accept(this, data).boxInlineClasses(it.type).materialized.type, null)
|
||||
}
|
||||
mv.invokevirtual("java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false)
|
||||
}
|
||||
@@ -877,7 +905,7 @@ class ExpressionCodegen(
|
||||
val isExpression = true //TODO: more wise check is required
|
||||
var savedValue: Int? = null
|
||||
if (isExpression) {
|
||||
tryResult.coerce(tryAsmType).materialize()
|
||||
tryResult.coerce(tryAsmType, aTry.type).materialize()
|
||||
savedValue = frameMap.enterTemp(tryAsmType)
|
||||
mv.store(savedValue, tryAsmType)
|
||||
} else {
|
||||
@@ -905,7 +933,7 @@ class ExpressionCodegen(
|
||||
catchBody.markLineNumber(true)
|
||||
val catchResult = catchBody.accept(this, data)
|
||||
if (savedValue != null) {
|
||||
catchResult.coerce(tryAsmType).materialize()
|
||||
catchResult.coerce(tryAsmType, aTry.type).materialize()
|
||||
mv.store(savedValue, tryAsmType)
|
||||
} else {
|
||||
catchResult.discard()
|
||||
@@ -1027,14 +1055,13 @@ class ExpressionCodegen(
|
||||
putJavaLangClassInstance(mv, classType.type, null, typeMapper.kotlinTypeMapper)
|
||||
return classReference.onStack
|
||||
} else {
|
||||
val kotlinType = classType.toKotlinType()
|
||||
val classifier = classType.classifierOrNull
|
||||
if (classifier is IrTypeParameterSymbol) {
|
||||
assert(classifier.owner.isReified) { "Non-reified type parameter under ::class should be rejected by type checker: ${classifier.owner.dump()}" }
|
||||
putReifiedOperationMarkerIfTypeIsReifiedParameter(classType, ReifiedTypeInliner.OperationKind.JAVA_CLASS, mv, this)
|
||||
}
|
||||
|
||||
putJavaLangClassInstance(mv, typeMapper.mapType(classType), kotlinType, typeMapper.kotlinTypeMapper)
|
||||
putJavaLangClassInstance(mv, typeMapper.mapType(classType), classType.toKotlinType(), typeMapper.kotlinTypeMapper)
|
||||
}
|
||||
} else {
|
||||
throw AssertionError("not an IrGetClass or IrClassReference: ${classReference.dump()}")
|
||||
@@ -1046,9 +1073,8 @@ class ExpressionCodegen(
|
||||
return classReference.onStack
|
||||
}
|
||||
|
||||
private fun resolveToCallable(irCall: IrFunctionAccessExpression, isSuper: Boolean): Callable {
|
||||
return typeMapper.mapToCallableMethod(irCall.symbol.owner, isSuper)
|
||||
}
|
||||
private fun resolveToCallable(irCall: IrFunctionAccessExpression, isSuper: Boolean) =
|
||||
typeMapper.mapToCallableMethod(irCall.symbol.owner, isSuper)
|
||||
|
||||
private fun getOrCreateCallGenerator(element: IrFunctionAccessExpression, data: BlockInfo): IrCallGenerator {
|
||||
if (!element.symbol.owner.isInlineFunctionCall(context)) {
|
||||
@@ -1182,12 +1208,12 @@ class ExpressionCodegen(
|
||||
|
||||
fun DefaultCallArgs.generateOnStackIfNeeded(callGenerator: IrCallGenerator, isConstructor: Boolean, codegen: ExpressionCodegen): Boolean {
|
||||
val toInts = toInts()
|
||||
if (!toInts.isEmpty()) {
|
||||
if (toInts.isNotEmpty()) {
|
||||
for (mask in toInts) {
|
||||
callGenerator.putValueIfNeeded(Type.INT_TYPE, StackValue.constant(mask, Type.INT_TYPE), ValueKind.DEFAULT_MASK, -1, codegen)
|
||||
}
|
||||
|
||||
val parameterType = if (isConstructor) AsmTypes.DEFAULT_CONSTRUCTOR_MARKER else AsmTypes.OBJECT_TYPE
|
||||
val parameterType = if (isConstructor) AsmTypes.DEFAULT_CONSTRUCTOR_MARKER else OBJECT_TYPE
|
||||
callGenerator.putValueIfNeeded(
|
||||
parameterType,
|
||||
StackValue.constant(null, parameterType),
|
||||
|
||||
+7
-2
@@ -145,12 +145,17 @@ open class FunctionCodegen(
|
||||
?.expression
|
||||
}
|
||||
|
||||
private fun IrFrameMap.enterDispatchReceiver(parameter: IrValueParameter) {
|
||||
val type = classCodegen.typeMapper.mapTypeAsDeclaration(parameter.type)
|
||||
enter(parameter, type)
|
||||
}
|
||||
|
||||
private fun createFrameMapWithReceivers(signature: JvmMethodSignature): IrFrameMap {
|
||||
val frameMap = IrFrameMap()
|
||||
if (irFunction is IrConstructor) {
|
||||
frameMap.enter((irFunction.parent as IrClass).thisReceiver!!, AsmTypes.OBJECT_TYPE)
|
||||
frameMap.enterDispatchReceiver(irFunction.constructedClass.thisReceiver!!)
|
||||
} else if (irFunction.dispatchReceiverParameter != null) {
|
||||
frameMap.enter(irFunction.dispatchReceiverParameter!!, AsmTypes.OBJECT_TYPE)
|
||||
frameMap.enterDispatchReceiver(irFunction.dispatchReceiverParameter!!)
|
||||
}
|
||||
|
||||
for (parameter in signature.valueParameters) {
|
||||
|
||||
+3
-3
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.codegen.ValueKind
|
||||
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
interface IrCallGenerator {
|
||||
@@ -40,14 +41,13 @@ interface IrCallGenerator {
|
||||
}
|
||||
|
||||
fun genValueAndPut(
|
||||
irValueParameter: IrValueParameter?,
|
||||
irValueParameter: IrValueParameter,
|
||||
argumentExpression: IrExpression,
|
||||
parameterType: Type,
|
||||
parameterIndex: Int,
|
||||
codegen: ExpressionCodegen,
|
||||
blockInfo: BlockInfo
|
||||
) {
|
||||
codegen.gen(argumentExpression, parameterType, blockInfo)
|
||||
codegen.gen(argumentExpression, parameterType, irValueParameter.type, blockInfo)
|
||||
}
|
||||
|
||||
fun putValueIfNeeded(parameterType: Type, value: StackValue, kind: ValueKind, parameterIndex: Int, codegen: ExpressionCodegen) {
|
||||
|
||||
+8
-7
@@ -18,6 +18,8 @@ import org.jetbrains.kotlin.ir.declarations.IrVariable
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.util.dump
|
||||
import org.jetbrains.kotlin.ir.util.getArguments
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.utils.keysToMap
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.Method
|
||||
@@ -45,14 +47,13 @@ class IrInlineCodegen(
|
||||
}
|
||||
|
||||
override fun genValueAndPut(
|
||||
irValueParameter: IrValueParameter?,
|
||||
irValueParameter: IrValueParameter,
|
||||
argumentExpression: IrExpression,
|
||||
parameterType: Type,
|
||||
parameterIndex: Int,
|
||||
codegen: ExpressionCodegen,
|
||||
blockInfo: BlockInfo
|
||||
) {
|
||||
if (irValueParameter?.isInlineParameter() == true && isInlineIrExpression(argumentExpression)) {
|
||||
if (irValueParameter.isInlineParameter() && isInlineIrExpression(argumentExpression)) {
|
||||
val irReference: IrFunctionReference =
|
||||
(argumentExpression as IrBlock).statements.filterIsInstance<IrFunctionReference>().single()
|
||||
val boundReceiver = argumentExpression.statements.filterIsInstance<IrVariable>().singleOrNull()
|
||||
@@ -65,7 +66,7 @@ class IrInlineCodegen(
|
||||
activeLambda = null
|
||||
}
|
||||
} else {
|
||||
putValueOnStack(argumentExpression, parameterType, irValueParameter?.index ?: -1, blockInfo)
|
||||
putValueOnStack(argumentExpression, parameterType, irValueParameter.index, blockInfo, irValueParameter.type)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,14 +83,14 @@ class IrInlineCodegen(
|
||||
}
|
||||
|
||||
private fun putCapturedValueOnStack(argumentExpression: IrExpression, valueType: Type, capturedParamIndex: Int) {
|
||||
val onStack = codegen.gen(argumentExpression, valueType, BlockInfo())
|
||||
val onStack = codegen.gen(argumentExpression, valueType, argumentExpression.type, BlockInfo())
|
||||
putArgumentOrCapturedToLocalVal(
|
||||
JvmKotlinType(onStack.type, onStack.kotlinType), onStack, capturedParamIndex, capturedParamIndex, ValueKind.CAPTURED
|
||||
)
|
||||
}
|
||||
|
||||
private fun putValueOnStack(argumentExpression: IrExpression, valueType: Type, paramIndex: Int, blockInfo: BlockInfo) {
|
||||
val onStack = codegen.gen(argumentExpression, valueType, blockInfo)
|
||||
private fun putValueOnStack(argumentExpression: IrExpression, valueType: Type, paramIndex: Int, blockInfo: BlockInfo, irType: IrType) {
|
||||
val onStack = codegen.gen(argumentExpression, valueType, irType, blockInfo)
|
||||
putArgumentOrCapturedToLocalVal(JvmKotlinType(onStack.type, onStack.kotlinType), onStack, -1, paramIndex, ValueKind.CAPTURED)
|
||||
}
|
||||
|
||||
|
||||
+8
-1
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.codegen
|
||||
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.OwnerKind
|
||||
import org.jetbrains.kotlin.codegen.signature.JvmSignatureWriter
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||
@@ -57,9 +58,15 @@ class IrTypeMapper(val kotlinTypeMapper: KotlinTypeMapper) {
|
||||
fun mapType(irType: IrType, sw: JvmSignatureWriter, mode: TypeMappingMode) =
|
||||
kotlinTypeMapper.mapType(irType.toKotlinType(), sw, mode)
|
||||
|
||||
fun mapTypeAsDeclaration(irType: IrType) =
|
||||
kotlinTypeMapper.mapTypeAsDeclaration(irType.toKotlinType())
|
||||
|
||||
fun mapTypeParameter(irType: IrType, signatureWriter: JvmSignatureWriter) =
|
||||
kotlinTypeMapper.mapTypeParameter(irType.toKotlinType(), signatureWriter)
|
||||
|
||||
fun writeFormalTypeParameters(irParameters: List<IrTypeParameter>, sw: JvmSignatureWriter) =
|
||||
kotlinTypeMapper.writeFormalTypeParameters(irParameters.map { it.descriptor }, sw)
|
||||
}
|
||||
|
||||
fun boxType(irType: IrType) =
|
||||
AsmUtil.boxType(mapType(irType), irType.toKotlinType(), kotlinTypeMapper)
|
||||
}
|
||||
|
||||
+73
-12
@@ -5,28 +5,43 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.codegen
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.InlineClassAbi
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.classOrNull
|
||||
import org.jetbrains.kotlin.ir.types.toKotlinType
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
// A value that may not have been fully constructed yet. The ability to "roll back" code generation
|
||||
// is useful for certain optimizations.
|
||||
abstract class PromisedValue(val mv: InstructionAdapter, val type: Type) {
|
||||
abstract class PromisedValue(val codegen: ExpressionCodegen, val type: Type, val irType: IrType?) {
|
||||
// If this value is immaterial, construct an object on the top of the stack. This
|
||||
// must always be done before generating other values or emitting raw bytecode.
|
||||
abstract fun materialize()
|
||||
|
||||
val mv: InstructionAdapter
|
||||
get() = codegen.mv
|
||||
|
||||
val typeMapper: IrTypeMapper
|
||||
get() = codegen.typeMapper
|
||||
|
||||
val kotlinType: KotlinType?
|
||||
get() = irType?.toKotlinType()
|
||||
}
|
||||
|
||||
// A value that *has* been fully constructed.
|
||||
class MaterialValue(mv: InstructionAdapter, type: Type) : PromisedValue(mv, type) {
|
||||
class MaterialValue(codegen: ExpressionCodegen, type: Type, irType: IrType?) : PromisedValue(codegen, type, irType) {
|
||||
override fun materialize() {}
|
||||
}
|
||||
|
||||
// A value that can be branched on. JVM has certain branching instructions which can be used
|
||||
// to optimize these.
|
||||
abstract class BooleanValue(mv: InstructionAdapter) : PromisedValue(mv, Type.BOOLEAN_TYPE) {
|
||||
abstract class BooleanValue(codegen: ExpressionCodegen) : PromisedValue(codegen, Type.BOOLEAN_TYPE, codegen.context.irBuiltIns.booleanType) {
|
||||
abstract fun jumpIfFalse(target: Label)
|
||||
abstract fun jumpIfTrue(target: Label)
|
||||
|
||||
@@ -46,7 +61,7 @@ abstract class BooleanValue(mv: InstructionAdapter) : PromisedValue(mv, Type.BOO
|
||||
val PromisedValue.materialized: MaterialValue
|
||||
get() {
|
||||
materialize()
|
||||
return MaterialValue(mv, type)
|
||||
return MaterialValue(codegen, type, irType)
|
||||
}
|
||||
|
||||
// Materialize and disregard this value. Materialization is forced because, presumably,
|
||||
@@ -55,22 +70,68 @@ fun PromisedValue.discard(): MaterialValue {
|
||||
materialize()
|
||||
if (type !== Type.VOID_TYPE)
|
||||
AsmUtil.pop(mv, type)
|
||||
return MaterialValue(mv, Type.VOID_TYPE)
|
||||
return codegen.voidValue
|
||||
}
|
||||
|
||||
private val IrType.unboxed: IrType
|
||||
get() = InlineClassAbi.getUnderlyingType(erasedUpperBound)
|
||||
|
||||
fun PromisedValue.coerceInlineClasses(type: Type, irType: IrType, target: Type, irTarget: IrType): PromisedValue? {
|
||||
val isFromTypeInlineClass = irType.erasedUpperBound.isInline
|
||||
val isToTypeInlineClass = irTarget.erasedUpperBound.isInline
|
||||
if (!isFromTypeInlineClass && !isToTypeInlineClass) return null
|
||||
|
||||
val isFromTypeUnboxed = isFromTypeInlineClass && typeMapper.mapType(irType.unboxed) == type
|
||||
val isToTypeUnboxed = isToTypeInlineClass && typeMapper.mapType(irTarget.unboxed) == target
|
||||
|
||||
return when {
|
||||
isFromTypeUnboxed && !isToTypeUnboxed -> object : PromisedValue(codegen, target, irTarget) {
|
||||
override fun materialize() {
|
||||
this@coerceInlineClasses.materialize()
|
||||
// TODO: This is broken for type parameters
|
||||
StackValue.boxInlineClass(irType.toKotlinType(), mv)
|
||||
}
|
||||
}
|
||||
!isFromTypeUnboxed && isToTypeUnboxed -> object : PromisedValue(codegen, target, irTarget) {
|
||||
override fun materialize() {
|
||||
val value = this@coerceInlineClasses.materialized
|
||||
StackValue.unboxInlineClass(value.type, irTarget.toKotlinType(), mv)
|
||||
}
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
// On materialization, cast the value to a different type.
|
||||
fun PromisedValue.coerce(target: Type) = when (target) {
|
||||
type -> this
|
||||
else -> object : PromisedValue(mv, target) {
|
||||
// TODO remove dependency
|
||||
override fun materialize() = StackValue.coerce(this@coerce.materialized.type, type, mv)
|
||||
fun PromisedValue.coerce(target: Type, irTarget: IrType? = null): PromisedValue {
|
||||
if (irType != null && irTarget != null)
|
||||
coerceInlineClasses(type, irType, target, irTarget)?.let { return it }
|
||||
return when (type) {
|
||||
// All unsafe coercions between irTypes should use the UnsafeCoerce intrinsic
|
||||
target -> this
|
||||
else -> object : PromisedValue(codegen, target, irTarget) {
|
||||
override fun materialize() {
|
||||
val value = this@coerce.materialized
|
||||
StackValue.coerce(value.type, type, mv)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun PromisedValue.coerce(irTarget: IrType) =
|
||||
coerce(typeMapper.mapType(irTarget), irTarget)
|
||||
|
||||
fun PromisedValue.coerceToBoxed(irTarget: IrType) =
|
||||
coerce(typeMapper.boxType(irTarget), irTarget)
|
||||
|
||||
fun PromisedValue.boxInlineClasses(irTarget: IrType) =
|
||||
if (irTarget.classOrNull?.owner?.isInline == true)
|
||||
coerceToBoxed(irTarget) else this
|
||||
|
||||
// Same as above, but with a return type that allows conditional jumping.
|
||||
fun PromisedValue.coerceToBoolean() = when (val coerced = coerce(Type.BOOLEAN_TYPE)) {
|
||||
is BooleanValue -> coerced
|
||||
else -> object : BooleanValue(mv) {
|
||||
else -> object : BooleanValue(codegen) {
|
||||
override fun jumpIfFalse(target: Label) = coerced.materialize().also { mv.ifeq(target) }
|
||||
override fun jumpIfTrue(target: Label) = coerced.materialize().also { mv.ifne(target) }
|
||||
override fun materialize() = coerced.materialize()
|
||||
@@ -78,4 +139,4 @@ fun PromisedValue.coerceToBoolean() = when (val coerced = coerce(Type.BOOLEAN_TY
|
||||
}
|
||||
|
||||
val ExpressionCodegen.voidValue: MaterialValue
|
||||
get() = MaterialValue(mv, Type.VOID_TYPE)
|
||||
get() = MaterialValue(this, Type.VOID_TYPE, null)
|
||||
|
||||
+2
-2
@@ -222,12 +222,12 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf
|
||||
val endLabel = Label()
|
||||
for ((thenExpression, label) in expressionToLabels) {
|
||||
mv.visitLabel(label)
|
||||
thenExpression.accept(codegen, data).coerce(expression.asmType).materialized
|
||||
thenExpression.accept(codegen, data).coerce(expression.type).materialized
|
||||
mv.goTo(endLabel)
|
||||
}
|
||||
mv.visitLabel(defaultLabel)
|
||||
val stackValue = elseExpression?.accept(codegen, data) ?: voidValue
|
||||
val result = stackValue.coerce(expression.asmType).materialized
|
||||
val result = stackValue.coerce(expression.type).materialized
|
||||
mv.mark(endLabel)
|
||||
return result
|
||||
}
|
||||
|
||||
+15
-1
@@ -43,7 +43,21 @@ import org.jetbrains.org.objectweb.asm.Type
|
||||
import java.util.ArrayList
|
||||
import java.util.LinkedHashSet
|
||||
|
||||
class IrFrameMap : FrameMapBase<IrSymbol>()
|
||||
class IrFrameMap : FrameMapBase<IrSymbol>() {
|
||||
private val typeMap = mutableMapOf<IrSymbol,Type>()
|
||||
|
||||
override fun enter(descriptor: IrSymbol, type: Type): Int {
|
||||
typeMap[descriptor] = type
|
||||
return super.enter(descriptor, type)
|
||||
}
|
||||
|
||||
override fun leave(descriptor: IrSymbol): Int {
|
||||
typeMap.remove(descriptor)
|
||||
return super.leave(descriptor)
|
||||
}
|
||||
|
||||
fun typeOf(descriptor: IrSymbol): Type = typeMap.getValue(descriptor)
|
||||
}
|
||||
|
||||
internal val IrFunction.isStatic
|
||||
get() = (this.dispatchReceiverParameter == null && this !is IrConstructor)
|
||||
|
||||
@@ -12,7 +12,7 @@ import org.jetbrains.org.objectweb.asm.Label
|
||||
|
||||
object AndAnd : IntrinsicMethod() {
|
||||
|
||||
private class BooleanConjunction(val arg0: IrExpression, val arg1: IrExpression, val codegen: ExpressionCodegen, val data: BlockInfo) : BooleanValue(codegen.mv) {
|
||||
private class BooleanConjunction(val arg0: IrExpression, val arg1: IrExpression, codegen: ExpressionCodegen, val data: BlockInfo) : BooleanValue(codegen) {
|
||||
|
||||
override fun jumpIfFalse(target: Label) {
|
||||
arg0.accept(codegen, data).coerceToBoolean().jumpIfFalse(target)
|
||||
|
||||
@@ -29,6 +29,6 @@ object Clone : IntrinsicMethod() {
|
||||
assert(!AsmUtil.isPrimitive(result.type)) { "clone() of primitive type" }
|
||||
val opcode = if (expression is IrCall && expression.superQualifier != null) Opcodes.INVOKESPECIAL else Opcodes.INVOKEVIRTUAL
|
||||
mv.visitMethodInsn(opcode, "java/lang/Object", "clone", "()Ljava/lang/Object;", false)
|
||||
MaterialValue(codegen.mv, AsmTypes.OBJECT_TYPE)
|
||||
MaterialValue(codegen, AsmTypes.OBJECT_TYPE, context.irBuiltIns.anyNType)
|
||||
}
|
||||
}
|
||||
|
||||
+3
-4
@@ -30,7 +30,6 @@ import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
import java.lang.UnsupportedOperationException
|
||||
|
||||
object CompareTo : IntrinsicMethod() {
|
||||
private fun genInvoke(type: Type?, v: InstructionAdapter) {
|
||||
@@ -64,7 +63,7 @@ object CompareTo : IntrinsicMethod() {
|
||||
}
|
||||
}
|
||||
|
||||
class BooleanComparison(val op: IElementType, val a: MaterialValue, val b: MaterialValue) : BooleanValue(a.mv) {
|
||||
class BooleanComparison(val op: IElementType, val a: MaterialValue, val b: MaterialValue) : BooleanValue(a.codegen) {
|
||||
override fun jumpIfFalse(target: Label) {
|
||||
// TODO 1. get rid of the dependency; 2. take `b.type` into account.
|
||||
val opcode = if (a.type.sort == Type.OBJECT)
|
||||
@@ -90,8 +89,8 @@ class PrimitiveComparison(
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? {
|
||||
val parameterType = codegen.typeMapper.kotlinTypeMapper.mapType(primitiveNumberType)
|
||||
val (left, right) = expression.receiverAndArgs()
|
||||
val a = left.accept(codegen, data).coerce(parameterType).materialized
|
||||
val b = right.accept(codegen, data).coerce(parameterType).materialized
|
||||
val a = left.accept(codegen, data).coerce(parameterType, left.type).materialized
|
||||
val b = right.accept(codegen, data).coerce(parameterType, right.type).materialized
|
||||
return BooleanComparison(operatorToken, a, b)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,12 @@ import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
||||
import org.jetbrains.kotlin.ir.types.toKotlinType
|
||||
import org.jetbrains.kotlin.ir.util.isInlined
|
||||
import org.jetbrains.kotlin.ir.util.isNullConst
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.kotlin.types.isNullable
|
||||
import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberOrNullableType
|
||||
import org.jetbrains.kotlin.types.typeUtil.upperBoundedByPrimitiveNumberOrNullableType
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
@@ -27,7 +29,7 @@ import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
class Equals(val operator: IElementType) : IntrinsicMethod() {
|
||||
private class BooleanNullCheck(val value: PromisedValue) : BooleanValue(value.mv) {
|
||||
private class BooleanNullCheck(val value: PromisedValue) : BooleanValue(value.codegen) {
|
||||
override fun jumpIfFalse(target: Label) = value.materialize().also { mv.ifnonnull(target) }
|
||||
override fun jumpIfTrue(target: Label) = value.materialize().also { mv.ifnull(target) }
|
||||
}
|
||||
@@ -42,15 +44,16 @@ class Equals(val operator: IElementType) : IntrinsicMethod() {
|
||||
val rightType = with(codegen) { b.asmType }
|
||||
val opToken = expression.origin
|
||||
val useEquals = opToken !== IrStatementOrigin.EQEQEQ && opToken !== IrStatementOrigin.EXCLEQEQ &&
|
||||
(a.type.isInlined() || b.type.isInlined() ||
|
||||
// `==` is `equals` for objects and floating-point numbers. In the latter case, the difference
|
||||
// is that `equals` is a total order (-0 < +0 and NaN == NaN) and `===` is IEEE754-compliant.
|
||||
(!isPrimitive(leftType) || leftType != rightType || leftType == Type.FLOAT_TYPE || leftType == Type.DOUBLE_TYPE)
|
||||
!isPrimitive(leftType) || leftType != rightType || leftType == Type.FLOAT_TYPE || leftType == Type.DOUBLE_TYPE)
|
||||
val operandType = if (!isPrimitive(leftType) || useEquals) AsmTypes.OBJECT_TYPE else leftType
|
||||
val aValue = a.accept(codegen, data).coerce(operandType).materialized
|
||||
val bValue = b.accept(codegen, data).coerce(operandType).materialized
|
||||
val aValue = a.accept(codegen, data).coerce(operandType, a.type).materialized
|
||||
val bValue = b.accept(codegen, data).coerce(operandType, b.type).materialized
|
||||
if (useEquals) {
|
||||
AsmUtil.genAreEqualCall(codegen.mv)
|
||||
return MaterialValue(codegen.mv, Type.BOOLEAN_TYPE)
|
||||
return MaterialValue(codegen, Type.BOOLEAN_TYPE, codegen.context.irBuiltIns.booleanType)
|
||||
}
|
||||
return BooleanComparison(operator, aValue, bValue)
|
||||
}
|
||||
@@ -86,8 +89,8 @@ class Ieee754Equals(val operandType: Type) : IntrinsicMethod() {
|
||||
if (!arg1Type.isPrimitiveNumberOrNullableType() && !arg1Type.upperBoundedByPrimitiveNumberOrNullableType())
|
||||
throw AssertionError("Should be primitive or nullable primitive type: $arg1Type")
|
||||
|
||||
val arg0isNullable = arg0Type.isMarkedNullable
|
||||
val arg1isNullable = arg1Type.isMarkedNullable
|
||||
val arg0isNullable = arg0Type.isNullable()
|
||||
val arg1isNullable = arg1Type.isNullable()
|
||||
|
||||
return when {
|
||||
!arg0isNullable && !arg1isNullable ->
|
||||
|
||||
@@ -35,7 +35,7 @@ object HashCode : IntrinsicMethod() {
|
||||
irFunction.origin == IrDeclarationOrigin.GENERATED_INLINE_CLASS_MEMBER || irFunction.origin == IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER ->
|
||||
AsmUtil.genHashCode(mv, mv, result.type, target)
|
||||
target == JvmTarget.JVM_1_6 -> {
|
||||
result.coerce(AsmUtil.boxType(result.type)).materialize()
|
||||
result.coerceToBoxed(receiver.type).materialize()
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Object", "hashCode", "()I", false)
|
||||
}
|
||||
else -> {
|
||||
@@ -49,6 +49,6 @@ object HashCode : IntrinsicMethod() {
|
||||
)
|
||||
}
|
||||
}
|
||||
MaterialValue(codegen.mv, Type.INT_TYPE)
|
||||
MaterialValue(codegen, Type.INT_TYPE, codegen.context.irBuiltIns.intType)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ abstract class IntrinsicMethod {
|
||||
with(codegen) {
|
||||
val descriptor = typeMapper.mapSignatureSkipGeneric(expression.symbol.owner)
|
||||
val stackValue = toCallable(expression, descriptor, context).invoke(mv, codegen, data)
|
||||
return object : PromisedValue(mv, stackValue.type) {
|
||||
return object : PromisedValue(this, stackValue.type, expression.type) {
|
||||
override fun materialize() = stackValue.put(mv)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ object IrEnumValueOf : IntrinsicMethod() {
|
||||
return object : IrIntrinsicFunction(expression, newSignature, context, listOf(stringType)) {
|
||||
override fun invoke(v: InstructionAdapter, codegen: ExpressionCodegen, data: BlockInfo): StackValue {
|
||||
v.tconst(enumType)
|
||||
codegen.gen(expression.getValueArgument(0)!!, stringType, data)
|
||||
codegen.gen(expression.getValueArgument(0)!!, stringType, codegen.context.irBuiltIns.stringType, data)
|
||||
v.invokestatic("java/lang/Enum", "valueOf", "(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;", false)
|
||||
v.checkcast(enumType)
|
||||
return StackValue.onStack(enumType)
|
||||
|
||||
+1
-1
@@ -92,7 +92,7 @@ open class IrIntrinsicFunction(
|
||||
}
|
||||
|
||||
private fun genArg(expression: IrExpression, codegen: ExpressionCodegen, index: Int, data: BlockInfo) {
|
||||
codegen.gen(expression, argsTypes[index], data)
|
||||
codegen.gen(expression, argsTypes[index], expression.type, data)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
+2
-1
@@ -102,7 +102,8 @@ class IrIntrinsicMethods(val irBuiltIns: IrBuiltIns, val symbols: JvmSymbols) {
|
||||
irBuiltIns.illegalArgumentExceptionSymbol.toKey()!! to IrIllegalArgumentException,
|
||||
irBuiltIns.throwNpeSymbol.toKey()!! to ThrowNPE,
|
||||
irBuiltIns.andandSymbol.toKey()!! to AndAnd,
|
||||
irBuiltIns.ororSymbol.toKey()!! to OrOr
|
||||
irBuiltIns.ororSymbol.toKey()!! to OrOr,
|
||||
symbols.unsafeCoerceIntrinsicSymbol.toKey()!! to UnsafeCoerce
|
||||
) +
|
||||
numberConversionMethods() +
|
||||
unaryFunForPrimitives("plus", UnaryPlus) +
|
||||
|
||||
+18
-11
@@ -20,23 +20,30 @@ import org.jetbrains.kotlin.backend.jvm.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil.boxType
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil.isPrimitive
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.util.isInlined
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
object JavaClassProperty : IntrinsicMethod() {
|
||||
fun invokeWith(value: PromisedValue) {
|
||||
if (value.type == Type.VOID_TYPE) {
|
||||
return invokeWith(value.coerce(AsmTypes.UNIT_TYPE))
|
||||
}
|
||||
if (isPrimitive(value.type)) {
|
||||
value.discard()
|
||||
value.mv.getstatic(boxType(value.type).internalName, "TYPE", "Ljava/lang/Class;")
|
||||
} else {
|
||||
value.materialize()
|
||||
value.mv.invokevirtual("java/lang/Object", "getClass", "()Ljava/lang/Class;", false)
|
||||
}
|
||||
private fun invokeGetClass(value: PromisedValue) {
|
||||
value.materialize()
|
||||
value.mv.invokevirtual("java/lang/Object", "getClass", "()Ljava/lang/Class;", false)
|
||||
}
|
||||
|
||||
fun invokeWith(value: PromisedValue) =
|
||||
when {
|
||||
value.type == Type.VOID_TYPE ->
|
||||
invokeGetClass(value.coerce(AsmTypes.UNIT_TYPE))
|
||||
value.irType?.isInlined() == true ->
|
||||
invokeGetClass(value.coerceToBoxed(value.irType))
|
||||
isPrimitive(value.type) -> {
|
||||
value.discard()
|
||||
value.mv.getstatic(boxType(value.type).internalName, "TYPE", "Ljava/lang/Class;")
|
||||
}
|
||||
else ->
|
||||
invokeGetClass(value)
|
||||
}
|
||||
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? {
|
||||
invokeWith(expression.extensionReceiver!!.accept(codegen, data))
|
||||
return with(codegen) { expression.onStack }
|
||||
|
||||
@@ -13,7 +13,7 @@ import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
object NewArray : IntrinsicMethod() {
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? {
|
||||
codegen.gen(expression.getValueArgument(0)!!, Type.INT_TYPE, data)
|
||||
codegen.gen(expression.getValueArgument(0)!!, Type.INT_TYPE, codegen.context.irBuiltIns.intType, data)
|
||||
codegen.newArrayInstruction(expression.type)
|
||||
return with(codegen) { expression.onStack }
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
|
||||
object Not : IntrinsicMethod() {
|
||||
class BooleanNegation(val value: BooleanValue) : BooleanValue(value.mv) {
|
||||
class BooleanNegation(val value: BooleanValue) : BooleanValue(value.codegen) {
|
||||
override fun jumpIfFalse(target: Label) = value.jumpIfTrue(target)
|
||||
override fun jumpIfTrue(target: Label) = value.jumpIfFalse(target)
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import org.jetbrains.org.objectweb.asm.Label
|
||||
object OrOr : IntrinsicMethod() {
|
||||
|
||||
private class BooleanDisjunction(
|
||||
val arg0: IrExpression, val arg1: IrExpression, val codegen: ExpressionCodegen, val data: BlockInfo) : BooleanValue(codegen.mv) {
|
||||
val arg0: IrExpression, val arg1: IrExpression, codegen: ExpressionCodegen, val data: BlockInfo) : BooleanValue(codegen) {
|
||||
|
||||
override fun jumpIfFalse(target: Label) {
|
||||
val stayLabel = Label()
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
|
||||
/**
|
||||
* Implicit coercion between IrTypes with the same underlying representation.
|
||||
*
|
||||
* A call of the form `coerce<A,B>(x)` allows us to coerce the value of `x` to type `A` but treat the result as if
|
||||
* it had IrType `B`. This is useful for inline classes, whose coercion behavior depends on the IrType in
|
||||
* addition to the underlying asmType.
|
||||
*/
|
||||
object UnsafeCoerce : IntrinsicMethod() {
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? {
|
||||
val from = expression.getTypeArgument(0)!!
|
||||
val to = expression.getTypeArgument(1)!!
|
||||
require(with(codegen) { typeMapper.mapType(from) == typeMapper.mapType(to) })
|
||||
val arg = expression.getValueArgument(0)!!
|
||||
val result = arg.accept(codegen, data)
|
||||
return object : PromisedValue(codegen, codegen.typeMapper.mapType(to), to) {
|
||||
override fun materialize() = result.coerce(from).materialize()
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
-5
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.isInlineFunctionCall
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.isInlineIrExpression
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.isPrimaryInlineClassConstructor
|
||||
import org.jetbrains.kotlin.codegen.PropertyReferenceCodegen
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
@@ -341,6 +342,18 @@ internal class CallableReferenceLowering(val context: JvmBackendContext) : FileL
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrSimpleFunction.invokeInlineClassConstructor(irConstructor: IrConstructor): IrBody {
|
||||
val irBuilder = context.createIrBuilder(symbol, startOffset, endOffset)
|
||||
val param = irConstructor.valueParameters[0].copyTo(this)
|
||||
valueParameters.add(param)
|
||||
val from = irConstructor.valueParameters[0].type
|
||||
val to = irConstructor.returnType
|
||||
val call = irBuilder.irCall(context.ir.symbols.unsafeCoerceIntrinsicSymbol, to, listOf(from, to)).apply {
|
||||
putValueArgument(0, irBuilder.irGet(param))
|
||||
}
|
||||
return irBuilder.irExprBody(call)
|
||||
}
|
||||
|
||||
private fun createInvokeMethod(superFunction: IrSimpleFunction): IrSimpleFunction =
|
||||
buildFun {
|
||||
setSourceRange(irFunctionReference)
|
||||
@@ -357,6 +370,11 @@ internal class CallableReferenceLowering(val context: JvmBackendContext) : FileL
|
||||
|
||||
dispatchReceiverParameter = functionReferenceClass.thisReceiver?.copyTo(function)
|
||||
|
||||
if (callee.isPrimaryInlineClassConstructor) {
|
||||
body = invokeInlineClassConstructor(callee as IrConstructor)
|
||||
return this
|
||||
}
|
||||
|
||||
val unboundArgsSet = unboundCalleeParameters.toSet()
|
||||
if (useVararg) {
|
||||
valueParameters.add(superFunction.valueParameters[0].copyTo(function))
|
||||
@@ -432,11 +450,7 @@ internal class CallableReferenceLowering(val context: JvmBackendContext) : FileL
|
||||
irGet(valueParameters[unboundIndex++])
|
||||
}
|
||||
}
|
||||
when (parameter) {
|
||||
callee.dispatchReceiverParameter -> dispatchReceiver = argument
|
||||
callee.extensionReceiverParameter -> extensionReceiver = argument
|
||||
else -> putValueArgument(parameter.index, argument)
|
||||
}
|
||||
putArgument(callee, parameter, argument)
|
||||
}
|
||||
|
||||
if (!useVararg) assert(unboundIndex == valueParameters.size) { "Not all arguments of <invoke> are used" }
|
||||
|
||||
+389
@@ -0,0 +1,389 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.ir.copyParameterDeclarationsFrom
|
||||
import org.jetbrains.kotlin.backend.common.ir.passTypeArgumentsFrom
|
||||
import org.jetbrains.kotlin.backend.common.ir.primaryConstructor
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.common.lower.irBlockBody
|
||||
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.*
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addConstructor
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrSetVariableImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrVariableSymbol
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
val jvmInlineClassPhase = makeIrFilePhase(
|
||||
::JvmInlineClassLowering,
|
||||
name = "Inline Classes",
|
||||
description = "Lower inline classes"
|
||||
)
|
||||
|
||||
/**
|
||||
* Adds new constructors, box, and unbox functions to inline classes as well as replacement
|
||||
* functions and bridges to avoid clashes between overloaded function. Changes calls with
|
||||
* known types to call the replacement functions.
|
||||
*
|
||||
* We do not unfold inline class types here. Instead, the type mapper will lower inline class
|
||||
* types to the types of their underlying field.
|
||||
*/
|
||||
private class JvmInlineClassLowering(private val context: JvmBackendContext) : FileLoweringPass, IrElementTransformerVoid() {
|
||||
private val manager = MemoizedInlineClassReplacements()
|
||||
private val valueMap = mutableMapOf<IrValueSymbol, IrValueDeclaration>()
|
||||
|
||||
override fun lower(irFile: IrFile) {
|
||||
irFile.transformChildrenVoid()
|
||||
}
|
||||
|
||||
override fun visitClass(declaration: IrClass): IrStatement {
|
||||
// The arguments to the primary constructor are in scope in the initializers of IrFields.
|
||||
declaration.primaryConstructor?.let {
|
||||
manager.getReplacementFunction(it)?.let { replacement ->
|
||||
valueMap.putAll(replacement.valueParameterMap)
|
||||
}
|
||||
}
|
||||
|
||||
declaration.transformDeclarationsFlat { memberDeclaration ->
|
||||
if (memberDeclaration is IrFunction) {
|
||||
transformFunctionFlat(memberDeclaration)
|
||||
} else {
|
||||
memberDeclaration.accept(this, null)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
if (declaration.isInline) {
|
||||
val irConstructor = declaration.primaryConstructor!!
|
||||
declaration.declarations.removeIf {
|
||||
(it is IrConstructor && it.isPrimary) || (it is IrFunction && it.isInlineClassFieldGetter)
|
||||
}
|
||||
buildPrimaryInlineClassConstructor(declaration, irConstructor)
|
||||
buildBoxFunction(declaration)
|
||||
buildUnboxFunction(declaration)
|
||||
}
|
||||
|
||||
return declaration
|
||||
}
|
||||
|
||||
private fun transformFunctionFlat(function: IrFunction): List<IrDeclaration>? {
|
||||
if (function.isPrimaryInlineClassConstructor)
|
||||
return null
|
||||
|
||||
val replacement = manager.getReplacementFunction(function)
|
||||
if (replacement == null) {
|
||||
function.transformChildrenVoid()
|
||||
return null
|
||||
}
|
||||
|
||||
return when (function) {
|
||||
is IrSimpleFunction -> transformSimpleFunctionFlat(function, replacement)
|
||||
is IrConstructor -> transformConstructorFlat(function, replacement)
|
||||
else -> throw IllegalStateException()
|
||||
}
|
||||
}
|
||||
|
||||
private fun transformSimpleFunctionFlat(function: IrSimpleFunction, replacement: IrReplacementFunction): List<IrDeclaration> {
|
||||
val worker = replacement.function
|
||||
valueMap.putAll(replacement.valueParameterMap)
|
||||
worker.valueParameters.forEach { it.transformChildrenVoid() }
|
||||
worker.body = function.body?.transform(this, null)?.patchDeclarationParents(worker)
|
||||
|
||||
// Don't create a wrapper for functions which are only used in an unboxed context
|
||||
if (function.overriddenSymbols.isEmpty() || worker.dispatchReceiverParameter != null)
|
||||
return listOf(worker)
|
||||
|
||||
// Replace the function body with a wrapper
|
||||
context.createIrBuilder(function.symbol).run {
|
||||
val call = irCall(worker).apply {
|
||||
passTypeArgumentsFrom(function)
|
||||
for (parameter in function.explicitParameters) {
|
||||
val newParameter = replacement.valueParameterMap[parameter.symbol] ?: continue
|
||||
putArgument(newParameter, irGet(parameter))
|
||||
}
|
||||
}
|
||||
|
||||
function.body = irExprBody(call)
|
||||
}
|
||||
|
||||
return listOf(worker, function)
|
||||
}
|
||||
|
||||
// Secondary constructors for boxed types get translated to static functions returning
|
||||
// unboxed arguments. We remove the original constructor.
|
||||
private fun transformConstructorFlat(constructor: IrConstructor, replacement: IrReplacementFunction): List<IrDeclaration> {
|
||||
val worker = replacement.function
|
||||
|
||||
valueMap.putAll(replacement.valueParameterMap)
|
||||
worker.valueParameters.forEach { it.transformChildrenVoid() }
|
||||
worker.body = context.createIrBuilder(worker.symbol).irBlockBody(worker) {
|
||||
val thisVar = irTemporaryVarDeclaration(
|
||||
worker.returnType, nameHint = "\$this", isMutable = false
|
||||
)
|
||||
valueMap[constructor.constructedClass.thisReceiver!!.symbol] = thisVar
|
||||
|
||||
constructor.body?.contents?.forEach { statement ->
|
||||
+statement
|
||||
.transform(object : IrElementTransformerVoid() {
|
||||
// Don't recurse under nested class declarations
|
||||
override fun visitClass(declaration: IrClass): IrStatement {
|
||||
return declaration
|
||||
}
|
||||
|
||||
// Capture the result of a delegating constructor call in a temporary variable "thisVar".
|
||||
//
|
||||
// Within the constructor we replace references to "this" with references to "thisVar".
|
||||
// This is safe, since the delegating constructor call precedes all references to "this".
|
||||
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression {
|
||||
expression.transformChildrenVoid()
|
||||
return irSetVar(thisVar.symbol, expression)
|
||||
}
|
||||
|
||||
// A constructor body has type unit and may contain explicit return statements.
|
||||
// These early returns may have side-effects however, so we still have to evaluate
|
||||
// the return expression. Afterwards we return "thisVar".
|
||||
// For example, the following is a valid inline class declaration.
|
||||
//
|
||||
// inline class Foo(val x: String) {
|
||||
// constructor(y: Int) : this(y.toString()) {
|
||||
// if (y == 0) return throw java.lang.IllegalArgumentException()
|
||||
// if (y == 1) return
|
||||
// return Unit
|
||||
// }
|
||||
// }
|
||||
override fun visitReturn(expression: IrReturn): IrExpression {
|
||||
expression.transformChildrenVoid()
|
||||
if (expression.returnTargetSymbol != constructor.symbol)
|
||||
return expression
|
||||
|
||||
return irReturn(irBlock(expression.startOffset, expression.endOffset) {
|
||||
+expression.value
|
||||
+irGet(thisVar)
|
||||
})
|
||||
}
|
||||
}, null)
|
||||
.transform(this@JvmInlineClassLowering, null)
|
||||
.patchDeclarationParents(worker)
|
||||
}
|
||||
|
||||
+irReturn(irGet(thisVar))
|
||||
}
|
||||
|
||||
return listOf(worker)
|
||||
}
|
||||
|
||||
private fun typedArgumentList(function: IrFunction, expression: IrMemberAccessExpression) =
|
||||
listOfNotNull(
|
||||
function.dispatchReceiverParameter?.let { it to expression.dispatchReceiver },
|
||||
function.extensionReceiverParameter?.let { it to expression.extensionReceiver }
|
||||
) + function.valueParameters.map { it to expression.getValueArgument(it.index) }
|
||||
|
||||
private fun IrMemberAccessExpression.buildReplacement(
|
||||
originalFunction: IrFunction,
|
||||
original: IrMemberAccessExpression,
|
||||
replacement: IrReplacementFunction
|
||||
) {
|
||||
copyTypeArgumentsFrom(original)
|
||||
for ((parameter, argument) in typedArgumentList(originalFunction, original)) {
|
||||
if (argument == null) continue
|
||||
val newParameter = replacement.valueParameterMap[parameter.symbol] ?: continue
|
||||
putArgument(
|
||||
replacement.function,
|
||||
newParameter,
|
||||
argument.transform(this@JvmInlineClassLowering, null)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildReplacementCall(
|
||||
originalFunction: IrFunction,
|
||||
original: IrFunctionAccessExpression,
|
||||
replacement: IrReplacementFunction
|
||||
) = context.createIrBuilder(original.symbol)
|
||||
.irCall(replacement.function)
|
||||
.apply { buildReplacement(originalFunction, original, replacement) }
|
||||
|
||||
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
|
||||
val replacement = manager.getReplacementFunction(expression.symbol.owner)
|
||||
?: return super.visitFunctionReference(expression)
|
||||
val function = replacement.function
|
||||
|
||||
return IrFunctionReferenceImpl(
|
||||
expression.startOffset, expression.endOffset, expression.type,
|
||||
function.symbol, function.descriptor,
|
||||
function.typeParameters.size, function.valueParameters.size,
|
||||
expression.origin
|
||||
).apply {
|
||||
buildReplacement(expression.symbol.owner, expression, replacement)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitFunctionAccess(expression: IrFunctionAccessExpression): IrExpression {
|
||||
val function = expression.symbol.owner
|
||||
val replacement = manager.getReplacementFunction(function)
|
||||
?: return super.visitFunctionAccess(expression)
|
||||
return buildReplacementCall(function, expression, replacement)
|
||||
}
|
||||
|
||||
private fun coerceInlineClasses(argument: IrExpression, from: IrType, to: IrType) =
|
||||
IrCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, to, context.ir.symbols.unsafeCoerceIntrinsicSymbol).apply {
|
||||
putTypeArgument(0, from)
|
||||
putTypeArgument(1, to)
|
||||
putValueArgument(0, argument)
|
||||
}
|
||||
|
||||
override fun visitCall(expression: IrCall): IrExpression {
|
||||
val function = expression.symbol.owner
|
||||
if (!function.isInlineClassFieldGetter)
|
||||
return super.visitCall(expression)
|
||||
val arg = expression.dispatchReceiver!!.transform(this, null)
|
||||
return coerceInlineClasses(arg, function.dispatchReceiverParameter!!.type, expression.type)
|
||||
}
|
||||
|
||||
override fun visitGetField(expression: IrGetField): IrExpression {
|
||||
val field = expression.symbol.owner
|
||||
val parent = field.parent
|
||||
if (parent is IrClass && parent.isInline && field.name == parent.inlineClassFieldName) {
|
||||
val receiver = expression.receiver!!.transform(this, null)
|
||||
return coerceInlineClasses(receiver, receiver.type, field.type)
|
||||
}
|
||||
return super.visitGetField(expression)
|
||||
}
|
||||
|
||||
override fun visitReturn(expression: IrReturn): IrExpression {
|
||||
expression.returnTargetSymbol.owner.safeAs<IrFunction>()?.let { target ->
|
||||
manager.getReplacementFunction(target)?.let {
|
||||
return context.createIrBuilder(it.function.symbol).irReturn(
|
||||
expression.value.transform(this, null)
|
||||
)
|
||||
}
|
||||
}
|
||||
return super.visitReturn(expression)
|
||||
}
|
||||
|
||||
private fun visitStatementContainer(container: IrStatementContainer) {
|
||||
container.statements.transformFlat { statement ->
|
||||
if (statement is IrFunction)
|
||||
transformFunctionFlat(statement)
|
||||
else
|
||||
listOf(statement.transform(this, null))
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitContainerExpression(expression: IrContainerExpression): IrExpression {
|
||||
visitStatementContainer(expression)
|
||||
return expression
|
||||
}
|
||||
|
||||
override fun visitBlockBody(body: IrBlockBody): IrBody {
|
||||
visitStatementContainer(body)
|
||||
return body
|
||||
}
|
||||
|
||||
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
||||
valueMap[expression.symbol]?.let {
|
||||
return IrGetValueImpl(
|
||||
expression.startOffset, expression.endOffset,
|
||||
it.type, it.symbol, expression.origin
|
||||
)
|
||||
}
|
||||
return super.visitGetValue(expression)
|
||||
}
|
||||
|
||||
override fun visitSetVariable(expression: IrSetVariable): IrExpression {
|
||||
valueMap[expression.symbol]?.let {
|
||||
return IrSetVariableImpl(
|
||||
expression.startOffset, expression.endOffset,
|
||||
it.type, it.symbol as IrVariableSymbol, expression.origin
|
||||
).apply {
|
||||
value = expression.value.transform(this@JvmInlineClassLowering, null)
|
||||
}
|
||||
}
|
||||
return super.visitSetVariable(expression)
|
||||
}
|
||||
|
||||
private fun buildPrimaryInlineClassConstructor(irClass: IrClass, irConstructor: IrConstructor) {
|
||||
// Add the default primary constructor
|
||||
val primaryConstructor = irClass.addConstructor {
|
||||
updateFrom(irConstructor)
|
||||
visibility = Visibilities.PRIVATE
|
||||
origin = JvmLoweredDeclarationOrigin.SYNTHETIC_INLINE_CLASS_MEMBER
|
||||
returnType = irConstructor.returnType
|
||||
}.apply {
|
||||
copyParameterDeclarationsFrom(irConstructor)
|
||||
body = context.createIrBuilder(this.symbol).irBlockBody(this) {
|
||||
+irDelegatingConstructorCall(context.irBuiltIns.anyClass.owner.constructors.single())
|
||||
+irSetField(
|
||||
irGet(irClass.thisReceiver!!),
|
||||
getInlineClassBackingField(irClass),
|
||||
irGet(this@apply.valueParameters[0])
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Add a static bridge method to the primary constructor.
|
||||
// This is a placeholder for null-checks and default arguments.
|
||||
val function = manager.getReplacementFunction(irConstructor)!!.function
|
||||
with(context.createIrBuilder(function.symbol)) {
|
||||
val argument = function.valueParameters[0]
|
||||
function.body = irExprBody(
|
||||
coerceInlineClasses(irGet(argument), argument.type, function.returnType)
|
||||
)
|
||||
}
|
||||
irClass.declarations += function
|
||||
}
|
||||
|
||||
private fun buildBoxFunction(irClass: IrClass) {
|
||||
val function = manager.getBoxFunction(irClass)
|
||||
with(context.createIrBuilder(function.symbol)) {
|
||||
function.body = irExprBody(
|
||||
irCall(irClass.primaryConstructor!!.symbol).apply {
|
||||
passTypeArgumentsFrom(function)
|
||||
putValueArgument(0, irGet(function.valueParameters[0]))
|
||||
}
|
||||
)
|
||||
}
|
||||
irClass.declarations += function
|
||||
}
|
||||
|
||||
private fun buildUnboxFunction(irClass: IrClass) {
|
||||
val function = manager.getUnboxFunction(irClass)
|
||||
val builder = context.createIrBuilder(function.symbol)
|
||||
val field = getInlineClassBackingField(irClass)
|
||||
|
||||
function.body = builder.irBlockBody {
|
||||
val thisVal = irGet(function.dispatchReceiverParameter!!)
|
||||
+irReturn(irGetField(thisVal, field))
|
||||
}
|
||||
|
||||
irClass.declarations += function
|
||||
}
|
||||
}
|
||||
|
||||
private val IrBody.contents: List<IrStatement>
|
||||
get() = when (this) {
|
||||
is IrBlockBody -> statements
|
||||
is IrExpressionBody -> listOf(expression)
|
||||
is IrSyntheticBody -> error("Synthetic body contains no statements $this")
|
||||
else -> throw IllegalStateException()
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.lower.inlineclasses
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.ir.primaryConstructor
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
|
||||
import org.jetbrains.kotlin.codegen.state.md5base64
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.classOrNull
|
||||
import org.jetbrains.kotlin.ir.types.isPrimitiveType
|
||||
import org.jetbrains.kotlin.ir.types.makeNullable
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
/**
|
||||
* Replace inline classes by their underlying types.
|
||||
*/
|
||||
fun IrType.unboxInlineClass() = InlineClassAbi.unboxType(this) ?: this
|
||||
|
||||
object InlineClassAbi {
|
||||
/**
|
||||
* Unwraps inline class types to their underlying representation.
|
||||
* Returns null if the type cannot be unboxed.
|
||||
*/
|
||||
internal fun unboxType(type: IrType): IrType? {
|
||||
val klass = type.classOrNull?.owner ?: return null
|
||||
if (!klass.isInline) return null
|
||||
|
||||
val underlyingType = getUnderlyingType(klass).unboxInlineClass()
|
||||
if (!type.isNullable())
|
||||
return underlyingType
|
||||
if (underlyingType.isNullable() || underlyingType.isPrimitiveType())
|
||||
return null
|
||||
return underlyingType.makeNullable()
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the underlying type of an inline class based on the single argument to its
|
||||
* primary constructor. This is what the current jvm backend does.
|
||||
*
|
||||
* Looking for a backing field does not work for unsigned types, which don't
|
||||
* contain a field.
|
||||
*/
|
||||
fun getUnderlyingType(irClass: IrClass): IrType {
|
||||
require(irClass.isInline)
|
||||
return irClass.primaryConstructor!!.valueParameters[0].type
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a mangled name for a function taking inline class arguments
|
||||
* to avoid clashes between overloaded methods.
|
||||
*/
|
||||
fun mangledNameFor(irFunction: IrFunction): Name {
|
||||
val base = when {
|
||||
irFunction is IrConstructor ->
|
||||
"constructor"
|
||||
irFunction.isGetter ->
|
||||
JvmAbi.getterName(irFunction.propertyName.asString())
|
||||
irFunction.isSetter ->
|
||||
JvmAbi.setterName(irFunction.propertyName.asString())
|
||||
irFunction.name.isSpecial ->
|
||||
error("Unhandled special name in mangledNameFor: ${irFunction.name}")
|
||||
else ->
|
||||
irFunction.name.asString()
|
||||
}
|
||||
|
||||
val suffix = when {
|
||||
irFunction.fullValueParameterList.any { it.type.erasedUpperBound.isInline } ->
|
||||
hashSuffix(irFunction)
|
||||
(irFunction.parent as? IrClass)?.isInline == true -> "impl"
|
||||
else -> return irFunction.name
|
||||
}
|
||||
|
||||
return Name.identifier("$base-$suffix")
|
||||
}
|
||||
|
||||
private val IrFunction.propertyName: Name
|
||||
get() = (this as IrSimpleFunction).correspondingPropertySymbol!!.owner.name
|
||||
|
||||
private fun hashSuffix(irFunction: IrFunction) =
|
||||
md5base64(irFunction.fullValueParameterList.joinToString { it.type.eraseToString() })
|
||||
|
||||
private fun IrType.eraseToString() = buildString {
|
||||
append('L')
|
||||
append(erasedUpperBound.fqNameWhenAvailable!!)
|
||||
if (isNullable()) append('?')
|
||||
append(';')
|
||||
}
|
||||
}
|
||||
|
||||
internal val IrFunction.fullValueParameterList: List<IrValueParameter>
|
||||
get() = listOfNotNull(extensionReceiverParameter) + valueParameters
|
||||
|
||||
internal val IrClass.inlineClassFieldName: Name
|
||||
get() = primaryConstructor!!.valueParameters.single().name
|
||||
|
||||
val IrFunction.isInlineClassFieldGetter: Boolean
|
||||
get() = (parent as? IrClass)?.isInline == true && this is IrSimpleFunction && extensionReceiverParameter == null &&
|
||||
correspondingPropertySymbol?.let { it.owner.getter == this && it.owner.name == parentAsClass.inlineClassFieldName } == true
|
||||
|
||||
val IrFunction.isPrimaryInlineClassConstructor: Boolean
|
||||
get() = this is IrConstructor && isPrimary && constructedClass.isInline
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.lower.inlineclasses
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.ir.copyTo
|
||||
import org.jetbrains.kotlin.backend.common.ir.copyTypeParameters
|
||||
import org.jetbrains.kotlin.backend.common.ir.copyTypeParametersFrom
|
||||
import org.jetbrains.kotlin.backend.common.ir.createDispatchReceiverParameter
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.InlineClassAbi.mangledNameFor
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.util.constructedClass
|
||||
import org.jetbrains.kotlin.ir.util.defaultType
|
||||
import org.jetbrains.kotlin.ir.util.explicitParameters
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
||||
|
||||
class IrReplacementFunction(
|
||||
val function: IrFunction,
|
||||
val valueParameterMap: Map<IrValueParameterSymbol, IrValueParameter>
|
||||
)
|
||||
|
||||
/**
|
||||
* Keeps track of replacement functions and inline class box/unbox functions.
|
||||
*/
|
||||
class MemoizedInlineClassReplacements {
|
||||
private val storageManager = LockBasedStorageManager("inline-class-replacements")
|
||||
|
||||
/**
|
||||
* Get a replacement for a function or a constructor.
|
||||
*/
|
||||
val getReplacementFunction: (IrFunction) -> IrReplacementFunction? =
|
||||
storageManager.createMemoizedFunctionWithNullableValues {
|
||||
when {
|
||||
!it.hasInlineClassParameters || it.isSyntheticInlineClassMember || it.origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA -> null
|
||||
it.hasMethodReplacement -> createMethodReplacement(it)
|
||||
it.hasStaticReplacement -> createStaticReplacement(it)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private val IrFunction.hasInlineClassParameters: Boolean
|
||||
get() = explicitParameters.any { it.type.erasedUpperBound.isInline } || (this is IrConstructor && constructedClass.isInline)
|
||||
|
||||
private val IrFunction.hasStaticReplacement: Boolean
|
||||
get() = origin != IrDeclarationOrigin.FAKE_OVERRIDE &&
|
||||
(this is IrSimpleFunction || this is IrConstructor && constructedClass.isInline)
|
||||
|
||||
private val IrFunction.hasMethodReplacement: Boolean
|
||||
get() = dispatchReceiverParameter != null && this is IrSimpleFunction && (parent as? IrClass)?.isInline != true
|
||||
|
||||
private val IrFunction.isSyntheticInlineClassMember: Boolean
|
||||
get() = origin == JvmLoweredDeclarationOrigin.SYNTHETIC_INLINE_CLASS_MEMBER || isInlineClassFieldGetter
|
||||
|
||||
/**
|
||||
* Get the box function for an inline class. Concretely, this is a synthetic
|
||||
* static function named "box-impl" which takes an unboxed value and returns
|
||||
* a boxed value.
|
||||
*/
|
||||
val getBoxFunction: (IrClass) -> IrSimpleFunction =
|
||||
storageManager.createMemoizedFunction { irClass ->
|
||||
require(irClass.isInline)
|
||||
buildFun {
|
||||
name = Name.identifier("box-impl")
|
||||
origin = JvmLoweredDeclarationOrigin.SYNTHETIC_INLINE_CLASS_MEMBER
|
||||
returnType = irClass.defaultType
|
||||
}.apply {
|
||||
parent = irClass
|
||||
copyTypeParametersFrom(irClass)
|
||||
addValueParameter {
|
||||
name = Name.identifier("v")
|
||||
type = InlineClassAbi.getUnderlyingType(irClass)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the unbox function for an inline class. Concretely, this is a synthetic
|
||||
* member function named "unbox-impl" which returns an unboxed result.
|
||||
*/
|
||||
val getUnboxFunction: (IrClass) -> IrSimpleFunction =
|
||||
storageManager.createMemoizedFunction { irClass ->
|
||||
require(irClass.isInline)
|
||||
buildFun {
|
||||
name = Name.identifier("unbox-impl")
|
||||
origin = JvmLoweredDeclarationOrigin.SYNTHETIC_INLINE_CLASS_MEMBER
|
||||
returnType = InlineClassAbi.getUnderlyingType(irClass)
|
||||
}.apply {
|
||||
parent = irClass
|
||||
createDispatchReceiverParameter()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createMethodReplacement(function: IrFunction): IrReplacementFunction? {
|
||||
require(function.dispatchReceiverParameter != null && function is IrSimpleFunction)
|
||||
val overrides = function.overriddenSymbols.mapNotNull {
|
||||
getReplacementFunction(it.owner)?.function?.symbol as? IrSimpleFunctionSymbol
|
||||
}
|
||||
if (function.origin == IrDeclarationOrigin.FAKE_OVERRIDE && overrides.isEmpty())
|
||||
return null
|
||||
|
||||
val parameterMap = mutableMapOf<IrValueParameterSymbol, IrValueParameter>()
|
||||
val replacement = buildReplacement(function) {
|
||||
annotations += function.annotations
|
||||
metadata = function.metadata
|
||||
overriddenSymbols.addAll(overrides)
|
||||
|
||||
for ((index, parameter) in function.explicitParameters.withIndex()) {
|
||||
val name = if (parameter == function.extensionReceiverParameter) Name.identifier("\$receiver") else parameter.name
|
||||
val newParameter = parameter.copyTo(this, index = index - 1, name = name)
|
||||
if (parameter == function.dispatchReceiverParameter)
|
||||
dispatchReceiverParameter = newParameter
|
||||
else
|
||||
valueParameters.add(newParameter)
|
||||
parameterMap[parameter.symbol] = newParameter
|
||||
}
|
||||
}
|
||||
return IrReplacementFunction(replacement, parameterMap)
|
||||
}
|
||||
|
||||
private fun createStaticReplacement(function: IrFunction): IrReplacementFunction {
|
||||
val parameterMap = mutableMapOf<IrValueParameterSymbol, IrValueParameter>()
|
||||
val replacement = buildReplacement(function) {
|
||||
if (function !is IrSimpleFunction || function.overriddenSymbols.isEmpty())
|
||||
metadata = function.metadata
|
||||
|
||||
for ((index, parameter) in function.explicitParameters.withIndex()) {
|
||||
val name = when (parameter) {
|
||||
function.dispatchReceiverParameter -> Name.identifier("\$this")
|
||||
function.extensionReceiverParameter -> Name.identifier("\$receiver")
|
||||
else -> parameter.name
|
||||
}
|
||||
|
||||
val newParameter = parameter.copyTo(this, index = index, name = name)
|
||||
valueParameters.add(newParameter)
|
||||
parameterMap[parameter.symbol] = newParameter
|
||||
}
|
||||
}
|
||||
return IrReplacementFunction(replacement, parameterMap)
|
||||
}
|
||||
|
||||
private fun buildReplacement(function: IrFunction, body: IrFunctionImpl.() -> Unit) =
|
||||
buildFun {
|
||||
updateFrom(function)
|
||||
name = mangledNameFor(function)
|
||||
returnType = function.returnType
|
||||
}.apply {
|
||||
parent = function.parent
|
||||
if (function is IrConstructor) {
|
||||
copyTypeParameters(function.constructedClass.typeParameters + function.typeParameters)
|
||||
} else {
|
||||
copyTypeParametersFrom(function)
|
||||
}
|
||||
body()
|
||||
}
|
||||
}
|
||||
+13
@@ -7,10 +7,14 @@ package org.jetbrains.kotlin.ir.expressions
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrTypeParameter
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.defaultType
|
||||
import org.jetbrains.kotlin.ir.types.toKotlinType
|
||||
import org.jetbrains.kotlin.ir.util.dump
|
||||
import org.jetbrains.kotlin.ir.util.render
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
|
||||
interface IrMemberAccessExpression : IrExpression {
|
||||
@@ -102,3 +106,12 @@ inline fun <T : IrMemberAccessExpression> T.mapValueParametersIndexed(transform:
|
||||
}
|
||||
}
|
||||
|
||||
fun IrMemberAccessExpression.putArgument(callee: IrFunction, parameter: IrValueParameter, argument: IrExpression) =
|
||||
when (parameter) {
|
||||
callee.dispatchReceiverParameter -> dispatchReceiver = argument
|
||||
callee.extensionReceiverParameter -> extensionReceiver = argument
|
||||
else -> putValueArgument(parameter.index, argument)
|
||||
}
|
||||
|
||||
fun IrFunctionAccessExpression.putArgument(parameter: IrValueParameter, argument: IrExpression) =
|
||||
putArgument(symbol.owner, parameter, argument)
|
||||
|
||||
Reference in New Issue
Block a user