Search code examples
javascriptexceptionscriptingrhino

Rhino pass Java class to Javascript function


I'm embedding Rhino in my Java application and trying to get a JavaAdapter from my Java code. If I do this, it works fine

(function() {
  return new JavaAdapter(packageName.InterfaceName, {
    methodA: function() {},
    methodB: function() {}
  });
})();

But when I create this function

var getJavaAdapter = function(type, obj) {
  return new JavaAdapter(type, obj);
};

modify my test.js file like this

(function() {
  return {
    methodA: function() {},
    methodB: function() {}
  };
})();

and make a call from my Java code

private static Object invokeFunction(String functionName, Object... args) {
  Object obj = scope.get(functionName, scope);
  if (obj instanceof Function) {
    Function function = (Function) obj;
    Object result = function.call(context, scope, scope, args);
    return result;
  }
  return null;
}

private static <T> T getImplementation(Class<T> type, Object obj) {
  Object implementation = invokeFunction("getJavaAdapter", type, obj);
  return (T) JavaAdapter.convertResult(implementation, type);
}

...
Object obj = evalResource("/test.js");
getImplementation(InterfaceName.class, obj);

I get some wierd exception

Exception in thread "main" org.mozilla.javascript.EcmaError: TypeError: Argument 0 is not Java class: interface packageName.InterfaceName. (/common.js#2)

I tried type.class,
I tried typeOf(type),
I tried passing only the class name then java.lang.Class.forName(className)
but still get some similar exception "Argument 0 is not Java class"

So how can I pass my class?


Solution

  • I'm not sure the JavaAdapter JavaScript type is really expecting an object of type java.lang.Class though the error message suggests otherwise.

    Context cx = Context.enter();
    try {
      Scriptable scope = cx.initStandardObjects();
      String script = "java.lang.Runnable";
      Object result = cx.evaluateString(scope, script, "<cmd>", 1, null);
      System.out.println(result.getClass());
    } finally {
      Context.exit();
    }
    

    The above code prints class org.mozilla.javascript.NativeJavaClass.

    This works:

    Context cx = Context.enter();
    try {
      Scriptable scope = cx.initStandardObjects();
      NativeJavaClass rType = new NativeJavaClass(scope, Runnable.class);
      scope.put("rType", scope, rType);
      String script = "new JavaAdapter(rType," +
            "{ run : function() { java.lang.System.out.println('hi'); }" +
            "});";
      Object r = cx.evaluateString(scope, script, "<cmd>", 1, null);
      Runnable runnable = (Runnable) JavaAdapter.convertResult(r, Runnable.class);
      runnable.run();
    } finally {
      Context.exit();
    }
    

    Version tested was Rhino 1.7R4.