[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 lateinitNullableFields = mutableMapOf<IrField, IrField>()
|
||||
|
||||
val memberMap = mutableMapOf<IrSimpleFunctionSymbol, IrSimpleFunction>()
|
||||
|
||||
override val builtIns = module.builtIns
|
||||
|
||||
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.IrGetValueImpl
|
||||
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.IrValueParameterSymbolImpl
|
||||
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") {}
|
||||
|
||||
class PrivateMembersLowering(val context: JsIrBackendContext) : FileLoweringPass {
|
||||
private val memberMap = mutableMapOf<IrSimpleFunctionSymbol, IrSimpleFunction>()
|
||||
private val memberMap = context.memberMap
|
||||
|
||||
override fun lower(irFile: IrFile) {
|
||||
transformPrivateDeclarations(irFile)
|
||||
transformPrivateUseSites(irFile)
|
||||
|
||||
memberMap.clear()
|
||||
}
|
||||
|
||||
private fun transformPrivateDeclarations(irFile: IrFile) {
|
||||
@@ -49,7 +47,6 @@ class PrivateMembersLowering(val context: JsIrBackendContext) : FileLoweringPass
|
||||
declaration.declarations.transformFlat {
|
||||
when (it) {
|
||||
is IrSimpleFunction -> transformMemberToStaticFunction(it)?.let { staticFunction ->
|
||||
memberMap[it.symbol] = staticFunction
|
||||
listOf(staticFunction)
|
||||
}
|
||||
is IrProperty -> listOf(it.apply {
|
||||
@@ -69,7 +66,7 @@ class PrivateMembersLowering(val context: JsIrBackendContext) : FileLoweringPass
|
||||
override fun visitCall(expression: IrCall): IrExpression {
|
||||
super.visitCall(expression)
|
||||
|
||||
return memberMap[expression.symbol]?.let {
|
||||
return getOrPutStaticFunction(expression.symbol)?.let {
|
||||
transformPrivateToStaticCall(expression, it)
|
||||
} ?: expression
|
||||
}
|
||||
@@ -77,7 +74,7 @@ class PrivateMembersLowering(val context: JsIrBackendContext) : FileLoweringPass
|
||||
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
|
||||
super.visitFunctionReference(expression)
|
||||
|
||||
return memberMap[expression.symbol]?.let {
|
||||
return getOrPutStaticFunction(expression.symbol)?.let {
|
||||
transformPrivateToStaticReference(expression) {
|
||||
IrFunctionReferenceImpl(
|
||||
expression.startOffset, expression.endOffset,
|
||||
@@ -92,7 +89,10 @@ class PrivateMembersLowering(val context: JsIrBackendContext) : FileLoweringPass
|
||||
override fun visitPropertyReference(expression: IrPropertyReference): IrExpression {
|
||||
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) {
|
||||
IrPropertyReferenceImpl(
|
||||
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.typeArgumentsCount,
|
||||
expression.field,
|
||||
memberMap[expression.getter]?.symbol ?: expression.getter,
|
||||
memberMap[expression.setter]?.symbol ?: expression.setter,
|
||||
staticGetter?.symbol ?: expression.getter,
|
||||
staticSetter?.symbol ?: expression.setter,
|
||||
expression.origin
|
||||
)
|
||||
}
|
||||
@@ -127,6 +127,7 @@ class PrivateMembersLowering(val context: JsIrBackendContext) : FileLoweringPass
|
||||
|
||||
return newExpression
|
||||
}
|
||||
|
||||
private fun transformPrivateToStaticReference(
|
||||
expression: IrCallableReference,
|
||||
builder: () -> IrCallableReference
|
||||
@@ -147,28 +148,11 @@ class PrivateMembersLowering(val context: JsIrBackendContext) : FileLoweringPass
|
||||
}, null)
|
||||
}
|
||||
|
||||
private fun transformAccessor(accessor: IrSimpleFunction) =
|
||||
transformMemberToStaticFunction(accessor)?.also { memberMap[accessor.symbol] = it } ?: accessor
|
||||
private fun transformAccessor(accessor: IrSimpleFunction) = transformMemberToStaticFunction(accessor) ?: accessor
|
||||
|
||||
private fun transformMemberToStaticFunction(function: IrSimpleFunction): IrSimpleFunction? {
|
||||
|
||||
if (function.visibility != Visibilities.PRIVATE || function.dispatchReceiverParameter == null) 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
|
||||
}
|
||||
}
|
||||
val staticFunction = getOrPutStaticFunction(function.symbol) ?: return null
|
||||
|
||||
// Detach old function from corresponding property
|
||||
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 =
|
||||
listOfNotNull(function.extensionReceiverParameter, function.dispatchReceiverParameter) + function.valueParameters
|
||||
val newParameters = listOfNotNull(staticFunction.extensionReceiverParameter) + staticFunction.valueParameters
|
||||
@@ -220,4 +181,53 @@ class PrivateMembersLowering(val context: JsIrBackendContext) : FileLoweringPass
|
||||
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");
|
||||
}
|
||||
|
||||
@TestMetadata("privateVarFromInline.kt")
|
||||
public void testPrivateVarFromInline() throws Exception {
|
||||
runTest("js/js.translator/testData/box/inlineMultiFile/privateVarFromInline.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("regeneratedLambdaName.kt")
|
||||
public void testRegeneratedLambdaName() throws Exception {
|
||||
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");
|
||||
}
|
||||
|
||||
@TestMetadata("privateVarFromInline.kt")
|
||||
public void testPrivateVarFromInline() throws Exception {
|
||||
runTest("js/js.translator/testData/box/inlineMultiFile/privateVarFromInline.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("regeneratedLambdaName.kt")
|
||||
public void testRegeneratedLambdaName() throws Exception {
|
||||
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