From 6b2d874ccc2094646e9e28e5f3cde0961b9fe0e3 Mon Sep 17 00:00:00 2001 From: pyos Date: Mon, 20 May 2019 12:46:38 +0200 Subject: [PATCH] JVM_IR: generate non-null assertions for arguments --- .../kotlin/backend/jvm/DeclarationOrigins.kt | 4 +++- .../backend/jvm/codegen/ExpressionCodegen.kt | 22 +++++++++++++++++++ .../jvm/descriptors/JvmDeclarationFactory.kt | 14 +++++++++++- .../mapGetOrDefault/noTypeSafeBridge.kt | 4 ++++ .../extensionReceiverParameter.kt | 1 - ...bilityAssertionOnExtensionReceiver_lv11.kt | 1 - ...bilityAssertionOnExtensionReceiver_lv12.kt | 1 - ...bilityAssertionOnExtensionReceiver_lv11.kt | 1 - .../codegen/box/specialBuiltins/emptyMap.kt | 4 ++++ ...onsForInlineClassesBasedOnNullableTypes.kt | 1 + .../notNullAsNotNullable.kt | 1 - .../redundantSafeCall.kt | 1 - 12 files changed, 47 insertions(+), 8 deletions(-) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/DeclarationOrigins.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/DeclarationOrigins.kt index 95f796f6fb1..da661fd9e7a 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/DeclarationOrigins.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/DeclarationOrigins.kt @@ -23,7 +23,9 @@ import org.jetbrains.kotlin.ir.expressions.IrStatementOriginImpl interface JvmLoweredDeclarationOrigin : IrDeclarationOrigin { object CLASS_STATIC_INITIALIZER : IrDeclarationOriginImpl("CLASS_STATIC_INITIALIZER") - object DEFAULT_IMPLS : IrDeclarationOriginImpl("DEFAULT_IMPL") + object DEFAULT_IMPLS : IrDeclarationOriginImpl("DEFAULT_IMPLS") + object DEFAULT_IMPLS_BRIDGE : IrDeclarationOriginImpl("DEFAULT_IMPLS_BRIDGE") + object DEFAULT_IMPLS_BRIDGE_TO_SYNTHETIC : IrDeclarationOriginImpl("DEFAULT_IMPLS_BRIDGE_TO_SYNTHETIC", isSynthetic = true) object FIELD_FOR_OUTER_THIS : IrDeclarationOriginImpl("FIELD_FOR_OUTER_THIS") object FUNCTION_REFERENCE_IMPL : IrDeclarationOriginImpl("FUNCTION_REFERENCE_IMPL") object SYNTHETIC_ACCESSOR : IrDeclarationOriginImpl("SYNTHETIC_ACCESSOR", isSynthetic = true) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt index 8551b43b136..f054fb46e25 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt @@ -156,6 +156,7 @@ class ExpressionCodegen( val startLabel = markNewLabel() val info = BlockInfo() val body = irFunction.body!! + generateNonNullAssertions() val result = body.accept(this, info) // If this function has an expression body, return the result of that expression. // Otherwise, if it does not end in a return statement, it must be void-returning, @@ -176,6 +177,27 @@ class ExpressionCodegen( mv.visitEnd() } + private fun generateNonNullAssertions() { + val isSyntheticOrBridge = irFunction.origin.isSynthetic || + // Although these are accessible from Java, the functions they bridge to already have the assertions. + irFunction.origin == IrDeclarationOrigin.BRIDGE_SPECIAL || + irFunction.origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE || + irFunction.origin == JvmLoweredDeclarationOrigin.JVM_STATIC_WRAPPER + if (!isInlineLambda && !isSyntheticOrBridge && !Visibilities.isPrivate(irFunction.visibility)) { + irFunction.extensionReceiverParameter?.let { generateNonNullAssertion(it) } + irFunction.valueParameters.forEach(::generateNonNullAssertion) + } + } + + private fun generateNonNullAssertion(param: IrValueParameter) { + val asmType = param.type.asmType + if (!param.type.isNullable() && !isPrimitive(asmType)) { + mv.load(findLocalIndex(param.symbol), asmType) + mv.aconst(param.name.asString()) + mv.invokestatic("kotlin/jvm/internal/Intrinsics", "checkParameterIsNotNull", "(Ljava/lang/Object;Ljava/lang/String;)V", false) + } + } + private fun writeParameterInLocalVariableTable(startLabel: Label, endLabel: Label) { if (!irFunction.isStatic) { mv.visitLocalVariable("this", classCodegen.type.descriptor, null, startLabel, endLabel, 0) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/descriptors/JvmDeclarationFactory.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/descriptors/JvmDeclarationFactory.kt index 13bb9adde3e..dc279e60f2b 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/descriptors/JvmDeclarationFactory.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/descriptors/JvmDeclarationFactory.kt @@ -148,7 +148,19 @@ class JvmDeclarationFactory( createStaticFunctionWithReceivers( defaultImpls, name, interfaceFun, dispatchReceiverType = parent.defaultType, - origin = JvmLoweredDeclarationOrigin.DEFAULT_IMPLS + // If `interfaceFun` is not a real implementation, then we're generating stubs in a descendant + // interface's DefaultImpls. For example, + // + // interface I1 { fun f() { ... } } + // interface I2 : I1 + // + // is supposed to allow using `I2.DefaultImpls.f` as if it was inherited from `I1.DefaultImpls`. + // The classes are not actually related and `I2.DefaultImpls.f` is not a fake override but a bridge. + origin = when { + interfaceFun.origin != IrDeclarationOrigin.FAKE_OVERRIDE -> interfaceFun.origin + interfaceFun.resolveFakeOverride()!!.origin.isSynthetic -> JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE_TO_SYNTHETIC + else -> JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE + } ) } } diff --git a/compiler/testData/codegen/box/builtinStubMethods/mapGetOrDefault/noTypeSafeBridge.kt b/compiler/testData/codegen/box/builtinStubMethods/mapGetOrDefault/noTypeSafeBridge.kt index 76b1dbadea6..61c9640f0ef 100644 --- a/compiler/testData/codegen/box/builtinStubMethods/mapGetOrDefault/noTypeSafeBridge.kt +++ b/compiler/testData/codegen/box/builtinStubMethods/mapGetOrDefault/noTypeSafeBridge.kt @@ -3,6 +3,10 @@ // FULL_JDK // WITH_RUNTIME +// Bridges are not generated because their signatures would conflict. The logic +// should be inserted directly into existing methods, but this is not implemented. +// IGNORE_BACKEND: JVM_IR + class A : MutableMap { override val entries: MutableSet> get() = throw UnsupportedOperationException() diff --git a/compiler/testData/codegen/box/javaInterop/notNullAssertions/extensionReceiverParameter.kt b/compiler/testData/codegen/box/javaInterop/notNullAssertions/extensionReceiverParameter.kt index d7e7353fb57..ef2c2bd1e49 100644 --- a/compiler/testData/codegen/box/javaInterop/notNullAssertions/extensionReceiverParameter.kt +++ b/compiler/testData/codegen/box/javaInterop/notNullAssertions/extensionReceiverParameter.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // TARGET_BACKEND: JVM // FILE: Test.java diff --git a/compiler/testData/codegen/box/javaInterop/notNullAssertions/incWithNullabilityAssertionOnExtensionReceiver_lv11.kt b/compiler/testData/codegen/box/javaInterop/notNullAssertions/incWithNullabilityAssertionOnExtensionReceiver_lv11.kt index 6bbf5750e6b..129038b65b7 100644 --- a/compiler/testData/codegen/box/javaInterop/notNullAssertions/incWithNullabilityAssertionOnExtensionReceiver_lv11.kt +++ b/compiler/testData/codegen/box/javaInterop/notNullAssertions/incWithNullabilityAssertionOnExtensionReceiver_lv11.kt @@ -1,5 +1,4 @@ // !LANGUAGE: -NullabilityAssertionOnExtensionReceiver -// IGNORE_BACKEND: JVM_IR // TARGET_BACKEND: JVM // FILE: test.kt // WITH_RUNTIME diff --git a/compiler/testData/codegen/box/javaInterop/notNullAssertions/incWithNullabilityAssertionOnExtensionReceiver_lv12.kt b/compiler/testData/codegen/box/javaInterop/notNullAssertions/incWithNullabilityAssertionOnExtensionReceiver_lv12.kt index 9a7bc4582ce..436ddba9e59 100644 --- a/compiler/testData/codegen/box/javaInterop/notNullAssertions/incWithNullabilityAssertionOnExtensionReceiver_lv12.kt +++ b/compiler/testData/codegen/box/javaInterop/notNullAssertions/incWithNullabilityAssertionOnExtensionReceiver_lv12.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // TARGET_BACKEND: JVM // FILE: test.kt // WITH_RUNTIME diff --git a/compiler/testData/codegen/box/javaInterop/notNullAssertions/nullabilityAssertionOnExtensionReceiver_lv11.kt b/compiler/testData/codegen/box/javaInterop/notNullAssertions/nullabilityAssertionOnExtensionReceiver_lv11.kt index 461a094c288..5b4312efffe 100644 --- a/compiler/testData/codegen/box/javaInterop/notNullAssertions/nullabilityAssertionOnExtensionReceiver_lv11.kt +++ b/compiler/testData/codegen/box/javaInterop/notNullAssertions/nullabilityAssertionOnExtensionReceiver_lv11.kt @@ -1,5 +1,4 @@ // !LANGUAGE: -NullabilityAssertionOnExtensionReceiver -// IGNORE_BACKEND: JVM_IR // TARGET_BACKEND: JVM // WITH_RUNTIME // FILE: test.kt diff --git a/compiler/testData/codegen/box/specialBuiltins/emptyMap.kt b/compiler/testData/codegen/box/specialBuiltins/emptyMap.kt index c0770bd6b2a..16469edd235 100644 --- a/compiler/testData/codegen/box/specialBuiltins/emptyMap.kt +++ b/compiler/testData/codegen/box/specialBuiltins/emptyMap.kt @@ -1,3 +1,7 @@ +// Bridges are not generated because their signatures would conflict. The logic +// should be inserted directly into existing methods, but this is not implemented. +// IGNORE_BACKEND: JVM_IR + private object EmptyMap : Map { override val size: Int get() = 0 override fun isEmpty(): Boolean = true diff --git a/compiler/testData/codegen/bytecodeText/inlineClasses/noAssertionsForInlineClassesBasedOnNullableTypes.kt b/compiler/testData/codegen/bytecodeText/inlineClasses/noAssertionsForInlineClassesBasedOnNullableTypes.kt index 79abce39e38..3802efb67aa 100644 --- a/compiler/testData/codegen/bytecodeText/inlineClasses/noAssertionsForInlineClassesBasedOnNullableTypes.kt +++ b/compiler/testData/codegen/bytecodeText/inlineClasses/noAssertionsForInlineClassesBasedOnNullableTypes.kt @@ -1,4 +1,5 @@ // !LANGUAGE: +InlineClasses +// IGNORE_BACKEND: JVM_IR inline class AsAny(val a: Any?) diff --git a/compiler/testData/codegen/bytecodeText/nullCheckOptimization/notNullAsNotNullable.kt b/compiler/testData/codegen/bytecodeText/nullCheckOptimization/notNullAsNotNullable.kt index 5c7db0afa37..beae25a1566 100644 --- a/compiler/testData/codegen/bytecodeText/nullCheckOptimization/notNullAsNotNullable.kt +++ b/compiler/testData/codegen/bytecodeText/nullCheckOptimization/notNullAsNotNullable.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR fun foo(s: String) = s as CharSequence // 0 IFNULL diff --git a/compiler/testData/codegen/bytecodeText/nullCheckOptimization/redundantSafeCall.kt b/compiler/testData/codegen/bytecodeText/nullCheckOptimization/redundantSafeCall.kt index 2ae9e240591..5a1fe4989db 100644 --- a/compiler/testData/codegen/bytecodeText/nullCheckOptimization/redundantSafeCall.kt +++ b/compiler/testData/codegen/bytecodeText/nullCheckOptimization/redundantSafeCall.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR fun test(s: String) = s?.length // 0 IFNULL