From 35c6da1aa7808d89a301cd6dd58386f6e26ae82b Mon Sep 17 00:00:00 2001 From: Kirill Rakhman Date: Tue, 18 Apr 2023 15:58:03 +0200 Subject: [PATCH] [FIR2IR] Fix and refactor generation of dynamic call receiver generation #KT-57988 Fixed --- .../generators/CallAndReferenceGenerator.kt | 56 ++++++++++--------- .../box/js/getOperatorOnDynamicThis.fir.txt | 25 +++++++++ .../box/js/getOperatorOnDynamicThis.kt | 20 +++++++ .../js/test/JsCodegenBoxTestGenerated.java | 6 ++ .../fir/FirJsCodegenBoxTestGenerated.java | 6 ++ .../test/ir/IrJsCodegenBoxTestGenerated.java | 6 ++ .../ir/IrJsES6CodegenBoxTestGenerated.java | 6 ++ 7 files changed, 99 insertions(+), 26 deletions(-) create mode 100644 compiler/testData/codegen/box/js/getOperatorOnDynamicThis.fir.txt create mode 100644 compiler/testData/codegen/box/js/getOperatorOnDynamicThis.kt diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt index ca8faf4844d..3ea60d68579 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt @@ -290,7 +290,7 @@ class CallAndReferenceGenerator( private fun convertToIrCallForDynamic( qualifiedAccess: FirQualifiedAccessExpression, - explicitReceiverExpression: IrExpression, + explicitReceiverExpression: IrExpression?, type: IrType, calleeReference: FirReference, symbol: FirBasedSymbol<*>, @@ -298,7 +298,7 @@ class CallAndReferenceGenerator( dynamicOperator: IrDynamicOperator? = null, noArguments: Boolean = false, ): IrExpression { - var convertedExplicitReceiver = explicitReceiverExpression + val selectedReceiver = qualifiedAccess.findIrDynamicReceiver(explicitReceiverExpression) return qualifiedAccess.convertWithOffsets { startOffset, endOffset -> when (symbol) { @@ -314,22 +314,24 @@ class CallAndReferenceGenerator( } else { type } - if (operator == IrDynamicOperator.INVOKE && qualifiedAccess !is FirImplicitInvokeCall) { - convertedExplicitReceiver = IrDynamicMemberExpressionImpl( - startOffset, endOffset, type, name.identifier, explicitReceiverExpression - ) + IrDynamicOperatorExpressionImpl(startOffset, endOffset, theType, operator).apply { + receiver = if (operator == IrDynamicOperator.INVOKE && qualifiedAccess !is FirImplicitInvokeCall) { + IrDynamicMemberExpressionImpl(startOffset, endOffset, type, name.identifier, selectedReceiver) + } else { + selectedReceiver + } } - IrDynamicOperatorExpressionImpl(startOffset, endOffset, theType, operator) } is FirPropertySymbol -> { 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) } - }.applyTypeArguments(qualifiedAccess).applyReceivers(qualifiedAccess, convertedExplicitReceiver) + } + .applyTypeArguments(qualifiedAccess) .applyCallArguments((qualifiedAccess as? FirCall)?.takeIf { !noArguments }, annotationMode) } @@ -356,7 +358,7 @@ class CallAndReferenceGenerator( if (isDynamicAccess) { return convertToIrCallForDynamic( qualifiedAccess, - explicitReceiverExpression ?: error("Must've had a receiver"), + explicitReceiverExpression, type, calleeReference, firSymbol ?: error("Must have had a symbol"), @@ -483,33 +485,43 @@ class CallAndReferenceGenerator( private fun convertToIrSetCallForDynamic( variableAssignment: FirVariableAssignment, - receiverExpression: IrExpression, + explicitReceiverExpression: IrExpression?, type: IrType, calleeReference: FirReference, symbol: FirBasedSymbol<*>, assignedValue: IrExpression, ): IrExpression { - var convertedExplicitReceiver = receiverExpression + val selectedReceiver = + (variableAssignment.unwrapLValue() ?: error("Assignment has no lValue")).findIrDynamicReceiver(explicitReceiverExpression) return variableAssignment.convertWithOffsets { startOffset, endOffset -> when (symbol) { is FirPropertySymbol -> { 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 { + receiver = IrDynamicMemberExpressionImpl( + startOffset, endOffset, type, name.identifier, selectedReceiver + ) arguments.add(assignedValue) } } 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 { try { val type = irBuiltIns.unitType @@ -520,13 +532,9 @@ class CallAndReferenceGenerator( val isDynamicAccess = firSymbol?.origin == FirDeclarationOrigin.DynamicScope if (isDynamicAccess) { - val receiverExpression = (explicitReceiverExpression - ?: (variableAssignment.dispatchReceiver as? FirThisReceiverExpression)?.let(visitor::convertToIrExpression) - ?: error("Must've had a receiver")) - return convertToIrSetCallForDynamic( variableAssignment, - receiverExpression, + explicitReceiverExpression, type, calleeReference, firSymbol ?: error("Must've had a symbol"), @@ -1152,10 +1160,6 @@ class CallAndReferenceGenerator( receiver = qualifiedAccess.findIrDispatchReceiver(explicitReceiverExpression) } } - - is IrDynamicOperatorExpression -> { - receiver = explicitReceiverExpression ?: error("No receiver for dynamic") - } } return this } diff --git a/compiler/testData/codegen/box/js/getOperatorOnDynamicThis.fir.txt b/compiler/testData/codegen/box/js/getOperatorOnDynamicThis.fir.txt new file mode 100644 index 00000000000..47eaeb8a98c --- /dev/null +++ b/compiler/testData/codegen/box/js/getOperatorOnDynamicThis.fir.txt @@ -0,0 +1,25 @@ +FILE: getOperatorOnDynamicThis.kt + public final inline fun jso(): R|T| { + ^jso R|kotlin/js/js|(String(({}))) + } + public final inline fun jso(block: R|T.() -> kotlin/Unit|): R|T| { + ^jso R|/jso|().R|kotlin/apply|(R|/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|kotlin/apply|( = apply@fun R|Z|.(): R|kotlin/Unit| { + this@R|special/anonymous|.R|/Z.a| = R|/jso|( = jso@fun R|dynamic|.(): R|kotlin/Unit| { + this@R|special/anonymous|.R|/get|(vararg(this@R|special/anonymous|.R|/foo|.R|/bar|)) + } + ) + } + ) + } + public final fun box(): R|kotlin/String| { + ^box String(OK) + } diff --git a/compiler/testData/codegen/box/js/getOperatorOnDynamicThis.kt b/compiler/testData/codegen/box/js/getOperatorOnDynamicThis.kt new file mode 100644 index 00000000000..ff8389b9cb3 --- /dev/null +++ b/compiler/testData/codegen/box/js/getOperatorOnDynamicThis.kt @@ -0,0 +1,20 @@ +// TARGET_BACKEND: JS +// ISSUE: KT-57988 +// FIR_DUMP + +inline fun jso(): T = js("({})") +inline fun jso(block: T.() -> Unit): T = jso().apply(block) + +external interface Z { + var a: dynamic +} + +fun foo() { + jso().apply { + a = jso { + this[foo.bar] + } + } +} + +fun box() = "OK" \ No newline at end of file diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java index 0a2e806ff28..3c0cd37d4f0 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java @@ -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); } + @Test + @TestMetadata("getOperatorOnDynamicThis.kt") + public void testGetOperatorOnDynamicThis() throws Exception { + runTest("compiler/testData/codegen/box/js/getOperatorOnDynamicThis.kt"); + } + @Test @TestMetadata("lambdaWithDynamicReceiver.kt") public void testLambdaWithDynamicReceiver() throws Exception { diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsCodegenBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsCodegenBoxTestGenerated.java index 12e9ba513fc..cf38fa55a3e 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsCodegenBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsCodegenBoxTestGenerated.java @@ -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); } + @Test + @TestMetadata("getOperatorOnDynamicThis.kt") + public void testGetOperatorOnDynamicThis() throws Exception { + runTest("compiler/testData/codegen/box/js/getOperatorOnDynamicThis.kt"); + } + @Test @TestMetadata("lambdaWithDynamicReceiver.kt") public void testLambdaWithDynamicReceiver() throws Exception { diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java index 09f6763243d..689762b6e1a 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java @@ -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); } + @Test + @TestMetadata("getOperatorOnDynamicThis.kt") + public void testGetOperatorOnDynamicThis() throws Exception { + runTest("compiler/testData/codegen/box/js/getOperatorOnDynamicThis.kt"); + } + @Test @TestMetadata("lambdaWithDynamicReceiver.kt") public void testLambdaWithDynamicReceiver() throws Exception { diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsES6CodegenBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsES6CodegenBoxTestGenerated.java index 22ece5804a2..c2d1ecbf247 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsES6CodegenBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsES6CodegenBoxTestGenerated.java @@ -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); } + @Test + @TestMetadata("getOperatorOnDynamicThis.kt") + public void testGetOperatorOnDynamicThis() throws Exception { + runTest("compiler/testData/codegen/box/js/getOperatorOnDynamicThis.kt"); + } + @Test @TestMetadata("lambdaWithDynamicReceiver.kt") public void testLambdaWithDynamicReceiver() throws Exception {