JDK9 features

Eric Pederson ericacm at gmail.com
Fri May 13 14:46:21 UTC 2016


Hi Sundar -

It would be helpful for sure.  We use JSON for serialization and debugging
a lot.  Also if it worked it could simplify our custom ScriptObjectMirror
-> native object conversion logic from 30 lines down to
JSON.parse(JSON.stringify(scriptObjMirror)) :).

Besides the JSON stuff our biggest problem has been ScriptObjectMirrors
being treated like regular objects and passed down call chains and then
either throwing exceptions or silently failing and it's hard to figure out
why.  Junior developers get completely baffled.  We have solved the problem
with the commonjs/require loading pattern on the script loading side and
the proxy pattern on the Java calling side.  It would be nice to have
official solutions for these but it's not urgent.  It might be something
worth adding to the documentation.

Thanks,



-- Eric

On Fri, May 13, 2016 at 12:30 AM, Sundararajan Athijegannathan <
sundararajan.athijegannathan at oracle.com> wrote:

> Hi Eric,
>
> That commonjs/require loading pattern is not bad [wrapping eval'ed code
> inside a function]. If your loaded scripts are (somewhat) well-behaved, it
> does solve the problem of global namespace pollution.
>
> Yes, ScriptObjectMirrors are not uniformly treated like ScriptObject
> everywhere.  But, I'm not sure how far we can fix that :( Is JSON handling
> (w.r.t ScriptObjectMirror) your main pain point? We *may* be able to
> address that.
>
> Thanks,
>
> -Sundar
>
> On 5/12/2016 11:10 PM, Eric Pederson wrote:
>
> 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>
> 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>
> 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