Object.create() fails on function in SimpleBindings
Frantzius, Jörg
Joerg.Frantzius at aperto.com
Wed Mar 1 15:10:10 UTC 2017
Hi Sundararajan,
thanks for pointing out the missing context argument, you are absolutely right.
Back to the drawing board for me…
Regards,
Jörg
Am 01.03.2017 um 11:28 schrieb Sundararajan Athijegannathan <sundararajan.athijegannathan at oracle.com<mailto:sundararajan.athijegannathan at oracle.com>>:
Hi Jörg,
Actually you're dealing with two JS global scopes in this case as well! The first eval ("function() { Object.create(this) }") is evaluating code using the script engine default context - which has it's own ENGINE_SCOPE Bindings and it's associated JS global scope object.
In your second eval, you're using a fresh (non-default) ScriptContext - which has it's own ENGINE_SCOPE Bindings (and hence it's own associated JS global which is different from that of the engine's default ScriptContext)!
"this" in Object.create(this) call in your first eval would be the "this" object passed to function "f". You're passing an object created another context's ENGINE_SCOPE as param!
I changed your code to use the same (non-default) context for both the eval calls (to force the same JS global in Object.create calling eval and the caller eval). The modified code below works.
import javax.script.*;
import jdk.nashorn.api.scripting.*;
public class Main {
public static void main(String[] args) throws Exception {
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
SimpleScriptContext context = new SimpleScriptContext();
// It was -> context.setAttribute("f", engine.eval("(function () {Object.create(this)})"), ScriptContext.ENGINE_SCOPE);
context.setAttribute("f", engine.eval("(function () {Object.create(this)})", context), ScriptContext.ENGINE_SCOPE);
engine.eval("f.call({})", context);
}
}
Hope this explains,
-Sundar
On 01/03/17, 2:21 PM, Frantzius, Jörg wrote:
Hi Sundararajan,
I created this gist with syntax highlighting for better readability: https://gist.github.com/jfrantzius/db6474f87300b45c887cdc2e45cd2dff
testObjectCreateInFunctionFails() shows where eval() seems to wrongly assume a different global and wraps unnecessarily.
Regards,
Jörg
Am 01.03.2017 um 09:27 schrieb Frantzius, Jörg<Joerg.Frantzius at aperto.com<mailto:Joerg.Frantzius at aperto.com>>:
Hi Sundarajan,
thx a lot for the explanations, this cleared the picture much for me.
If you look at the second code snippet, though, it is not obvious where there should be any crossing of global boundaries:
SimpleScriptContext context = new SimpleScriptContext();
context.setAttribute("f", engine.eval("(function () {Object.create(this)})"), ScriptContext.ENGINE_SCOPE);
engine.eval("f.call({})", context);
Here the function object is put into a ScriptContext that is then also passed to eval() for invoking it, so there is no second ScriptContext involved. This is in contrast to the first code snippet, where there is both a SimpleBindings object and the engine’s default ScriptContext.
By the way, if the last line passes „f" as „this“ instead of a new object „{}":
engine.eval("f.call(f)", context);
Then the test succeeds. So the new object „{}“ seems to be associated with a different global than that of the given ScriptContext, or at least Nashorn thinks so?
Does that mean eval() keeps or creates a global also when it is given a ScriptContext or Bindings argument? Or is there maybe some wrapping going on here that shouldn’t happen?
Regards,
Jörg
Am 01.03.2017 um 06:39 schrieb Sundararajan Athijegannathan<sundararajan.athijegannathan at oracle.com<mailto:sundararajan.athijegannathan at oracle.com><mailto:sundararajan.athijegannathan at oracle.com>>:
Hi,
In Nashorn, each ENGINE_SCOPE Bindings instance is associated with it's own Nashorn global instance (an instance of jdk.nashorn.internal.objects.Global class). i.e., each ENGINE_SCOPE Bindings instance is associated with a fresh ECMAScript/JS global scope object - with it's own "Object", "Function", "RegExp" etc.
Nashorn represents script objects crossing JS global boundary as ScriptObjectMirror instances. ScriptObjectMirror is also the way Java code can access any script object (without having to deal with internal jdk.nashorn.internal.runtime.ScriptObject). See also: https://docs.oracle.com/javase/8/docs/jdk/api/nashorn/jdk/nashorn/api/scripting/ScriptObjectMirror.html
If you access a script object from a JS global scope g1 from another JS global scope g2, you'll get a ScriptObjectMirror wrap. Nashorn attempts to provide seamless integration of ScriptObjectMirror instances - you can treat ScriptObjectMirrors almost like those are script objects. But this integration is not complete. Please see also: https://wiki.openjdk.java.net/display/Nashorn/Nashorn+jsr223+engine+notes
Not every JS API can work with ScriptObjectMirrors (like these APIs work with script objects that belong to the "current" JS global scope). Object.create is one such API. You can adjust your code slightly. For example:
import javax.script.*;
import jdk.nashorn.api.scripting.*;
public class Main {
public static void main(String[] args) throws Exception {
ScriptEngine e = new ScriptEngineManager().getEngineByName("nashorn");
e.put("foo", e.eval("function() { return {} }", new SimpleBindings()));
// get "foo"
ScritptObjectMirror foo = (ScriptObjectMirror)e.get("foo");
// eval Object.create in the global where "foo" belongs - with "foo" set as "this"
System.out.println(foo.eval("Object.create(this)"));
}
}
Hope this helps,
-Sundar
On 01/03/17, 2:35 AM, Frantzius, Jörg wrote:
Hi,
in my code I’m running into an issue for which https://github.com/coveo/nashorn-commonjs-modules/issues/3 luckily provides a snippet for reproducing:
@Test
public void testObjectCreateOnFunction() throws ScriptException {
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
engine.put("foo", engine.eval("function() { return {}; }", new SimpleBindings()));
engine.eval("Object.create(foo());");
}
This fails with:
Caused by:<eval>:1 TypeError: [object Object] is not an Object
at jdk.nashorn.internal.runtime.ECMAErrors.error(ECMAErrors.java:57)
at jdk.nashorn.internal.runtime.ECMAErrors.typeError(ECMAErrors.java:213)
at jdk.nashorn.internal.runtime.ECMAErrors.typeError(ECMAErrors.java:185)
at jdk.nashorn.internal.runtime.ECMAErrors.typeError(ECMAErrors.java:172)
at jdk.nashorn.internal.objects.Global.checkObject(Global.java:2073)
at jdk.nashorn.internal.objects.NativeObject.create(NativeObject.java:261)
at jdk.nashorn.internal.scripts.Script$21$\^eval\_.:program(<eval>:1)
at jdk.nashorn.internal.runtime.ScriptFunctionData.invoke(ScriptFunctionData.java:623)
at jdk.nashorn.internal.runtime.ScriptFunction.invoke(ScriptFunction.java:494)
at jdk.nashorn.internal.runtime.ScriptRuntime.apply(ScriptRuntime.java:393)
at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:446)
... 31 more
In my own code, the pattern is slightly different, but it fails similarly (SimpleScriptContext internally uses SimpleBindings as well):
@Test
public void testObjectCreateInFunction() throws ScriptException {
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
SimpleScriptContext context = new SimpleScriptContext();
context.setAttribute("f", engine.eval("(function () {Object.create(this)})"), ScriptContext.ENGINE_SCOPE);
engine.eval("f.call({})", context);
}
The issue seems to be that the function object internally isn’t turned from ScriptObjectMirror into ScriptObject, so Global.checkObject() fails.
I’d be thankful for any hints on whether this may be a bug or intended behaviour.
Regards,
Jörg
---
Dipl. Inf. Jörg von Frantzius, Technical Director
E-Mail joerg.frantzius at aperto.com<mailto:joerg.frantzius at aperto.com><mailto:joerg.frantzius at aperto.com>
Phone +49 30 283921-318
Fax +49 30 283921-29
Aperto GmbH – An IBM Company
Chausseestraße 5, D-10115 Berlin
http://www.aperto.com<http://www.aperto.de/>
http://www.facebook.com/aperto
https://www.xing.com/companies/apertoag
HRB 77049 B, AG Berlin Charlottenburg
Geschäftsführer: Dirk Buddensiek, Kai Großmann, Stephan Haagen, Daniel Simon
---
Dipl. Inf. Jörg von Frantzius, Technical Director
E-Mail joerg.frantzius at aperto.com
Phone +49 30 283921-318
Fax +49 30 283921-29
Aperto GmbH – An IBM Company
Chausseestraße 5, D-10115 Berlin
http://www.aperto.com<http://www.aperto.de/>
http://www.facebook.com/aperto
https://www.xing.com/companies/apertoag
HRB 77049 B, AG Berlin Charlottenburg
Geschäftsführer: Dirk Buddensiek, Kai Großmann, Stephan Haagen, Daniel Simon
---
Dipl. Inf. Jörg von Frantzius, Technical Director
E-Mail joerg.frantzius at aperto.com<mailto:joerg.frantzius at aperto.com>
Phone +49 30 283921-318
Fax +49 30 283921-29
Aperto GmbH – An IBM Company
Chausseestraße 5, D-10115 Berlin
http://www.aperto.com
http://www.facebook.com/aperto
https://www.xing.com/companies/apertoag
HRB 77049 B, AG Berlin Charlottenburg
Geschäftsführer: Dirk Buddensiek, Kai Großmann, Stephan Haagen, Daniel Simon
---
Dipl. Inf. Jörg von Frantzius, Technical Director
E-Mail joerg.frantzius at aperto.com
Phone +49 30 283921-318
Fax +49 30 283921-29
Aperto GmbH – An IBM Company
Chausseestraße 5, D-10115 Berlin
http://www.aperto.com<http://www.aperto.de/>
http://www.facebook.com/aperto
https://www.xing.com/companies/apertoag
HRB 77049 B, AG Berlin Charlottenburg
Geschäftsführer: Dirk Buddensiek, Kai Großmann, Stephan Haagen, Daniel Simon
More information about the nashorn-dev
mailing list