JVM IR: Always produce stubs for interface methods in inline classes
This matches the behavior of the JVM backend, with the exception of @JvmDefault methods, which are currently broken on the JVM backend.
This commit is contained in:
committed by
Alexander Udalov
parent
8c9ebc1bf9
commit
465e9f2d68
+20
-10
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
* Copyright 2010-2020 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.
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -116,6 +116,17 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun createBridgeBody(source: IrSimpleFunction, target: IrSimpleFunction) {
|
||||||
|
source.body = context.createIrBuilder(source.symbol, source.startOffset, source.endOffset).run {
|
||||||
|
irExprBody(irCall(target).apply {
|
||||||
|
passTypeArgumentsFrom(source)
|
||||||
|
for ((parameter, newParameter) in source.explicitParameters.zip(target.explicitParameters)) {
|
||||||
|
putArgument(newParameter, irGet(parameter))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun transformSimpleFunctionFlat(function: IrSimpleFunction, replacement: IrSimpleFunction): List<IrDeclaration> {
|
private fun transformSimpleFunctionFlat(function: IrSimpleFunction, replacement: IrSimpleFunction): List<IrDeclaration> {
|
||||||
replacement.valueParameters.forEach { it.transformChildrenVoid() }
|
replacement.valueParameters.forEach { it.transformChildrenVoid() }
|
||||||
replacement.body = function.body?.transform(this, null)?.patchDeclarationParents(replacement)
|
replacement.body = function.body?.transform(this, null)?.patchDeclarationParents(replacement)
|
||||||
@@ -125,16 +136,15 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
|
|||||||
if (function.overriddenSymbols.isEmpty() || replacement.dispatchReceiverParameter != null)
|
if (function.overriddenSymbols.isEmpty() || replacement.dispatchReceiverParameter != null)
|
||||||
return listOf(replacement)
|
return listOf(replacement)
|
||||||
|
|
||||||
// Replace the function body with a wrapper
|
// Update the overridden symbols to point to their inline class replacements
|
||||||
context.createIrBuilder(function.symbol, function.startOffset, function.endOffset).run {
|
function.overriddenSymbols = replacement.overriddenSymbols
|
||||||
val call = irCall(replacement).apply {
|
|
||||||
passTypeArgumentsFrom(function)
|
|
||||||
for ((parameter, newParameter) in function.explicitParameters.zip(replacement.explicitParameters)) {
|
|
||||||
putArgument(newParameter, irGet(parameter))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function.body = irExprBody(call)
|
// Replace the function body with a wrapper
|
||||||
|
if (!function.isFakeOverride || !function.parentAsClass.isInline) {
|
||||||
|
createBridgeBody(function, replacement)
|
||||||
|
} else {
|
||||||
|
// Fake overrides redirect from the replacement to the original function, which is in turn replaced during interfacePhase.
|
||||||
|
createBridgeBody(replacement, function)
|
||||||
}
|
}
|
||||||
|
|
||||||
return listOf(replacement, function)
|
return listOf(replacement, function)
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
* Copyright 2010-2020 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.
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -101,7 +101,7 @@ private val IrFunction.fullValueParameterList: List<IrValueParameter>
|
|||||||
get() = listOfNotNull(extensionReceiverParameter) + valueParameters
|
get() = listOfNotNull(extensionReceiverParameter) + valueParameters
|
||||||
|
|
||||||
internal val IrFunction.hasMangledParameters: Boolean
|
internal val IrFunction.hasMangledParameters: Boolean
|
||||||
get() = dispatchReceiverParameter?.type?.getClass()?.isInline == true ||
|
get() = dispatchReceiverParameter != null && parentAsClass.isInline ||
|
||||||
fullValueParameterList.any { it.type.requiresMangling } ||
|
fullValueParameterList.any { it.type.requiresMangling } ||
|
||||||
(this is IrConstructor && constructedClass.isInline)
|
(this is IrConstructor && constructedClass.isInline)
|
||||||
|
|
||||||
|
|||||||
+34
-31
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
* Copyright 2010-2020 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.
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -39,27 +39,26 @@ class MemoizedInlineClassReplacements {
|
|||||||
val getReplacementFunction: (IrFunction) -> IrSimpleFunction? =
|
val getReplacementFunction: (IrFunction) -> IrSimpleFunction? =
|
||||||
storageManager.createMemoizedFunctionWithNullableValues {
|
storageManager.createMemoizedFunctionWithNullableValues {
|
||||||
when {
|
when {
|
||||||
!it.hasMangledParameters ||
|
// Don't mangle anonymous or synthetic functions
|
||||||
it.isSyntheticInlineClassMember ||
|
it.origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA ||
|
||||||
it.origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA ||
|
|
||||||
it.origin == IrDeclarationOrigin.DELEGATED_PROPERTY_ACCESSOR ||
|
it.origin == IrDeclarationOrigin.DELEGATED_PROPERTY_ACCESSOR ||
|
||||||
it.origin.isSynthetic -> null
|
it.origin == JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_REPLACEMENT ||
|
||||||
it.hasMethodReplacement -> createMethodReplacement(it)
|
it.origin.isSynthetic ||
|
||||||
it.hasStaticReplacement -> createStaticReplacement(it)
|
it.isInlineClassFieldGetter -> null
|
||||||
else -> null
|
|
||||||
|
// Mangle all functions in the body of an inline class
|
||||||
|
it.parent.safeAs<IrClass>()?.isInline == true ->
|
||||||
|
createStaticReplacement(it)
|
||||||
|
|
||||||
|
// Otherwise, mangle functions with mangled parameters, while ignoring constructors
|
||||||
|
it is IrSimpleFunction && it.hasMangledParameters ->
|
||||||
|
if (it.dispatchReceiverParameter != null) createMethodReplacement(it) else createStaticReplacement(it)
|
||||||
|
|
||||||
|
else ->
|
||||||
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
* 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
|
* static function named "box-impl" which takes an unboxed value and returns
|
||||||
@@ -150,20 +149,24 @@ class MemoizedInlineClassReplacements {
|
|||||||
buildReplacement(function, JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_REPLACEMENT) {
|
buildReplacement(function, JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_REPLACEMENT) {
|
||||||
val newValueParameters = ArrayList<IrValueParameter>()
|
val newValueParameters = ArrayList<IrValueParameter>()
|
||||||
for ((index, parameter) in function.explicitParameters.withIndex()) {
|
for ((index, parameter) in function.explicitParameters.withIndex()) {
|
||||||
val name = when (parameter) {
|
newValueParameters += when (parameter) {
|
||||||
function.dispatchReceiverParameter -> Name.identifier("arg$index")
|
// FAKE_OVERRIDEs have broken dispatch receivers
|
||||||
function.extensionReceiverParameter -> Name.identifier("\$this\$${function.name}")
|
function.dispatchReceiverParameter ->
|
||||||
else -> parameter.name
|
function.parentAsClass.thisReceiver!!.copyTo(
|
||||||
|
this, index = index, name = Name.identifier("arg$index"),
|
||||||
|
type = function.parentAsClass.defaultType, origin = IrDeclarationOrigin.MOVED_DISPATCH_RECEIVER
|
||||||
|
)
|
||||||
|
function.extensionReceiverParameter ->
|
||||||
|
parameter.copyTo(
|
||||||
|
this, index = index, name = Name.identifier("\$this\$${function.name}"),
|
||||||
|
origin = IrDeclarationOrigin.MOVED_EXTENSION_RECEIVER
|
||||||
|
)
|
||||||
|
else ->
|
||||||
|
parameter.copyTo(this, index = index, defaultValue = null).also {
|
||||||
|
// See comment next to a similar line above.
|
||||||
|
it.defaultValue = parameter.defaultValue?.patchDeclarationParents(this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val parameterOrigin = when (parameter) {
|
|
||||||
function.dispatchReceiverParameter -> IrDeclarationOrigin.MOVED_DISPATCH_RECEIVER
|
|
||||||
function.extensionReceiverParameter -> IrDeclarationOrigin.MOVED_EXTENSION_RECEIVER
|
|
||||||
else -> parameter.origin
|
|
||||||
}
|
|
||||||
val newParameter = parameter.copyTo(this, index = index, name = name, defaultValue = null, origin = parameterOrigin)
|
|
||||||
newValueParameters += newParameter
|
|
||||||
// See comment next to a similar line above.
|
|
||||||
newParameter.defaultValue = parameter.defaultValue?.patchDeclarationParents(this)
|
|
||||||
}
|
}
|
||||||
valueParameters = newValueParameters
|
valueParameters = newValueParameters
|
||||||
}
|
}
|
||||||
|
|||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
// !LANGUAGE: +InlineClasses
|
||||||
|
// FILE: test.kt
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val b = B(0)
|
||||||
|
return b.f() + b.g()
|
||||||
|
}
|
||||||
|
|
||||||
|
interface A {
|
||||||
|
fun f() = "O"
|
||||||
|
fun g() = "K"
|
||||||
|
}
|
||||||
|
|
||||||
|
inline class B(val x: Int) : A
|
||||||
|
|
||||||
|
// 1 public static f-impl\(I\)Ljava/lang/String;
|
||||||
|
// 1 public f\(\)Ljava/lang/String;
|
||||||
|
|
||||||
|
// 1 public static g-impl\(I\)Ljava/lang/String;
|
||||||
|
// 1 public g\(\)Ljava/lang/String;
|
||||||
|
|
||||||
|
// JVM_TEMPLATES:
|
||||||
|
// The JVM backend calls f-impl, g-impl from f, g, respectively, in addition to the call from box.
|
||||||
|
// 2 INVOKESTATIC B.g-impl \(I\)Ljava/lang/String;
|
||||||
|
// 2 INVOKESTATIC B.f-impl \(I\)Ljava/lang/String;
|
||||||
|
|
||||||
|
// JVM_IR_TEMPLATES:
|
||||||
|
// 1 INVOKESTATIC B.g-impl \(I\)Ljava/lang/String;
|
||||||
|
// 1 INVOKESTATIC B.f-impl \(I\)Ljava/lang/String;
|
||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
// !LANGUAGE: +InlineClasses
|
||||||
|
|
||||||
|
// IGNORE_BACKEND: JVM
|
||||||
|
// The JVM backend does not generate the g-impl method, but ends up calling it from box.
|
||||||
|
|
||||||
|
// !JVM_DEFAULT_MODE: enable
|
||||||
|
// JVM_TARGET: 1.8
|
||||||
|
// FILE: test.kt
|
||||||
|
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val b = B(0)
|
||||||
|
return b.f() + b.g()
|
||||||
|
}
|
||||||
|
|
||||||
|
interface A {
|
||||||
|
fun f() = "O"
|
||||||
|
@JvmDefault
|
||||||
|
fun g() = "K"
|
||||||
|
}
|
||||||
|
|
||||||
|
inline class B(val x: Int) : A
|
||||||
|
|
||||||
|
// 1 public static f-impl\(I\)Ljava/lang/String;
|
||||||
|
// 1 public f\(\)Ljava/lang/String;
|
||||||
|
|
||||||
|
// 1 public static g-impl\(I\)Ljava/lang/String;
|
||||||
|
// 0 public g\(\)Ljava/lang/String;
|
||||||
|
|
||||||
|
// 1 INVOKESTATIC B.g-impl \(I\)Ljava/lang/String;
|
||||||
|
|
||||||
|
// JVM_TEMPLATES:
|
||||||
|
// 2 INVOKESTATIC B.f-impl \(I\)Ljava/lang/String;
|
||||||
|
|
||||||
|
// JVM_IR_TEMPLATES:
|
||||||
|
// 1 INVOKESTATIC B.f-impl \(I\)Ljava/lang/String;
|
||||||
@@ -2775,6 +2775,16 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
|
|||||||
runTest("compiler/testData/codegen/bytecodeText/inlineClasses/inlineClassesUnboxingAfterAssertionOperator.kt");
|
runTest("compiler/testData/codegen/bytecodeText/inlineClasses/inlineClassesUnboxingAfterAssertionOperator.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("interfaceDefaultImplStubs.kt")
|
||||||
|
public void testInterfaceDefaultImplStubs() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/bytecodeText/inlineClasses/interfaceDefaultImplStubs.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("interfaceJvmDefaultImplStubs.kt")
|
||||||
|
public void testInterfaceJvmDefaultImplStubs() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/bytecodeText/inlineClasses/interfaceJvmDefaultImplStubs.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("isCheckForInlineClass.kt")
|
@TestMetadata("isCheckForInlineClass.kt")
|
||||||
public void testIsCheckForInlineClass() throws Exception {
|
public void testIsCheckForInlineClass() throws Exception {
|
||||||
runTest("compiler/testData/codegen/bytecodeText/inlineClasses/isCheckForInlineClass.kt");
|
runTest("compiler/testData/codegen/bytecodeText/inlineClasses/isCheckForInlineClass.kt");
|
||||||
|
|||||||
+10
@@ -2820,6 +2820,16 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest {
|
|||||||
runTest("compiler/testData/codegen/bytecodeText/inlineClasses/inlineClassesUnboxingAfterAssertionOperator.kt");
|
runTest("compiler/testData/codegen/bytecodeText/inlineClasses/inlineClassesUnboxingAfterAssertionOperator.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("interfaceDefaultImplStubs.kt")
|
||||||
|
public void testInterfaceDefaultImplStubs() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/bytecodeText/inlineClasses/interfaceDefaultImplStubs.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("interfaceJvmDefaultImplStubs.kt")
|
||||||
|
public void testInterfaceJvmDefaultImplStubs() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/bytecodeText/inlineClasses/interfaceJvmDefaultImplStubs.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("isCheckForInlineClass.kt")
|
@TestMetadata("isCheckForInlineClass.kt")
|
||||||
public void testIsCheckForInlineClass() throws Exception {
|
public void testIsCheckForInlineClass() throws Exception {
|
||||||
runTest("compiler/testData/codegen/bytecodeText/inlineClasses/isCheckForInlineClass.kt");
|
runTest("compiler/testData/codegen/bytecodeText/inlineClasses/isCheckForInlineClass.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user