I’ve started writing multi-platform modules and pretty quickly have started noticing some inconsistencies between JVM and JS and as such wanted to get a better understanding of what I should be doing.
For example, given the following sample code:
class Upper(id: String) {
val upper: String = id.toUpperCase()
}
JavaScript compiles this to:
function Upper(id) {
this.upper = id.toUpperCase();
}
String is supposed to be non-null and the JVM compiler adds in Intrinsics.checkParameterIsNotNull(id, “id”); to enforce this.
However, as the JavaScript version lacks this check calling the function with a null value will result in an exception when toUpperCase is called, generally a TypeError. In more complex code this exception might be outside of the constructor, but ultimately in an unpredictable location.
As a potential solution I was thinking to add in checkNotNull(id) however there are still differences in implementation, in Java you get an IllegalArgumentException and in Javascript an IllegalStateException.
The question I have, is there a better solution or indeed will null checks be added by the javascript compiler at some point?
By the way, according to the doc checkNonNull should throw IllegalStateException. If that’s not the case in some version, that’s a bug. Could you double check that? If it does throw the wrong exception, could you tell me which version is affected?
Also could you go into a bit more detail, when this difference in behaviour is noticeable?
Do you need those asserts in production? Or is it for debugging only? Would your case be solved by adding some “debug” compilation mode, which is slow, but is closer to JVM semantics?
I will file something. Its inconsistencies like this between platforms that make Kotlin a harder choice to make for multi-platform development.
In Java the checkNotNull wouldn’t be called as the compiler inserted Preconditions check is called first, hence why you don’t end up with the same type.
Most noticeable if you are writing, for example, a validation library that you want to share across platforms. As a library, you have no control over the data being passed in so the null checks become more important. Having to explicitly add it in for Javascript means that the Java code would check for nulls twice.
I guess it could also be implemented with expect, where the Java implementation does nothing and the JavaScript implementation does the null check. Kotlin on the JVM is nice because you wouldn’t typically need to pollute your code with these null checks, so its a shame to need to do this because JavaScript doesn’t work the same.
Certainly, it would be solved by a compatibility mode. To truly share code between platforms you need this consistency.