tc39 / proposal-class-fields

Orthogonally-informed combination of public and private fields proposals
https://arai-a.github.io/ecma262-compare/?pr=1668
1.72k stars 113 forks source link

Interaction with Integrity Levels? #309

Closed mgaudet closed 4 years ago

mgaudet commented 4 years ago

I find myself increasingly surprised that private fields don't interact with the object integrity system at all.

class A {
  #x = 10;
  gx() {
    return this.#x
  }
  mx() {
    this.#x++;
  }
  freezeme() {
    Object.freeze(this);
  }
};

var a = new A;
a.freezeme();
print(a.gx())  // Prints 10
a.mx()
print(a.gx())  // Prints 11

Given that the name of the game with private fields is integrity, integrating them into existing integrity mechanisms seems very sensible. At the very least I find the above to be unexpected.

This example also gives me pause too:

class B extends class {
  constructor(o) { // Stamping fields into a random object trick
    return o;
  }
}
{
  #x = 'stamped';
  static gx(o) {
    return o.#x;
  }
}

var obj = {};
Object.seal(obj);
new B(obj);
print(B.gx(obj));

Someone has attempted to freeze an object, but we can freely modify it by stamping in private fields still.

I've given a bit of thought as to how to change the spec: but it seems at minimum a bit invasive, as PrivateFields currently don't have property descriptors. It is not of course, impossible.

bakkot commented 4 years ago

See https://github.com/tc39/proposal-private-fields/issues/69. The idea was that this makes sense by analogy to putting a frozen object into a WeakMap.

ljharb commented 4 years ago

This is already the case with a number things in the spec - you can't make Maps/Sets/WeakMaps/WeakSets immutable, nor Promises.

Additionally, the common desugaring of your code would be:

const x = new WeakMap();
class B extends class { constructor(o) { return o; } } {
  constructor(...args) {
    super(...args);
    x.set(this, 'stamped');
  }
  static gx(o) {
    if (!x.has(o)) { throw new TypeError(); }
    return x.get(o);
  }
}

var obj = {};
Object.seal(obj);
new B(obj);
print(B.gx(obj));
mgaudet commented 4 years ago

See tc39/proposal-private-fields#69. The idea was that this makes sense by analogy to putting a frozen object into a WeakMap.

/me facepalms.

Didn't think to search the original private-fields repo; just this combined one. That's looking like an extremely helpful read -- this is exactly the discussion I was looking for and didn't find in this repo.

mgaudet commented 4 years ago

Alright this looks covered by tc39/proposal-private-fields#69 -- closing this one :D Thanks!