I'm trying to print a simple JSON from my javascript using Rhino 1.7.7.1 from maven.
public static void main(String[] args) throws JsonProcessingException {
Context context = Context.enter();
try {
ScriptableObject scope = context.initStandardObjects();
System.out.println(context.evaluateString(scope, "JSON.stringify({a:1})", null, 1, null));
} finally {
Context.exit();
}
}
But I'm got:
Exception in thread "main" org.mozilla.javascript.EcmaError: ReferenceError: "JSON" is not defined. (unnamed script#1)
at org.mozilla.javascript.ScriptRuntime.constructError(ScriptRuntime.java:3654)
at org.mozilla.javascript.ScriptRuntime.constructError(ScriptRuntime.java:3632)
at org.mozilla.javascript.ScriptRuntime.notFoundError(ScriptRuntime.java:3717)
at org.mozilla.javascript.ScriptRuntime.name(ScriptRuntime.java:1692)
at org.mozilla.javascript.gen.c1._c0(unnamed script:1)
at org.mozilla.javascript.gen.c1.call(unnamed script)
at org.mozilla.javascript.ContextFactory.doTopCall(ContextFactory.java:398)
at org.mozilla.javascript.ScriptRuntime.doTopCall(ScriptRuntime.java:3065)
at org.mozilla.javascript.gen.c1.call(unnamed script)
at org.mozilla.javascript.gen.c1.exec(unnamed script)
at org.mozilla.javascript.Context.evaluateString(Context.java:1104)
at mypackage.RhinoService.main(RhinoService.java:34)
JSON.stringify is not implemented in the Rhino 1.7 engine. See the Rhino compatibility table.
Related
Here is my simple java script test.js with class :
class Car {
constructor(name, year) {
this.name = name;
this.year = year;
}
}
myCar = new Car("Ford", 2014);
Print("done")
Here is my java code which will try to load test.js
public class Controller {
public static void main(String[] args) {
try {
NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
ScriptEngine engine = factory.getScriptEngine("--language=es6");
engine.eval(Files.newBufferedReader(Paths.get("<Path-to-test-js>/test.js"), StandardCharsets.UTF_8));
} catch (Exception _e){
System.out.println(_e);
}
}
but its throwing exception :
javax.script.ScriptException: <eval>:1:0 Expected an operand but found class
class Car {
^ in <eval> at line number 1 at column number 0
Why its throwing exception here.
I am not able to run nashorn with my scripts , i think class is not supported.
If anyone is planning to use nashorn do not use it :
I removed class from my scripts and i tested its performance , its very slow. not even to close to expected performace of my application.
Even for study purpose i will not recommend it as its getting absolute.
Nashorn, the JavaScript engine in the OpenJDK, has been deprecated in JDK 11 by JEP 335 and has recently been scheduled to be removed in a future JDK version by JEP 372.
I am using Nashorn JS engine from OpenJDK 12. Nashorn seems to be deprecated. I am looking which are the available alternatives. I found GraalVM, but I am not sure if this is the best.
How can I execute a GraalVM JavaScript from Java ? Do you have any example ?
With Nashorn was using from Java:
NashornScriptEngineFactory nsef = new NashornScriptEngineFactory();
ScriptEngine engine = nsef.getScriptEngine( BasicDBObject.class.getClassLoader() );
final Bindings binding = engine.getContext().getBindings(ScriptContext.ENGINE_SCOPE);
In Nashorn I create a WrappedMongoDatabase which extends AbstractJSObject. There I add some 'virtual' methods to simulate the MongoDB Query language, which does for example getCollection('persons').find()...
Do you know a way to replace the AbstractJSObject in GraalVM?
I had a look to ProxyObject, somehow I couldn't find a way to override the call(Object thiz, Object... args) like in AbstractJSObject.
public class WrappedMongoDatabase extends AbstractJSObject {
#Override
public boolean hasMember(String name) {
return "getCollection".equals( name ) || "createCollection".equals(name)||...;
}
#Override
public Object getMember(final String name) {
if ( hasMember( name ) ){
return new AbstractJSObject() {
#Override
public Object call(Object thiz, Object... args) {
switch( name ) {
case "getCollection":
if (args.length == 1 && args[0] instanceof String) {
return getCollection((String) args[0]);
}
break;
...
}
}
}
}
}
}
GraalVM is a solid way to go. We've been using it for a while now, it runs well, and the JavaScript implementation is far better than Nashorn (or Rhino). In particular, it is ECMA2020 compliant, it supports Node requires (that's huge!), it performs much better, etc...
GraalVM is a big step forward if you're using Nashorn, but it does require some adjustments, which are covered reasonably well in the GraalVM documentation.
Follow GraalVM ScriptEngine
ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");
Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);
bindings.put("polyglot.js.allowHostAccess", true);
bindings.put("polyglot.js.allowHostClassLookup", (Predicate<String>) s -> true);
bindings.put("javaObj", new Object());
engine.eval("(javaObj instanceof Java.type('java.lang.Object'));"); // would not work without allowHostAccess and allowHostClassLookup
Notice nashorn compatibility mode:
These options control the sandboxing rules applied to evaluated JavaScript code and are set to false by default, unless the application was started in Nashorn compatibility mode (--js.nashorn-compat=true).
I understand that this helper method can be called from a Java code to check if your Java/JS Object has a property you are looking for
but i would like to know if this is called by the Nashorn Engine while we use this JSObject/AbstractJSObject implementation in a JavaScript code.
I am aware of the fact that doing a . inside JavaScript will in turn invoke the Java method .getMember()
If "in" operator in used in JavaScript (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in) on a JSObject instance, Nashorn will call hasMember method on that JSObject.
Example code:
import javax.script.*;
import jdk.nashorn.api.scripting.*;
public class Main {
public static void main(String[] args) throws Exception {
ScriptEngineManager m = new ScriptEngineManager();
ScriptEngine e = m.getEngineByName("nashorn");
e.put("obj", new AbstractJSObject() {
#Override
public boolean hasMember(String name) {
System.out.println("hasMember called for " + name);
return false;
}
});
// in operator triggers hasMember call on JSObject instance
e.eval("if ('foo' in obj) print('yes')");
}
}
The output from the above program looks like:
hasMember called for foo
Trying to implement JS library with my C# code.
Is very simple but I am getting this error:
EntryPointNotFoundException: Test
TalkDB.Start () (at Assets/Scripts/TalkDB.cs:30)
C# code is in scripts folder and JS library at plugins/webgl with .jslib extension.
Also read this article, but no idea what I am missing: https://docs.unity3d.com/Manual/webgl-interactingwithbrowserscripting.html?_ga=1.27144893.1658401563.1487328483
C# Code:
public class TalkDB : MonoBehaviour
{
[DllImport("__Internal")]
private static extern void Test();
void Start()
{
Test();
}
}
JS Library:
var HighscorePlugin = {
Test: function()
{
window.alert("Testing 1, 2, 3,...");
}
};
mergeInto(LibraryManager.library, HighscorePlugin);
Found the answer, is quite simple in fact.
I does not work when running locally, just when running from a server.
To prevent this error this should be done:
#if UNITY_WEBGL && !UNITY_EDITOR
[DllImport("__Internal")]
private static extern void Test();
#else
// something else to emulate what you want to do
#endif
And do this also when calling the function.
Happy programming :)
EntryPointNotFoundExceptionmeans means that the function "Test" is either (A) not marked as exportable (not visible) or (B) signature does not match C# definition.
Most likely your issue is the former (A).
I would recommend running DUMPBIN.EXE against your library to verify your "test" function is getting exported and that the its respective signature matches your C# definition. Could be some code-injection on JS-side.
There is a strange difference when I evaluate a nashorn script with or without bindings: without bindings there is no problem invoking a function, but with the bindings the function is not found. Here is an example:
public class SimpleNashornTester {
public static void main(String[] args) throws ScriptException, NoSuchMethodException {
ScriptEngine jsEngine = new ScriptEngineManager().getEngineByName("nashorn");
Compilable jsCompilable = (Compilable) jsEngine;
Invocable jsInvocable = (Invocable) jsEngine;
ScriptContext scriptCtxt = new SimpleScriptContext();
Bindings engineScope = scriptCtxt.getBindings(ScriptContext.ENGINE_SCOPE);
CompiledScript jsScript = jsCompilable.compile("function definition() {print(\"Hello\")}");
jsScript.eval(engineScope); // no error with jsScript.eval() !
jsInvocable.invokeFunction("definition", new Object[] {});
}
}
This yields the error:
Exception in thread "main" java.lang.NoSuchMethodException: No such function definition
at jdk.nashorn.api.scripting.ScriptObjectMirror.callMember(ScriptObjectMirror.java:204)
Without the parameter engineScope in the evaluation of the script the function is found and invoked. Can anybody explain this difference? How can I use bindings without getting an error?
You're using a new ScriptContext and it's associated ENGINE_SCOPE Bindings to compile the script. invokeFunction/invokeMethod use the default ScriptContext (and it's associated ENGINE_SCOPE Bindings) to search the function. Each different ENGINE_SCOPE Bindings is associated with its own ECMAScript global object (and it's own ECMAScript global objects).
So, you can fix your program by
changing the default context to be the new context before invoke:
// change the default ScriptContext
jsEngine.setContext(scriptCtxt);
jsInvocable.invokeFunction("definition", new Object[] {});
Use the default ScriptContext for compiled script as well. As in:
ScriptContext scriptCtxt = jsEngine.getContext(); // new SimpleScriptContext();