JDK9 features
Eric Pederson
ericacm at gmail.com
Thu May 12 17:40:53 UTC 2016
Hi Sundar:
1. I investigated loadWithNewGlobal because it looked promising for this
use case. Say you use loadWithNewGlobal to load a function. If you call
the loaded function and it returns an object or array the caller gets a
ScriptObjectMirror. The problems with a ScriptObjectMirror "object" are:
- Cannot call Object methods like keys or getOwnPropertyNames. You get
an exception: TypeError: cannot call undefined. To iterate over the
properties you must use a for in loop.
- Cannot convert ScriptObjectMirror to JSON using JSON.stringify. It
returns undefined.
If the function loaded with loadWithNewGlobal returns an array then things
are better. Interestingly you can call most Array methods (like forEach
and map) on a ScriptObjectMirror "array". But JSON.stringify also returns
undefined.
I did find one issue with an array returned by a loadWithNewGlobal loaded
function - calling sort with a comparator. For example, if the loaded
function returned testArray, a ScriptObjectMirror "array":
*var **sorted *= *testArray*.sort(*function*(a, b) {
*return *a - b;
});
throws an exception*: *TypeError: function(a, b) { return a - b; } is not a
function.
These were the same "mutant" issues that we also saw with JSObjects
returned by Java methods.
The hack that we are using now to load code without effecting the global
namespace is the same one discussed in this thread:
http://thread.gmane.org/gmane.comp.java.openjdk.nashorn.devel/1722.
The code that we are using is copied/adapted from Vertx (thanks, Tim!):
function loadEval(path) {
*var *dir = *new **File*(path).getParent();
*var *body = *readFile*(path);
*var *moduleFunc =
*"function(exports, module, require, __filename, __dirname){" *+ body +
*"**\n**}**\n**//# sourceURL=" *+ path;
*try {*
*var *func = eval(moduleFunc);
} *catch *(ex) {
*if *(ex *instanceof **SyntaxError*) {
*// WARNING! Large pile of Yak hair ahead!*
*var *ne = ex.nashornException;
*var *cause = ne.cause;
*var *msg = cause.message.replace(*"<eval>"*, file);
*var *lineNumber = cause.*lineNumber;*
console.log(*"ERROR: " *+ msg + *" in file " *+ file + *" at line
" *+ lineNumber);
}
*throw *ex;
}
*var *module = { *exports*: {} };
func(module.*exports*, module, require, path, dir);
*return *module.*exports*;
}
This seems like a hack to me - but I'm coming from the Java world so this
may be par for the course in Javascript-land :) Hack or no, it is the best
of both worlds: it does not change the global namespace, yet the code that
it returns lives in the global context, so if you call a loaded function it
will return a native object, not a ScriptObjectMirror.
We'd like to have a built-in equivalent of this loadEval function. It
doesn't need to use those specific arguments (export, module, etc), but you
must be able to pass arguments in to provide a context to the loaded code.
Alternatively if you could make ScriptObjectMirrors 100% compatible with
native objects that would work too. Then we could use loadWithNewGlobal.
2. The problem that we are having with objects returned by Java methods is
the same as what I described above because the returned JSObjects are seen
as ScriptObjectMirrors by the calling Javascript code.
What we are doing now is wrapping each Java object with a Javascript
proxy. When you call the proxy it calls the corresponding Java method,
then converts the returned ScriptObjectMirror into a native JS object using
a custom conversion function.
What would be nice for this use case is a standard function to convert
ScriptObjectMirrors to native JS objects (what I was calling asJSONCompatible
below). Or if ScriptObjectMirrors were 100% compatible that would be even
better - we could get rid of our JS proxy objects.
I'm happy to file some enhancement requests. Though it seems like the bug
trackers are read-only to the general public though, how would I get access?
Thanks,
-- Eric
On Thu, May 12, 2016 at 12:36 AM, Sundararajan Athijegannathan <
sundararajan.athijegannathan at oracle.com> wrote:
> Hi,
>
> Thanks for your comments!
>
> Making comments on forthcoming JDK releases is hard :) Whatever I'm
> saying now, may not happen - the usual disclaimer applies.
>
> No, we expect that only a subset of ES6 features will be implemented for
> Java 9.
>
> 1. On loading definitions without changing global namespace: you meant
> current global namespace? loadWithNewGlobal creates a new global and
> loads definitions into that.
>
> Would that be useful for you? or anything else? Which hack you're
> referring to? Please file an enhancement with your requirements.
>
> 2. on JSON: Again, will you please provide a simple test case and/or
> file an enhancement with requirements?
>
> Thanks,
> -Sundar
>
> On 5/12/2016 12:56 AM, Eric Pederson wrote:
> > I've been noticing the Java 9 ES6 features tweeted by @sundararajan_a
> > <https://twitter.com/sundararajan_a>. Looks awesome! Will there be
> full
> > ES6 support in Java 9?
> >
> > There are a couple of other things we would love to see in the updated
> > Nashorn:
> >
> > 1. We've been using the same hack that you recommended to Tim Fox for
> > loading functions without changing the global namespace - the Avatar/js
> > CommonJS/require hack. It would be great if this was supported natively
> in
> > Nashorn via a new loadXXX().
> >
> > 2. It would be also be great to have the inverse of asJSONCompatible for
> a)
> > JSObjects returned by Java code and b) objects from other scopes. Our
> name
> > for ScriptObjectMirrors in Javascript code is "mutant objects": they look
> > like regular JS objects but they are missing most of their DNA, and you
> > don't realize until you get an exception or silent failure somewhere down
> > the call chain where it's difficult to figure out why :)
> >
> > Anyway, the upcoming stuff looks great!
> >
> > Thanks,
> >
> > -- Eric
>
>
More information about the nashorn-dev
mailing list