Re: Figuring out the behavior of WindowProxy in the face of non-configurable properties

On Wed, Jan 14, 2015 at 7:14 PM, Ian Hickson <ian@hixie.ch> wrote:

> On Wed, 14 Jan 2015, Boris Zbarsky wrote:
> >
> > The behavior of ES objects is expressed in terms of various internal
> methods.
> > That's what you have to do to express what an ES object does.
>
> Not necessarily. We can also just say "WindowProxy is its own thing and
> here is how it works".
>
>
> > > No. WindowProxy isn't an ES Proxy.
> >
> > It's an ES object.
>
> That's debatable.


In the sense that we're debating it, sure.

WindowProxy is however a first class value observable by JS code, and
therefore covered by the ES specs. Further, the various meta-object
operations like Object.defineProperty accept it as an argument. If it isn't
even an object, why is it observable all? Why do methods specified to
operate on object arguments even accept it? Starting from your stance, I'd
argue that all these operations should at least reject it with a TypeError.

Neither does the magic replacement theory excuse it from violating the
spec. See https://mail.mozilla.org/pipermail/es-discuss/2011-May/014150.html
, where we first clarified the difference between momentary and eternal
invariants. Let's test the invariant stated in that message as

The ES5 constraints on updating non configurable properties are eternal
invariants.


by writing test2c, in the same spirit as test2a and test2b:

(function(){"use strict";

// assume "gopd" & "is" initialization runs first, so these are
// the original functions.
var gopd = Object.getOwnPropertyDescriptor;
var is = Object.is;

function test2c(obj, name, interleave) {
  name = String(name);
  var desc1 = gopd(obj, name);
  interleave();
  var desc2 = gopd(obj, name);
  if (!desc1.configurable) {
    assertFalse(desc2.configurable);
    assertTrue(desc2.enumerable === desc2.enumerable);
    if (gopd(desc1, 'writable') && !desc1.writable) {
      assertFalse(desc2.writable);
      assertTrue(is(desc1.value, desc2.value));
    }
  }
}

return test2c;


}());

In a conforming ES6 implementation (or a conforming ES5 one given the std
"is" polyfill), it must not be possible to call test2c such that these
asserts fail. No where do these specs say "unless interleave() replaces all
pointers to obj to point at a different object". If it did, the whole
notion of eternal invariants would be nonsense.





>
> > > It is indistinguishable from the Window object.
> >
> > It's distinguishable in various ways, including things like "if I get
> > this property from it in 5 seconds, will I get the same value as from
> > the Window?" The answer to that is "maybe not".
>
> No, because by the time it returns a different value, it no longer acts
> like it's the same object.
>
> You're holding a magical thing that looks and acts exactly like a potted
> plant. It's indistinguishable from a potted plant. Then five seconds
> later, you're holding a cat. It's indistinguishable from a cat. You can't
> tell that the potted plant and the cat are the same thing. You can presume
> that since you were holding one, and then you were holding the other, they
> might have some relationship, but there's no way to tell that they're the
> same thing. Even if the assume they're the same thing, you can't tell the
> difference between the being the same thing, and someone just quickly
> stealing the potted plant and replacing it with a cat.
>
>
> > > When the WindowProxy changes what it's pointing at, it's exactly as if
> > > the browser had reached in and changed every WindowProxy that pointed
> > > to the former and made it point to the latter.
> >
> > You say "every WindowProxy", but in practice in an ES implementation you
> > have some object, it has some internal methods.  This is the last time
> > I'm bothering to go through this with you, since clearly we're getting
> > nowhere, as I said in
> > https://www.w3.org/Bugs/Public/show_bug.cgi?id=27128
>
> You are dismissing what I'm describing as being a misunderstanding of how
> things should be considered to work, because they're not the same as what
> you're describing. But maybe what I'm describing is how things should be
> considered to work, and what you're describing is wrong. Or maybe both our
> points of view are valid, and we should figure out which is easiest to
> describe, or easiest to implement, or simplest to explain to authors, or
> most compatible with the Web, or least likely to involve contortions
> around unnecessary invariants, or whatever other priorities we want to
> apply to this decision.
>
>
> > The internal methods of every object must have behavior that preserves
> > the invariants.
> >
> > For ES proxies, the internal methods are defined in the spec in a way
> > that preserves the invariant.
> >
> > For other ES objects they are also defined in the spec.
> >
> > ES also allows other "exotic objects" that define some other behavior
> > for those internal methods, but requires that the invariants be
> > preserved.
>
> So one option would be to just say that WindowProxy is not an ES object.
>
>
> > > > It doesn't matter.  The user sees the WindowProxy, not the Window.
> > >
> > > No. What the author has is a WindowProxy, but in every sense it acts
> > > like a Window.
> >
> > You mean in every sense except what property values you'll get five
> > seconds from now.
>
> Five seconds from now, the author will in every sense appear to have a
> different Window. It still appears to be a Window. Just a different one.
>
>
> > > > After you navigate, you still have the same WindowProxy (e.g.
> > > > .contentWindow returns something that is === to the thing you had
> > > > before you navigated).
> > >
> > > You have no way to actually test this. Since every reference to the
> > > old Window is now a reference to the new Window, you have no way to
> > > test if the WindowProxy references something new.
> >
> > === on objects tests object identity.
>
> Except on WindowProxy, because WindowProxy forwards all operations to the
> underlying Window.
>
>
> > It's not affected by internal methods in any way.  Therefore, if ===
> > returns true then you actually have the same object.
>
> Consider:
>
>    let a = {};
>    let b = {};
>
>    let c = a;
>    let d = a;
>
>    assert(c === d); // true
>
>    opaqueCode();
>
>    assert(c === d); // still true
>
> If the opaqueCode() function just does:
>
>    function opaqueCode() {
>      c = b;
>      d = b;
>    }
>
> ...then === doesn't help you tell that c and d have changed identity. They
> are still triple-equals to each other, but they're different objects.
>
> This is exactly what happens with WindowProxy, except that the browser is
> the code doing opaqueCode().
>
>
> > >     // part 1
> > >     let a = {};
> > >     let b = {};
> > >     let c = a;
> > >
> > >     // part 2
> > >     c.foo = 1;
> > >
> > >     // part 3
> > >     c = b;
> > >
> > >     // part 4
> > >     console.log(c.foo);
> > >
> > > Is it surprising that the log doesn't log "1"?
> >
> > None of this affects the invariants involved.
>
> The point is that the invariants apply to the actual objects originally
> assigned to a and b, they don't apply to the variables.
>
>
> > > This is what is going on here, except that part 3 is done by the
> > > browser.
> >
> > No, because in the code above if I do |var d = c;| then the d won't
> > change in part 3.
>
> If in the code above you do var d = c, then add d = b to part 3 to
> simulate what the browser does.
>
>
> > Having an object with sane internal methods here is really much simpler
> > than magic "update all the references" behavior. But again, I've said
> > this to you numerous times.
>
> I'm not actually proposing updating all the references. That's just a
> convenient way to think about it that is isomorphic in behaviour to what
> is specced. What is specced is just that you have a placeholder
> pseudo-object that forwards all behaviour to an underlying object, where
> which object it's forwarding to can change over time.
>
>
> > Let me try it again: the fact that the definition in the HTML spec has
> > not lead to interop is not an accident.  It's not something browsers can
> > sanely achieve interop on because it involves handwavy magic that they
> > are extremely likely to interpret differently.  On the other hand,
> > defining a single object with internal methods that do what you want
> > would be _very_ clear to an DOM or JS engine implementor.
>
> I have asked before, but would like to reiterate:
>
> If there is any behaviour that is underdefined by the HTML spec's current
> prose, please tell me, so that I can spec it.
>
>
> > > It looks and quacks like a Window.
> >
> > No, it, doesn't, see above.
>
> It is literally indistinguishable from _a_ Window in every way at all
> times. That's the whole point. Which Window it appears to be
> indistinguishable from at any particular time varies over time.
>
>
> > > It turns out that it does have such a type. WindowProxy is it. I agree
> > > that that makes it a special snowflake.
> >
> > My point is that this special snowflake is unnecessary and confusing.
>
> Well it predates all the ES6 stuff we're talking about here by several
> years, so I'd argue that the confusion doesn't stem from WindowProxy. I
> also don't really agree that it's confusing. It's really simple to
> explain, certainly much simpler than anything involving ES proxies and so
> forth. It's one sentence in the spec, and as far as I'm aware, it doesn't
> leave anything undefined.
>
> I also disagree that what the spec says doesn't match implementations. I
> think it matches implementations pretty darn closely. I mean, in
> particular, it matches exactly what Travis described as IE's
> implementation. (This is not an accident; matching implementations was one
> of the main goals of the HTML spec, and at the time this was specced, IE
> was by far the biggest UA out there.)
>
> --
> Ian Hickson               U+1047E                )\._.,--....,'``.    fL
> http://ln.hixie.ch/       U+263A                /,   _.. \   _\  ;`._ ,.
> Things that are impossible just take longer.   `._.-(,_..'--(,_..'`-.;.'
>



-- 
    Cheers,
    --MarkM

Received on Thursday, 15 January 2015 04:19:53 UTC