Call for opinions: attribute defaults and renaming "flexible"

# Allen Wirfs-Brock (17 years ago)

At the Thursday, August 21, ES3.1 design conference call we debated two decision points relating to property attributes but did not reach consensus on either item. There are strong points on each side of these issues but so far too few contributors have weighed in to reach any consensus on the alternatives. We would like to make a final decision during the next conference call (Tuesday August 26) but in preparation we would like to get the opinion of more of the community. If, after reviewing the alternatives below, you have an opinion replay to the list and please call in Tuesday if you are a TC-39 member who wants to help make the final decision.

Issue 1: When a property is defined using Object.defineProperty and similar functions what are the values of any properties attributes that are not explicitly specified using the property descriptor argument.

Background: The attributes we are talking about are [[Writable]], [[Enumerable]], [[Flexible]]. See ES3.1 Draft Sections 8.6.1, 8.6.2.9, 15.2.3.7 and similar functions in 15.2.3. "Proposed ECMAScript 3.1 Static Object Functions: Use Cases and Rationale" es3.1:rationale_for_es3_1_static_object_methods.pdf

Alternative #1: When creating a property using defineProperty all unspecified attributes default to false.

  • A major goal of ES3.1 and beyond is to make ES a better language for writing high integrity software.
  • The attribute names were chosen such that they affirmatively name a privilege associated with a property. A true value means the privilege is granted to the program and a false value means that it is withheld.
  • We know from experience that programmers are "lazy" and that if a specific attribute setting is not essential to their application they will usually just not specify it in a defineProperty descriptor.
  • Following the "principle of least authority", a privilege should only be granted if it is explicitly requested.
  • Defaulting attributes to anything but false violates POLA and makes it more difficult to write high integrity software.
  • Few casual or unsophisticated programmers will be using defineProperty or the other functions that use property desciptors.

Alternative #2: When creating a property using defineProperty all unspecified attributes default to the same values they have when a property is created by assignment or within an object literal.

  • The "normal" way to define properties is via assignment or within an object literal. The attribute values implicitly set by these syntactic forms are defined by ES3 and previous editions to be the equivalent of setting each of [[Writable]], [[Enumerable]], and [[Flexible]] to true.
  • Object.defineProperty is a supplemental mechanism for use in situations when non-default attribute values are required and for other out of the ordinary scenarios.
  • Because of compatibility constraints, we cannot change the attributes that are implicitly set by existing syntactic forms.
  • The consistency principle suggests that in all situations where attribute value are implicitly specified, their implicit default values should be the same.
  • Inconsistencies makes it harder to learn and use the language.
  • The majority of ES programmers are unsophisticated and are not trying to write high integrity software.
  • Usability for a minority of users should not be achieved at the expense of decreasing usability for the majority.

Issue 2: The property attribute currently named [[Flexible]] (and which replaces ES3's [[dontDelete]]) should be renamed to [[Configurable]].

Argument for:

  • The common meaning of the word "configurable" is a closer match to the actual semantics of the attribute than the common meaning of "flexible"
  • The other attributes name all follow the form of a semantically meaningful verb ("write" and "enumerate") converted to an adjective by adding "able". "flex" is not semantically meaningful within ES.
  • If we had thought of "configurable" when we considering names we probably would have chosen it over "flexible".

Argument against:

  • "configurable" is too long and long names make programs less readable.
  • This will be particularly true for Object.defineProperty calls if alternative #2 above is chosen and programmers who care about high integrity software must frequently say "configurable:false" in property descriptors.
# Michael Haufe (17 years ago)

My suggestions:

[[IsMod]] "Is capable of being Modified"

[[Amendable]]

[[Adaptable]]

[[Alter]] "Alterable"

# Ingvar von Schoultz (17 years ago)

Allen Wirfs-Brock wrote:

At the Thursday, August 21, ES3.1 design conference call we debated two decision points relating to property attributes but did not reach consensus on either item. [...] If, after reviewing the alternatives below, you have an opinion replay to the list

Executive summary:

I think the best name is "configurable". It becomes very concrete indeed if Object.defineProperty() is changed to Object.configure().

I think defaulting the attributes to false is best.

Reasons, apart from those mentioned by Allen:

-- Many people will use abstracting helper functions that manipulate these attributes. When you're reasoning about several such functions at a time, negatively written attributes can become confusing.

-- If instead you manipulate the attributes directly, you'll probably still want helper functions, for brevity and to get the defaults that suit you best. Then the above applies again.

-- If you're not using any helper functions, and want positive defaults, you can get this by first creating and then modifying:

    obj.hidden = ...;
    Object.defineProperty (obj, "hidden", {enumerable: false});

-- When people are uncertain about the consequences of touching a setting, some will see the default as a strong recommendation, to be disobeyed only if you have an even more compelling reason and have thought through the consequences.

Regarding the name:

-- If you work on your helper functions so seldom that you forget the details, "configurable" helps you remember by saying what it's for, "flexible" doesn't.

-- The name "configurable" makes a surprisingly big difference in clarity. Try reading parts of the ES3.1 draft while imagining [[Flexible]] replaced with [[Configurable]]. The text feels very much clearer and more specific.

-- If Object.defineProperty() were changed to Object.configure(), the names would reinforce each other so that both would become more concrete. The name .configure() could be used both for singular and plural, distinguished by the different parameters. .getProperty() could become .getConfig().

# Garrett Smith (17 years ago)

2008/8/21 Allen Wirfs-Brock <Allen.Wirfs-Brock at microsoft.com>:

At the Thursday, August 21, ES3.1 design conference call we debated two decision points relating to property attributes but did not reach consensus on either item. There are strong points on each side of these issues but so far too few contributors have weighed in to reach any consensus on the alternatives.

Issue 1: When a property is defined using Object.defineProperty and similar functions what are the values of any properties attributes that are not explicitly specified using the property descriptor argument.

Background:

The attributes we are talking about are [[Writable]], [[Enumerable]], [[Flexible]].

See ES3.1 Draft Sections 8.6.1, 8.6.2.9, 15.2.3.7 and similar functions in 15.2.3.

However, it seems that what I one might guess [[Flexible]] to mean is partially defined as [[Extensible]].

| [[Extensible]] If true, own properties may be added | to the object.

The opposite of [[Extensible]] still allows property values to be modified. The object is not "sealed". The term "sealed" seems to lack proper definition.

"Proposed ECMAScript 3.1 Static Object Functions: Use Cases and Rationale" es3.1:rationale_for_es3_1_static_object_methods.pdf

Issue 2: The property attribute currently named [[Flexible]] (and which replaces ES3's [[dontDelete]]) should be renamed to [[Configurable]].

Argument for:

  • The common meaning of the word "configurable" is a closer match to the actual semantics of the attribute than the common meaning of "flexible"

[[Configurable]] would seem to mean a lot more than [[Deletable]]. Configurable sounds like [[Extensible]] new properties can be added/deleted. configurable=false would seem as though the object were not [[Extensible]]

  • The other attributes name all follow the form of a semantically meaningful verb ("write" and "enumerate") converted to an adjective by adding "able". "flex" is not semantically meaningful within ES.

Has [[Deletable]] been considered?

Garrett

# Garrett Smith (17 years ago)

On Sun, Aug 24, 2008 at 10:45 AM, Garrett Smith <dhtmlkitchen at gmail.com> wrote:

2008/8/21 Allen Wirfs-Brock <Allen.Wirfs-Brock at microsoft.com>:

However, it seems that what I one might guess [[Flexible]] to mean is partially defined as [[Extensible]].

That was poorly worded.

What one might guess [[Flexible]] to mean (going by the meaning of the word) is partially defined as [[Extensible]].

# Mark S. Miller (17 years ago)

If I'm reading the general sense of the list correctly, [[Configurable]] is generally preferred to [[Flexible]], so I'll use that below.

We need to distinguish between

  • attributes of a property, such as [[Writable]] and [[Configurable]]
  • internal properties of an object, such as [[Prototype]] and [[Extensible]]
  • predicates over an object's semantic state such as "sealed" and "frozen".

I think the spec language may be causing confusion by saying by using terms like "sealed" to describe property states as well as object states. (I think I may have introduced this confusion -- apologies if so.) I will avoid such multiple use of terms below.

So only properties are configurable or not. Only objects are extensible or not.

If a property is configurable, it may be deleted, its enumerability may be changed, it may be changed between being a data property (with [[Value]] and [[Writable]]) and an accessor property (with [[Getter]] and [[Setter]]), and it may be made non-configurable, at which point none of these changes remain possible. As an unfortunate implication of legacy issues, a non-configurable writable data property may be made non-writable. If it weren't for this special case, we'd have to split deletable from configurable.

If an object is extensible, new own properties may be added to it, and it can be made non-extensible.

If an object is non-extensible and all of its own properties are non-configurable, then it is sealed.

If an object is sealed and all of its own data properties are non-writable, then it is frozen.

# Ingvar von Schoultz (17 years ago)

Mark S. Miller wrote:

If a property is configurable, it may be deleted, its enumerability may be changed, it may be changed between being a data property (with [[Value]] and [[Writable]]) and an accessor property (with [[Getter]] and [[Setter]]), and it may be made non-configurable, at which point none of these changes remain possible.

If you try to do something that is disabled by attribute settings, does this throw an error or just fail silently?

I ask because I was dismayed to see that ES3.1 will fail silently if you try to assign to a const in non-cautious mode. This is hard to debug. You'll have to avoid this const and instead use let or var. Then errors will be better contained and easier to understand and trace down.

As an unfortunate implication of legacy issues, a non-configurable writable data property may be made non-writable. If it weren't for this special case, we'd have to split deletable from configurable.

That sounds amazing since no legacy code can call the configuring function. Do you have a link to a discussion or information about this?

Of course this is just curiosity, but it really sounds very intriguing.

If an object is non-extensible and all of its own properties are non-configurable, then it is sealed.

I think locked would be more concrete, and Object.lock().

Sealed and frozen are so vague and similar that I have to think three times to distinguish between them. Locked becomes concrete due to the real locks that you see every day.

Thanks for a very clarifying description. This should probably be on the wiki somewhere.

# Brendan Eich (17 years ago)

On Aug 24, 2008, at 2:12 PM, Ingvar von Schoultz wrote:

If an object is non-extensible and all of its own properties are non-configurable, then it is sealed.

I think locked would be more concrete, and Object.lock().

No good, lock connotes "acquire mutex" and suggests unlocking is
possible.

Sealed and frozen are so vague and similar that I have to think three times to distinguish between them. Locked becomes concrete due to the real locks that you see every day.

What real locks? If you mean on doors, the problem is that "lock" has
a more common meaning in programming, and it has nothing to do with
doors.

# Mark S. Miller (17 years ago)

On Sun, Aug 24, 2008 at 2:12 PM, Ingvar von Schoultz <ingvar-v-s at comhem.se> wrote:

Mark S. Miller wrote:

If a property is configurable, it may be deleted, its enumerability may be changed, it may be changed between being a data property (with [[Value]] and [[Writable]]) and an accessor property (with [[Getter]] and [[Setter]]), and it may be made non-configurable, at which point none of these changes remain possible.

If you try to do something that is disabled by attribute settings, does this throw an error or just fail silently?

I ask because I was dismayed to see that ES3.1 will fail silently if you try to assign to a const in non-cautious mode. This is hard to debug. You'll have to avoid this const and instead use let or var. Then errors will be better contained and easier to understand and trace down.

We expect to change "cautious" to "strict", so I'll use "strict" below.

Interesting point. We may have let property semantics again needlessly corrupt variable semantics. Non-strict assignment to a non-writable property must fail silently for legacy compatibility. But I don't think there's any corresponding legacy constraint on assignment to a const variable. I agree that this should throw regardless of strictness. Where in the draft ES3.1 manual did you spot this?

As an unfortunate implication of legacy issues, a non-configurable writable data property may be made non-writable. If it weren't for this special case, we'd have to split deletable from configurable.

That sounds amazing since no legacy code can call the configuring function. Do you have a link to a discussion or information about this?

Of course this is just curiosity, but it really sounds very intriguing.

Legacy demands that new properties added to the global object by assignment or top-level declarations start as non-deletable but writable. But runtimes for secure subsets of ES, like ADSafe and Caja, need to be able to freeze the global object of their frame during their initialization. If not for legacy, we could make new properties of the global object start as configurable. If deletable and configurable were distinct, we could make new properties of the global object start as configurable but not deletable. However, adding another attribute to deal with this one problematic case seemed overkill. Allowing non-configurable properties to be made non-writable seems like the simplest adequate solution.

If an object is non-extensible and all of its own properties are non-configurable, then it is sealed.

I think locked would be more concrete, and Object.lock().

As Brendan says, in code, "lock" will suggest mutex and concurrency control.

# Brendan Eich (17 years ago)

On Aug 24, 2008, at 3:40 PM, Mark S. Miller wrote:

Interesting point. We may have let property semantics again needlessly corrupt variable semantics.

That's a recurring risk due to the spec's modeling scopes via objects
(a botch that most of us involved all along regret, but hard to fix
in any kind of 3.x spec delta).

Non-strict assignment to a non-writable property must fail silently for legacy compatibility. But I don't think there's any corresponding legacy constraint on assignment to a const variable. I agree that this should throw regardless of strictness.

Yes, it would be best to throw always -- this is easy to get right
now, while we have the chance with ES3.1, for variables that do not
map in the spec or implementations to properties.

But const at top level makes a global object property. And thanks to
the spec's abusing objects for scopes, it seems to happen at function
body top-level in function activations. If you have

function foo(x, z) { const y = x; return eval(z); }

The spec talks about the activation object for foo having properties
x and z, and any extension a la 3.1 would probably have to do
likewise for const. Otherwise you're making a new kind of scope, not
visible to eval'ed code.

# Ingvar von Schoultz (17 years ago)

Mark S. Miller wrote:

I ask because I was dismayed to see that ES3.1 will fail silently if you try to assign to a const in non-cautious mode. This is hard to debug. You'll have to avoid this const and instead use let or var. Then errors will be better contained and easier to understand and trace down.

We expect to change "cautious" to "strict", so I'll use "strict" below.

Interesting point. We may have let property semantics again needlessly corrupt variable semantics. Non-strict assignment to a non-writable property must fail silently for legacy compatibility.

Silently failing on properties has the same problem -- symptoms far away from the bug, debugging gets expensive. Entrenching such an expensive bug in the language for all future is a serious matter, and deserves very careful thought, in my opinion.

Would the following be too expensive? If the user configures a property, we assume that he wants to be told. So when you call Object.defineProperty(), an internal object-wide flag is set, [[ConfiguredByUser]], which enables throwing on forbidden write to any of the object's properties.

Or maybe name the flag [[BecomeWellBehaved]], and remove several legacy issues. Configuring becomes an opt-in that makes objects behave better.

I'm curious about the legacy problem. It makes me wonder if the nature of the problem can suggest a cheap solution.

I agree that this should throw regardless of strictness.

I'm happy to hear that. I hope nothing will prevent it.

Where in the draft ES3.1 manual did you spot this?

I spotted it in archived email discussions from before my arrival here. The ES3.1 spec draft of Aug 18 confirms it, see 11.13.1 and 11.13.1.1 (page 69).

As an unfortunate implication of legacy issues, a non-configurable writable data property may be made non-writable. If it weren't for this special case, we'd have to split deletable from configurable. That sounds amazing since no legacy code can call the configuring function. Do you have a link to a discussion or information about this?

Of course this is just curiosity, but it really sounds very intriguing.

Legacy demands that new properties added to the global object by assignment or top-level declarations start as non-deletable but writable. But runtimes for secure subsets of ES, like ADSafe and Caja, need to be able to freeze the global object of their frame during their initialization. If not for legacy, we could make new properties of the global object start as configurable. If deletable and configurable were distinct, we could make new properties of the global object start as configurable but not deletable. However, adding another attribute to deal with this one problematic case seemed overkill. Allowing non-configurable properties to be made non-writable seems like the simplest adequate solution.

Could this be solved with a [[BecomeWellBehaved]] flag? Before anything else is put on the global object, ADSafe and Caja could make a configuration call on the global object. The resulting Well-Behaved global object sets [[Configurable]] on all new variables.

If that's not suitable, ADSafe and Caja might use try-catch. Always throw when the program tries to make a non-configurable property non-writable, except if there are two calls in quick succession to Object.freeze() on the global object, called not from within a function but from the global scope. Only then make the change.

Or, as a last resort, turn this into special semantics of Object.freeze() only, not Object.defineProperty(). It does make sense for a drastic function like Object.freeze() to have drastic powers.

If an object is non-extensible and all of its own properties are non-configurable, then it is sealed. I think locked would be more concrete, and Object.lock().

As Brendan says, in code, "lock" will suggest mutex and concurrency control.

Oops! But of course. Sorry!

I've spent too many years living a sheltered JavaScript life without seeing such things.

# David-Sarah Hopwood (17 years ago)

Mark S. Miller wrote:

If a property is configurable, it may be deleted, its enumerability may be changed, it may be changed between being a data property (with [[Value]] and [[Writable]]) and an accessor property (with [[Getter]] and [[Setter]]), and it may be made non-configurable, at which point none of these changes remain possible. As an unfortunate implication of legacy issues, a non-configurable writable data property may be made non-writable. If it weren't for this special case, we'd have to split deletable from configurable.

I don't see why any special case is needed here, or why removing it would require splitting [[Deletable]] from [[Configurable]].

Suppose that [[Configurable]] = false prevents a writable data property from being deleted or changed to non-writable. What compatibility problem does this introduce? ES3 had no way for user code to change attributes, so I don't see how there can be a compatibility problem.

# Mark S. Miller (17 years ago)

On Wed, Sep 3, 2008 at 9:49 PM, David-Sarah Hopwood <david.hopwood at industrial-designers.co.uk> wrote:

I don't see why any special case is needed here, or why removing it would require splitting [[Deletable]] from [[Configurable]].

Suppose that [[Configurable]] = false prevents a writable data property from being deleted or changed to non-writable. What compatibility problem does this introduce? ES3 had no way for user code to change attributes, so I don't see how there can be a compatibility problem.

As I said in an earlier question from Ingvar on the same issue:

Legacy demands that new properties added to the global object by assignment or top-level declarations start as non-deletable but writable. But runtimes for secure subsets of ES, like ADSafe and Caja, need to be able to freeze the global object of their frame during their initialization. If not for legacy, we could make new properties of the global object start as configurable. If deletable and configurable were distinct, we could make new properties of the global object start as configurable but not deletable. However, adding another attribute to deal with this one problematic case seemed overkill. Allowing non-configurable properties to be made non-writable seems like the simplest adequate solution.

# Brendan Eich (17 years ago)

On Sep 3, 2008, at 10:51 PM, Mark S. Miller wrote:

On Wed, Sep 3, 2008 at 9:49 PM, David-Sarah Hopwood <david.hopwood at industrial-designers.co.uk> wrote:

I don't see why any special case is needed here, or why removing it would require splitting [[Deletable]] from [[Configurable]].

Suppose that [[Configurable]] = false prevents a writable data
property from being deleted or changed to non-writable. What compatibility
problem does this introduce? ES3 had no way for user code to change
attributes, so I don't see how there can be a compatibility problem.

As I said in an earlier question from Ingvar on the same issue:

Legacy demands that new properties added to the global object by assignment or top-level declarations

Not by assignment -- only by var or function declaration not in eval
code.

start as non-deletable but writable. But runtimes for secure subsets of ES, like ADSafe and Caja, need to be able to freeze the global object of their frame during their initialization. If not for legacy, we could make new properties of the global object start as configurable.

Except that deoptimizes all high performance JS engines.

If deletable and configurable were distinct, we could make new properties of the global object start as configurable but not deletable.

Saving perf but to what end? Does the Caja, etc. programming model
require configurability before freezing for all global props, or a
few, or one?

However, adding another attribute to deal with this one problematic case seemed overkill. Allowing non-configurable properties to be made non-writable seems like the simplest adequate solution.

Not arguing, just recapitulating and asking for the Caja use-case.

# Mark S. Miller (17 years ago)

On Wed, Sep 3, 2008 at 10:58 PM, Brendan Eich <brendan at mozilla.org> wrote:

If deletable and configurable were distinct, we could make new properties of the global object start as configurable but not deletable.

Saving perf but to what end? Does the Caja, etc. programming model require configurability before freezing for all global props, or a few, or one?

However, adding another attribute to deal with this one problematic case seemed overkill. Allowing non-configurable properties to be made non-writable seems like the simplest adequate solution.

Not arguing, just recapitulating and asking for the Caja use-case.

The use case is that the Caja runtime gets loaded into a frame after writable-but-non-deletable properties that we wish to whitelist have been added to the global object. The Caja runtime needs to freeze all whitelisted objects and properties reachable from from the global object.

In ES3.1 as we're proposing it, the only way to represent that a property is non-deletable is to make it non-configurable. If writable non-configurable properties could not be made non-writable, the use case above would be screwed.

# Ingvar von Schoultz (17 years ago)

Mark S. Miller skrev:

On Wed, Sep 3, 2008 at 10:58 PM, Brendan Eich <brendan at mozilla.org> wrote:

If deletable and configurable were distinct, we could make new properties of the global object start as configurable but not deletable. Saving perf but to what end? Does the Caja, etc. programming model require configurability before freezing for all global props, or a few, or one?

However, adding another attribute to deal with this one problematic case seemed overkill. Allowing non-configurable properties to be made non-writable seems like the simplest adequate solution. Not arguing, just recapitulating and asking for the Caja use-case.

The use case is that the Caja runtime gets loaded into a frame after writable-but-non-deletable properties that we wish to whitelist have been added to the global object. The Caja runtime needs to freeze all whitelisted objects and properties reachable from from the global object.

In ES3.1 as we're proposing it, the only way to represent that a property is non-deletable is to make it non-configurable. If writable non-configurable properties could not be made non-writable, the use case above would be screwed.

It would be much better if Object.freeze() got an added parameter, a bit flag that, when set, makes it more powerful and allows it to override the configurability flag. Then the override happens only when you enable it explicitly.

Otherwise we have a case of silent failure, and silent failures are often very expensive to debug.

# Mark S. Miller (17 years ago)

On Fri, Sep 5, 2008 at 2:33 PM, Ingvar von Schoultz <ingvar-v-s at comhem.se> wrote:

It would be much better if Object.freeze() got an added parameter, a bit flag that, when set, makes it more powerful and allows it to override the configurability flag. Then the override happens only when you enable it explicitly.

Otherwise we have a case of silent failure, and silent failures are often very expensive to debug.

I don't understand. As currently proposed, Object.freeze always succeeds. What silent failure are you concerned about?

# Ingvar von Schoultz (17 years ago)

Mark S. Miller skrev:

On Fri, Sep 5, 2008 at 2:33 PM, Ingvar von Schoultz <ingvar-v-s at comhem.se> wrote:

It would be much better if Object.freeze() got an added parameter, a bit flag that, when set, makes it more powerful and allows it to override the configurability flag. Then the override happens only when you enable it explicitly.

Otherwise we have a case of silent failure, and silent failures are often very expensive to debug.

I don't understand. As currently proposed, Object.freeze always succeeds. What silent failure are you concerned about?

If you set a property to non-configurable it means you don't want it reconfigured. If code is still allowed to change it from writable to non-writable, this is a silent failure of the guard against reconfiguring.

# Ingvar von Schoultz (17 years ago)

Ingvar von Schoultz skrev:

Mark S. Miller skrev:

On Fri, Sep 5, 2008 at 2:33 PM, Ingvar von Schoultz <ingvar-v-s at comhem.se> wrote:

and silent failures are often very expensive to debug. I don't understand. As currently proposed, Object.freeze always succeeds. What silent failure are you concerned about?

If you set a property to non-configurable it means you don't want it reconfigured. If code is still allowed to change it from writable to non-writable, this is a silent failure of the guard against reconfiguring.

Another silent failure, much more expensive, is when you try to write to non-writable properties. (And, apart from this, also the related similar problem with constant variables).

I would really like to know the reason for this strange problem with non-writable properties, and try to see if there's any way a better solution can be found than this seriously expensive language bug.

# Brendan Eich (17 years ago)

On Sep 5, 2008, at 4:33 PM, Ingvar von Schoultz wrote:

Ingvar von Schoultz skrev:

Mark S. Miller skrev:

On Fri, Sep 5, 2008 at 2:33 PM, Ingvar von Schoultz <ingvar-v-s at comhem.se> wrote:

and silent failures are often very expensive to debug. I don't understand. As currently proposed, Object.freeze always succeeds. What silent failure are you concerned about?

If you set a property to non-configurable it means you don't want it reconfigured. If code is still allowed to change it from writable to non-writable, this is a silent failure of the guard against reconfiguring.

Another silent failure, much more expensive, is when you try to write to non-writable properties. (And, apart from this, also the related similar problem with constant variables).

This goes back to ES1. In Netscape's original JS implementation, I
reported an error which stopped the script on assignment to read-only
properties (this from memory, I don't have source code for the old
"Mocha" runtime). Without try/catch in ES1, this was viewed as too
harsh by the TG1 group in TC39, leaving only silent-but-deadly
failure to modify the lvalue (the result of the assignment expression
is the unfiltered right-hand-side value, so you can't tell).

This was not revisited when exception handling was added in ES3 :-(.

I would really like to know the reason for this strange problem with non-writable properties, and try to see if there's any way a better solution can be found than this seriously expensive language bug.

The proposed solution for ES3.1 and beyond is "use strict".

# Ingvar von Schoultz (17 years ago)

Brendan Eich skrev:

On Sep 5, 2008, at 4:33 PM, Ingvar von Schoultz wrote:

Another silent failure, much more expensive, is when you try to write to non-writable properties. (And, apart from this, also the related similar problem with constant variables).

This goes back to ES1. In Netscape's original JS implementation, I reported an error which stopped the script on assignment to read-only properties (this from memory, I don't have source code for the old "Mocha" runtime). Without try/catch in ES1, this was viewed as too harsh by the TG1 group in TC39, leaving only silent-but-deadly failure to modify the lvalue (the result of the assignment expression is the unfiltered right-hand-side value, so you can't tell).

This was not revisited when exception handling was added in ES3 :-(.

Interesting, thanks for telling me.

I would really like to know the reason for this strange problem with non-writable properties, and try to see if there's any way a better solution can be found than this seriously expensive language bug.

The proposed solution for ES3.1 and beyond is "use strict".

Yes, certainly, but there will always be those who find that unsuitable, or who find that it breaks the old code that they must work with. It's very undesirable that the non-strict mode become bad quality.

Despite lots of years using JavaScript I'm not even aware of any non-writable properties, so I'm surprised that programs can depend on them. It's even more strange, then, that program bugs with clearly incorrect writes to such exotic properties are so frequent that throwing would break a significant number of programs.

Can these properties be distinguished somehow? Then maybe the bug can at least be limited to a few old properties.

# Brendan Eich (17 years ago)

On Sep 5, 2008, at 5:19 PM, Ingvar von Schoultz wrote:

Can these properties be distinguished somehow? Then maybe the bug can at least be limited to a few old properties.

Search through chapter 15 of ES3 for ReadOnly.

# Garrett Smith (17 years ago)

On Fri, Sep 5, 2008 at 5:19 PM, Ingvar von Schoultz <ingvar-v-s at comhem.se> wrote:

Brendan Eich skrev:

On Sep 5, 2008, at 4:33 PM, Ingvar von Schoultz wrote:

This goes back to ES1. In Netscape's original JS implementation, I reported an error which stopped the script on assignment to read-only properties (this from memory, I don't have source code for the old "Mocha" runtime). Without try/catch in ES1, this was viewed as too harsh by the TG1 group in TC39, leaving only silent-but-deadly failure to modify the lvalue (the result of the assignment expression is the unfiltered right-hand-side value, so you can't tell).

This was not revisited when exception handling was added in ES3 :-(.

But seems to have been the spirit of assigning to a property that has a getter and no setter. That throws, if I'm not mistaken.

Can these properties be distinguished somehow? Then maybe the bug can at least be limited to a few old properties.

The attributes (ReadOnly, DontDelete, DontEnum) were kind of hidden to the user and I think that's what the getPropertyDescriptor is trying to address. It seems that the only way to detect ReadOnly is to try to set a value and then check to see if it was successful. Knowing ahead of time what is ReadOnly is more useful. It's not usually a problem. For example:

function x(){ } x.length++; // can't do it; x.length; => 0

You probably knew it but just didn't know it.

Garrett

# Ingvar von Schoultz (17 years ago)

Brendan Eich wrote:

On Sep 5, 2008, at 5:19 PM, Ingvar von Schoultz wrote:

Can these properties be distinguished somehow? Then maybe the bug can at least be limited to a few old properties.

Search through chapter 15 of ES3 for ReadOnly.

Thanks. Oops, I should have thought of that myself.

So it's limited to the prototype and length of native constructors, the various constants on the Math object, the source and flags of RegExp instances, and the length and internal name of functions.

I'd say trying to change any of these is odd enough that people will be well aware that it may not work, and so will try it, and then check if it worked, and when it didn't, remove the useless line.

I believe that having this in real code must be rare enough that throwing can be enabled not only in strict mode, but also when you opt in by specifying the language version number. This way we get rid of the bug now, while it's still possible. Once people are using Object.configure(), the response to forbidden write, whichever it is, becomes firmly entrenched and impossible to change.

Has this been checked by spidering? I'd really be surprised if assignment to these properties is frequent enough to be a problem in any way.

# Brendan Eich (17 years ago)

On Sep 5, 2008, at 7:00 PM, Ingvar von Schoultz wrote:

Has this been checked by spidering? I'd really be surprised if assignment to these properties is frequent enough to be a problem in any way.

Spidering is not sufficient, only suggestive. If you find more than a
few true positives, you probably can't make the change. If you find
nothing, you don't know what is behind firewalls and pay- or
registration-walls. It would take several beta cycles of a major
browser to be more sure, and you still might get in trouble later.
Probably you could tough it out if you made through the beta period.

I'm not volunteering Firefox 3.1 for this, we've got enough on that
burner.

# Ingvar von Schoultz (17 years ago)

Brendan Eich skrev:

On Sep 5, 2008, at 7:00 PM, Ingvar von Schoultz wrote:

Has this been checked by spidering? I'd really be surprised if assignment to these properties is frequent enough to be a problem in any way.

Spidering is not sufficient, only suggestive. If you find more than a few true positives, you probably can't make the change. If you find nothing, you don't know what is behind firewalls and pay- or registration-walls. It would take several beta cycles of a major browser to be more sure, and you still might get in trouble later. Probably you could tough it out if you made through the beta period.

I'm not volunteering Firefox 3.1 for this, we've got enough on that burner.

So then you consider correcting this bug too risky for opt-in by language version number?

In that case, suppose instead silent failure is limited to those few objects only. The rule could be simple: All native constructors, all RegExp instances, and .length on functions.

If making exceptions seems too inconsistent, consider that the silent failure matters only very little on these objects since users aren't developing them. It's completely different and much more serious when we're dealing with objects that the user is working on all the time, building complicated structures that change daily. Then you really need information early when things go wrong at some point hidden somewhere deep inside the structure.

All the costs and considerations are completely different, so consistency would be the wrong ideal here.

# Mark S. Miller (17 years ago)

On Fri, Sep 5, 2008 at 10:48 PM, Ingvar von Schoultz <ingvar-v-s at comhem.se> wrote:

If making exceptions seems too inconsistent, consider that the silent failure matters only very little on these objects since users aren't developing them. It's completely different and much more serious when we're dealing with objects that the user is working on all the time, building complicated structures that change daily. Then you really need information early when things go wrong at some point hidden somewhere deep inside the structure.

All the costs and considerations are completely different, so consistency would be the wrong ideal here.

So use strict mode; that's the kind of thing it's for.

# Ingvar von Schoultz (17 years ago)

Mark S. Miller skrev:

So use strict mode; that's the kind of thing it's for.

Sure, but as I said, there will always be those who find that unsuitable, or who find that it breaks the old code that they must work with. It's very undesirable that the non-strict mode become bad quality.

And failing silently here is very bad quality.

# Mark S. Miller (17 years ago)

On Fri, Sep 5, 2008 at 11:44 PM, Ingvar von Schoultz <ingvar-v-s at comhem.se> wrote:

Mark S. Miller skrev:

So use strict mode; that's the kind of thing it's for.

Sure, but as I said, there will always be those who find that unsuitable, or who find that it breaks the old code that they must work with. It's very undesirable that the non-strict mode become bad quality.

And failing silently here is very bad quality.

Strict mode is there to provide those quality-supporting rules that are not sufficiently compatible to propose for the non-strict language. You propose to throw an error for a case that is currently silent both according to the ES3 spec and by all browser behaviors. Any behavior where the ES3 spec and all browsers currently agree is likely to be legacy we can't break.

We have considered making some breaking changes conditional on version opt-in rather than strictness-opt-in. But in all cases, it is to support additional functionality (lexically nested named functions, new keywords, both of which are technically compatible within the language of ES3). If you want to propose turning off old functionality for the sake of quality, or to turn silent failures into noisy ones, again, that's what strict mode is for.

# Ingvar von Schoultz (17 years ago)

Mark S. Miller skrev:

On Fri, Sep 5, 2008 at 11:44 PM, Ingvar von Schoultz <ingvar-v-s at comhem.se> wrote:

Mark S. Miller skrev:

So use strict mode; that's the kind of thing it's for. Sure, but as I said, there will always be those who find that unsuitable, or who find that it breaks the old code that they must work with. It's very undesirable that the non-strict mode become bad quality.

And failing silently here is very bad quality.

Strict mode is there to provide those quality-supporting rules that are not sufficiently compatible to propose for the non-strict language. You propose to throw an error for a case that is currently silent both according to the ES3 spec and by all browser behaviors. Any behavior where the ES3 spec and all browsers currently agree is likely to be legacy we can't break.

We have considered making some breaking changes conditional on version opt-in rather than strictness-opt-in. But in all cases, it is to support additional functionality (lexically nested named functions, new keywords, both of which are technically compatible within the language of ES3). If you want to propose turning off old functionality for the sake of quality, or to turn silent failures into noisy ones, again, that's what strict mode is for.

I did propose a fully compatible variant where silent failure occurs in all the situations where it occurs today, and noisy failure occurs only in new situations that can't arise today:

,------- | In that case, suppose instead silent failure is limited to | those few objects only. The rule could be simple: All native | constructors, all RegExp instances, and .length on functions. `-------

Those are the only ReadOnly cases that exist in ES3.

This would essentially make it noisy where it matters, on objects configured with Object.configure(), and silent where it doesn't matter much, on objects where configuring isn't very useful anyway.

If making these rare write bugs noisy is too great a change, then switching an old program to strict mode is a far more dramatic change that will introduce far more breakage, and therefore in many cases it just isn't a viable option.

It's very good that strict mode exists, but it mustn't become an excuse for giving non-strict less care. If the quality of non-strict can be significantly improved, it's worth careful consideration.