Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
titlelambda as function example
var JFunction = Java.type('java.util.function.Function')

var obj = new JFunction() { 
   apply: function(x) { print(x*x) } 
}

// every lambda object is a 'function'

print(typeof obj); // prints "function"

// 'calls' lambda as though it is a function
obj(91);

Script Proxy implementation

Sometimes you may want to create a script friendly proxy object adapting another object. You can implement such a proxy in Java code or JavaScript code.

Plugging-in your own JSObject

Nashorn exposes script objects as an object of jdk.nashorn.api.scripting.JSObject to java code. It is also possible to provide your own jdk.nashorn.api.scripting.JSObject implementation and nashorn will treat such objects specially (by calling magic methods for property getter, setter, delete etc.)

Code Block
titleJSObject implementation example
import jdk.nashorn.api.scripting.AbstractJSObject;
import java.nio.DoubleBuffer;
/**
 * Simple class demonstrating pluggable script object
 * implementation. By implementing jdk.nashorn.api.scripting.JSObject
 * (or extending AbstractJSObject which implements it), you
 * can supply a friendly script object. Nashorn will call
 * 'magic' methods on such a class on 'obj.foo, obj.foo = 33,
 * obj.bar()' etc. from script.
 *
 * In this example, Java nio DoubleBuffer object is wrapped
 * as a friendly script object that provides indexed acces
 * to buffer content and also support array-like "length"
 * readonly property to retrieve buffer's capacity. This class
 * also demonstrates a function valued property called "buf".
 * On 'buf' method, we return the underlying nio buffer object
 * that is being wrapped.
 */
public class BufferArray extends AbstractJSObject {
    // underlying nio buffer
    private final DoubleBuffer buf;
    public BufferArray(int size) {
        buf = DoubleBuffer.allocate(size);
    }
    public BufferArray(DoubleBuffer buf) {
        this.buf = buf;
    }
    // called to check if indexed property exists
    @Override
    public boolean hasSlot(int index) {
        return index > 0 && index < buf.capacity();
    }
    // get the value from that index
    @Override
    public Object getSlot(int index) {
       return buf.get(index);
    }
    // set the value at that index
    @Override
    public void setSlot(int index, Object value) {
       buf.put(index, ((Number)value).doubleValue());
    }
    // do you have a property of that given name?
    @Override
    public boolean hasMember(String name) {
       return "length".equals(name) || "buf".equals(name);
    }
    // get the value of that named property
    @Override
    public Object getMember(String name) {
       switch (name) {
          case "length":
              return buf.capacity();
          case "buf":
              // return a 'function' value for this property
              return new AbstractJSObject() {
                  @Override
                  public Object call(Object thiz, Object... args) {
                      return BufferArray.this.buf;
                  }
                  // yes, I'm a function !
                  @Override
                  public boolean isFunction() {
                      return true;
                  }
              };
       }
       return null;
    }
}

JS example using the above JSObject implementation is as follows:

Code Block
titleJSObject usage example
// Usage: jjs -scripting -cp . jsobject.js
// This sample demonstrats how to expose a
// script friendly object from your java code
// by implementing jdk.nashorn.api.scripting.JSObject
// compile the java program
`javac BufferArray.java`
// print error, if any and exit
if ($ERR != '') {
    print($ERR)
    exit($EXIT)
}
// create BufferArray
var BufferArray = Java.type("BufferArray")
var bb = new BufferArray(10)
// 'magic' methods called to retrieve set/get
// properties on BufferArray instance
var len = bb.length
print("bb.length = " + len)
for (var i = 0; i < len; i++) {
    bb[i] = i*i
}
for (var i = 0; i < len; i++) {
    print(bb[i])
}
// get underlying buffer by calling a method
// on BufferArray magic object
// 'buf' is a function member
print(typeof bb.buf)
var buf = bb.buf()
// use retrieved underlying nio buffer
var cap = buf.capacity()
print("buf.capacity() = " + cap)
for (var i = 0; i < cap; i++) {
   print(buf.get(i))
}

JSAdapter constructor

Anchor
jsadapter
jsadapter

JSAdapter

...

JSAdapter supports java.lang.reflect.Proxy-like proxy mechanism for script objects. i.e., it allows script proxy

objects be implemented in script itself.

 

Code Block
titleJSAdapter example
// A JSAdapter object with proxy-like special hooks
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) {ECMAscript typed arrays
        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!");

// calls __getIds__ to get array of properties
for (i in obj) {
    print(i);
}

// calls __getValues__ to get array of property values
for each (i in obj) {
    print(i);
}

...