Developer API docs

Tal Liron tal.liron at threecrickets.com
Thu Oct 15 16:51:27 UTC 2015


Hey guys, nobody ever responded to my message...

Do you really think that my usage of these internal Nashorn APIs is so 
unwarranted?

I tried to prove that some useful libraries need to use Nashorn APIs 
that some of you insist should not be made public.

On 07/06/2015 09:10 PM, Tal Liron wrote:
> 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