While converting an existing code base to run in Javascript, I've encountered this bug many times. It often manifests itself after using functional methods in Lists and Maps which, by default, return Obj?.
Example:
class Example {
Int:Str strs := [:]
Void main() {
ints := Int:Int?[1:1, 2:2]
strs = ints.map { it.toStr }
// --> sys::CastErr: [sys::Int:sys::Obj?] cannot be cast to [sys::Int:sys::Str]
}
}
The current workaround is, one-by-one, convert the functions to use a full signature:
SlimerDude Sat 5 Sep 2015
Hi, I found some differences in how Javascript handles nullable List and Map types:
And the following raises an Err in Javascript (but is fine in Java's runtime):
It may look inconspicuous but it also occurs when using default method params:
Void stuff(Int[]? list := [,]) { ... } ... stuff() // --> Cast ErrOr even:
Void stuff(Int[]? list) { ... } ... list := [,] stuff(list) // --> Cast ErrSlimerDude Wed 9 Sep 2015
While converting an existing code base to run in Javascript, I've encountered this bug many times. It often manifests itself after using functional methods in
ListsandMapswhich, by default, returnObj?.Example:
class Example { Int:Str strs := [:] Void main() { ints := Int:Int?[1:1, 2:2] strs = ints.map { it.toStr } // --> sys::CastErr: [sys::Int:sys::Obj?] cannot be cast to [sys::Int:sys::Str] } }The current workaround is, one-by-one, convert the functions to use a full signature:
class Example { Int:Str strs := [:] Void main() { ints := Int:Int?[1:1, 2:2] strs = ints.map |Int doh -> Str| { doh.toStr } } }andy Wed 9 Sep 2015
Ticket promoted to #2453 and assigned to andy
SlimerDude Wed 9 Sep 2015
I suspect this is the same down cast problem; when you call
TabPane.tabs()in Javascript you get:The following workaround patch fixes it:
diff -r a77e9070251b src/fwt/fan/TabPane.fan --- a/src/fwt/fan/TabPane.fan Fri Sep 04 09:11:51 2015 -0400 +++ b/src/fwt/fan/TabPane.fan Wed Sep 09 23:34:30 2015 +0100 @@ -33,7 +33,7 @@ ** Get the list of installed tabs. Tabs are added and ** removed using normal `Widget.add` and `Widget.remove`. ** - Tab[] tabs() { return Tab[,].addAll(children) } + Tab[] tabs() { children.map |kid->Tab| { kid } } ** ** The currently selected index of `tabs`.SlimerDude Tue 20 Oct 2015
A quick test and a fix for the JS casting bugs:
diff -r 978760e66cf1 src/sys/js/fan/Type.js --- a/src/sys/js/fan/Type.js Mon Oct 19 19:58:53 2015 -0400 +++ b/src/sys/js/fan/Type.js Wed Oct 21 02:59:15 2015 +0100 @@ -852,7 +852,8 @@ if (that instanceof fan.sys.MapType) { - return this.k.is(that.k) && this.v.is(that.v); + return ((this.k.qname() == "sys::Obj") || this.k.is(that.k)) && + ((this.v.qname() == "sys::Obj") || this.v.is(that.v)); } if (that instanceof fan.sys.Type) { diff -r 978760e66cf1 src/testSys/fan/TypeTest.fan --- a/src/testSys/fan/TypeTest.fan Mon Oct 19 19:58:53 2015 -0400 +++ b/src/testSys/fan/TypeTest.fan Wed Oct 21 02:59:15 2015 +0100 @@ -207,6 +207,13 @@ map2 := (Int:Int[]?) Obj:Obj?[:] map3 := (Int:Int?[]?) Obj:Obj[:] map4 := (Int:Int?[]?) Obj:Obj?[:] + + // JS casting bug - see #2453 + // sys::CastErr: [sys::Int:sys::Obj?] cannot be cast to [sys::Int:sys::Str] + map5 := (Int:Str) Int:Int[1:1].map { it.toStr } + + // always worked okay + list5 := (Str[]) Int[1].map { it.toStr } } Void verifyFits(Type a, Type b, Bool expected)andy Thu 22 Oct 2015
Ticket resolved in 1.0.68
That seems pretty safe - pushed