Developer API docs

Tal Liron tal.liron at threecrickets.com
Tue Jul 7 02:10:46 UTC 2015


Hi Atilla (and Sundar and everyone else, really),

You asked which Nashorn APIs I'm using that are not documented. I will 
reply in full detail.

For the BSON/JSON codecs, the most important thing is to access the 
NativeBoolean, NativeNumber, NativeArray, ConsString, Undefined, etc., 
classes. This allows the codecs to check for these classes incoming, and 
also to easily create instances of them using their constructor() static 
method.

ScriptObject has no constructor(), so I use Global.newEmptyInstance(). 
(By the way: NativeDate and NativeArray name the method construct() 
instead of constructor(). I assume this is a consistency mistake.)

But I also need to access their data. This means get()/set() for 
NativeScriptObject and NativeArray, getOwnKeys(), getArray() (which 
means I need access to the ArrayData class), and also getTime() for 
NativeData. NativeRegExp is a bit harder, but I use get("source"), 
get("multiline"), etc.

(In Rhino, some of these classes are actually private! This required an 
awkward workaround: I do a string equality check on the classname. 
That's neither efficient nor portable, though more "dynamic", I guess. 
Also, for these classes I can use Context.newObject() to create 
instances by JS name, for example "RegExp". Then I can do 
ScriptableObject.callMethod() to access their internals. So, there are 
workarounds to not being to able to access them.)

ScriptObjectMirror is awkward. Though it's stable and documented, the 
issue with unwrap() is that it needs a global. Documented, but of course 
unclear what to do! For me, this actually means calling 
Context.getGlobal(), which is not publicly documented. (Another issue is 
that, of course, unwrap won't work for other globals. This has created 
difficulties in some threaded environments in which multiple globals are 
used intentionally. More on that below.)

So much for BSON/JSON. The Scripturian implementation of Nashorn is much 
more complex. As you may know, Scripturian is a rethinking of and 
alternative to JSR-223, so it has to do much of the same work.

Scripturian works by purposely creating different global instances for 
each "execution context" (which *can* be a thread, but doesn't have to 
be: it's an abstraction over, well, execution contexts). I use 
Context.createGlobal(), and set it via Context.setGlobal().

We then need to compile scripts in the Context, so I use 
Source.sourceFor() and Context.compileScript(), which returns a 
ScriptFunction, so I also need access to that. Compilations errors are 
via Context.getErrorManager(), so I need access to ErrorManager. To run 
the scripts, I use ScriptRuntime.apply(). A small fix I need to add is 
that Nashorn's NativeArray does not support java.util.List, so if an 
array is returned from apply(), I call NativeJava.to() to get list 
access. Thats's just a bit friendlier for users of Scripturian, who are 
otherwise agnostic about implementation specifics.

There's an important issue here: remember, there might be many different 
globals, but of course I want them all to use the same code cache, which 
is stored in the Context. So, I use one singleton Context and switch 
globals via Context.setGlobal(). To create a Context, I also need access 
to Options. A limitation in Nashorn is that I can't change stdout and 
stderr after I create the Context (Scripturian allows different onces 
per ExecutionContext), so my workaround is to use a custom Writer 
wrapper class that underneath delegates to the correct "real" Writers (I 
use the same mechanism for a few other Scripturian language engines, too).

I grumbled here before that I have no programmatic access to the code 
cache. Behind the scenes, ScriptFunction might retrieve from the cache 
instead of being compiled. I can control the cache base location via the 
"nashorn.persistent.code.cache" system property, but it's unfortunate 
that I can't control the structure and naming the way I can with other 
languages supported by Scripturian. In particular, the problem is that 
it's a global property for the whole JVM, whereas compilation and 
caching location is ideally controlled per ExecutionContext in 
Scripturian. This makes Nashorn support in Scripturian a bit more limited.

Finally, for errors during execution, I use NashornException 
(documented!) to extract specific information into Scripturian's more 
generic ExecutionException.

Small extras: I use Version.version() and 
NashornScriptEngineFactory.getLanguageVersion() to get version data.

I think that's everything! Of course, I also had to "reverse engineer" 
much of this (=read a lot of code) and work around many quirks (and big 
differences to Rhino's implementation) before streamlining it down to 
just these few classes. (I tried to work around the caching limitations, 
but gave up due to its complexity. Also, I think some of the key classes 
I would need are private.) I did my best not to delve to much into 
internals, but I hope I made it clear to you that it would have been 
impossible to achieve all the above goals without it.

-Tal

On 07/06/2015 04:18 AM, Attila Szegedi wrote:
> What APIs are you using, BTW? Just curious if I can suggest an 
> alternative approach, or even consider if something you use should be 
> publicly supported.



More information about the nashorn-dev mailing list