Docs/examples for JSAdapter
A. Sundararajan
sundararajan.athijegannathan at oracle.com
Tue Oct 22 09:55:24 PDT 2013
It is possible to subclass jdk.nashorn.api.scripting.JSObject by an
arbitrary Java class. Such a class will be linked by a special dynalink
linker. Script can do normal obj.foo, obj.bar() etc. and these would be
routed to JSObject.getMember/getSlot, JSObject.removeMember,
JSObject.call etc.
That said, being able to call/invoke arbitrary Java class/method from
script is very useful as well. Allows lot of useful Java libraries be
accessed from script code 'as is'. For eg. it is very easy to extend
and/or use JavaFX library from script. Nice samples have been written.
-Sundar
On Tuesday 22 October 2013 10:13 PM, Rick Bullotta wrote:
> Short version:
>
> I don't want my users to have to code anything like Java.type(XXX), which they didn't need to do in Rhino and makes for some ugly code. I want business people to be able to write scripts in most cases. A proper script engine should be able to enable the developer to hide complexity from the script author/user, not the other way around, I'd think.
>
> I need to be able to expose Java object(s) of a variety of types to the scripting environment and be able to intercept the following:
>
> Object get(string)
> put(string, Object)
> Object get(int)
> put(int, value)
> Object[] ids() (for enumeration)
> delete(string)
> delete(i)
>
> get methods should be able to return values or function references (which would of course implement a "call" method).
>
> This is what Rhino allows, and it is powerful and elegant, and GREATLY simplifies the user's experience.
>
>
> From: Jim Laskey (Oracle) [mailto:james.laskey at oracle.com]
> Sent: Tuesday, October 22, 2013 12:34 PM
> To: Rick Bullotta
> Cc: nashorn-dev at openjdk.java.net
> Subject: Re: Docs/examples for JSAdapter
>
> In Nashorn, there is no need to wrap classes/types in JavaScript objects. You can work with them directly or extended them directly. As an example,
>
> jjs> var ArrayList = Java.type("java.util.ArrayList"); // or var ArrayList = java.util.ArrayList;
> jjs> var Arrays = Java.type("java.util.Arrays");
> jjs> var list = new ArrayList();
> jjs> list.add("a");
> true
> jjs> list.add("b");
> true
> jjs> list.add("c");
> true
> jjs> list.addAll(Arrays.asList(["d", "e", "f"]));
> true
> jjs> list.addAll(Arrays.asList("g", "h", "i"));
> true
> jjs> for each(var e in list) print(e);
> a
> b
> c
> d
> e
> f
> g
> h
> i
>
> Is there a specific example of something you want to translate? We can help you out.
>
> Cheers,
>
> -- Jim
>
>
>
>
>
>
> On 2013-10-22, at 1:02 PM, Rick Bullotta <rick.bullotta at thingworx.com<mailto:rick.bullotta at thingworx.com>> wrote:
>
>
> In Rhino, you register custom adapters in a "wrap factory" that tells the script engine to use these adapters for accessing instances of those classes/types. What's the analog in Nashorn? Can custom types implement interfaces that Nashorn will use?
>
> From: Jim Laskey (Oracle) [mailto:james.laskey at oracle.com<http://oracle.com>]
> Sent: Tuesday, October 22, 2013 11:52 AM
> To: Rick Bullotta
> Cc: nashorn-dev at openjdk.java.net<mailto:nashorn-dev at openjdk.java.net>
> Subject: Re: Docs/examples for JSAdapter
>
> In general the Nashorn docs start here. https://wiki.openjdk.java.net/display/Nashorn/Nashorn+Documentation, but as you point out, we seem to be lacking JSAdapter details. I should start an FAQ.
>
> >From the javadoc (cd make; ant javadoc # ./dist/javadoc/index.html)
>
>
> * public final class NativeJSAdapter
>
> * extends ScriptObject<file:///\\Volumes\Elephant\Projects\nashorn~jdk8\nashorn\dist\javadoc\jdk\nashorn\internal\runtime\ScriptObject.html>
> This class is the implementation of the Nashorn-specific global object named JSAdapter. It can be thought of as the Proxy<http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Proxy.html?is-external=true> equivalent for JavaScript. NativeJSAdapter calls specially named JavaScript methods on an adaptee object when property access/update/call/new/delete is attempted on it. Example:
>
> var y = {
>
> __get__ : function (name) { ... }
>
> __has__ : function (name) { ... }
>
> __put__ : function (name, value) {...}
>
> __call__ : function (name, arg1, arg2) {...}
>
> __new__ : function (arg1, arg2) {...}
>
> __delete__ : function (name) { ... }
>
> __getIds__ : function () { ... }
>
> };
>
>
>
> var x = new JSAdapter(y);
>
>
>
> x.i; // calls y.__get__
>
> x.foo(); // calls y.__call__
>
> new x(); // calls y.__new__
>
> i in x; // calls y.__has__
>
> x.p = 10; // calls y.__put__
>
> delete x.p; // calls y.__delete__
>
> for (i in x) { print(i); } // calls y.__getIds__
>
>
>
> JavaScript caller of adapter object is isolated from the fact that the property access/mutation/deletion are really calls to JavaScript methods on adaptee.
>
> JSAdapter constructor can optionally receive an "overrides" object. Properties of overrides object is copied to JSAdapter instance. When user accessed property is one of these, then adaptee's methods like __get__, __put__ etc. are not called for those. This can be used to make certain "preferred" properties that can be accessed in the usual/faster way avoiding proxy mechanism. Example:
>
> var x = new JSAdapter({ foo: 444, bar: 6546 }) {
>
> __get__: function(name) { return name; }
>
> };
>
>
>
> x.foo; // 444 directly retrieved without __get__ call
>
> x.bar = 'hello'; // "bar" directly set without __put__ call
>
> x.prop // calls __get__("prop") as 'prop' is not overridden
>
>
> It is possible to pass a specific prototype for JSAdapter instance by passing three arguments to JSAdapter constructor. So exact signature of JSAdapter constructor is as follows:
>
> JSAdapter([proto], [overrides], adaptee);
>
>
> Both proto and overrides are optional - but adaptee is not. When proto is not passed JSAdapter.prototype is used.
> *
> As well as the test ./test/script/basic/jsadapter.js
>
> var obj = new JSAdapter() {
> __get__: function(name) {
> print("getter called for '" + name + "'"); return name;
> },
>
> __put__: function(name, value) {
> print("setter called for '" + name + "' with " + value);
> },
>
> __call__: function(name, arg1, arg2) {
> print("method '" + name + "' called with " + arg1 + ", " + arg2);
> },
>
> __new__: function(arg1, arg2) {
> print("new with " + arg1 + ", " + arg2);
> },
>
> __getIds__: function() {
> print("__getIds__ called");
> return [ "foo", "bar" ];
> },
>
> __getValues__: function() {
> print("__getValues__ called");
> return [ "fooval", "barval" ];
> },
>
> __has__: function(name) {
> print("__has__ called with '" + name + "'");
> return name == "js";
> },
>
> __delete__: function(name) {
> print("__delete__ called with '" + name + "'");
> return true;
> }
> };
>
> // calls __get__
> print(obj.foo);
>
> // calls __put__
> obj.foo = 33;
>
> // calls __call__
> obj.func("hello", "world");
>
> // calls __new__
> new obj("hey!", "it works!");
>
> for (i in obj) {
> print(i);
> }
>
> for each (i in obj) {
> print(i);
> }
>
> var x = "foo" in obj;
> print(x);
>
> var y = "js" in obj;
> print(y);
>
> print(delete obj.prop);
>
> print(obj["js"]);
> obj["js"] = "javascript";
> print(obj["javascript"]);
> Hope that helps. Is there a specific question?
>
> Cheers,
>
> -- Jim
>
> On 2013-10-22, at 11:50 AM, Rick Bullotta <rick.bullotta at thingworx.com<mailto:rick.bullotta at thingworx.com>> wrote:
>
>
>
> Hi, all.
>
> I've been searching everywhere for any form of documentation or substantive examples as to how JSAdapter works in Nashorn, as it is an essential piece of the puzzle for many of us who will naturally be migrating from Rhino.
>
> Can anyone provide and additional info?
>
> Thanks,
>
> Rick
> ThingWorx
>
>
More information about the nashorn-dev
mailing list