jsni support, once again

81 views
Skip to first unread message

Matthew Neeley

unread,
Apr 7, 2012, 11:40:18 PM4/7/12
to scal...@googlegroups.com
Hi All-

Motivated by this latest burst of activity, I got jsni support working
again on top of the latest version of scalagwt. Right now it only
supports calling into native javascript from scala, not back out
again, but that's already enough to do some pretty cool things,
including implementing JavaScriptObjects using scala classes. I've
added an example to the sample project to test/show off some of what
is possible: https://github.com/maffoo/scalagwt-sample/blob/master/src/com/google/gwt/sample/jsni/client/HelloJsni.scala

To specify the method bodies, you need a function called 'nativeCode'
that takes a single string parameter and returns Nothing (ie raises an
exception). We discussed adding this method to the scala.util
package, but for now the gwt compiler will accept any method called
nativeCode, regardless of the package, so you can define nativeCode in
your project as I've done in the sample. Also, note that you have to
annotate the methods as @native so that the devMode system, which
looks at the compiled class files, will see that they are native and
look up the jsni method bodies, rather than trying to execute them.
(One issue with jribble right now is that even though it has a Native
flag for method declarations, the flag is not being set correctly. It
would be nice to fix this, but I didn't make much headway on that the
last time I looked at it.)

My repo with the jsni updates is here:
https://github.com/maffoo/scalagwt-gwt

Also, I've generated a pull request to get this in to the main branch:
https://github.com/scalagwt/scalagwt-gwt/pull/39

Please let me know what you think.

-Matthew


PS- One caveat regarding the use of JavaScriptObjects with scala. A
common pattern with gwt in java is to define a Jso class with static
native constructor methods, e.g.:

class Point extends JavaScriptObject {
public static native Point create(int x, int y) /*-{ return [x, y]; }-*/;

protected Point() {}

public native int getX() /*-{ return this[0]; }-*/;
public native int getY() /*-{ return this[1]; }-*/;
}

The roughly equivalent thing we'd like to do in scala would be to have
a Point class with a companion object to hold the statics:

class Point protected() extends JavaScriptObject {
@native def x: Int = nativeCode(" return this[0]; ")
@native def y: Int = nativeCode(" return this[1]; ")
}

object Point {
@native def create(x: Int, y: Int): Point = nativeCode(" return [x, y]; ")
}

Unfortunately, this does not work, because having a companion object
is not exactly the same as having static methods on a class. In
hosted mode the call to Point.create fails with an error saying the
MODULE$ field is not found. If you compile down to javascript it
turns out everything works fine again, but not having hosted mode is
sort of a deal breaker. If anyone with more knowledge of scala
internals and jribble has any ideas about how to fix this, that would
be great, but for now, just put your jso constructor methods on some
other object which is not a companion and everything works fine.

Grzegorz Kossakowski

unread,
Apr 8, 2012, 6:51:27 AM4/8/12
to scal...@googlegroups.com
On 8 April 2012 05:40, Matthew Neeley <mne...@gmail.com> wrote:
Hi All-

Motivated by this latest burst of activity, I got jsni support working
again on top of the latest version of scalagwt.  Right now it only
supports calling into native javascript from scala, not back out
again, but that's already enough to do some pretty cool things,
including implementing JavaScriptObjects using scala classes.  I've
added an example to the sample project to test/show off some of what
is possible: https://github.com/maffoo/scalagwt-sample/blob/master/src/com/google/gwt/sample/jsni/client/HelloJsni.scala

Hi Matthew!

The example looks really cool :-)

To specify the method bodies, you need a function called 'nativeCode'
that takes a single string parameter and returns Nothing (ie raises an
exception).  We discussed adding this method to the scala.util
package, but for now the gwt compiler will accept any method called
nativeCode, regardless of the package, so you can define nativeCode in
your project as I've done in the sample.  

I agree with this decision. We should start thinking about having our own library of scala-gwt-extras where this should go.

For now matching just on name of the method sounds good.

Also, note that you have to
annotate the methods as @native so that the devMode system, which
looks at the compiled class files, will see that they are native and
look up the jsni method bodies, rather than trying to execute them.
(One issue with jribble right now is that even though it has a Native
flag for method declarations, the flag is not being set correctly.  It
would be nice to fix this, but I didn't make much headway on that the
last time I looked at it.)


I looked into that and fixed it. Could you pull, rebuild scala and verify that it fixes all of your problems?


My repo with the jsni updates is here:
https://github.com/maffoo/scalagwt-gwt

Also, I've generated a pull request to get this in to the main branch:
https://github.com/scalagwt/scalagwt-gwt/pull/39

Please let me know what you think.

Your change looks very promising already. Two things I'd like to get added:
  • comments explaining all the logic behind detecting and extracting javascript code
  • tests, they are easy to write now
Once this is done and you verify that scala compiler fix I posted works for you let's have another iteration (new pull request with some commits squashed). I'll have a more detailed look into your change then.

I want to cut next release around next Friday (or Monday just before Scala Days). Let me know if you can make it for the release.

Hmmm. Not sure if I understand the issue. Do you want to say that output of one class refers to non-existing MODULE$ filed that is not defined in some other jribble file?

--
Grzegorz Kossakowski

Matthew Neeley

unread,
Apr 8, 2012, 12:01:16 PM4/8/12
to scal...@googlegroups.com

Your fix works for me, thanks for the quick response. Given that
methods have to be marked @native to work with devmode anyway, I
propose to remove the isNative() code which looks at the jribble, and
instead just go by the @native flag. Then, inside jsniGetNativeCode
I'll do all the checks to ensure that the jribble is properly formed
for a native method call. What do you think?

>
>> My repo with the jsni updates is here:
>> https://github.com/maffoo/scalagwt-gwt
>>
>> Also, I've generated a pull request to get this in to the main branch:
>> https://github.com/scalagwt/scalagwt-gwt/pull/39
>>
>> Please let me know what you think.
>
>
> Your change looks very promising already. Two things I'd like to get added:
>
> comments explaining all the logic behind detecting and extracting javascript
> code
> tests, they are easy to write now
>
> Once this is done and you verify that scala compiler fix I posted works for
> you let's have another iteration (new pull request with some commits
> squashed). I'll have a more detailed look into your change then.
>
> I want to cut next release around next Friday (or Monday just before Scala
> Days). Let me know if you can make it for the release.
>

I think it should be no problem to have this ready by Friday. I
update the pull request with some comments and I'll try to get some
tests written today.

I'm not exactly sure what's going wrong. The example I gave above
compiles to javascript and works just fine, but in hosted mode a call
to Point.create(x, y) will fail with an error
java.lang.NoSuchFieldError: MODULE$
At first I thought the issue was with how JavaScriptObject objects are
represented at runtime in devmode, but the MODULE$ field lives on the
Point$ class, not the Point class which is the JavaScriptObject, so
I'm still confused by this error. If you have any insight, that would
be great.

-Matthew

Matthew Neeley

unread,
Apr 9, 2012, 11:50:32 PM4/9/12
to scal...@googlegroups.com
I've added a test of jsni support to JribbleAstBuilderTest, as well as
improving the documentation of the jsni-related code in
JribbleAstBuilder itself. Please have a look at the pull request and

let me know what you think.
https://github.com/scalagwt/scalagwt-gwt/pull/39

Cheers,
Matthew

Baldur Norddahl

unread,
Jun 30, 2012, 5:02:17 PM6/30/12
to scal...@googlegroups.com
I am having trouble with the jsni support. My code is like this:

object util {
  def nativeCode(body: String) = sys.error("nativeCode: method body not provided")
  @native def scrollDown(what: Element): Unit = nativeCode {"""
    what.scrollTo(0, what.scrollHeight);
  """}
}

When I call scrollDown the javascript console will output "nativeCode: method body not provided".

What am I doing wrong?

I am using the latest version from GIT:

scalagwt-gwt commit f9af196396c4298220c4bbed4782d15c579dbac1
scalagwt-library commit 94597b688c42dd6cc37a2d73420728992deef77f
scalagwt-scala commit ac6fe1a0362ed7c132ec153da5751cc105da4437

Thanks,

Baldur

Matthew Neeley

unread,
Jun 30, 2012, 8:09:14 PM6/30/12
to scal...@googlegroups.com
Hi Baldur-

The jsni patch is not yet merged into the main project, so if you want
to try it out you'd need to add a remote to
git://github.com/maffoo/scalagwt-gwt.git and pull from my repo
directly.

-Matthew

Baldur Norddahl

unread,
Jul 1, 2012, 7:34:34 AM7/1/12
to scal...@googlegroups.com
Hi Mathew,

Thanks for your help. Using the version from your git repository works.

Any chances for this patch to make it into the long awaited 1.0 version?

Regards

Baldur

Matthew Neeley

unread,
Jul 1, 2012, 9:49:58 AM7/1/12
to scal...@googlegroups.com
Hi Baldur-

Glad it worked for you!

I do think there's a chance of getting this in to 1.0. The patch is
in reasonably good shape, but of course there are still a number of
things missing. However, I personally haven't had much time to work
on it, and I'm not sure what the current plan for 1.0 is. Maybe other
on the list can weigh in on the timeline.

-Matthew
Reply all
Reply to author
Forward
0 new messages