Skip to content

syg/proposal-nonextensible-applies-to-private

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

19 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Disallow Adding New Private Fields to Non-extensible Objects

Prevent stamping private fields to non-extensible object, just as we already prevent adding public properties.

Status

The TC39 Process

Stage: 2.7

Champions:

  • Mark Miller (@erights)
  • Shu-yu Guo (@syg)
  • Chip Morningstar (@fudco)
  • Erik Marks (@rekmarks)

Talks

Background

A JavaScript object is extensible if new properties can be added to it. Objects are born extensible, and may be made non-extensible by Object.preventExtensions(). Being made non-extensible is one way. Once non-extensible, an object cannot become extensible again.

Private fields do not respect objects' extensibility. They can be added to any object.

For example, the following snippet works.

class NonExtensibleBase {
  constructor() {
    Object.preventExtensions(this);
  }
}

class ClassWithPrivateField extends NonExtensibleBase {
  #val;

  constructor(v) {
    super();
    this.#val = v;
  }
}

new ClassWithPrivateField(42); // doesn't throw

Contrast it with using properties, which doesn't work.

class ClassWithProperty extends NonExtensibleBase {
  _val;

  constructor(v) {
    super();
    this._val = v;
  }
}

new ClassWithProperty(42); // throws TypeError

Proposal

This proposal proposes to change the behavior of non-extensibility to also apply to private fields. That is, to make it so that private fields cannot be added to non-extensible objects.

For the above example, that means new ClassWithPrivateField(42) would throw a TypeError, exactly like new ClassWithProperty(42).

Motivation: fixed layout guarantee

The JS structs proposal and the JS interop for WasmGC proposal are adding fixed layout objects. Specifically, fixed layout means that once an object is constructed, its layout in memory is immutable.

The closest notion the JS language has to fixed layout is being sealed, which implies non-extensibility. However, this has the notable exception of private fields not respecting non-extensibility. This proposal addresses the issue by proposing to change non-extensibility to mean fixed layout, inclusive of private fields.

While private fields can be implemented with out-of-line storage outside of the object that contains it (by using a WeakMap) doing so is inefficient and slow. Performant JS engines implement private fields as in-line in the object, much like properties.

The champions also contend the use of private fields suggests in-line storage, given its syntactic similarity to properties. Private fields' usage in the normal case (ignoring meta-object protocol manipulation like getting property descriptors) also exactly mirror the usage of properties, suggesting that extending extensibility to be inclusive of private fields would match developer intuition.

Web compatibility

Private fields have shipped with the extensibility-disrespecting behavior from the beginning. Therefore there is a non-zero chance that the proposed change is not web compatible. To that end, Chrome has a use counter tracking occurrences of extending non-extensible objects with private fields in the wild.

Alternative

If this proposed change is not web compatible, the alternative design is to make a new integrity trait called "fixed shape", which implies non-extensibility and also prevents private fields from being added. This new trait would be exposed via Object.makeFixedShape() and Object.isFixedShape().

This is less ergonomic for the developer, as the champions contend it is already developer intuition that non-extensibility ought to include private fields.

This is also less desirable for implementation, as it requires objects to track another bit in addition to extensibility to see if new private fields may be added.

About

Proposal to disallow adding new private fields to non-extensible objects

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published