[JS IR] Make PrivateMembersLowering global to handle inlined private references correctly
- Fixes [KT-33334]
This commit is contained in:
@@ -51,6 +51,8 @@ class JsIrBackendContext(
|
|||||||
override val transformedFunction = mutableMapOf<IrFunctionSymbol, IrSimpleFunctionSymbol>()
|
override val transformedFunction = mutableMapOf<IrFunctionSymbol, IrSimpleFunctionSymbol>()
|
||||||
override val lateinitNullableFields = mutableMapOf<IrField, IrField>()
|
override val lateinitNullableFields = mutableMapOf<IrField, IrField>()
|
||||||
|
|
||||||
|
val memberMap = mutableMapOf<IrSimpleFunctionSymbol, IrSimpleFunction>()
|
||||||
|
|
||||||
override val builtIns = module.builtIns
|
override val builtIns = module.builtIns
|
||||||
|
|
||||||
override var inVerbosePhase: Boolean = false
|
override var inVerbosePhase: Boolean = false
|
||||||
|
|||||||
+62
-52
@@ -21,7 +21,7 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
|||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrPropertyReferenceImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrPropertyReferenceImpl
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||||
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
|
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
|
||||||
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
|
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
|
||||||
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
|
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
|
||||||
@@ -33,13 +33,11 @@ import org.jetbrains.kotlin.name.Name
|
|||||||
private val STATIC_THIS_PARAMETER = object : IrDeclarationOriginImpl("STATIC_THIS_PARAMETER") {}
|
private val STATIC_THIS_PARAMETER = object : IrDeclarationOriginImpl("STATIC_THIS_PARAMETER") {}
|
||||||
|
|
||||||
class PrivateMembersLowering(val context: JsIrBackendContext) : FileLoweringPass {
|
class PrivateMembersLowering(val context: JsIrBackendContext) : FileLoweringPass {
|
||||||
private val memberMap = mutableMapOf<IrSimpleFunctionSymbol, IrSimpleFunction>()
|
private val memberMap = context.memberMap
|
||||||
|
|
||||||
override fun lower(irFile: IrFile) {
|
override fun lower(irFile: IrFile) {
|
||||||
transformPrivateDeclarations(irFile)
|
transformPrivateDeclarations(irFile)
|
||||||
transformPrivateUseSites(irFile)
|
transformPrivateUseSites(irFile)
|
||||||
|
|
||||||
memberMap.clear()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun transformPrivateDeclarations(irFile: IrFile) {
|
private fun transformPrivateDeclarations(irFile: IrFile) {
|
||||||
@@ -49,7 +47,6 @@ class PrivateMembersLowering(val context: JsIrBackendContext) : FileLoweringPass
|
|||||||
declaration.declarations.transformFlat {
|
declaration.declarations.transformFlat {
|
||||||
when (it) {
|
when (it) {
|
||||||
is IrSimpleFunction -> transformMemberToStaticFunction(it)?.let { staticFunction ->
|
is IrSimpleFunction -> transformMemberToStaticFunction(it)?.let { staticFunction ->
|
||||||
memberMap[it.symbol] = staticFunction
|
|
||||||
listOf(staticFunction)
|
listOf(staticFunction)
|
||||||
}
|
}
|
||||||
is IrProperty -> listOf(it.apply {
|
is IrProperty -> listOf(it.apply {
|
||||||
@@ -69,7 +66,7 @@ class PrivateMembersLowering(val context: JsIrBackendContext) : FileLoweringPass
|
|||||||
override fun visitCall(expression: IrCall): IrExpression {
|
override fun visitCall(expression: IrCall): IrExpression {
|
||||||
super.visitCall(expression)
|
super.visitCall(expression)
|
||||||
|
|
||||||
return memberMap[expression.symbol]?.let {
|
return getOrPutStaticFunction(expression.symbol)?.let {
|
||||||
transformPrivateToStaticCall(expression, it)
|
transformPrivateToStaticCall(expression, it)
|
||||||
} ?: expression
|
} ?: expression
|
||||||
}
|
}
|
||||||
@@ -77,7 +74,7 @@ class PrivateMembersLowering(val context: JsIrBackendContext) : FileLoweringPass
|
|||||||
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
|
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
|
||||||
super.visitFunctionReference(expression)
|
super.visitFunctionReference(expression)
|
||||||
|
|
||||||
return memberMap[expression.symbol]?.let {
|
return getOrPutStaticFunction(expression.symbol)?.let {
|
||||||
transformPrivateToStaticReference(expression) {
|
transformPrivateToStaticReference(expression) {
|
||||||
IrFunctionReferenceImpl(
|
IrFunctionReferenceImpl(
|
||||||
expression.startOffset, expression.endOffset,
|
expression.startOffset, expression.endOffset,
|
||||||
@@ -92,7 +89,10 @@ class PrivateMembersLowering(val context: JsIrBackendContext) : FileLoweringPass
|
|||||||
override fun visitPropertyReference(expression: IrPropertyReference): IrExpression {
|
override fun visitPropertyReference(expression: IrPropertyReference): IrExpression {
|
||||||
super.visitPropertyReference(expression)
|
super.visitPropertyReference(expression)
|
||||||
|
|
||||||
return if (expression.getter in memberMap || expression.setter in memberMap) {
|
val staticGetter = expression.getter?.let { getOrPutStaticFunction(it) }
|
||||||
|
val staticSetter = expression.setter?.let { getOrPutStaticFunction(it) }
|
||||||
|
|
||||||
|
return if (staticGetter != null || staticSetter != null) {
|
||||||
transformPrivateToStaticReference(expression) {
|
transformPrivateToStaticReference(expression) {
|
||||||
IrPropertyReferenceImpl(
|
IrPropertyReferenceImpl(
|
||||||
expression.startOffset, expression.endOffset,
|
expression.startOffset, expression.endOffset,
|
||||||
@@ -100,8 +100,8 @@ class PrivateMembersLowering(val context: JsIrBackendContext) : FileLoweringPass
|
|||||||
expression.symbol, // TODO remap property symbol based on remapped getter/setter?
|
expression.symbol, // TODO remap property symbol based on remapped getter/setter?
|
||||||
expression.typeArgumentsCount,
|
expression.typeArgumentsCount,
|
||||||
expression.field,
|
expression.field,
|
||||||
memberMap[expression.getter]?.symbol ?: expression.getter,
|
staticGetter?.symbol ?: expression.getter,
|
||||||
memberMap[expression.setter]?.symbol ?: expression.setter,
|
staticSetter?.symbol ?: expression.setter,
|
||||||
expression.origin
|
expression.origin
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -127,6 +127,7 @@ class PrivateMembersLowering(val context: JsIrBackendContext) : FileLoweringPass
|
|||||||
|
|
||||||
return newExpression
|
return newExpression
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun transformPrivateToStaticReference(
|
private fun transformPrivateToStaticReference(
|
||||||
expression: IrCallableReference,
|
expression: IrCallableReference,
|
||||||
builder: () -> IrCallableReference
|
builder: () -> IrCallableReference
|
||||||
@@ -147,28 +148,11 @@ class PrivateMembersLowering(val context: JsIrBackendContext) : FileLoweringPass
|
|||||||
}, null)
|
}, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun transformAccessor(accessor: IrSimpleFunction) =
|
private fun transformAccessor(accessor: IrSimpleFunction) = transformMemberToStaticFunction(accessor) ?: accessor
|
||||||
transformMemberToStaticFunction(accessor)?.also { memberMap[accessor.symbol] = it } ?: accessor
|
|
||||||
|
|
||||||
private fun transformMemberToStaticFunction(function: IrSimpleFunction): IrSimpleFunction? {
|
private fun transformMemberToStaticFunction(function: IrSimpleFunction): IrSimpleFunction? {
|
||||||
|
|
||||||
if (function.visibility != Visibilities.PRIVATE || function.dispatchReceiverParameter == null) return null
|
val staticFunction = getOrPutStaticFunction(function.symbol) ?: return null
|
||||||
|
|
||||||
val descriptor = WrappedSimpleFunctionDescriptor()
|
|
||||||
val symbol = IrSimpleFunctionSymbolImpl(descriptor)
|
|
||||||
val staticFunction = function.run {
|
|
||||||
IrFunctionImpl(
|
|
||||||
startOffset, endOffset, origin,
|
|
||||||
symbol, name, visibility, modality,
|
|
||||||
returnType,
|
|
||||||
isInline = isInline, isExternal = isExternal, isTailrec = isTailrec, isSuspend = isSuspend, isExpect = isExpect,
|
|
||||||
isFakeOverride = origin == IrDeclarationOrigin.FAKE_OVERRIDE
|
|
||||||
).also {
|
|
||||||
descriptor.bind(it)
|
|
||||||
it.parent = parent
|
|
||||||
it.correspondingPropertySymbol = correspondingPropertySymbol
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detach old function from corresponding property
|
// Detach old function from corresponding property
|
||||||
val correspondingProperty = function.correspondingPropertySymbol?.owner
|
val correspondingProperty = function.correspondingPropertySymbol?.owner
|
||||||
@@ -179,29 +163,6 @@ class PrivateMembersLowering(val context: JsIrBackendContext) : FileLoweringPass
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
staticFunction.typeParameters += function.typeParameters.map { it.deepCopyWithSymbols(staticFunction) }
|
|
||||||
|
|
||||||
staticFunction.extensionReceiverParameter = function.extensionReceiverParameter?.copyTo(staticFunction)
|
|
||||||
val thisDesc = WrappedValueParameterDescriptor()
|
|
||||||
val thisSymbol = IrValueParameterSymbolImpl(thisDesc)
|
|
||||||
staticFunction.valueParameters += IrValueParameterImpl(
|
|
||||||
UNDEFINED_OFFSET,
|
|
||||||
UNDEFINED_OFFSET,
|
|
||||||
STATIC_THIS_PARAMETER,
|
|
||||||
thisSymbol,
|
|
||||||
Name.identifier("\$this"),
|
|
||||||
0,
|
|
||||||
function.dispatchReceiverParameter!!.type,
|
|
||||||
null,
|
|
||||||
false,
|
|
||||||
false
|
|
||||||
).also {
|
|
||||||
thisDesc.bind(it)
|
|
||||||
it.parent = staticFunction
|
|
||||||
}
|
|
||||||
|
|
||||||
staticFunction.valueParameters += function.valueParameters.map { it.copyTo(staticFunction, index = it.index + 1) }
|
|
||||||
|
|
||||||
val oldParameters =
|
val oldParameters =
|
||||||
listOfNotNull(function.extensionReceiverParameter, function.dispatchReceiverParameter) + function.valueParameters
|
listOfNotNull(function.extensionReceiverParameter, function.dispatchReceiverParameter) + function.valueParameters
|
||||||
val newParameters = listOfNotNull(staticFunction.extensionReceiverParameter) + staticFunction.valueParameters
|
val newParameters = listOfNotNull(staticFunction.extensionReceiverParameter) + staticFunction.valueParameters
|
||||||
@@ -220,4 +181,53 @@ class PrivateMembersLowering(val context: JsIrBackendContext) : FileLoweringPass
|
|||||||
return staticFunction
|
return staticFunction
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getOrPutStaticFunction(functionSymbol: IrFunctionSymbol): IrSimpleFunction? {
|
||||||
|
val function = functionSymbol.owner
|
||||||
|
if (function !is IrSimpleFunction) return null
|
||||||
|
if (function.visibility != Visibilities.PRIVATE || function.dispatchReceiverParameter == null) return null
|
||||||
|
|
||||||
|
return memberMap.getOrPut(function.symbol) {
|
||||||
|
val descriptor = WrappedSimpleFunctionDescriptor()
|
||||||
|
val symbol = IrSimpleFunctionSymbolImpl(descriptor)
|
||||||
|
val staticFunction = function.run {
|
||||||
|
IrFunctionImpl(
|
||||||
|
startOffset, endOffset, origin,
|
||||||
|
symbol, name, visibility, modality,
|
||||||
|
returnType,
|
||||||
|
isInline = isInline, isExternal = isExternal, isTailrec = isTailrec, isSuspend = isSuspend, isExpect = isExpect,
|
||||||
|
isFakeOverride = origin == IrDeclarationOrigin.FAKE_OVERRIDE
|
||||||
|
).also {
|
||||||
|
descriptor.bind(it)
|
||||||
|
it.parent = parent
|
||||||
|
it.correspondingPropertySymbol = correspondingPropertySymbol
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
staticFunction.typeParameters += function.typeParameters.map { it.deepCopyWithSymbols(staticFunction) }
|
||||||
|
|
||||||
|
staticFunction.extensionReceiverParameter = function.extensionReceiverParameter?.copyTo(staticFunction)
|
||||||
|
val thisDesc = WrappedValueParameterDescriptor()
|
||||||
|
val thisSymbol = IrValueParameterSymbolImpl(thisDesc)
|
||||||
|
staticFunction.valueParameters += IrValueParameterImpl(
|
||||||
|
UNDEFINED_OFFSET,
|
||||||
|
UNDEFINED_OFFSET,
|
||||||
|
STATIC_THIS_PARAMETER,
|
||||||
|
thisSymbol,
|
||||||
|
Name.identifier("\$this"),
|
||||||
|
0,
|
||||||
|
function.dispatchReceiverParameter!!.type,
|
||||||
|
null,
|
||||||
|
isCrossinline = false,
|
||||||
|
isNoinline = false
|
||||||
|
).also {
|
||||||
|
thisDesc.bind(it)
|
||||||
|
it.parent = staticFunction
|
||||||
|
}
|
||||||
|
|
||||||
|
staticFunction.valueParameters += function.valueParameters.map { it.copyTo(staticFunction, index = it.index + 1) }
|
||||||
|
|
||||||
|
staticFunction
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+5
@@ -4249,6 +4249,11 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
|
|||||||
runTest("js/js.translator/testData/box/inlineMultiFile/lambdaInLambdaNoInline.kt");
|
runTest("js/js.translator/testData/box/inlineMultiFile/lambdaInLambdaNoInline.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("privateVarFromInline.kt")
|
||||||
|
public void testPrivateVarFromInline() throws Exception {
|
||||||
|
runTest("js/js.translator/testData/box/inlineMultiFile/privateVarFromInline.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("regeneratedLambdaName.kt")
|
@TestMetadata("regeneratedLambdaName.kt")
|
||||||
public void testRegeneratedLambdaName() throws Exception {
|
public void testRegeneratedLambdaName() throws Exception {
|
||||||
runTest("js/js.translator/testData/box/inlineMultiFile/regeneratedLambdaName.kt");
|
runTest("js/js.translator/testData/box/inlineMultiFile/regeneratedLambdaName.kt");
|
||||||
|
|||||||
+5
@@ -4264,6 +4264,11 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
|
|||||||
runTest("js/js.translator/testData/box/inlineMultiFile/lambdaInLambdaNoInline.kt");
|
runTest("js/js.translator/testData/box/inlineMultiFile/lambdaInLambdaNoInline.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("privateVarFromInline.kt")
|
||||||
|
public void testPrivateVarFromInline() throws Exception {
|
||||||
|
runTest("js/js.translator/testData/box/inlineMultiFile/privateVarFromInline.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("regeneratedLambdaName.kt")
|
@TestMetadata("regeneratedLambdaName.kt")
|
||||||
public void testRegeneratedLambdaName() throws Exception {
|
public void testRegeneratedLambdaName() throws Exception {
|
||||||
runTest("js/js.translator/testData/box/inlineMultiFile/regeneratedLambdaName.kt");
|
runTest("js/js.translator/testData/box/inlineMultiFile/regeneratedLambdaName.kt");
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
// FILE: f.kt
|
||||||
|
package foo
|
||||||
|
|
||||||
|
/**
|
||||||
|
* KT-33334
|
||||||
|
*/
|
||||||
|
|
||||||
|
internal fun foo(c: C) = c.getter()
|
||||||
|
|
||||||
|
internal fun bar(c: C) { c.setter("OK") }
|
||||||
|
|
||||||
|
// FILE: c.kt
|
||||||
|
package foo
|
||||||
|
|
||||||
|
var log = ""
|
||||||
|
|
||||||
|
internal class C {
|
||||||
|
private var tokenPosition: String = "FAIL"
|
||||||
|
get() { logg("G"); return field }
|
||||||
|
set(value) { logg("S"); field = value }
|
||||||
|
|
||||||
|
private fun logg(s: String) { log += s }
|
||||||
|
|
||||||
|
internal inline fun setter(s: String) { tokenPosition = s }
|
||||||
|
internal inline fun getter(): String = tokenPosition
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: m.kt
|
||||||
|
package foo
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
|
||||||
|
val c = C()
|
||||||
|
|
||||||
|
bar(c)
|
||||||
|
val r = foo(c)
|
||||||
|
if (log != "SG") return "FAIL1: $log"
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user