Thank you for your reply, Sundar.
Unfortunately, your suggestions are insufficient.
In the BSON translator, I do not have access to the script engine at
all. It might not even be created: after all, the BSON translator can be
invoked by Nashorn directly, not in JSR-223 mode.
Likewise, you have not addressed much more of what I write about: access
to Script, Source, Context, etc.
I understand that you do not want to have to support internal APIs. I
merely wanted to prove to you that there is a cost in doing so: many
powerful things can only be achieved by access to those APIs. This means
that various 3rd party products might break with newer versions of Nashorn.
This is not the end of the world: many libraries, of course, break
dependent products, even in regards to public APIs.
Still, I hope you can read my original email in depth and think more
about what I'm trying to do. Perhaps you will agree that some of those
APIs could benefit from being public, or suggest other solutions for me.
On my end, I will continue to maintain my code as is, even if it uses
internal APIs, because it allows me to use Nashorn for some very cool
projects.
-Tal
On 10/15/2015 09:56 PM, Sundararajan Athijegannathan wrote:
Hi Tal Liron,
Sorry about missing reply for this email. I somehow remember replying
similar nashorn internal usage email.
It is difficult maintain all of jdk.nashorn.internal.* between
versions. Also, with security manager around, jdk.nashorn.internal.*
are not accessible without explicit 'package access'
RuntimePermission. In addition, with jdk9, nashorn module exports only
jdk.nashorn.api.* packages. i.e., even without any security manager,
user code won't be able to access nashorn internal implementation
classes. It is possible to use jdk.nashorn.api.scripting.JSObject to
get constructors and create objecs.
JSObject numberConstructor = (JSObject) engine.eval("Number");
JSObject numberObj = numberConstructor.newObject();
Is it not possible to work with these? Please check out latest 8u-dev
code. Mostly explicit wrap/unwrap won't be required from user's code.
Thanks,
-Sundar
On 10/15/2015 10:21 PM, Tal Liron wrote:
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.