Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Counter proposal to avoid a new class of proxy #4

Open
caridy opened this issue Jul 12, 2018 · 27 comments
Open

Counter proposal to avoid a new class of proxy #4

caridy opened this issue Jul 12, 2018 · 27 comments

Comments

@caridy
Copy link

caridy commented Jul 12, 2018

This proposal is based on the SES discussion documented here: #3, it also attempt to solve most of the concerns reflected in #3.

Proposal:

const p = new Proxy(o, { unwrap: (target) => target });

By introducing a new hook called unwrap for existing proxies, the author of the proxy can control how the proxy behaves when in comes to operations that require access to internal slots, private fields and others. This hook is intended to be invoked by the engine and should return the object that should be used to extract internal slots, private fields and others "creatures".

I don't see the necessity to expose a way to hit that hook from user-land. The premise has always be that you can't really observe when an object is a proxy in user land, and that invariant should remain in place IMO.

Backward compatibility:

For this to be backward compatible, the default behavior of the hook must always return the proxy instance instead of the original target, which seems analog to choosing Proxy.transparent instead of Proxy.

Pros:

  • Consumer is in control, and authors don't have to worry (or guess) about what kind of proxies the consumer is trying to use.
  • shadow target mechanism will work just fine, e.g.: { unwrap: (target) => this.originalTarget }.
  • No more unwrap mechanism to worry about, users cannot unwrap.
  • No need to choose between two different types of proxies, just decide if you need to customize the hook or not.
  • Works just fine with Proxy.revocable.
@littledan
Copy link
Owner

A possible tweak: We could have a {transparent: true} option. (I'm not sure if we want an observable callback for each private name read!)

@caridy
Copy link
Author

caridy commented Jul 12, 2018

{transparent: true} is not enough because shadow target requires more control. The owner of the proxy stores the original target somewhere (some folks put it on the handler if it is created per instance, others use a weakmap, etc). It has to be something that allows some computations to determine how to unwrap.

@ljharb
Copy link

ljharb commented Jul 12, 2018

The premise has always be that you can't really observe when an object is a proxy in user land

Currently, you can observe when any builtin is a proxy (except for arrays, and plain objects), via checking for the presence of specific internal slots.

@caridy
Copy link
Author

caridy commented Jul 12, 2018

Yes, thanks for the clarification @ljharb.

@littledan
Copy link
Owner

@ljharb I'm not sure what you mean; you can observe whether something is a built-in type, but I don't see how you can use that to determine whether it's a Proxy or another kind of object that's not the built-in type.

@ljharb
Copy link

ljharb commented Jul 16, 2018

@littledan ah, that is true - but you can determine if it's attempting to masquerade as a built-in-type (yet lacks the appropriate slots).

@littledan
Copy link
Owner

Not sure what you're referring to. Anyway I believe it's a design goal for Proxy to be undetectable.

@ljharb
Copy link

ljharb commented Jul 16, 2018

As I understand it, it's a design goal for it to be undetectable in the presence of a membrane - one that can control access to all builtins. To be truly undetectable, indeed, it would need to tunnel all internal slot access.

@littledan
Copy link
Owner

I'm not sure what you mean by "in the presence of a membrane". The idea is, given an object, you can't tell if it's a Proxy. Tunneling is a feature request but not a prerequisite to meet that goal.

@ljharb
Copy link

ljharb commented Jul 16, 2018

I don’t agree that’s the idea, otherwise internal slots would have tunneled in the first place.

Proxies to functions, in particular, are detectable via a combo of typeof and Function.prototype.toString.call.

@littledan
Copy link
Owner

littledan commented Jul 16, 2018

I am not saying that a Proxy of an object is indistiguishable from its target. That is clearly not a goal, and not met by the definition of Proxy. What is a goal, I believe, is that it should be impossible to implement a Proxy.isProxy function, that, given an object, checks whether it's a Proxy. cc @erights

@ljharb
Copy link

ljharb commented Jul 16, 2018

Ah, indeed - and this is currently impossible for an arbitrary object, but still quite possible for an object of a subset of known alleged types.

@littledan
Copy link
Owner

Yes, I agree that it is possible to use a Proxy to create an object which is not an instance of a particular type (just as it is possible to do the same with object literals).

@dead-claudia
Copy link

Just a thought: shouldn't the second parameter just be the proxy itself, rather than some object wrapper? That way, you're not wastefully allocating an object just to choose how to unwrap. (Most handlers would just return either the target or the handler, without any real logic going on.)

@caridy
Copy link
Author

caridy commented Jul 18, 2018

@isiahmeadows those are mechanism that can be implemented in user-land, just as we do it today in membranes with shadow targets. You can tie the target or handler to the proxy instance itself, or just the closure. Either way, the default behavior is to return the proxy during unwrapping, so if you define that hook is probably because you want to do something else.

@dead-claudia
Copy link

@caridy Good point. So what's the second argument for, then?

@caridy
Copy link
Author

caridy commented Jul 19, 2018

Oh, jejeje! my bad, copy and paste issue, there is no second argument, the handler is the this value. I will update the description.

@Igmat
Copy link

Igmat commented Apr 6, 2019

Does anybody has a plans to move forward with it?

@littledan
Copy link
Owner

Help would be welcome here! I don't plan to push this proposal forward myself.

@Igmat
Copy link

Igmat commented Apr 6, 2019

@littledan, ok, I understand you. As for me it seems pretty solid proposal, how can I help?
Docs, polyfill, spec?

@littledan
Copy link
Owner

I'm not pursuing this proposal personally because it's already gotten a bit of negative feedback. The next steps from here would be to figure out how to respond to that feedback.

At this point, it's more about working through the design issues with the people who raised the concerns. Maybe @erights can invite you to the SES call to discuss the issue. It'd also be good to discuss with implementers whether it's practical to implement these proposals.

@Igmat
Copy link

Igmat commented Apr 6, 2019

I would love to join SES call if it may help to adress his concerns. So @erights, are you interested?

@caridy
Copy link
Author

caridy commented Apr 7, 2019

@Igmat provide an email and we can invite you to the SES meeting on Tue.

@Igmat
Copy link

Igmat commented Apr 7, 2019

@mhofman
Copy link

mhofman commented Apr 25, 2019

I think for this unwrap approach to work for all hidden data "creatures", it needs to work for key identity tests for containers such a WeakMap.

However I'm having a hard time figuring out how that would work. @rdking and I are discussing that in rdking/proposal-proxy-transparency#1 and I created https://github.com/mhofman/proposal-proxy-resolve-playground to help figuring out a solution.

Any ideas and other insight would be greatly appreciated :)

@littledan
Copy link
Owner

I don't think we could make transparent proxies have the same identity... This just gets too weird.

@mhofman
Copy link

mhofman commented Apr 25, 2019

I don't think we could make transparent proxies have the same identity... This just gets too weird.

I tend to agree but we need to find a way to make transparent proxies work with user implementations of hidden data. Restricting it to internal slots and other host objects isn't sufficient in my opinion.

I'm ok however restricting which kind of user implementations can handle transparent proxies. WeakMap and other containers seem like a good place to start.

For that reason I'm trying to see if @isiahmeadows / @rdking idea for a resolve trap could apply to such container, which basically means some way of having the proxy trap override what object should be used as the key in containers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants