Automatic preventExtensions on `Object.prototype`?

I originally posted “Let’s just freeze Object.prototype" as a joke in Matrix but then I started thinking: could we?

This would not only partially address the issue with thenables, but also a host of other problems.

Before you say “no way”, bear with me:

“But that would break polyfillability!”

Except we have resolved to never add anything on Object.prototype ever again, so that’s not an issue.

“But that would break existing websites!”

Wouldn’t it be great if we had a use counter about what % of websites today depend on Object.prototype extensions? Oh wait, it seems like this already exists!

I had to do some digging to figure out what this use counter actually does, but based on these tests and this commit message, it appears that this is exactly what it’s measuring: how many non-built-in elements there are on Object.prototype! Actually, it’s measuring a larger number: whether members exist, not whether they are used.

If these stats are reliable, the % of page loads that actually do this is effectively 0%.

Actually freezing Object.prototype runs into the override problem (o.toString = … no longer works) but preventExtensions should work, I think? Could… this actually be a possibility?! :face_with_peeking_eye:

2 Likes

I think this is V8 using “elements” to mean specifically “array index properties”, not any properties.

In previous discussions it came out that old versions of corejs were unconditionally adding a method to Object.prototype so I think that’s probably happening a fair bit. Would be nice to get a use counter in case it’s not though.

1 Like

I think doing an Object.freeze() on Object.prototype would be feasible if the semantics around checking writable of the prototype property attribute were removed. this is probably a backwards compatible change though it should probably be measured first.

as in make it so that

const o = Object.create(Object.create(null, { foo: { value: 1, writable: true } }))

o.foo = 2

works the same as

const o = Object.create(Object.create(null, { foo: { value: 1, writable: false } }))

o.foo = 2

in non strict mode the former sets an own property and the latter is a no-op.
in strict mode, the former sets an own property and the latter is an exception.

my suggestion is to make it so that they both set an own property. this would mean there’d be no drawback to just fully freezing Object.prototype.

as to backwards compatibility this would only break code in non strict mode that is relying on the funky no-op behaviour and strict mode code that is explicitly trying to test for this exception which I would guess to be only a small amount of code if any. though once again, needs measurement. the change in semantics might need to be limited to only strict mode.

credit to my friend Fayti for suggesting this

That’s the so called “override mistake” and it has not proven practical to fix. We might yet get there but it would affect a lot of code.

1 Like