|
|
The Problem
Currently, all JSNI methods are required to guarantee that they never return the value undefined. This leads to the following sorts of coersion in native code:
native int getFoo() /*-{
return this.foo || 0;
}-*/;
native Object getBar() /*-{
return this.bar || null;
}-*/;
native String getBaz() /*-{
var s = this.baz;
return (s == null) ? null : s;
}-*/;This is both suboptimal and difficult to get right in practice. Failure to do so (i.e. letting undefined escape into the Java type system) can lead to very difficult-to-debug runtime failures. This is compounded by the fact that different browsers return null and undefined at different times.
The Solution
Let both null and undefined be treated equally in generated Javascript code. If they are treated equally, those writing JSNI methods will no longer be required to coerce all return values to null (this will still be necessary for non-String primitive types).
In hosted-mode, this can be dealt with by simply coercing undefined return values to null in JsValueGlue. In web-mode, the generated Javascript code will simply need to ensure that all generated constructs will work properly in the presence of either null or undefined.
Details
The only Java expression affected by this change will be identity comparison for Object types (e.g. Object a,b; if (a == b)).
Consider the case where a is an empty string and b in an Integer with value 0. GWT uses native JavaScript string and numeric types for the respective Java types. A JavaScript equality comparison would compare "" == 0, which is true is JavaScript. One might choose to emit the Java == operator as the JavaScript === operator, however this is incorrect when you consider the following scenario.
Assume the same Java comparison of Object a == Object b. Suppose a had been initialized from a JSNI method with a value of null and b had been initialized with a value of undefined. The JavaScript equality comparison null == undefined is true, while the identity comparison null === undefined is false. This would produce an unexpected result since undefined and null should be equal as far as the Java semantics are concerned.
The net result of these cases is that the Java object identity operator cannot always be mapped into one JavaScript operator.
The matrix below describes the required comparison operators in each case. In all cases where the values are known to be non-null, we can continue to use the === operator as before. In some other cases, it is only necessary to switch to the == operator (because undefined == null).
There are two special cases, dubbed (1) and (2) below, where extra code will need to be generated.
| !null | ?null | |||||||
| Object | String | Widget | Object | String | Widget | null | ||
| Object | === | |||||||
| !null | String | === | === | |||||
| Widget | === | false | === | |||||
| Object | === | === | === | (1) | ||||
| ?null | String | === | === | false | (1) | == | ||
| Widget | === | false | === | (1) | (2) | == | ||
| null | false | false | false | == | == | == | true |
µ(O) := ((O === undefined) ? null : O) (1) := µ(O1) === µ(O2) (2) := (O1 == null) & (O2 == null)
- Widget: Any non-String subclass of Object
- !null: A value that is statically determined to be non-null
- ?null: A value that may or may not be null
Performance
We expect that most comparisons between Objects will be performed using the == operator [TODO: performance metrics on == vs. ===].
The 1 and 2 cases will only be required when the comparison being performed is of one of the following forms:
- Object == Object
- String == Object
- Widget == Object
- Widget == String
While these cases do occur in practice, they are not the norm -- three of them involve comparisons with untyped (and un-type-tightened) Objects, and the last is extremely odd [TODO: performance metrics on === vs. 1].
The initial implementation will be performed without knowledge of the !null cases described above. Gathering this information in the compiler will allow many cases to be reverted to the === operator, and in some cases statically evaluated to false.
Caveats
It will still be necessary for JSNI methods that return non-string primitive values to coerce their return values if there is any chance it may be undefined.
Sign in to add a comment
