[FIR2IR] Fix and refactor generation of dynamic call receiver generation

#KT-57988 Fixed
This commit is contained in:
Kirill Rakhman
2023-04-18 15:58:03 +02:00
committed by Space Team
parent 8b47a4fa48
commit 35c6da1aa7
7 changed files with 99 additions and 26 deletions
@@ -290,7 +290,7 @@ class CallAndReferenceGenerator(
private fun convertToIrCallForDynamic( private fun convertToIrCallForDynamic(
qualifiedAccess: FirQualifiedAccessExpression, qualifiedAccess: FirQualifiedAccessExpression,
explicitReceiverExpression: IrExpression, explicitReceiverExpression: IrExpression?,
type: IrType, type: IrType,
calleeReference: FirReference, calleeReference: FirReference,
symbol: FirBasedSymbol<*>, symbol: FirBasedSymbol<*>,
@@ -298,7 +298,7 @@ class CallAndReferenceGenerator(
dynamicOperator: IrDynamicOperator? = null, dynamicOperator: IrDynamicOperator? = null,
noArguments: Boolean = false, noArguments: Boolean = false,
): IrExpression { ): IrExpression {
var convertedExplicitReceiver = explicitReceiverExpression val selectedReceiver = qualifiedAccess.findIrDynamicReceiver(explicitReceiverExpression)
return qualifiedAccess.convertWithOffsets { startOffset, endOffset -> return qualifiedAccess.convertWithOffsets { startOffset, endOffset ->
when (symbol) { when (symbol) {
@@ -314,22 +314,24 @@ class CallAndReferenceGenerator(
} else { } else {
type type
} }
if (operator == IrDynamicOperator.INVOKE && qualifiedAccess !is FirImplicitInvokeCall) { IrDynamicOperatorExpressionImpl(startOffset, endOffset, theType, operator).apply {
convertedExplicitReceiver = IrDynamicMemberExpressionImpl( receiver = if (operator == IrDynamicOperator.INVOKE && qualifiedAccess !is FirImplicitInvokeCall) {
startOffset, endOffset, type, name.identifier, explicitReceiverExpression IrDynamicMemberExpressionImpl(startOffset, endOffset, type, name.identifier, selectedReceiver)
) } else {
selectedReceiver
}
} }
IrDynamicOperatorExpressionImpl(startOffset, endOffset, theType, operator)
} }
is FirPropertySymbol -> { is FirPropertySymbol -> {
val name = calleeReference.resolved?.name ?: error("There must be a name") val name = calleeReference.resolved?.name ?: error("There must be a name")
IrDynamicMemberExpressionImpl(startOffset, endOffset, type, name.identifier, explicitReceiverExpression) IrDynamicMemberExpressionImpl(startOffset, endOffset, type, name.identifier, selectedReceiver)
} }
else -> generateErrorCallExpression(startOffset, endOffset, calleeReference, type) else -> generateErrorCallExpression(startOffset, endOffset, calleeReference, type)
} }
}.applyTypeArguments(qualifiedAccess).applyReceivers(qualifiedAccess, convertedExplicitReceiver) }
.applyTypeArguments(qualifiedAccess)
.applyCallArguments((qualifiedAccess as? FirCall)?.takeIf { !noArguments }, annotationMode) .applyCallArguments((qualifiedAccess as? FirCall)?.takeIf { !noArguments }, annotationMode)
} }
@@ -356,7 +358,7 @@ class CallAndReferenceGenerator(
if (isDynamicAccess) { if (isDynamicAccess) {
return convertToIrCallForDynamic( return convertToIrCallForDynamic(
qualifiedAccess, qualifiedAccess,
explicitReceiverExpression ?: error("Must've had a receiver"), explicitReceiverExpression,
type, type,
calleeReference, calleeReference,
firSymbol ?: error("Must have had a symbol"), firSymbol ?: error("Must have had a symbol"),
@@ -483,33 +485,43 @@ class CallAndReferenceGenerator(
private fun convertToIrSetCallForDynamic( private fun convertToIrSetCallForDynamic(
variableAssignment: FirVariableAssignment, variableAssignment: FirVariableAssignment,
receiverExpression: IrExpression, explicitReceiverExpression: IrExpression?,
type: IrType, type: IrType,
calleeReference: FirReference, calleeReference: FirReference,
symbol: FirBasedSymbol<*>, symbol: FirBasedSymbol<*>,
assignedValue: IrExpression, assignedValue: IrExpression,
): IrExpression { ): IrExpression {
var convertedExplicitReceiver = receiverExpression val selectedReceiver =
(variableAssignment.unwrapLValue() ?: error("Assignment has no lValue")).findIrDynamicReceiver(explicitReceiverExpression)
return variableAssignment.convertWithOffsets { startOffset, endOffset -> return variableAssignment.convertWithOffsets { startOffset, endOffset ->
when (symbol) { when (symbol) {
is FirPropertySymbol -> { is FirPropertySymbol -> {
val name = calleeReference.resolved?.name ?: error("There must be a name") val name = calleeReference.resolved?.name ?: error("There must be a name")
convertedExplicitReceiver = IrDynamicMemberExpressionImpl(
startOffset, endOffset, type, name.identifier, receiverExpression
)
IrDynamicOperatorExpressionImpl(startOffset, endOffset, type, IrDynamicOperator.EQ).apply { IrDynamicOperatorExpressionImpl(startOffset, endOffset, type, IrDynamicOperator.EQ).apply {
receiver = IrDynamicMemberExpressionImpl(
startOffset, endOffset, type, name.identifier, selectedReceiver
)
arguments.add(assignedValue) arguments.add(assignedValue)
} }
} }
else -> generateErrorCallExpression(startOffset, endOffset, calleeReference) else -> generateErrorCallExpression(startOffset, endOffset, calleeReference)
} }
}.apply {
variableAssignment.unwrapLValue()?.let { applyReceivers(it, convertedExplicitReceiver) }
} }
} }
/**
* A dynamic call has either an explicit receiver or an implicit this dispatch receiver.
*/
private fun FirQualifiedAccessExpression.findIrDynamicReceiver(
explicitReceiverExpression: IrExpression?,
): IrExpression {
return explicitReceiverExpression
?: (dispatchReceiver as? FirThisReceiverExpression)?.let(visitor::convertToIrExpression)
?: error("No receiver for dynamic call")
}
fun convertToIrSetCall(variableAssignment: FirVariableAssignment, explicitReceiverExpression: IrExpression?): IrExpression { fun convertToIrSetCall(variableAssignment: FirVariableAssignment, explicitReceiverExpression: IrExpression?): IrExpression {
try { try {
val type = irBuiltIns.unitType val type = irBuiltIns.unitType
@@ -520,13 +532,9 @@ class CallAndReferenceGenerator(
val isDynamicAccess = firSymbol?.origin == FirDeclarationOrigin.DynamicScope val isDynamicAccess = firSymbol?.origin == FirDeclarationOrigin.DynamicScope
if (isDynamicAccess) { if (isDynamicAccess) {
val receiverExpression = (explicitReceiverExpression
?: (variableAssignment.dispatchReceiver as? FirThisReceiverExpression)?.let(visitor::convertToIrExpression)
?: error("Must've had a receiver"))
return convertToIrSetCallForDynamic( return convertToIrSetCallForDynamic(
variableAssignment, variableAssignment,
receiverExpression, explicitReceiverExpression,
type, type,
calleeReference, calleeReference,
firSymbol ?: error("Must've had a symbol"), firSymbol ?: error("Must've had a symbol"),
@@ -1152,10 +1160,6 @@ class CallAndReferenceGenerator(
receiver = qualifiedAccess.findIrDispatchReceiver(explicitReceiverExpression) receiver = qualifiedAccess.findIrDispatchReceiver(explicitReceiverExpression)
} }
} }
is IrDynamicOperatorExpression -> {
receiver = explicitReceiverExpression ?: error("No receiver for dynamic")
}
} }
return this return this
} }
@@ -0,0 +1,25 @@
FILE: getOperatorOnDynamicThis.kt
public final inline fun <T : R|kotlin/Any|> jso(): R|T| {
^jso R|kotlin/js/js|(String(({})))
}
public final inline fun <T : R|kotlin/Any|> jso(block: R|T.() -> kotlin/Unit|): R|T| {
^jso R|/jso|<R|T|>().R|kotlin/apply|<R|T|>(R|<local>/block|)
}
public abstract external interface Z : R|kotlin/Any| {
public abstract var a: R|dynamic|
public get(): R|dynamic|
public set(value: R|dynamic|): R|kotlin/Unit|
}
public final fun foo(): R|kotlin/Unit| {
R|/jso|<R|Z|>().R|kotlin/apply|<R|Z|>(<L> = apply@fun R|Z|.<anonymous>(): R|kotlin/Unit| <inline=Inline, kind=EXACTLY_ONCE> {
this@R|special/anonymous|.R|/Z.a| = R|/jso|<R|dynamic|>(<L> = jso@fun R|dynamic|.<anonymous>(): R|kotlin/Unit| <inline=Inline, kind=UNKNOWN> {
this@R|special/anonymous|.R|<dynamic>/get|(vararg(this@R|special/anonymous|.R|<dynamic>/foo|.R|<dynamic>/bar|))
}
)
}
)
}
public final fun box(): R|kotlin/String| {
^box String(OK)
}
@@ -0,0 +1,20 @@
// TARGET_BACKEND: JS
// ISSUE: KT-57988
// FIR_DUMP
inline fun <T : Any> jso(): T = js("({})")
inline fun <T : Any> jso(block: T.() -> Unit): T = jso<T>().apply(block)
external interface Z {
var a: dynamic
}
fun foo() {
jso<Z>().apply {
a = jso {
this[foo.bar]
}
}
}
fun box() = "OK"
@@ -21665,6 +21665,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/js"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS, true); KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/js"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS, true);
} }
@Test
@TestMetadata("getOperatorOnDynamicThis.kt")
public void testGetOperatorOnDynamicThis() throws Exception {
runTest("compiler/testData/codegen/box/js/getOperatorOnDynamicThis.kt");
}
@Test @Test
@TestMetadata("lambdaWithDynamicReceiver.kt") @TestMetadata("lambdaWithDynamicReceiver.kt")
public void testLambdaWithDynamicReceiver() throws Exception { public void testLambdaWithDynamicReceiver() throws Exception {
@@ -21803,6 +21803,12 @@ public class FirJsCodegenBoxTestGenerated extends AbstractFirJsCodegenBoxTest {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/js"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true); KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/js"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true);
} }
@Test
@TestMetadata("getOperatorOnDynamicThis.kt")
public void testGetOperatorOnDynamicThis() throws Exception {
runTest("compiler/testData/codegen/box/js/getOperatorOnDynamicThis.kt");
}
@Test @Test
@TestMetadata("lambdaWithDynamicReceiver.kt") @TestMetadata("lambdaWithDynamicReceiver.kt")
public void testLambdaWithDynamicReceiver() throws Exception { public void testLambdaWithDynamicReceiver() throws Exception {
@@ -21803,6 +21803,12 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/js"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true); KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/js"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true);
} }
@Test
@TestMetadata("getOperatorOnDynamicThis.kt")
public void testGetOperatorOnDynamicThis() throws Exception {
runTest("compiler/testData/codegen/box/js/getOperatorOnDynamicThis.kt");
}
@Test @Test
@TestMetadata("lambdaWithDynamicReceiver.kt") @TestMetadata("lambdaWithDynamicReceiver.kt")
public void testLambdaWithDynamicReceiver() throws Exception { public void testLambdaWithDynamicReceiver() throws Exception {
@@ -21803,6 +21803,12 @@ public class IrJsES6CodegenBoxTestGenerated extends AbstractIrJsES6CodegenBoxTes
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/js"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR_ES6, true); KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/js"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR_ES6, true);
} }
@Test
@TestMetadata("getOperatorOnDynamicThis.kt")
public void testGetOperatorOnDynamicThis() throws Exception {
runTest("compiler/testData/codegen/box/js/getOperatorOnDynamicThis.kt");
}
@Test @Test
@TestMetadata("lambdaWithDynamicReceiver.kt") @TestMetadata("lambdaWithDynamicReceiver.kt")
public void testLambdaWithDynamicReceiver() throws Exception { public void testLambdaWithDynamicReceiver() throws Exception {