Assigning to ReadOnly properties
On Nov 12, 2007, at 9:00 AM, Jonathan Watt wrote:
Hi,
As I understand it the reason assignment to ReadOnly properties
fails silently is that there was no try-catch prior to ECMAScript 3.
Right. Netscape 2 reported a fatal error, but during ES1
standardization we agreed to go with silence is (golden|deadly).
Could/will edition 4 require an exception to be thrown in strict mode?
Overtly incompatible, also highly desirable. So opt-in versioning
could enable this, but it's one more migration headache (see recent
exchange between Mark Miller and myself). An alternative would be a
pragma of some sort:
use readonly error
or perhaps
use readonly throw
We try to make pragmas more readable, sometimes with an extra word;
not sure this is the best way to phrase it still, but the idea would
be to enable throwing of a ReadOnlyError on assignment to a ReadOnly
property. Comments?
Brendan Eich wrote:
On Nov 12, 2007, at 9:00 AM, Jonathan Watt wrote:
Hi,
As I understand it the reason assignment to ReadOnly properties
fails silently is that there was no try-catch prior to ECMAScript 3.Right. Netscape 2 reported a fatal error, but during ES1
standardization we agreed to go with silence is (golden|deadly).Could/will edition 4 require an exception to be thrown in strict mode?
Overtly incompatible, also highly desirable. So opt-in versioning
could enable this, but it's one more migration headache (see recent
exchange between Mark Miller and myself). An alternative would be a
pragma of some sort:
I like the idea of a pragma more than opt-in versioning since it can be used in-place, and (I guess) placed before and after a section of code to turn the handling on then off again.
use readonly error
or perhaps
use readonly throw
We try to make pragmas more readable, sometimes with an extra word;
not sure this is the best way to phrase it still, but the idea would
be to enable throwing of a ReadOnlyError on assignment to a ReadOnly
property. Comments?
Why not just:
use ReadOnlyError
It's short, intuitive, and you don't need to remember whether it's "error" or "throw" in the pragma. If you can use pragmas multiple times to toggle things on and then off again, perhaps make it:
use ReadOnlyError on
// some code that should throw on assignment to ReadOnly properties
use ReadOnlyError off
Ultimately, the ability to catch this class of error is more important to me than the syntax to turn the feature on.
Jonathan
On Nov 12, 2007 9:05 AM, Brendan Eich <brendan at mozilla.org> wrote:
[..], but the idea would be to enable throwing of a ReadOnlyError on assignment to a ReadOnly property. Comments?
Hi Brendan,
I'm glad to hear that this possibility is in scope. In the Caja spec, we write:
\item[Silent errors.] In JavaScript, various operations, such as setting a ReadOnly property, fail silently rather than throwing an error. Program logic then proceeds along normal control flow paths premised on the assumption that these operations succeeded, leading to inconsistency. To program defensively in the face of this hazard, every assignment would be followed by a ``\emph{did it really happen?}'' test. This would render programs unreadable and unmaintainable. Where practical, Caja deviates from standard JavaScript by throwing an exception rather than failing silently.
For defensive consistency (as defined in my thesis) to be practical, I believe this change is necessary. I also believe that defensive consistency (or some close relative) is necessary for secure programming. Caja goes to a lot of trouble to achieve this while running on an ES3 platform. If ES4 made this change, it might make Caja's future life easier.
On Nov 12, 2007, at 9:34 AM, Jonathan Watt wrote:
I like the idea of a pragma more than opt-in versioning since it
can be used in-place, and (I guess) placed before and after a section of code
to turn the handling on then off again.
Pragmas are block-scoped, so just one should do.
use readonly error
or perhaps
use readonly throw
We try to make pragmas more readable, sometimes with an extra word; not sure this is the best way to phrase it still, but the idea would be to enable throwing of a ReadOnlyError on assignment to a ReadOnly property. Comments?
Why not just:
use ReadOnlyError
Sure -- pragma convention so far has been terse and lower-case, e.g.
'use strict' or 'use decimal ...'.
It's short, intuitive, and you don't need to remember whether it's
"error" or "throw" in the pragma. If you can use pragmas multiple times to
toggle things on and then off again, perhaps make it:use ReadOnlyError on
// some code that should throw on assignment to ReadOnly properties
use ReadOnlyError off
I'm going to summon Jeff Dyer here.
Ultimately, the ability to catch this class of error is more
important to me than the syntax to turn the feature on.
Indeed, but syntax is necessary and we may as well hash it out here.
Speaking of catching, it would be unfortunate if you wrote
try { something that might set a ReadOnly property } catch (e : ReadOnlyError) { ... }
but forgot the pragma, and never heard a word of warning. There are
no warnings in the spec, so normative error seems best. But if you're
bothering to catch ReadOnlyError, must you also 'use ReadOnlyError'
first (in the try, or in a block enclosing the whole try-catch)?
The pragma is necessary, since you may want termination semantics
(use ReadOnlyError w/o ever catching that exception). But if you do
catch, must you use as well? I'm throwing this out for some quick
discussion. Thanks for posting about ReadOnly errors, I had it on my
to-do list but you beat me to it :-).
On Nov 12, 2007, at 9:40 AM, Mark Miller wrote:
On Nov 12, 2007 9:05 AM, Brendan Eich <brendan at mozilla.org> wrote:
[..], but the idea would be to enable throwing of a ReadOnlyError on assignment to a ReadOnly property. Comments?
Hi Brendan,
I'm glad to hear that this possibility is in scope.
Me too -- this was one of the biggest botches during standardization
of ES1, and it should have been revisited when exception handling was
added in ES3.
In the Caja spec, we write:
\item[Silent errors.] In JavaScript, various operations, such as
setting a ReadOnly property, fail silently rather than throwing an error.
Program logic then proceeds along normal control flow paths premised on the assumption that these operations succeeded, leading to
inconsistency. To program defensively in the face of this hazard, every assignment
would be followed by a ``\emph{did it really happen?}'' test. This would
render programs unreadable and unmaintainable. Where practical, Caja
deviates from standard JavaScript by throwing an exception rather than failing
silently.For defensive consistency (as defined in my thesis) to be practical, I believe this change is necessary. I also believe that defensive consistency (or some close relative) is necessary for secure programming. Caja goes to a lot of trouble to achieve this while running on an ES3 platform. If ES4 made this change, it might make Caja's future life easier.
This may have been what I was thinking of when I wrote "incompatible
subset" the other week, now that you mention it. Syntactically Caja
is a subset of ES3, but the runtime semantics not only add
capabilities, they make even programs that are running in a single
trust label world, and that blithely continue past an attempt to set
a ReadOnly property in ES3, stop cold instead. As any sane runtime
semantics should!
We could just "make this change" if ES4 is selected. But ES4 already
makes Object, Array, etc. ReadOnly|DontDelete bindings in the global
object, necessary for sound typing, JSON defense in depth, etc. We
reckoned any AOP on these constructor functions was rare compared to
AOP on prototype methods (a spider of the web confirmed this; only an
old MSN .js file hacked Error for some reason). And it's not as if
ES1-3 provide for consistent AOP on the global constructor functions
anway:
Therefore ES4 making the standard constructors bound by immutable
global properties is one of the two true incompatibilities mentioned in
www.ecmascript.org/es4/spec/incompatibilities.pdf
and we think it won't break the web, since the situation in real
browsers is inconsistent, not only per ES3's wild inconsistencies,
but because some browsers more than others avoid reflecting on
current values of the mutable bindings in spite of ES3 specifying
that sporadically.
If some script somewhere does set Error = MyError, though, the
silence-is-golden behavior will let that pass without exception in
today's implementations. So changing ES4 to throw ReadOnlyError will
"break" what might have been benignly broken already. Not sure this
is going to cause hardship, or that it should not cause hardship.
You could argue that it's better to find out sooner than later. But
the costs may be born by browser users, not web developers, since the
content was written in 2002, the team that wrote it is long gone, and
no one helpful is reading hostmaster at bigcompany.com.
If we instead add a pragma to enable ReadOnlyError, that would still
help Caja at some point, right?
On Nov 12, 2007 11:40 AM, Mark Miller <erights at gmail.com> wrote:
On Nov 12, 2007 9:05 AM, Brendan Eich <brendan at mozilla.org> wrote:
[..], but the idea would be to enable throwing of a ReadOnlyError on assignment to a ReadOnly property. Comments?
Hi Brendan,
I'm glad to hear that this possibility is in scope. In the Caja spec, we write:
\item[Silent errors.] In JavaScript, various operations, such as setting a ReadOnly property, fail silently rather than throwing an error. Program logic then proceeds along normal control flow paths premised on the assumption that these operations succeeded, leading to inconsistency.
Out of curiosity, what other JS operations fail silently?
-Yuh-Ruey Chen
Brendan Eich wrote:
On Nov 12, 2007, at 9:34 AM, Jonathan Watt wrote:
I like the idea of a pragma more than opt-in versioning since it
can be used in-place, and (I guess) placed before and after a section of code
to turn the handling on then off again.Pragmas are block-scoped, so just one should do.
Okay.
use readonly error
or perhaps
use readonly throw
We try to make pragmas more readable, sometimes with an extra word; not sure this is the best way to phrase it still, but the idea would be to enable throwing of a ReadOnlyError on assignment to a ReadOnly property. Comments? Why not just:
use ReadOnlyError
Sure -- pragma convention so far has been terse and lower-case, e.g.
'use strict' or 'use decimal ...'.
I'm not sure what maintaining case convention gives you here. Using |use ReadOnlyError| is about as intuitive and easy to remember as it could be I think.
Speaking of catching, it would be unfortunate if you wrote
try { something that might set a ReadOnly property } catch (e : ReadOnlyError) { ... }
but forgot the pragma, and never heard a word of warning. There are
no warnings in the spec, so normative error seems best. But if you're
bothering to catch ReadOnlyError, must you also 'use ReadOnlyError'
first (in the try, or in a block enclosing the whole try-catch)?
Implicitly enabling ReadOnlyError seems like a very good idea to me (assuming it can be implemented efficiently). Of course implementations that have facilities to warn should still warn. Maybe the spec can have non-normative text saying as much.
Alternatively, maybe it should simply be an error to use ReadOnlyError if it hasn't been turned on.
Jonathan
On Nov 12, 2007, at 1:43 PM, YR Chen wrote:
On Nov 12, 2007 11:40 AM, Mark Miller <erights at gmail.com> wrote: On Nov 12, 2007 9:05 AM, Brendan Eich <brendan at mozilla.org> wrote:
[..], but the idea would be to enable throwing of a ReadOnlyError on assignment to a ReadOnly property. Comments?
Hi Brendan,
I'm glad to hear that this possibility is in scope. In the Caja
spec, we write:\item[Silent errors.] In JavaScript, various operations, such as
setting a ReadOnly property, fail silently rather than throwing an error.
Program logic then proceeds along normal control flow paths premised on the assumption that these operations succeeded, leading to inconsistency.Out of curiosity, what other JS operations fail silently?
Nothing as badly silent as assigning to a ReadOnly property, but here
I'll bitch about a similar change during ES1 standardization: delete
x => false if x is in the scope chain but bound with the DontDelete
attribute -- otherwise delete x => true, either if it was found
without DontDelete and therefore deleted -- or just not found! So
delete asdfewr => true. Same for any more qualified reference, e.g.
delete foo.bar when you mean foo.baz. Oh, and if bar was found in a
prototype object of foo, delete foo.bar => true without doing anything!
This all seems wrong, and IIRC in Netscape 2, JS1, the original buggy
progenitor language, delete would report an uncatchable error on bad
inputs. Just as assigning to ReadOnly would. Someone with an old PC
and Netscape 2 or 3, please confirm.
I forget why I gave this up in committee. Lot of horse-trading back
then to get to ES1.
Anyway, with exception handling these both seem like warts. At least
with delete, the expression using that operator at the top level has
a boolean result -- but the result could be true and the property is
still "in" the object (via prototype-based delegation). Beware.
new here.
On Nov 12, 2007 2:22 PM, Jonathan Watt <jwatt at jwatt.org> wrote:
use readonly error
or perhaps
use readonly throw
We try to make pragmas more readable, sometimes with an extra word; not sure this is the best way to phrase it still, but the idea would be to enable throwing of a ReadOnlyError on assignment to a ReadOnly property. Comments? Why not just:
use ReadOnlyError
Sure -- pragma convention so far has been terse and lower-case, e.g. 'use strict' or 'use decimal ...'.
I'm not sure what maintaining case convention gives you here. Using |use ReadOnlyError| is about as intuitive and easy to remember as it could be I think.
Since the existing pragma directives are all single words, they could be camel-humped or underscored for all we know.
Actually, no, I guess there's "use xml namespace [string or Namespace object]", which might suggest "use error [Class reference or object]". The latter could be used for other types of errors as well.
T. Michael Keesey wrote:
On Nov 12, 2007 2:22 PM, Jonathan Watt <jwatt at jwatt.org> wrote:
I'm not sure what maintaining case convention gives you here. Using |use ReadOnlyError| is about as intuitive and easy to remember as it could be I think.
Since the existing pragma directives are all single words, they could be camel-humped or underscored for all we know.
Actually, no, I guess there's "use xml namespace [string or Namespace object]", which might suggest "use error [Class reference or object]". The latter could be used for other types of errors as well.
use error ReadOnlyError
seems fine to me, if a little redundant.
On Nov 12, 2007 2:50 PM, Jonathan Watt <jwatt at jwatt.org> wrote:
Actually, no, I guess there's "use xml namespace [string or Namespace object]", which might suggest "use error [Class reference or object]". The latter could be used for other types of errors as well.
use error ReadOnlyError
seems fine to me, if a little redundant.
Then again, it doesn't exactly lend itself to an obvious negative....
On Nov 12, 2007 2:30 PM, Brendan Eich <brendan at mozilla.org> wrote:
Nothing as badly silent as assigning to a ReadOnly property, but here I'll bitch about a similar change during ES1 standardization: delete x => false
Anyway, with exception handling these both seem like warts. At least with delete, the expression using that operator at the top level has a boolean result -- but the result could be true and the property is still "in" the object (via prototype-based delegation). Beware.
Another point of agreement! Caja attempts to be approximately a "fail-stop subset" of ES3 (or ES3R), meaning (from the Caja spec):
Caja is only a subset of JavaScript in a limited sense: While a Caja program
has not explicitly indicated a failure, it executes within JavaScript's
semantics. By \emph{indicate a failure}, we mean either throwing an exception
or returning \code{undefined} for a property read.
To fix delete within this constraint, "delete <whatever>" in Caja
either return true or throws.
On Nov 12, 2007, at 4:01 PM, Mark Miller wrote:
To fix delete within this constraint, "delete <whatever>" in Caja either return true or throws.
At this point, either ES4 slides down the slippery slope a bit and
courts migration pain that holds it back (we really do expect most
pages on the web today to work if shipped as application/
ecmascript;version=4 or application/javascript;version=2); or I think
we need a better pragma story.
We could support
use DeleteError, ReadOnlyError
and encourage users to litter their new code with this line at the
top. There's good precedent from Perl and other languages for a
superstitious
use strict
at the top. But the Errors are a mouthful and eyeful. Since I just
mentioned strict mode, I'll note again that the single runtime
semantic change strict mode causes from standard mode (assuming the
program passes strict mode's type checker and lint-like sanity
checker) is a change to eval: strict mode results in a different
runtime eval, such that eval cannot create bindings in its dynamic
scope. We could slide down this slope a bit by adding DeleteError
and ReadOnlyError exception behavior to 'use strict'.
Comments welcome, but you're probably in favor of opt-in versioning
making all of these changes implicitly ;-). Or perhaps a 'use Caja'
pragma :-P.
On Nov 12, 2007 4:25 PM, Brendan Eich <brendan at mozilla.org> wrote:
On Nov 12, 2007, at 4:01 PM, Mark Miller wrote:
To fix delete within this constraint, "delete <whatever>" in Caja either return true or throws.
At this point, either ES4 slides down the slippery slope a bit and courts migration pain that holds it back (we really do expect most pages on the web today to work if shipped as application/ ecmascript;version=4 or application/javascript;version=2); or I think we need a better pragma story.
We could support
use DeleteError, ReadOnlyError
and encourage users to litter their new code with this line at the top. There's good precedent from Perl and other languages for a superstitious
use strict
at the top. But the Errors are a mouthful and eyeful. Since I just mentioned strict mode, I'll note again that the single runtime semantic change strict mode causes from standard mode (assuming the program passes strict mode's type checker and lint-like sanity checker) is a change to eval: strict mode results in a different runtime eval, such that eval cannot create bindings in its dynamic scope. We could slide down this slope a bit by adding DeleteError and ReadOnlyError exception behavior to 'use strict'.
Comments welcome, but you're probably in favor of opt-in versioning making all of these changes implicitly ;-). Or perhaps a 'use Caja' pragma :-P.
IMO the only sane thing to do in this regard, if we were to do anything, is to add what we consider bug-fixing behavior to the effects of "use strict". We've already hung the eval change there, and though I don't remember right now if "with" has to be "reformed with" in strict mode I know we've talked about it (obviously this is a syntactic constraint only); if everyone agrees that deleting an unknown property would be an error in a rational world, or that writing to a read-only property should be, then these are clear language bugs that don't need their own knobs. We need knobs, but too many knobs are if anything worse than none at all.
We made a very special exception for eval wrt changing run-time semantics in strict mode for the code in the scope of the "use strict" pragma, and we've been careful to state that it's the exception that proves the rule: no run-time differences between strict and standard modes. The question about the bug fixes for delete and read-only properties is: are these errors so important and wide-spread that they warrant breaking that rule? The value of the rule is that "use strict" gives you verification only, after that you don't have to remember anything special (except for the change to eval); if we attach these bug fixes to the same rule, then (a) where do we stop and (b) is the language better, worse, or just different?
On Nov 12, 2007, at 5:19 PM, Lars T Hansen wrote:
IMO the only sane thing to do in this regard, if we were to do anything, is to add what we consider bug-fixing behavior to the effects of "use strict". We've already hung the eval change there,
For good reason too -- if eval can create bindings in its dynamic
scope, the type checker and even name sanity checks in strict mode
are a bit lost, aren't they?
and though I don't remember right now if "with" has to be "reformed with" in strict mode I know we've talked about it (obviously this is a syntactic constraint only);
I asked whether strict mode should require "reformed with" here:
There may be a meeting note soon after that date, but wiki search
seems broken, especially at finding meeting notes. Do you want a
ticket on this?
if everyone agrees that deleting an unknown property would be an error in a rational world, or that writing to a read-only property should be, then these are clear language bugs that don't need their own knobs. We need knobs, but too many knobs are if anything worse than none at all.
Agreed.
We made a very special exception for eval wrt changing run-time semantics in strict mode for the code in the scope of the "use strict" pragma, and we've been careful to state that it's the exception that proves the rule: no run-time differences between strict and standard modes. The question about the bug fixes for delete and read-only properties is: are these errors so important and wide-spread that they warrant breaking that rule? The value of the rule is that "use strict" gives you verification only, after that you don't have to remember anything special (except for the change to eval); if we attach these bug fixes to the same rule, then (a) where do we stop and (b) is the language better, worse, or just different?
This is the slippery slope, indeed.
But first, let me check my understanding: 'use strict' must restrict
eval from creating bindings in its dynamic scope, or type checking is
defeated (eval could shadow an outer binding). Right?
On Nov 12, 2007, at 2:30 PM, Brendan Eich wrote:
On Nov 12, 2007, at 1:43 PM, YR Chen wrote:
Out of curiosity, what other JS operations fail silently?
Nothing as badly silent as assigning to a ReadOnly property, but
here I'll bitch about a similar change during ES1 standardization:
delete x => false if x is in the scope chain but bound with the
DontDelete attribute -- otherwise delete x => true, either if it
was found without DontDelete and therefore deleted -- or just not
found! So delete asdfewr => true. Same for any more qualified
reference, e.g. delete foo.bar when you mean foo.baz. Oh, and if
bar was found in a prototype object of foo, delete foo.bar => true
without doing anything!This all seems wrong, and IIRC in Netscape 2, JS1, the original
buggy progenitor language, delete would report an uncatchable error
on bad inputs. Just as assigning to ReadOnly would. Someone with an
old PC and Netscape 2 or 3, please confirm.
My memory failed me -- I was thinking of ReadOnly errors, which were
indeed uncatchable errors in Netscape 2 and 3. There was no delete
operator until Netscape 4, as far as I can tell now (but I can't rely
on memory, which must mean something -- too much time has passed, or
perhaps too much trauma from the pre-historic JS days ;-)).
As I understand it the reason assignment to ReadOnly properties fails silently is that there was no try-catch prior to ECMAScript 3. Could/will edition 4 require an exception to be thrown in strict mode?
Jonathan