JVM_IR: generate non-null assertions for arguments

This commit is contained in:
pyos
2019-05-20 12:46:38 +02:00
committed by Georgy Bronnikov
parent 266976ac1e
commit 6b2d874ccc
12 changed files with 47 additions and 8 deletions
@@ -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)
@@ -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)
@@ -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
}
)
}
}
@@ -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<Any, Any?> {
override val entries: MutableSet<MutableMap.MutableEntry<Any, Any?>>
get() = throw UnsupportedOperationException()
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// FILE: Test.java
@@ -1,5 +1,4 @@
// !LANGUAGE: -NullabilityAssertionOnExtensionReceiver
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// FILE: test.kt
// WITH_RUNTIME
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// FILE: test.kt
// WITH_RUNTIME
@@ -1,5 +1,4 @@
// !LANGUAGE: -NullabilityAssertionOnExtensionReceiver
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// WITH_RUNTIME
// FILE: test.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<Any, Nothing> {
override val size: Int get() = 0
override fun isEmpty(): Boolean = true
@@ -1,4 +1,5 @@
// !LANGUAGE: +InlineClasses
// IGNORE_BACKEND: JVM_IR
inline class AsAny(val a: Any?)
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
fun foo(s: String) = s as CharSequence
// 0 IFNULL
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
fun test(s: String) = s?.length
// 0 IFNULL