Help wanted: Membrane proxies + Object.freeze() == paradox

# Alex Vincent (7 years ago)

Full details are at ajvincent/es7-membrane#79 , which is where I'm hoping to find a solution.

In short: if

  1. I have a function wetB() and an instance of wetB called wet_b,
  2. I call Object.freeze(wet_b);
  3. I create matching "membrane" proxies for wetB and wet_b named dryB and dry_b,

Then:

  • Reflect.get(wetB, "prototype") === Reflect.getPrototypeOf(wet_b)
  • Reflect.get(dryB, "prototype") !== Reflect.getPrototypeOf(dry_b)

This is a paradox: I claim that what's true on the "wet" object graph should be true on the "dry" object graph.

Please, if you can offer help on this to fix the sample code I've posted, or if you can confirm that my claim above is incorrect and never can be correct, I'd appreciate it.

Alex Vincent

Hayward, CA, USA

# Boris Zbarsky (7 years ago)

On 5/23/17 3:44 AM, Alex Vincent wrote:

  1. I create matching "membrane" proxies for wetB and wet_b named dryB and dry_b,

Shouldn't you also have a proxy for webB.prototype (called dryproto, let's say)? Otherwise your "wet" and "dry" object graphs are actually a single kind of weird object graph...

Then:

  • Reflect.get(wetB, "prototype") === Reflect.getPrototypeOf(wet_b)
  • Reflect.get(dryB, "prototype") !== Reflect.getPrototypeOf(dry_b)

And then Reflect.get(dryB, "prototype") === dryproto === Reflect.getPrototypeOf(dry_b), I would hope.

# Jason Orendorff (7 years ago)

On Tue, May 23, 2017 at 2:44 AM, Alex Vincent <ajvincent at gmail.com> wrote:

Full details are at ajvincent/es7-membrane#79 , which is where I'm hoping to find a solution.

To fix this, you must create a third object for each dry proxy, to serve as its [[ProxyTarget]]. There's no way around it. And you're right, it's not easy; you have to implement all of the handler methods, because none of the defaults do the right thing anymore.

All the target checks in the design were intended to (a) allow proxies to claim to have immutable parts; and yet (b) maintain the language's invariants that immutable parts cannot change, even for proxies. [[ProxyTarget]] looks like an ease-of-use feature, but it's not; it's really in support of (a) and (b), and as you've found, it actually makes some things harder.

# Mark S. Miller (7 years ago)

Take a look at Tom's explanation of the "shadow target" technique at research.google.com/pubs/pub40736.html section 4.3

# Alex Vincent (7 years ago)

Yes, I've been using the shadow technique in my es7-membrane project for quite some time. I was trying to minimize a fairly complex test case here.

Obviously, I can reintroduce shadow targets into the minimal testcase, but then I need to figure out how to make the prototype object I'm trying to get consistent again... still working through the details of that in my head.

# Jason Orendorff (7 years ago)

I think the most straightforward thing is something like this:

gist.github.com/jorendorff/85d74ef7dce0118664535f84d57d6788

To restate the obvious, this is a minimal fix for your minimized test case, not production code you can take and use. You'd need to implement all the rest of the handler methods.

# Alex Vincent (7 years ago)

Thanks, Jason. I tried that approach last night (calling setPrototypeOf within the getPrototypeOf trap), and it caused a nasty regression in one of my other prototype-checking tests. I assumed at the time that I had done something wrong, but your quick summary shows that instead I was doing something right, and that my prototype chain handling was incorrectly implemented...