Using === across different contexts

Vivin Suresh Paliath vivin.paliath at gmail.com
Tue Dec 22 06:55:06 UTC 2015


I am looking at it from the perspective of someone writing code that is
expect to run in  JavaScript environment. I will describe the runtime
environment I have set up. Perhaps what I am running into is an artifact of
my approach.

I have a single script engine instance. In this context I evaluate some JS
source that populates the JS global scope with some objects. When a custom
script needs to be evaluated, I create a new script context with a new
global. Then I copy over all bindings from the parent script context into
the new one. I also have a module system. When a custom script requests a
core module, the module source is evaluated in the main script context and
a single instance of the object exposed by the module source is cached.
This way the source only needs to be evaluated once and benefits from
optimizations.

The problem I am running into has to do with some singleton objects I
expose; kind of like enums, and can be compared using ===. I am using a JS
library for this and this behavior holds in the browser and on NodeJS. On
Nashorn, because the instances were created in a different context, ===
returns false.

Is there a better way to accomplish what I am doing?

Regarding host objects, even if they are different from native objects, it
seems strange to me that the semantics of things like === would be
different especially when doing something like obj.prop === obj.prop. The
fact that such a statement could ever return false is non-obvious unless
implementation details of the runtime are known.

I am trying to expose my runtime environment as a JS environment where a
developer can write custom scripts using some runtime libraries and
utilities. So behavior like this would be very surprising, and requiring
them to know that objects might be from different contexts seems like
abstraction leakage from the way I have implemented the runtime. They
shouldn't have to know whether an object is a wrapped ScriptObjectMirror
instance, since from their point of view they are just working with JS
objects, and they don't know that they are from different "worlds" since
they don't even know that there are different worlds.

I guess I am also having a hard time seeing a scenario where === would need
to return false in the scenario I described, unless as a developer you knew
there were different worlds. Even then, it would essentially make some
forms of equality comparisons impossible (e.g., o.prop === o.prop if o is
foreign).

Does the snippet I posted earlier make sense?
On Dec 21, 2015 9:10 PM, "Sundararajan Athijegannathan" <
sundararajan.athijegannathan at oracle.com> wrote:

> Actually, I'm not sure if depending on === in the code is a good approach
> -- particularly, for objects  that are not script objects of the current
> world. These are to be treated like "host objects" in ECMAScript-speak.
> i.e., regular rules of script objects don't always apply to 'host
> objects'.  The === operator for Java objects is interpreted as object
> identity -- and the same rule for ScriptObjectMirrors -- in that both are
> "host objects" from the standpoint of the 'current world'.
>
> -Sundar
>
> On 12/22/2015 9:34 AM, Vivin Suresh Paliath wrote:
>
> Thanks for the response! I understand that in general it would be
> difficult for foreign objects. But this seems like surprising behavior
> given that these are both JavaScript objects. That they are unequal seems
> to be an artifact of the implementation (the fact that JS objects from
> different contexts are treated as foreign). Could JS objects be treated
> differently?
>
> After going through the Nashorn source, I decided to try this very naive
> approach adding the following test to ScriptRuntime#equalSameTypeValues:
>
> if(x instanceof ScriptObjectMirror && y instanceof ScriptObjectMirror) {
>     return x.equals(y);
> }
>
> After this change, the code now returns true, because
> ScriptObjectMirror#equals compares the actual objects. I am not sure if
> this breaks anything though (been trying to run the test suite, but end up
> getting some errors from make). Is there a reason this particular fix is a
> bad idea? I can't think of a particular reason why. From the perspective of
> the runtime, I can't see a reason why those two objects should be
> considered different.
>
> I will investigate the eval approach, but ideally I would like something
> that doesn't impose any changes on the JS code. I am developing a simple
> runtime that exposes some objects and utilities, and where custom scripts
> can run in their own contexts. The fact that === returns false in these
> cases leads to some very strange behavior.
>
> On Mon, Dec 21, 2015 at 8:48 PM, Sundararajan Athijegannathan <
> sundararajan.athijegannathan at oracle.com> wrote:
>
>> Unless we create mirrors as weak refs internally (i.e., maintain 1:1 with
>> underlying foreign object reference), there is no easy solution. And
>> maintaining such weak refs is unnecessarily complex. "Foreign object"
>> call/access is mean to be just a "lightweight wrapper" based access. That
>> said, you can do the === on the foreign context itself. You can call
>> ScriptObjectMirror's eval to evaluate === test in that foreign context.
>> That would get right object identity.
>>
>> -Sundar
>>
>>
>> On 12/22/2015 4:26 AM, Vivin Suresh Paliath wrote:
>>
>>> One more thing I noticed is that apparently a new ScriptObjectMirror
>>> instance is probably being created each time x is dereferenced, so "e.x
>>> ===
>>> e.x" also returns "false".
>>>
>>> On Mon, Dec 21, 2015 at 3:49 PM, Vivin Suresh Paliath <
>>> vivin.paliath at gmail.com> wrote:
>>>
>>> I ran into an issue where === returns false even when both should be
>>>> pointing to the same object. I'm assuming this is because one of the
>>>> objects is wrapped by a ScriptObjectMirror, because it was defined in a
>>>> different context.
>>>>
>>>> Here's some code that demonstrates this:
>>>>
>>>>          ScriptEngine engine = new
>>>> NashornScriptEngineFactory().getScriptEngine(
>>>>              new String[] { "-strict" }
>>>>          );
>>>>
>>>>          try {
>>>>              engine.eval("function Foo(src) { this.src = src }; var e =
>>>> {
>>>> x: new Foo(\"what\") };");
>>>>
>>>>              ScriptContext c = new SimpleScriptContext();
>>>>              c.setBindings(engine.createBindings(),
>>>> ScriptContext.ENGINE_SCOPE);
>>>>
>>>>
>>>> c.getBindings(ScriptContext.ENGINE_SCOPE).putAll(engine.getBindings(ScriptContext.ENGINE_SCOPE));
>>>>
>>>>              System.out.println(engine.eval("var z = e.x; z === e.x;",
>>>> c));
>>>>          } catch(Exception e) {
>>>>              throw new RuntimeException(e);
>>>>          }
>>>>
>>>> This prints out "false". Is there a way around this? I am also
>>>> explicitly
>>>> copying over all the bindings from the parent scope into the new scope
>>>> so
>>>> that I have access to "e". Could this be the source of the problem, and
>>>> if
>>>> so, is there a better way to achieve what I'm trying to do?
>>>>
>>>> --
>>>> Ruin untold;
>>>> And thine own sadness,
>>>> Sing in the grass,
>>>> When eve has forgot, that no more hear common things that gleam and
>>>> pass;
>>>> But seek alone to lip, sad Rose of love and ruin untold;
>>>> And thine own mother
>>>> Can know it as I know
>>>> More than another
>>>> What makes your own sadness,
>>>> Set in her eyes.
>>>>
>>>> map{@n=split//;$j.=$n[0]x$n[1]}split/:/,"01:11:02".
>>>> ":11:01:11:02:13:01:11:01:11:01:13:02:12:01:13:01".
>>>> ":11:04:11:06:12:04:11:01:12:01:13:02:12:01:14:01".
>>>> ":13:01:11:03:12:01:11:04:12:02:11:01:11:01:13:02".
>>>> ":11:03:11:06:11:01:11:05:12:02:11:01:11:01:13:02".
>>>> ":11:02:12:01:12:04:11:06:12:01:11:04:12:04:11:01".
>>>> ":12:03:12:01:12:01:11:01:12:01:12:02:11:01:11:01".
>>>> ":13:02:11:01:02:11:01:12:02";map{print chr unpack"
>>>> i",pack"B32",$_}$j=~m/.{8}/g
>>>>
>>>>
>>>
>>>
>>
>
>
> --
> Ruin untold;
> And thine own sadness,
> Sing in the grass,
> When eve has forgot, that no more hear common things that gleam and pass;
> But seek alone to lip, sad Rose of love and ruin untold;
> And thine own mother
> Can know it as I know
> More than another
> What makes your own sadness,
> Set in her eyes.
>
> map{@n=split//;$j.=$n[0]x$n[1]}split/:/,"01:11:02".
> ":11:01:11:02:13:01:11:01:11:01:13:02:12:01:13:01".
> ":11:04:11:06:12:04:11:01:12:01:13:02:12:01:14:01".
> ":13:01:11:03:12:01:11:04:12:02:11:01:11:01:13:02".
> ":11:03:11:06:11:01:11:05:12:02:11:01:11:01:13:02".
> ":11:02:12:01:12:04:11:06:12:01:11:04:12:04:11:01".
> ":12:03:12:01:12:01:11:01:12:01:12:02:11:01:11:01".
> ":13:02:11:01:02:11:01:12:02";map{print chr unpack"
> i",pack"B32",$_}$j=~m/.{8}/g
>
>
>


More information about the nashorn-dev mailing list