Just responding quickly to let you know we will look into this but the 5 of us are all away till Monday with JavaOne and much needed breaks.
Cheers -- Jim Sent from Jim's iPhone > On Oct 3, 2014, at 9:20 AM, David P. Caldwell <[email protected]> > wrote: > > P.S. on re-reading it occurs to me that it's possible I am crossing > Nashorn-internal code that calls Object.freeze on the Error property > of the global object, or something like that -- this is also > consistent with my symptom of not being able to add properties to my > replacement Error. > > On Fri, Oct 3, 2014 at 12:15 PM, David P. Caldwell > <[email protected]> wrote: >> So I have a big library of Rhino-compatible (and browser-compatible) >> code and have recently been working through the processes (debugger, >> profiler) and code changes necessary to get it working with Nashorn. >> >> A bit of background: the project probably differs from the typical >> early Nashorn use case in that it heavily manipulates scopes and >> targeting (i.e., 'this') in order to achieve its modularization goals. >> Much of this is abstracted into a "loader" layer whose innermost >> Nashorn invocation is the following code: >> >> this.compile = function(name,code,scope,target) { >> var compiled = >> Context.getContext().compileScript(toSource(name,code),scope); >> return >> Packages.jdk.nashorn.internal.runtime.ScriptRuntime.apply(compiled,target); >> }; >> >> Yes, internal interfaces, I've been warned, moving on ... :) >> >> The name "compile" is deceptive; it's actually the name of the >> strategy. (I had tried several, and they are still in the code, and >> selected at runtime via an environment variable. "compile" is the >> default. Feel free to comment.) The function would, if it were >> standalone, probably be called something like "run" or "load." >> toSource() calls the Source constructor or factory method, depending >> which is present (looks like that changed in early 1.8.0 JDKs). >> >> Here's the full source file: >> https://bitbucket.org/davidpcaldwell/slime/src/d04bd814156d4e4cc520492e65876cf1235a55e8/loader/rhino/nashorn.js >> >> For a while, this barely worked with Nashorn (requiring workarounds) >> because somehow with complicated scope chains Nashorn would get >> confused and variables from outer scopes would be missing inside inner >> scopes, particularly when Nashorn had been re-entered (i.e., the >> apply() call from which the inner scope was being executed was >> different from the apply() call in which the outer scope was >> executed). >> >> I vaguely wrote to the list about this but I was never able to >> reproduce the problem in a way that I could present or was small, and >> then it was somehow fixed by the u20 release of JDK 1.8, and suddenly >> everything worked. >> >> Except objects that have Error in the prototype chain. They still have >> strange behavior, and I've been poking at it for some time, and have a >> few guesses about it. >> >> The good news is, I have an easy way to reproduce it (instructions at >> end of this message). The bad news is, it involves my whole JavaScript >> shell because when I tried a minimal wrapper everything worked. The >> good news is, I'm confident it's not a bug in my code, because: >> >> 1. The loader layer abstracts the differences between the four major >> browsers, Rhino, and Nashorn, and everything except errors works the >> same in all six, and errors work the same in the first five. >> >> 2. When I replace the Error global object with my own, the code works. >> So it's something having to do with "your" Error object, or (my guess) >> some conditional code you have somewhere inside Nashorn that checks >> for it. >> >> My guess is that there is some optimization somewhere that walks the >> prototype chain and checks for Error. I think this because I tried a >> number of strategies for working around this: >> >> * Using Object.create >> * Using an intermediary function ErrorType that had new Error() as its >> prototype, and using new ErrorType() as the prototype for my function >> >> ... and they all had the same result if Error was anywhere in the >> prototype chain of my error. But using a different prototype >> (basically, writing my own Error) worked, also in several different >> configurations. >> >> Here's the code that fails (ignore the loader scaffolding): >> >> $exports.Error.Type = function(name) { >> var rv = function(message,properties) { >> if (this instanceof arguments.callee) { >> this.name = name; >> this.message = message; >> for (var x in properties) { >> this[x] = properties[x]; >> } >> } else { >> return new arguments.callee(message); >> } >> }; >> rv.prototype = new Error(); >> return rv; >> }; >> >> Here's some test code that exercises it: >> >> var Unimplemented = jsh.js.Error.Type("Unimplemented"); >> >> try { >> throw new Unimplemented("Not implemented."); >> } catch (e) { >> jsh.shell.echo("toString: " + e.toString()); >> jsh.shell.echo("Error?: " + Boolean(e instanceof Error)); >> jsh.shell.echo("Unimplemented?: " + Boolean(e instanceof Unimplemented)); >> jsh.shell.echo("name: " + e.name); >> jsh.shell.echo("message: " + e.message); >> jsh.shell.echo("stack: " + e.stack); >> } >> >> Here's the output if I use my own global Error object. That object is >> implemented at the top of nashorn.js (same file as above): >> https://bitbucket.org/davidpcaldwell/slime/src/d04bd814156d4e4cc520492e65876cf1235a55e8/loader/rhino/nashorn.js >> >> toString: Unimplemented: Not implemented. >> Error?: true >> Unimplemented?: true >> name: Unimplemented >> message: Not implemented. >> stack: at rhino/nashorn.js:35 >> at >> /Users/dcaldwell/tmp/nashorn.error/jsh/src/jsh/test/manual/errortype.jsh.js:11 >> at rhino/nashorn.js:131 >> at rhino/nashorn.js:173 >> at rhino/nashorn.js:203 >> at rhino/literal.js:65 >> at rhino/literal.js#36:9<eval>@6:132 >> at rhino/literal.js#36:9<eval>@6:217 >> at >> /Users/dcaldwell/tmp/nashorn.error/jsh/script/jsh/jsh.js#111:14<eval>@10:91 >> at /Users/dcaldwell/tmp/nashorn.error/jsh/script/jsh/jsh.js:252 >> >> That is what is intended. The output is roughly the same as that for >> all four major browsers and for Rhino. >> >> Here's the output if I don't replace the global Error object: >> >> toString: Error >> Error?: true >> Unimplemented?: false >> name: Error >> message: >> stack: >> >> So that's where I am at the moment. Basically, it looks like I'm >> getting the error subtype's prototype as my error, no matter what I >> do. >> >> A few comments: >> >> * My workaround *mostly* works. The main problems are, a.) I need to >> enumerate all my own error subtypes like TypeError or else TypeError >> objects are not instanceof Error. On the other hand, if I do that, b.) >> TypeErrors (presumably other errors, also) thrown from Nashorn itself >> are not instanceof TypeError or Error. So it's a little messy. >> >> * I haven't looked at the Nashorn source code surrounding any of this; >> I've been debugging it as a black box. (Obviously the fact that I am >> using ScriptRuntime, Context and Source means I've been in there a >> little bit, though. I have not built it myself.) >> >> * This may be a red herring, but developing with NetBeans, if I step >> through the $exports.Error.Type code (using a patched copy; NetBeans >> Nashorn support also isn't good enough yet to run my shell, though it >> becomes minimally usable with a little bit of patching; see >> https://netbeans.org/bugzilla/show_bug.cgi?id=247441), it is *very* >> confused. Inside the error subtype constructor (called "rv" above), >> the debugger (but not the code!) thinks that the global object is >> "this" (even when executing via new). But when the code executes, it >> does *not* treat the global object as "this." So don't know what the >> problem is here, but the whole thing smells funny. >> >> * My test environment is JDK 1.8.0u20. OS X Mavericks, though I can't >> imagine that matters. >> >> * Possibly unrelated, but another strange hard-coding symptom I seemed >> to have with Error is that if I assign properties to *my* replacement >> Error constructor -- not to Error objects, but to the constructor (I >> tried to use Error.nashorn to act as a conditional flag) -- those >> properties are invisible; not enumerable or accessible. But I haven't >> investigated this as thoroughly and it might be something dumb I'm >> doing; just reporting in case it rings a bell. Possibly the Error >> property of the global scope is used *by name* somewhere and handled >> specially, because that one symptom still seemed to impact my Error >> object even when I replaced yours. >> >> Anyway, I know this was long but I wanted to do my due diligence and >> communicate all of it. Here's how to reproduce, starting in an empty >> directory: >> >> 1. Build the shell to a subdirectory called "jsh": >> jrunscript -e >> "load('https://bitbucket.org/davidpcaldwell/slime/raw/d04bd81/jsh/etc/jrunscript.js?relative=./install.jrunscript.js')" >> jsh >> >> 2. To run the test case with my workaround enabled: >> java -jar jsh/jsh.jar jsh/src/jsh/test/manual/errortype.jsh.js >> >> 3. To run the test case with my workaround disabled, exposing the >> error (on a UNIX-like system; translate for Windows as necessary): >> env DISABLE_NASHORN_ERROR_HACK=true java -jar jsh/jsh.jar >> jsh/src/jsh/test/manual/errortype.jsh.js >> >> Hopefully it's something simple. :) >> >> -- David P. Caldwell >> http://www.davidpcaldwell.com/
