JsInterop: Casting native javascript objects in gwt

1,751 views
Skip to first unread message

Brian Pedersen

unread,
Oct 26, 2015, 6:10:28 AM10/26/15
to GWT Users
I have a dispatch method, exposed through JsInterop, which can take various kinds of payloads.

Once called from native javascript, I need to cast the payload, but this doesn't quite work as I would like it too.

Here is a simplified example.

GWT:
@JsExport
@JsType
@JsNamespace("foo")
public class Dispatcher {
public String dispatch(String action, Payload payload){
if("action1".equals(action))
return dispatchWithCast(payload);
return "Unknown action: " + action;
}
public String dispatchWithCast(Payload payload){
ConcretePayload w = (ConcretePayload) payload;
return w.foo;
}
}

@JsExport
@JsType
public class Payload {}

@JsExport
@JsType
public class ConcretePayload extends Payload {
public String foo;
public int bar;
}

Javascript:
var test = new foo.Dispatcher();
alert("Dispatch: " + test.dispatch("action1", {"foo": "a", "bar": 2})); // Uncaught java.lang.ClassCastException

Everything works just fine if I replace the Payload type in the Dispatcher class with ConcretePayload, and removes the cast.

I guess this this may be beyond the capabilities of the transpiler, I just need to figure out an alternative approach.

Any ideas?

(By the way, I am using the sso linker.)

Thomas Broyer

unread,
Oct 26, 2015, 6:43:32 AM10/26/15
to GWT Users
You may want to use GWT 2.8.0-SNAPSHOT and the new JsInterop annotations; as your usecase maybe is just not supported with the "old" annotations.

Brian Pedersen

unread,
Oct 26, 2015, 7:18:21 AM10/26/15
to GWT Users
I am using GWT 2.8.0-SNAPSHOT, but the new annotations does not work for me.

I tried using @JsType(namespace = "acme", name = "MyJavaScriptObject"), as described in the new version of the JsInterop document, but apparently the namespace and name attributes are not there in the jar file.
Same thing with the @JsExport and @JsNamespace, I currently have to use them or things stop working.

My gwt-user-2.8.0-SNAPSHOT.jar, which contains the annotations, is from today.


/Brian

Brian Pedersen

unread,
Oct 26, 2015, 7:25:35 AM10/26/15
to GWT Users
Ahhh, I just realized that the new annotations are in a different package, jsinterop.annotations instead of com.google.gwt.core.client.js :)

/Brian

Jens

unread,
Oct 26, 2015, 7:29:21 AM10/26/15
to GWT Users

I am using GWT 2.8.0-SNAPSHOT, but the new annotations does not work for me.

I tried using @JsType(namespace = "acme", name = "MyJavaScriptObject"), as described in the new version of the JsInterop document, but apparently the namespace and name attributes are not there in the jar file.
Same thing with the @JsExport and @JsNamespace, I currently have to use them or things stop working.

The new annotations have a different package: jsinterop.annotations. Also @JsExport does not exist anymore in the new JsInterop version, you just use @JsType.

To activate the new annotations you also have to change the JsInterop mode via -jsInteropMode JS_RC


-- J.

Brian Pedersen

unread,
Oct 26, 2015, 8:01:40 AM10/26/15
to GWT Users
Thank's for pointing that out. So i switched to the new annotations in jsinterop.annotations and activated the new mode with -jsInteropMode JS_RC.

The cast still doesn't work though :(

Instead of a java.lang.ClassCastException, I now get a  Uncaught [object Object] during the cast.
Once again, everything works if I use the concrete type and remove the cast.


/Brian

Brian Pedersen

unread,
Oct 26, 2015, 8:16:21 AM10/26/15
to GWT Users
It seems the generated function com_google_gwt_lang_Cast_canCast__Ljava_lang_Object_2Lcom_google_gwt_core_client_JavaScriptObject_2Z only supports strings, maps, numbers and booleans.

function com_google_gwt_lang_Cast_canCast__Ljava_lang_Object_2Lcom_google_gwt_core_client_JavaScriptObject_2Z(src_0, dstId){
  return typeof src_0 === 'string' && !!com_google_gwt_lang_Cast_stringCastMap[dstId]
      || src_0.java_lang_Object_castableTypeMap && !!src_0.java_lang_Object_castableTypeMap[dstId]
      || typeof src_0 === 'number' && !!com_google_gwt_lang_Cast_doubleCastMap[dstId]
      || typeof src_0 === 'boolean' && !!com_google_gwt_lang_Cast_booleanCastMap[dstId];
}

/Brian

Brian Pedersen

unread,
Oct 26, 2015, 8:30:31 AM10/26/15
to GWT Users
I didn't get that quite right, the generated function seem to fail doing the lookup in the castableTypeMap, which must be it's way of assuring that the types are actually castable to the target type.

Cristiano Costantini

unread,
Oct 27, 2015, 2:43:36 AM10/27/15
to GWT Users
jsinterop.annotations

Truly,
If one day I'll find the man who continue using non standard packages in GWT, I'll take all his keyboards and "hack" so that each key will output "org.gwtproject" as a prefix to any key stroke!

:-D

--
You received this message because you are subscribed to the Google Groups "GWT Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-tool...@googlegroups.com.
To post to this group, send email to google-we...@googlegroups.com.
Visit this group at http://groups.google.com/group/google-web-toolkit.
For more options, visit https://groups.google.com/d/optout.

Goktug Gokdogan

unread,
Oct 29, 2015, 4:37:48 AM10/29/15
to google-web-toolkit
You can only implement java contract (e.g. PayLoad class) in javascript if they are marked as isNative. See the related section in the new jsinterop doc.

--

Brian Pedersen

unread,
Nov 2, 2015, 5:11:35 AM11/2/15
to GWT Users
Thank you Gortug for making my aware of the isNative attribute. Unfortunately the cast issue remains.

I have taken the example from the new jsinterop doc, and modified it to illustrate this: https://github.com/bitwerk/jspoc/tree/master/src/main/java/com/acme

// in Java
package com.acme;

@JsType(isNative = true)
interface Foo {
}

@JsType(isNative = true)
class Baz implements Foo{
  public int qux;
}

class Bar {
  @JsMethod
  public static int action(Foo foo) {
    Baz baz = (Baz) foo;
    return baz.qux;
  }
}

// in JavaScript

var baz = { qux: 42 };
com.acme.Bar.action(baz); // will return 42!

The image below demonstrates the issue at runtime.



I can't seem to find anywhere inside the generated javascript, where a class literal is assigned to the value 33.

/Brian

Brian Pedersen

unread,
Nov 2, 2015, 5:14:31 AM11/2/15
to GWT Users
 I hate not being able to edit my posts on google groups :)

The comment "// will return 42!" should have contained "// Should return 42, but throws 'Uncaught [object Object]'!"

Brian Pedersen

unread,
Nov 4, 2015, 6:04:11 AM11/4/15
to GWT Users
Here is another variant, failing with the same exception

//In Java
package com.acme;

@JsType
public class IntParser {
  public static int parseArray(String[] args) {
    return Integer.parseInt(args[0]);
  }
}

//In Javascript
var num = com.acme.IntParser.parseArray(["42"]); // Should return 42, but throws 'Uncaught [object Object]'!

Goktug Gokdogan

unread,
Nov 5, 2015, 12:41:35 AM11/5/15
to GWT Users
This should work. I need to take a look why it fails.

Brian Pedersen

unread,
Nov 5, 2015, 2:18:05 AM11/5/15
to GWT Users
Thanks.

If it is any help, my code is here: https://github.com/bitwerk/jspoc/tree/master/src/main/java/com/acme

/Brian

Goktug Gokdogan

unread,
Nov 6, 2015, 1:57:42 AM11/6/15
to GWT Users
I couldn't reproduce the IntParser problem with following test:
  @JsType(namespace = JsPackage.GLOBAL)
  public static class IntParser {
    
public static int parseArray(String[] args) {
      return Integer.parseInt(args[0
]);
    }
  }

  public void testStringArray() {
    assertEquals(42, callIntParser());
  }

  private native int callIntParser() /*-{
    return $wnd.IntParser.parseArray(["42"]);
  }-*/;

Also in your earlier example, you are casting { qux: 42 } to Baz which will fail because ({ qux: 42 } instanceof com.acme.Baz) is not true as { } is instance of global Object. You can change Baz to point to that:

@JsType(isNative = true, name="Object", namespace=GLOBAL)
class Baz implements Foo {
  public int qux;
}
Reply all
Reply to author
Forward
0 new messages