-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Normative: Make non-writable prototype properties not prevent assigning to instance #1320
Conversation
…ng to instance TC39 is discussing making inherited non-writable properties overridable with Set, in tc39#1307. We have not yet assessed the web compatibility of the change or written tests, so this PR is not yet ready to merge, but this PR gives some concreteness to how the specification may be done, to help move the discussion forward. This patch implements the approach described in tc39#1307 (comment)
tc39/ecma262#1320 proposes changing the JS language so that the following: ``` let P = {}; Object.defineProperty('prop', { writable: false, value: 1 }); let O = { __proto__: P }; O.prop = 2; console.log(O.prop); ``` will print '2' (instead of '1' in sloppy mode, or throwing a TypeError in strict mode). We intend to measure the frequency of both cases to determine how web compatible the change will be. This is the Chromium side of required changes. The V8-side CL: https://crrev.com/c/1255549 BUG=v8:8175 Change-Id: If74e7aebcac01e034757c0c639bfb5518cd95c91 Reviewed-on: https://chromium-review.googlesource.com/c/1280618 Reviewed-by: Marja Hölttä <marja@chromium.org> Reviewed-by: Jochen Eisinger <jochen@chromium.org> Commit-Queue: Caitlin Potter <caitp@igalia.com> Cr-Commit-Position: refs/heads/master@{#601120}
It looks like this would make all writable properties ignored? |
@bmeck Is the line |
@caitp 's counters to assess the web compatibility have landed in Chrome (the initial figure seems to be invalid; the percentage can't be that high when it's in canary). These counters check how often there is a Set to an inherited, non-writable property, in strict and sloppy mode. |
that does look like quite a high figure... @mathiasbynens is this normal? |
http://jsfiddle.net/ is at least one site that seems to reproduce this consistently as seen in Chrome canary (via 2609 in chrome://histograms/Blink.UseCounter.Features). Haven't managed to track down what is causing the counter yet. Seems related to the JS bundle with their editor code which is using MooTools. MooTools itself does not fire this usage counter by merely loading up the library. |
I’m a little surprised by the UseCounter results as well. https://jsfiddle.net/js/_dist-editor.js contains a few occurrences of @zalun, do you know where/how JSFiddle relies on prototype properties that are |
I only see a single Object.defineProperty(exports, '__esModule', {
value: true
}); Note that If Babel’s output is the culprit, that would certainly explain the higher-than-expected use counter results! However, I can’t find any code that later tries to write to the I could imagine Babel’s loose mode doing something like |
Re-exporting should always skip |
I just converted jsfiddle to strict mode, and paused on every error. It's: function Zr(e) {
return null == e ? e === r ? re : Y : Dn && Dn in tt(e) ? function(e) {
var t = ct.call(e, Dn)
, n = e[Dn];
try {
e[Dn] = r;
var i = !0
} catch (e) {}
var o = dt.call(e);
return i && (t ? e[Dn] = n : delete e[Dn]),
o
}(e) : function(e) {
return dt.call(e)
}(e)
} Particularly, it's |
@loganfsmyth Ah, that code seems only necessary for the loose mode code. When |
@jridgewell Woah, nice catch (pun not intended)! @zalun Could you please tell us where the code Justin pointed out originates? Is this part of some kind of library, or is it custom code? |
Is this lodash? 😢 |
@jridgewell
That's code within a try- of a try-catch block though if that makes a difference. |
It is within a try-catch. But unfortunately, the proposal would change the behavior of the surrounding code: // Current semantics, overwrite will fail silently
baseGetTag(new DataView(new ArrayBuffer(1)));
// => "[object DataView]"
// With new semantics
baseGetTag(new DataView(new ArrayBuffer(1)));
// => "[object Object]" Fortunately, these are feature tests. When one fails (as it would with the new behavior), lodash falls back onto a known implementation of I think our counter is useless, though. 😞 |
@jdalton For full context, we’re trying to change the spec so that… const prototype = {};
Object.defineProperty(prototype, 'property', {
writable: false,
value: 1,
});
const object = Object.create(prototype);
object.property = 2;
console.log(object.property); …logs The lodash code appears to be in strict mode (the source code is a module) but is indeed within a In the case of Update: Woah, I clearly hadn’t refreshed when I posted that comment. |
Actually, there is a breakage in lodash: The fallback But, the code currently fails anyway! The regex matches for "Uint8", not "Uint8Array". So it may still be ok? |
That's in the master branch which is a bit of a mess, untested, and unreleased. |
Ok, so this will break lodash's |
Interesting! We had been hoping that the strict mode case would be "easier" to fix, but that seems to not be the case. Crazy idea: what if we made toStringTag a getter with no setter, so it always throws to assign to it, unless you do Object.defineProperty (including sloppy mode and with this patch)? |
Great work, @jridgewell! |
@erights: This code has changed behavior. Calling
Now:
|
fwiw, lodash merged lodash/lodash@aa1d7d8 today, which eliminates the weird overriding behaviour. |
@jdalton Could you cut a new lodash release? I imagine that'd make it easier for sites like JSFiddle to update. |
I can port the removal to the |
Realistically, I don't expect that slight observable change to break most websites in a noticeable way (please don't prove me wrong!) |
@caitp I bet we could stand the observable change in #1320 (comment) , but it might be really breaking if isTypedArray returns false on TypedArrays (as @jridgewell reported above). I'm wondering if it's possible to empirically investigate further whether @gibson042's minor change would be enough, or if there are other issues. |
Are we thinking of investigating how that change would affect the web / are there ways we can check compatibility of what happens since lodash is in userland instead of at the vm level? |
OK, due to the very high counters in #1320 (comment) , and the practical cases traced in this thread, it seems that we don't have much hope of changing semantics here in either strict or sloppy mode. Great teamwork in figuring this out, @bmeck @caitp @jridgewell. |
How many of those uses would actually break after this? Like, how many are legitimately expecting an error, rather than just trying to assign and ignoring errors if it fails? |
@bmeck from our last verbal conversation, it seemed there was still hope here. Still? If so, please reopen. Thanks. |
Does this discussion include frozen prototype objects? Here is an example: Moddable (run JS compile to bytecode on small embedded systems) freezes all prototype objects to allow their compiler to put such unmodifiable objects into ROM. Here is an example where that breaks: The not uncommon pattern to assign to Moddable-OpenSource/moddable#144 To me, that was quite unexpected and illogical. The " By the way, I still don't understand this inconsistency (from a logic PoV): Direct assignment fails, but if I do a |
@lll000111, yes it does, and Moddable (which is on TC39) has presented that use case to the rest of the committee. Unfortunately, we can't make this change, however weird the current behavior might be, unless we're confident it won't break the web. |
Well, sure, but in the example I gave code that was supposed to work and is in common broke as a consequence of this poorly documented effect, so I'm not sure that "breaking" doesn't already happen. Especially when the failure to assign to an instance property does not result in an error but is silent. |
I suggest that we move the existing behavior into annex b, where legacy crap that browsers dare not change gathers, and then proceed to fix this in the main spec. |
That way, non browser engines that already drop some annex b stuff can drop this as well and remain conformant. |
I'm not a big fan of moving things from the main specification to Annex B; I'd rather us make a decision one way or another on these things. Otherwise, I worry that the JavaScript ecosystem could become forked and incompatible. |
I was quite surprised to see this closed I must admit - I was really hoping there might still be a spec path forward here. Are we really sure we've exhausted all the options? So that the best thing to do at this point would be to define a new form of |
A different form of freezing may indeed be the way to overcome this. See https://github.com/Agoric/harden which is what the SES shim uses to freeze the intrinsics. It would be very natural to propose a
Please dig; thanks!!! |
Just stumbling across this to say - wow yeah I just discovered that |
@erights I've written a strawman gist for an Strawman here: https://gist.github.com/Jamesernator/dc9831f9c13d17ac9a75d1fe25a2e015 |
Yes! I'd love to work with you on advancing such a proposal. Please! See some of my thoughts on this at https://gist.github.com/Jamesernator/dc9831f9c13d17ac9a75d1fe25a2e015#gistcomment-3462493 Thanks! |
TC39 is discussing making inherited non-writable properties overridable with Set,
in #1307. We have not yet assessed the web compatibility of the change or written
tests, so this PR is not yet ready to merge, but this PR gives some concreteness
to how the specification may be done, to help move the discussion forward.
This patch implements the approach described in #1307 (comment)