https://kotlinlang.org logo
Title
j

Janar

09/10/2018, 11:17 AM
Hi Guys, I have a code like this:
import java.security.MessageDigest
fun foo(rp: String) {
    return bar(rp)
}
private fun bar(rp: String): ByteArray {
    return MessageDigest.getInstance("SHA-256").digest(rp.toByteArray())
}
The generarted code for kotlin uses:
Intrinsics.checkParameterIsNotNull
for
foo
and generates
if (rpId ==null)
in
bar
. In the later case it throws a
TypeCastException
in case of null and in former an
IllegalArgumentException
. So why does generated code need to handle null checks differently??
r

ribesg

09/10/2018, 11:22 AM
The provided code doesn't even compile
j

Janar

09/10/2018, 11:25 AM
import java.security.MessageDigest
fun foo(rp: String) {
    return bar(rp)
}
private fun bar(rp: String): ByteArray {
    return MessageDigest.getInstance("SHA-256").digest(rp.toByteArray())
}
if this because of using inline
String.toByteArray()
? Even then we could have still used the same null check in
Intrinsics.checkParameterIsNotNull(rp, "rp")
??
d

diesieben07

09/10/2018, 5:02 PM
The difference is between public and private function.
foo
is public, and therefor can be called from untrusted code (including Java), who might be passing null. In those cases we want
IllegalArgumentException
. However
bar
is private, and
null
should never end up there. If it for some reason does, you have done an unchecked cast somewhere in your code.
j

Janar

09/10/2018, 5:24 PM
@diesieben07 Thanks. however, when I change the argument in both
foo
and
bar
the null check in bar disappears!!? a primitive byte[] can be null too!
import java.security.MessageDigest
fun foo(rp: ByteArray):ByteArray {
    return bar(rp)
}
private fun bar(rp: ByteArray): ByteArray {
    return MessageDigest.getInstance("SHA-256").digest(rp)
}
decompiled java code for this is:
private static final byte[] bar(byte[] rp) {
      byte[] var10000 = MessageDigest.getInstance("SHA-256").digest(rp);
      Intrinsics.checkExpressionValueIsNotNull(var10000, "MessageDigest.getInstance(\"SHA-256\").digest(rp)");
      return var10000;
   }
d

diesieben07

09/10/2018, 5:26 PM
digest
is a Java method, so it's argument is a platform type (
ByteArray!
in this case). This type is allowed to accept nulls, so the compiler does not emit the check.
Actually... I am talking BS.
You are right, the
TypeCastException
is remnants of the
this as java.lang.String
in
String.toByteArray
.
j

Janar

09/10/2018, 5:29 PM
cool. Thanks for clarifying! 👍
just for sake of addition info for archival… [this thread here captures it well](https://github.com/jacoco/jacoco/issues/754)
d

dmitry.petrov

09/10/2018, 8:30 PM
1) There's some work in progress related to unification of nullability-related exceptions. However, this can potentially change a program behavior, and can happen in major release only (and we are somewhat late with that change for 1.3). 2) Yes, currently due to the way redundant null check elimination works in the Kotlin compiler, non-private functions get somewhat better treatment (because they have nullability assertions on parameters, and optimizer takes them into account). This can be improved in a minor release.
j

Janar

09/11/2018, 6:53 PM
Thanks @dmitry.petrov
My actual motivation for looking in to this behavior was that of code coverage where JaCoCo suddenly started complaining about this piece of code due to inline function being called (String::toByteArray() in this case) bringing in the TypeCastException.