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