littledan / proposal-proxy-transparent

Transparent Proxies--tunneling private fields, etc.
8 stars 0 forks source link

Unwrapping inside Weak/Strong Maps/Sets #1

Open jridgewell opened 6 years ago

jridgewell commented 6 years ago

I realize this is still in the design phase. I just want to reiterate support for unwrapping transparent proxies inside Maps/Sets (and weak variants) so that Babel's private field transform will work.

littledan commented 6 years ago

Err, I don't think we'd make all WeakMap keys do the unwrapping. This behavior would only apply for private element access.

jridgewell commented 6 years ago

Oh, I incorrectly thought Map.prototype.get in the readme as unwrapping the key not the map. 😳

How do we discuss this with the people who want to provide transparent proxies? Not being able to support this in Babel will be a bummer, it basically means it'll be several more years before they can support Proxying with private fields.

littledan commented 6 years ago

I think it should be doable in Babel. There are two components:

Those two components would probably be useful separately, actually, but together they'd form this proposal.

jridgewell commented 6 years ago

unwrap transparent proxies before hitting the WeakMap

How would this happen?

littledan commented 6 years ago

Something like, your routine for private field read has two steps:

rdking commented 6 years ago

Does this mean there will be something like Proxy.transparent.isProxy(obj) to inform ES if an object is a transparent proxy?

littledan commented 6 years ago

@rdking I was thinking you can just use Proxy.transparent.unwrap to get at that information--if the result doesn't equal the argument, it's a transparent proxy.

Jamesernator commented 6 years ago

Note that you only need to unwrap transparent proxies if the host environment supports them as transparent proxies aren't polyfillable. It's basically just whenever you access your private fields weakmap you need to call Proxy.transparent.unwrap before using it as a WeakMap key. (Also depending on feedback from issue #2 which I opened you might need to add a helper to recursively unwrap).

e.g. This is roughly a desugaring:

class Counter {
  #count = 0

  increment() {
    this.#count += 1
  }

  get count() {
    return #count
  }
}

->

const __countPrivateFields = new WeakMap()

function __unwrap(thisValue) {
  if (Proxy.transparent) {
    return Proxy.transparent.unwrap(thisValue)
  } else {
    return thisValue
  }
}

class Counter {
  constructor() { 
    __countPrivateFields.set(__unwrap(this), 0)
  }

  increment() {
    __countPrivateFields.set(
      __unwrap(this),
      __countPrivateFields.get(__unwrap(this)) + 1,
    )
  }

  get count() {
    return __countPrivateFields.get(__unwrap(this))
  }
}
littledan commented 6 years ago

@Jamesernator Thanks, that's a good description. (PRs to the repo to improve the README are welcome!)

Note, if you're transpiling private fields, it is possible to polyfill it, as a WeakMap from transparent proxies to their target, as long as you also monkey-patch most built-in methods to do a transparent Proxy unwrap as their first step.

rdking commented 6 years ago

So, it would be something like:

Proxy.transparent.unwrap = function unwrap(obj) {
   return [[isTransparentProxy]](obj) ? Proxy.transparent.[[targets]].get(obj) : obj;
}

? Works for me. The only thing I don't get is why anyone would ever need non-transparent proxies. Can you give an example of how transparent proxies will interfere with either of the use cases for Proxy?