How To Initialize Nashorn Context and engine directly? - javascript

I am trying to initialize Oracle's javascript nashorn engine directy from jdk.nashorn.* namespace.
(nashorn library is a beta version of 2013 Jan).
There is a web sample which calles Nashorn engine instance of engine, using javax.script.ScriptEngineManager utility class.
var engine = ScriptEngineManager.getEngineByName(*)
However, I like to keep away from ScriptEngineManager, so i need to call engine directly in the same way Rhino can.
Context cx = Context.enter();
Scriptable scope = cx.initStandardObjects();
How can I create nashorn engine instance directly?

javax script engine by type application/javascript Hashorn, get back a script engine and tell it to do stuff, it also provides invokable and compilable interfaces.
Yout might be interested to read this : How can I start coding with Oracle's Nashorn JS Engine and when will it replace Rhino in the OpenJDK?
Example usage:
import javax.*; //lib imports
// we should use the javax.script API for Nahsorn
ScriptEngineManager m = new ScripteEngineManager();
ScriptEngine e = m.getEngineByname("nashorn");
try {
e.eval("print('hello nashorn !')");
} catch(Exception e) {
// using jdk lower then version 8 maybe ?
}

I found the way to init engine directly using .NET without "
"javax.script.ScriptEngineManager"
Environment:
IKVM.NET Version 8 + .NET Framework 4.52
static void Main(string[] args)
{
jdk.nashorn.api.scripting.NashornScriptEngineFactory fact = new jdk.nashorn.api.scripting.NashornScriptEngineFactory();
jdk.nashorn.api.scripting.NashornScriptEngine nashornengine = fact.getScriptEngine() as jdk.nashorn.api.scripting.NashornScriptEngine;
nashornengine.eval("var x = 1/3;");
object result = nashornengine.get("x");
Console.WriteLine("{0}", result);
}
This allows me directly interact with nashorn context methods mot more directly.
compile()
getFactory()
invokeMethod()
invokeFunction()

Related

ExceptionInInitializerError when adding bindings to engine scope

Upgrading from nashorn to graalvm with openjdk 17. getting a PolyglotException: java.lang.ExceptionInInitializerError when adding bindings to engine scope. my script engine is getting initialized in init method & later used in different class. Wondering if its multi threading issue. unable to figure out the root cause
script engine gets initialized in init block of class 1 & 2.
Class 1
public void init() {
createScriptEngine
}
Class 2
public static ScriptEngine createScriptEngine (){
ScriptEngineManager manager = new ScriptEngineManager();
ret = manager.getEngineByName(engineName);
if(ret instanceof GraalJSScriptEngine) {
Bindings bindings = ret.getBindings(ScriptContext.ENGINE_SCOPE);
bindings.put("polyglot.js.nashorn-compat", true);
}
}
The engine itself is used in class 3 to evaluate script
class 3
private object evaluateScript(ScriptEngine scriptEngine, String script){
Bindings bindings = scriptEngine.getContext().getBindings(ScriptContext.ENGINE_SCOPE);
bindings.put(SCRIPT_VAR_t1, this);
bindings.put(SCRIPT_VAR_t2, cTag);
bindings.put(SCRIPT_VAR_t3, tAttribs
Object ret = scriptEngine.eval(script, scriptEngine.getContext());
}
The issue is resolved now after creating context only once.

Can V8 perform build-time precompilation of JS code?

We're trying to optimize for start-up time of JS code on mobile and looking for the opportunities. I've found Facebook Hermes JS engine created for a similar purpose but we heavily depend on V8 at the moment.
Can build-time precompilation be done with V8 meaning parsing and code optimization will be done in compile-time and saving some time in runtime? Generating LLVM bitcode from the source code and executing bitcode in runtime seems to be pretty close to what i imagine. WASM seems to be not an option (at least for mobile).
If it's possible, can one provide a simple example of trivial JS code optimized with V8?
PS. Probably it would also help with memory consumption which can be the secondary goal.
V8 supports heap snapshots for this very purpose. It's used by the Atom editor, for instance, to improve startup time. It's not so much about precompiling as it is about prebuilding your global environment and instantiating your functions (which may not be compiled [yet], just converted to bytecode for Ignition, which is sufficient). If you're using Electron, the mksnapshot npm package may be useful. (And if not, looking at how it works may still be useful.)
I haven't done any V8 hacking, but the example they link from the blog post above is as follows:
TEST(PerIsolateSnapshotBlobs) {
DisableTurbofan();
const char* source1 = "function f() { return 42; }";
const char* source2 =
"function f() { return g() * 2; }"
"function g() { return 43; }"
"/./.test('a')";
v8::StartupData data1 = v8::V8::CreateSnapshotDataBlob(source1);
v8::StartupData data2 = v8::V8::CreateSnapshotDataBlob(source2);
v8::Isolate::CreateParams params1;
params1.snapshot_blob = &data1;
params1.array_buffer_allocator = CcTest::array_buffer_allocator();
v8::Isolate* isolate1 = v8::Isolate::New(params1);
{
v8::Isolate::Scope i_scope(isolate1);
v8::HandleScope h_scope(isolate1);
v8::Local<v8::Context> context = v8::Context::New(isolate1);
delete[] data1.data; // We can dispose of the snapshot blob now.
v8::Context::Scope c_scope(context);
CHECK_EQ(42, CompileRun("f()")->ToInt32(isolate1)->Int32Value());
CHECK(CompileRun("this.g")->IsUndefined());
}
isolate1->Dispose();
v8::Isolate::CreateParams params2;
params2.snapshot_blob = &data2;
params2.array_buffer_allocator = CcTest::array_buffer_allocator();
v8::Isolate* isolate2 = v8::Isolate::New(params2);
{
v8::Isolate::Scope i_scope(isolate2);
v8::HandleScope h_scope(isolate2);
v8::Local<v8::Context> context = v8::Context::New(isolate2);
delete[] data2.data; // We can dispose of the snapshot blob now.
v8::Context::Scope c_scope(context);
CHECK_EQ(86, CompileRun("f()")->ToInt32(isolate2)->Int32Value());
CHECK_EQ(43, CompileRun("g()")->ToInt32(isolate2)->Int32Value());
}
isolate2->Dispose();
}
That blog post (and the associated example?) is from 2015 so things have probably moved on since.

How can I determine which javascript engine, rhino or nashorn is running my code?

There are several questions how to determine the javascript engine in browser.
I have to write javascript code that has to run on rhino and nashorn.
How can I determine if my code is running on rhino or nashorn? Are there typcial functions, variables, constants where you can determine the engine?
Looking at the Rhino to Nashorn migration guide, I see several possible ways.
If you're not using the Rhino compatibility script, this would do it:
var usingNashorn = typeof importClass !== "function";
...since importClass is defined for Rhino but not for Nashorn (unless you include the compatibility script).
I think Java.type is Nashorn-specific, so:
var usingNashorn = typeof Java !== "undefined" && Java && typeof Java.type === "function";
You could check for wrapping of exceptions:
var usingNashorn;
try {
// Anything that will throw an NPE from the Java layer
java.lang.System.loadLibrary(null);
} catch (e) {
// false!
usingNashorn = e instanceof java.lang.NullPointerException;
}
...since the migration guide says that will be true for Nashorn but false for Rhino. It does involve throwing an exception, which is unfortunate.
With --no-java option, "Java" is not defined as object in Nashorn. Best would be to check something that is available always in Nashorn. Something like DIR or FILE variable is a good candidate. Always there in nashorn.
jjs> typeof DIR
string
If you are using javax.script API (and not jjs), you can get the engine name and check as well:
import javax.script.*;
public class Main {
public static void main(String[] args) throws Exception {
ScriptEngineManager m = new ScriptEngineManager();
ScriptEngine e = m.getEngineByName("nashorn");
System.out.println(e.getFactory().getEngineName());
}
}
With Nashorn, you'd see "Oracle Nashorn" as the engine name.
I think the most robust and straightforward way to do this is to rely on the fact that both Rhino and Nashorn create per-thread execution contexts to do their work.
Nashorn's can be a bit more complicated to access because of the module (JPMS) changes introduced in Java 9, which makes it more difficult for scripts to access some of the classes involved.
Here's some code I use for this, in the SLIME codebase (which similarly supports both engines, and JDK 8/11 as of this writing). In each case, the engine provides a running method that returns the engine-specific context if we are running inside that engine (so for simple detection, it could be wrapped inside a call to Boolean or whatever). It is tested but as it's copy-paste out of context, it contains a bit of extraneous information, apologies for that:
Rhino (in this implementation you must call isPresent() and ensure it returns true before calling running(), although you could improve that):
var rhino = (
function(global) {
return {
isPresent: function() {
return typeof(global.Packages.org.mozilla.javascript.Context.getCurrentContext) == "function";
},
running: function() {
return global.Packages.org.mozilla.javascript.Context.getCurrentContext();
}
}
}
)(this);
Nashorn (relies on Packages, provided by the Mozilla compatibility layer, mostly to improve static type-checking, but could easily be rewritten to use the Java equivalents for Nashorn):
var nashorn = (
function() {
// TODO A little bit of this logic is duplicated in loader/jrunscript/nashorn.js; we could make this method
// available there somehow, perhaps, although it might be tricky getting things organized between
// bootstrapping Nashorn in the loader and loading the launcher bootstrap script
var Context = Packages.jdk.nashorn.internal.runtime.Context;
var $getContext;
if (typeof(Context) == "function") {
try {
// TODO is there any way to avoid repeating the class name?
$getContext = Packages.java.lang.Class.forName("jdk.nashorn.internal.runtime.Context").getMethod("getContext");
} catch (e) {
// do nothing; $getContext will remain undefined
}
}
var isPresent = function() {
if (!new Packages.javax.script.ScriptEngineManager().getEngineByName("nashorn")) {
$api.debug("Nashorn not detected via javax.script; removing.");
return false;
}
if (typeof(Context.getContext) != "function" && !$getContext) {
$api.debug("jdk.nashorn.internal.runtime.Context.getContext not accessible; removing Nashorn.")
return false;
}
return true;
}
return {
isPresent: isPresent,
running: function() {
if ($getContext) {
try {
return $getContext.invoke(null);
} catch (e) {
return null;
}
} else {
return null;
}
}
}
}
)();
Sure, the Nashorn implementation uses non-public APIs. But I still think this is better, and more straightforward, than relying on what are essentially side effects (existence of various cherry-picked APIs that one or the other engine might provide). These techniques are potentially breakable if either engine seeks to improve compatibility with the other.

Including a JavaScript file during Rhino eval

How would one include a script "Bar" from within another script "Foo" that is being evaluated by the Rhino Engine, running in Java.
IE, setup the script engine like this in Java:
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("javascript");
BufferedReader br = new BufferedReader(new FileReader(new File("Foo.js")));
engine.eval(br);
... and put the following in Foo:
load(["Bar.js"])
According to Rhino shell documentation, that's the way to do it. But when running from Java, it's clearly not implemented:
javax.script.ScriptException: sun.org.mozilla.javascript.internal.EcmaError: ReferenceError: "load" is not defined.
#Phillip:
Thanks for the creative response. Your solution as is doesn't quite work because eval doesn't make the parsed function available unless the Invocable interface is used. Further, it doesn't work in cases where the Java code is not accessible. I elaborated on your workaround and created something that will work in pure JS:
First create an engine, exposing the Invocable interface:
var engine = new Packages.javax.script.ScriptEngineManager().getEngineByName("javascript");
engine = Packages.javax.script.Invocable(engine);
Then "load()" the script(s)
engine.eval(new java.io.FileReader('./function.js'));
At this point, functions can be evaluated with:
engine.invokeFunction("printHello", null);
I managed to do it by passing the engine to itself and then calling eval() on it inside the Javascript code:
Java code:
ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");
engine.put("engine", engine);
engine.eval(new FileReader("test.js"));
test.js:
engine.eval(new java.io.FileReader('function.js'));
printHello();
function.js:
function printHello() {
print('Hello World!');
}
output:
Hello World!
I don't know if that's the most elegant way to do it, but it works.

Callbacks in JSR223 Javascript, difference between Oracle JRE 1.6 and OpenJDK 1.6 (as installed on, say, Debian)

Given the following, running with Oracle JRE 6 gives the output boo, but OpenJDK 6 gives an exception
javax.script.ScriptException: sun.org.mozilla.javascript.EvaluatorException: The choice of Java
constructor replace matching JavaScript argument types (function,string) is ambiguous; candidate
constructors are:
class java.lang.String replace(char,char)
class java.lang.String replace(java.lang.CharSequence,java.lang.CharSequence) (<Unknown source>#1)
in <Unknown source> at line number 1
That's presumably because with OpenJDK (presumably the rt.jar supplied with it) the function's getting a java.lang.String, but with Oracle's it's getting a JavaScript String (or something that can be implicitly coerced to one).
So which is more correct? The Javascript (in this case) is the API, so can we write the Java such that the API's the same for either implementation? (If the OpenJDK implementation is "more correct" (and so likely to be what everyone does in the future), then I guess changing the API (documentation, examples, tests) throwing in new String(...) as appropriate wouldn't be impossible, but I'd rather not uglify the API unless I'm more confident.)
import javax.script.*;
class st {
public static void main(String[] args) {
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine jsEngine = mgr.getEngineByName("JavaScript");
Bindings bindings = jsEngine.getBindings(ScriptContext.ENGINE_SCOPE);
Foo foo = new Foo();
bindings.put("v", foo);
try {
jsEngine.eval("v.run(function(a) {println(a.replace(/f/,\"b\"));})");
} catch (ScriptException ex) {
ex.printStackTrace();
}
}
}
and
public class Foo {
public void run(FooCB cb) {
cb.run("foo");
}
public static interface FooCB {
public void run(Object val);
}
}
The Java SE 6 spec (JSR 270) merely says:
There will be no requirement that any
particular scripting language be
supported by the platform;
implementors may choose to include
support for the scripting language(s)
of their choice as they see fit.
To the best of my knowledge, there is no formal spec for how to integrate Java types into JavaScript. It's unfortunate, but there's no reason to expect 100% compatibility across implementations.
I believe both the Oracle JRE and OpenJDK ship with Rhino, but there's no guarantee about version level, patches, etc.

Categories