Skip to content
This repository was archived by the owner on Jan 25, 2022. It is now read-only.

Consider prototype chain walk for private get/set #18

Closed
rbuckton opened this issue Oct 1, 2017 · 3 comments
Closed

Consider prototype chain walk for private get/set #18

rbuckton opened this issue Oct 1, 2017 · 3 comments

Comments

@rbuckton
Copy link

rbuckton commented Oct 1, 2017

In the September TC39 meeting we discussed issues with private static fields, e.g.:

class Base {
  static #field = 1;
  static get() { return this.#field; }
  static set(value) { this.#field = value; }
}
class Sub extends Base {
}
Sub.get(); // error
Sub.set(2); // error

The reason for the error is that the receiver is Sub, which does not have a #field defined on it. One possible approach is to allow for a prototype walk just as we do for any other property lookup. We could support this by splitting private members into several parts:

  • Space reservation for a private name on an object, indicating whether it is addressable when the object is the receiver.
  • Definition of a private member on an object.

Then we would apply the following semantics, given private name P:

  • non-static private methods/accessors:
    • During class declaration evaluation:
      • Add a reservation on the class prototype for P with the value "reserved".
      • Add a private property definition to the class prototype for P.
    • During construction:
      • Add a reservation on the instance for P with the value "addressable".
  • non-static private fields:
    • During construction:
      • Add a reservation on the instance for P with the value "addressable".
      • Add a private property definition to the instance for P.
  • static private methods/accessors and fields:
    • During class declaration evaluation:
      • Add a reservation on the class constructor for P with the value "addressable".
      • Add a private property definition to the class constructor for P.
      • If the class is a subclass, Add a reservation on the class constructor for each "addressable" private name on the superclass constructor.

Then, when performing a get or set of a private property P on object O, with receiver Receiver:

  1. If O does not have a reservation for P, throw a TypeError exception.
  2. Let ownDesc be the private property definition for P on O.
  3. If ownDesc is undefined, perform this operation on O.[[GetPrototypeOf]]() instead as O and passing P and Receiver as is.
  4. If Receiver does not have a reservation for P, or the reservation for P on Receiver is not "addressable" throw a TypeError exception.
  5. Perform the remaining get or set steps.

This would result in the following behavior:

  • non-static private methods/accessors would be defined on the prototype, but cannot be accessed directly (e.g. C.prototype.#method() is an error). This keeps the observable semantics the same as the current spec.
  • non-static fields are defined on the instance, as per the current spec.
  • static private methods, accessors, and fields are defined on the constructor.
  • static methods on a subclass can use this and have the same semantics for private fields that they do for public fields.
  • Calling Sub.get() above would succeed, returning the value of Base.#field.
  • Calling Sub.set(2) above would succeed, setting the value of Sub.#field to 2, but leaving Base.#field as 1.
@rbuckton
Copy link
Author

rbuckton commented Oct 1, 2017

Alternatively, we don't add the reservation and if we bottom-out on a private get or set where no object in the prototype chain defined the private member, we throw a TypeError exception.

@littledan
Copy link
Member

This does seem to be well-defined spec text which addresses @jridgewell's issue. However, it doesn't meet another design goal of private in classes: That no observable or hookable meta-object operation is done in the course of private field access. You can put a Proxy in the prototype chain and get a hint of what's going on here, or even change the thing being bound to dynamically. That's something the private class element proposals try to avoid.

@littledan
Copy link
Member

We've thought about a bunch of alternatives for how private methods could work through inheritance. I've summarized this idea and others, about some sort of prototype chain, in this document. Ultimately, it seems like the downsides outweigh the upsides.

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

No branches or pull requests

2 participants