From c1d350f8f37883a004c7a66040b557a26a3df1ce Mon Sep 17 00:00:00 2001 From: Kristoffer Andersen Date: Mon, 3 Feb 2020 13:46:43 +0100 Subject: [PATCH] [JVM IR] Added null-checks of handler for non-super $default calls --- .../lower/DefaultArgumentStubGenerator.kt | 19 ++++++++-- .../kotlin/backend/jvm/JvmSymbols.kt | 6 +++- .../jvm/lower/CollectionStubMethodLowering.kt | 4 +-- .../lower/JvmDefaultArgumentStubGenerator.kt | 36 +++++++++++++++---- .../box/defaultArguments/superCallCheck.kt | 1 - .../defaultArguments/superCallHandlerOrder.kt | 20 +++++++++++ .../methodHandlerElimination.kt | 3 -- .../codegen/BlackBoxCodegenTestGenerated.java | 5 +++ .../LightAnalysisModeTestGenerated.java | 5 +++ .../ir/FirBlackBoxCodegenTestGenerated.java | 5 +++ .../ir/IrBlackBoxCodegenTestGenerated.java | 5 +++ 11 files changed, 92 insertions(+), 17 deletions(-) create mode 100644 compiler/testData/codegen/box/defaultArguments/superCallHandlerOrder.kt diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/DefaultArgumentStubGenerator.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/DefaultArgumentStubGenerator.kt index 93bb9078c1a..2ea34ffc5b0 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/DefaultArgumentStubGenerator.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/DefaultArgumentStubGenerator.kt @@ -54,8 +54,9 @@ open class DefaultArgumentStubGenerator( private fun lower(irFunction: IrFunction): List? { val visibility = defaultArgumentStubVisibility(irFunction) - val newIrFunction = irFunction.generateDefaultsFunction(context, skipInlineMethods, skipExternalMethods, forceSetOverrideSymbols, visibility) - ?: return null + val newIrFunction = + irFunction.generateDefaultsFunction(context, skipInlineMethods, skipExternalMethods, forceSetOverrideSymbols, visibility) + ?: return null if (newIrFunction.origin == IrDeclarationOrigin.FAKE_OVERRIDE) { return listOf(irFunction, newIrFunction) } @@ -90,6 +91,8 @@ open class DefaultArgumentStubGenerator( newIrFunction.valueParameters[it.index] } + generateSuperCallHandlerCheckIfNeeded(irFunction, newIrFunction) + var sourceParameterIndex = -1 for (valueParameter in irFunction.valueParameters) { if (!valueParameter.isMovedReceiver()) { @@ -180,6 +183,12 @@ open class DefaultArgumentStubGenerator( } else dispatchCall } + protected open fun IrBlockBodyBuilder.generateSuperCallHandlerCheckIfNeeded( + irFunction: IrFunction, + newIrFunction: IrFunction) { + //NO-OP Stub + } + protected open fun needSpecialDispatch(irFunction: IrSimpleFunction) = false protected open fun IrBlockBodyBuilder.generateHandleCall( handlerDeclaration: IrValueParameter, @@ -506,7 +515,11 @@ private fun IrFunction.generateDefaultsFunctionImpl( val markerType = context.ir.symbols.defaultConstructorMarker.defaultType.makeNullable() newFunction.addValueParameter("marker".synthesizedString, markerType, IrDeclarationOrigin.DEFAULT_CONSTRUCTOR_MARKER) } else if (context.ir.shouldGenerateHandlerParameterForDefaultBodyFun()) { - newFunction.addValueParameter("handler".synthesizedString, context.irBuiltIns.anyNType, IrDeclarationOrigin.METHOD_HANDLER_IN_DEFAULT_FUNCTION) + newFunction.addValueParameter( + "handler".synthesizedString, + context.irBuiltIns.anyNType, + IrDeclarationOrigin.METHOD_HANDLER_IN_DEFAULT_FUNCTION + ) } // TODO some annotations are needed (e.g. @JvmStatic), others need different values (e.g. @JvmName), the rest are redundant. diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt index 44feeb15bc9..859a58d37ff 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt @@ -72,12 +72,16 @@ class JvmSymbols( override val ThrowTypeCastException: IrFunctionSymbol = typeCastExceptionClass.constructors.single() - val unsupportedOperationExceptionClass: IrClassSymbol = createClass(FqName("java.lang.UnsupportedOperationException")) { klass -> + private val unsupportedOperationExceptionClass: IrClassSymbol = createClass(FqName("java.lang.UnsupportedOperationException")) { klass -> klass.addConstructor().apply { addValueParameter("message", irBuiltIns.stringType.makeNullable()) } } + val ThrowUnsupportOperationExceptionClass: IrFunctionSymbol = + unsupportedOperationExceptionClass.constructors.single() + + private fun createPackage(fqName: FqName): IrPackageFragment = IrExternalPackageFragmentImpl(IrExternalPackageFragmentSymbolImpl(EmptyPackageFragmentDescriptor(context.state.module, fqName))) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/CollectionStubMethodLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/CollectionStubMethodLowering.kt index ac677685632..34fb828fa4b 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/CollectionStubMethodLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/CollectionStubMethodLowering.kt @@ -87,9 +87,7 @@ private class CollectionStubMethodLowering(val context: JvmBackendContext) : Cla body = context.createIrBuilder(function.symbol).irBlockBody { +irThrow( irCall( - this@CollectionStubMethodLowering.context.ir.symbols.unsupportedOperationExceptionClass.owner.constructors.single { - it.valueParameters.size == 1 && it.valueParameters.single().type.isNullableString() - } + this@CollectionStubMethodLowering.context.ir.symbols.ThrowUnsupportOperationExceptionClass ).apply { putValueArgument(0, irString("Operation is not supported for read-only collection")) } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmDefaultArgumentStubGenerator.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmDefaultArgumentStubGenerator.kt index 9ad9c051b90..6c2c8646f0c 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmDefaultArgumentStubGenerator.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmDefaultArgumentStubGenerator.kt @@ -5,17 +5,17 @@ package org.jetbrains.kotlin.backend.jvm.lower +import org.jetbrains.kotlin.backend.common.ir.isFinalClass import org.jetbrains.kotlin.backend.common.lower.DefaultArgumentStubGenerator import org.jetbrains.kotlin.backend.common.lower.irIfThen +import org.jetbrains.kotlin.backend.common.lower.irNot +import org.jetbrains.kotlin.backend.common.lower.irThrow import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.ir.getJvmVisibilityOfDefaultArgumentStub -import org.jetbrains.kotlin.ir.builders.IrBlockBodyBuilder -import org.jetbrains.kotlin.ir.builders.irCall -import org.jetbrains.kotlin.ir.builders.irGet -import org.jetbrains.kotlin.ir.declarations.IrFunction -import org.jetbrains.kotlin.ir.declarations.IrValueDeclaration -import org.jetbrains.kotlin.ir.declarations.IrValueParameter +import org.jetbrains.kotlin.ir.builders.* +import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.util.isTopLevelDeclaration class JvmDefaultArgumentStubGenerator(override val context: JvmBackendContext) : DefaultArgumentStubGenerator(context, false, false) { override fun IrBlockBodyBuilder.selectArgumentOrDefault( @@ -47,4 +47,28 @@ class JvmDefaultArgumentStubGenerator(override val context: JvmBackendContext) : } override fun defaultArgumentStubVisibility(function: IrFunction) = function.getJvmVisibilityOfDefaultArgumentStub() + + override fun IrBlockBodyBuilder.generateSuperCallHandlerCheckIfNeeded( + irFunction: IrFunction, + newIrFunction: IrFunction + ) { + if (irFunction !is IrSimpleFunction + || !this@JvmDefaultArgumentStubGenerator.context.ir.shouldGenerateHandlerParameterForDefaultBodyFun() + || irFunction.isTopLevelDeclaration + || (irFunction.parent as? IrClass)?.isFinalClass == true + ) + return + + val handlerDeclaration = newIrFunction.valueParameters.last() + +irIfThen( + context.irBuiltIns.unitType, + irNot(irEqualsNull(irGet(handlerDeclaration))), + irThrow(irCall(this@JvmDefaultArgumentStubGenerator.context.ir.symbols.ThrowUnsupportOperationExceptionClass).apply { + putValueArgument( + 0, + irString("Super calls with default arguments not supported in this target, function: ${irFunction.name.asString()}") + ) + }) + ) + } } diff --git a/compiler/testData/codegen/box/defaultArguments/superCallCheck.kt b/compiler/testData/codegen/box/defaultArguments/superCallCheck.kt index 7a014369169..311f3ad5af3 100644 --- a/compiler/testData/codegen/box/defaultArguments/superCallCheck.kt +++ b/compiler/testData/codegen/box/defaultArguments/superCallCheck.kt @@ -1,5 +1,4 @@ // IGNORE_BACKEND_FIR: JVM_IR -// IGNORE_BACKEND: JVM_IR // TARGET_BACKEND: JVM // WITH_RUNTIME diff --git a/compiler/testData/codegen/box/defaultArguments/superCallHandlerOrder.kt b/compiler/testData/codegen/box/defaultArguments/superCallHandlerOrder.kt new file mode 100644 index 00000000000..73d4028d622 --- /dev/null +++ b/compiler/testData/codegen/box/defaultArguments/superCallHandlerOrder.kt @@ -0,0 +1,20 @@ +// IGNORE_BACKEND_FIR: JVM_IR +// TARGET_BACKEND: JVM +// WITH_RUNTIME + +open class O { + open fun foo(s: String = throw Error("Fail: this expression should not be evaluated")) {} +} + +fun box(): String = try { + val f = O::class.java.declaredMethods.single { it.name == "foo\$default" } + f(null, O(), "s", 1, "non-null") + "Fail: exception should have been thrown" +} catch (e: Exception) { + val cause = e.cause + if (cause is UnsupportedOperationException) { + "OK" + } else { + cause.toString() + } +} \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/defaultArguments/methodHandlerElimination.kt b/compiler/testData/codegen/bytecodeText/defaultArguments/methodHandlerElimination.kt index f3a88105711..30afd11b3b2 100644 --- a/compiler/testData/codegen/bytecodeText/defaultArguments/methodHandlerElimination.kt +++ b/compiler/testData/codegen/bytecodeText/defaultArguments/methodHandlerElimination.kt @@ -1,6 +1,3 @@ -// IGNORE_BACKEND: JVM_IR -// TODO KT-36771 Missing special code for super calls to inline functions support in JVM_IR - //open modality to method handle check generation open class A { inline fun test(p: String = "OK"): String { diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 6e70c2abaec..7cd5bed518e 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -9369,6 +9369,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/defaultArguments/superCallCheck.kt"); } + @TestMetadata("superCallHandlerOrder.kt") + public void testSuperCallHandlerOrder() throws Exception { + runTest("compiler/testData/codegen/box/defaultArguments/superCallHandlerOrder.kt"); + } + @TestMetadata("useNextParamInLambda.kt") public void testUseNextParamInLambda() throws Exception { runTest("compiler/testData/codegen/box/defaultArguments/useNextParamInLambda.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index b4dc2a6cb40..1b7628a3630 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -9369,6 +9369,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/defaultArguments/superCallCheck.kt"); } + @TestMetadata("superCallHandlerOrder.kt") + public void testSuperCallHandlerOrder() throws Exception { + runTest("compiler/testData/codegen/box/defaultArguments/superCallHandlerOrder.kt"); + } + @TestMetadata("useNextParamInLambda.kt") public void testUseNextParamInLambda() throws Exception { runTest("compiler/testData/codegen/box/defaultArguments/useNextParamInLambda.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java index 927ce887bad..ac053b5169e 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java @@ -8239,6 +8239,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/defaultArguments/superCallCheck.kt"); } + @TestMetadata("superCallHandlerOrder.kt") + public void testSuperCallHandlerOrder() throws Exception { + runTest("compiler/testData/codegen/box/defaultArguments/superCallHandlerOrder.kt"); + } + @TestMetadata("useNextParamInLambda.kt") public void testUseNextParamInLambda() throws Exception { runTest("compiler/testData/codegen/box/defaultArguments/useNextParamInLambda.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 8e115c53db6..9bce6d3e12f 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -8239,6 +8239,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/defaultArguments/superCallCheck.kt"); } + @TestMetadata("superCallHandlerOrder.kt") + public void testSuperCallHandlerOrder() throws Exception { + runTest("compiler/testData/codegen/box/defaultArguments/superCallHandlerOrder.kt"); + } + @TestMetadata("useNextParamInLambda.kt") public void testUseNextParamInLambda() throws Exception { runTest("compiler/testData/codegen/box/defaultArguments/useNextParamInLambda.kt");