Discussion:
Throwing errors on mutating immutable bindings
Shu-yu Guo
2014-10-01 00:09:11 UTC
Permalink
Hi all,

In the current draft, I see 2 different places where assigning to an immutable binding ('const') throws an error:

1) Dynamically throwing a TypeError in SetMutableBinding, http://people.mozilla.org/~jorendorff/es6-draft.html#sec-declarative-environment-records-setmutablebinding-n-v-s
2) Statically throwing a Syntax Error in assignment expressions, http://people.mozilla.org/~jorendorff/es6-draft.html#sec-assignment-operators-static-semantics-early-errors

1) throws only in strict mode code, while 2) throws regardless. 2) is also best effort; seems to be implementation-dependent what "can statically determine" entails.

Is the intention that assigning to consts silently nops if the implementation cannot determine the assignment to be to a const statically, in non-strict code, but implementations *should* make a best effort to report such cases eagerly, regardless of strictness? Seems kind of odd to me; perhaps I am misreading?
--
shu
Allen Wirfs-Brock
2014-10-01 00:58:42 UTC
Permalink
Post by Shu-yu Guo
Hi all,
1) Dynamically throwing a TypeError in SetMutableBinding, http://people.mozilla.org/~jorendorff/es6-draft.html#sec-declarative-environment-records-setmutablebinding-n-v-s
2) Statically throwing a Syntax Error in assignment expressions, http://people.mozilla.org/~jorendorff/es6-draft.html#sec-assignment-operators-static-semantics-early-errors
see bug https://bugs.ecmascript.org/show_bug.cgi?id=3148 the "can" in that sentence isn't meant to be interpreted as "best effort" but instead more along the lines of "it is provable".

We need to refine that language, but the test is approximately that there are no with blocks inside the scope of the const declaration and surrounding the reference to the const. binding
Post by Shu-yu Guo
1) throws only in strict mode code, while 2) throws regardless. 2) is also best effort; seems to be implementation-dependent what "can statically determine" entails.
Is the intention that assigning to consts silently nops if the implementation cannot determine the assignment to be to a const statically, in non-strict code, but implementations *should* make a best effort to report such cases eagerly, regardless of strictness? Seems kind of odd to me; perhaps I am misreading?
1) looks like a bug to me. I pretty sure it was never the intent for assignments to const binding to silently fail in non-strict code. The current semantics of SetMutableBinding is a carry over from ES5 where immutable bindings were only used (I have to double check this) for FunctionExpression function name bindings. The legacy of ES3 (hence non-strict ES5) was to did not throw on assignments to such function name bindings.

I'll probably have to do some extra special casing to preserve the ES3/5 semantics for assignment to function names and make the throw unconditional to other immutable bindings

Allen


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20140930/d16f7e98/attachment.html>
Erik Arvidsson
2014-10-01 14:09:08 UTC
Permalink
The static error is problematic. I'm pretty sure that engines that do lazy
parsing of functions is not going to report static errors before doing a
full parse of the function.

I think we need to either enforce this or remove this restriction. Anything
in between will lead to inconsistent behavior between engines.

On Tue, Sep 30, 2014 at 8:58 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>
Post by Shu-yu Guo
Hi all,
In the current draft, I see 2 different places where assigning to an
1) Dynamically throwing a TypeError in SetMutableBinding,
http://people.mozilla.org/~jorendorff/es6-draft.html#sec-declarative-environment-records-setmutablebinding-n-v-s
2) Statically throwing a Syntax Error in assignment expressions,
http://people.mozilla.org/~jorendorff/es6-draft.html#sec-assignment-operators-static-semantics-early-errors
see bug https://bugs.ecmascript.org/show_bug.cgi?id=3148 the "can" in
that sentence isn't meant to be interpreted as "best effort" but instead
more along the lines of "it is provable".
We need to refine that language, but the test is approximately that there
are no with blocks inside the scope of the const declaration and
surrounding the reference to the const. binding
1) throws only in strict mode code, while 2) throws regardless. 2) is also
best effort; seems to be implementation-dependent what "can statically
determine" entails.
Is the intention that assigning to consts silently nops if the
implementation cannot determine the assignment to be to a const statically,
in non-strict code, but implementations *should* make a best effort to
report such cases eagerly, regardless of strictness? Seems kind of odd to
me; perhaps I am misreading?
1) looks like a bug to me. I pretty sure it was never the intent for
assignments to const binding to silently fail in non-strict code. The
current semantics of SetMutableBinding is a carry over from ES5 where
immutable bindings were only used (I have to double check this) for
FunctionExpression function name bindings. The legacy of ES3 (hence
non-strict ES5) was to did not throw on assignments to such function name
bindings.
I'll probably have to do some extra special casing to preserve the ES3/5
semantics for assignment to function names and make the throw unconditional
to other immutable bindings
Allen
_______________________________________________
es-discuss mailing list
es-discuss at mozilla.org
https://mail.mozilla.org/listinfo/es-discuss
--
erik
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141001/c62c5282/attachment.html>
Oliver Hunt
2014-10-01 15:20:15 UTC
Permalink
JSC does lazy parsing of nested functions and we have no problem reporting static errors, so i?m not sure what you believe is the problem here.

?Oliver
The static error is problematic. I'm pretty sure that engines that do lazy parsing of functions is not going to report static errors before doing a full parse of the function.
I think we need to either enforce this or remove this restriction. Anything in between will lead to inconsistent behavior between engines.
Post by Shu-yu Guo
Hi all,
1) Dynamically throwing a TypeError in SetMutableBinding, http://people.mozilla.org/~jorendorff/es6-draft.html#sec-declarative-environment-records-setmutablebinding-n-v-s <http://people.mozilla.org/~jorendorff/es6-draft.html#sec-declarative-environment-records-setmutablebinding-n-v-s>
2) Statically throwing a Syntax Error in assignment expressions, http://people.mozilla.org/~jorendorff/es6-draft.html#sec-assignment-operators-static-semantics-early-errors <http://people.mozilla.org/~jorendorff/es6-draft.html#sec-assignment-operators-static-semantics-early-errors>
see bug https://bugs.ecmascript.org/show_bug.cgi?id=3148 <https://bugs.ecmascript.org/show_bug.cgi?id=3148> the "can" in that sentence isn't meant to be interpreted as "best effort" but instead more along the lines of "it is provable".
We need to refine that language, but the test is approximately that there are no with blocks inside the scope of the const declaration and surrounding the reference to the const. binding
Post by Shu-yu Guo
1) throws only in strict mode code, while 2) throws regardless. 2) is also best effort; seems to be implementation-dependent what "can statically determine" entails.
Is the intention that assigning to consts silently nops if the implementation cannot determine the assignment to be to a const statically, in non-strict code, but implementations *should* make a best effort to report such cases eagerly, regardless of strictness? Seems kind of odd to me; perhaps I am misreading?
1) looks like a bug to me. I pretty sure it was never the intent for assignments to const binding to silently fail in non-strict code. The current semantics of SetMutableBinding is a carry over from ES5 where immutable bindings were only used (I have to double check this) for FunctionExpression function name bindings. The legacy of ES3 (hence non-strict ES5) was to did not throw on assignments to such function name bindings.
I'll probably have to do some extra special casing to preserve the ES3/5 semantics for assignment to function names and make the throw unconditional to other immutable bindings
Allen
_______________________________________________
es-discuss mailing list
es-discuss at mozilla.org <mailto:es-discuss at mozilla.org>
https://mail.mozilla.org/listinfo/es-discuss <https://mail.mozilla.org/listinfo/es-discuss>
--
erik
_______________________________________________
es-discuss mailing list
es-discuss at mozilla.org
https://mail.mozilla.org/listinfo/es-discuss
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141001/7756dbf2/attachment.html>
Mark S. Miller
2014-10-01 15:39:17 UTC
Permalink
Post by Oliver Hunt
JSC does lazy parsing of nested functions and we have no problem reporting
static errors, so i?m not sure what you believe is the problem here.
Hi Oliver,

First, I agree with you. This shouldn't be a problem.

However, both JSC and v8 cause confusion when they use the term "lazy
parsing". There are plenty of static errors that could not be reported with
a truly lazy parser, i.e., one which actually postpones parsing. v8 for
example uses a lightweight but accurate parser that is supposed to catch
early errors early, but, afaik, doesn't construct an actual ast.

See
https://code.google.com/p/v8/issues/detail?id=2728
https://code.google.com/p/v8/issues/detail?id=2470
https://code.google.com/p/google-caja/issues/detail?id=1616
https://bugs.webkit.org/show_bug.cgi?id=106160

(Oliver, I note that the last bug above is still open, which continues to
cause SES to use an expensive workaround when run on Safari.)

So the question is not "Can this early error be reported accurately without
parsing?", since essentially none can, so no engines postpone parsing. The
question is whether a particular new early error would require these
lightweight early parsers to do something that they can't do while
preserving their current efficiency.

Statically apparent assignment to const variables does not seem like a
burden.

If there is an intervening "with" or sloppy direct eval, then there is not
a statically apparent assignment to a const variable. Since this can only
occur in sloppy code anyway, it seems more consistent with the rest of
sloppy mode for this failed assignment to be silent, rather than
dynamically throwing an error.
Post by Oliver Hunt
?Oliver
The static error is problematic. I'm pretty sure that engines that do lazy
parsing of functions is not going to report static errors before doing a
full parse of the function.
I think we need to either enforce this or remove this restriction.
Anything in between will lead to inconsistent behavior between engines.
On Tue, Sep 30, 2014 at 8:58 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>
Post by Shu-yu Guo
Hi all,
In the current draft, I see 2 different places where assigning to an
1) Dynamically throwing a TypeError in SetMutableBinding,
http://people.mozilla.org/~jorendorff/es6-draft.html#sec-declarative-environment-records-setmutablebinding-n-v-s
2) Statically throwing a Syntax Error in assignment expressions,
http://people.mozilla.org/~jorendorff/es6-draft.html#sec-assignment-operators-static-semantics-early-errors
see bug https://bugs.ecmascript.org/show_bug.cgi?id=3148 the "can" in
that sentence isn't meant to be interpreted as "best effort" but instead
more along the lines of "it is provable".
We need to refine that language, but the test is approximately that there
are no with blocks inside the scope of the const declaration and
surrounding the reference to the const. binding
1) throws only in strict mode code, while 2) throws regardless. 2) is
also best effort; seems to be implementation-dependent what "can statically
determine" entails.
Is the intention that assigning to consts silently nops if the
implementation cannot determine the assignment to be to a const statically,
in non-strict code, but implementations *should* make a best effort to
report such cases eagerly, regardless of strictness? Seems kind of odd to
me; perhaps I am misreading?
1) looks like a bug to me. I pretty sure it was never the intent for
assignments to const binding to silently fail in non-strict code. The
current semantics of SetMutableBinding is a carry over from ES5 where
immutable bindings were only used (I have to double check this) for
FunctionExpression function name bindings. The legacy of ES3 (hence
non-strict ES5) was to did not throw on assignments to such function name
bindings.
I'll probably have to do some extra special casing to preserve the ES3/5
semantics for assignment to function names and make the throw unconditional
to other immutable bindings
Allen
_______________________________________________
es-discuss mailing list
es-discuss at mozilla.org
https://mail.mozilla.org/listinfo/es-discuss
--
erik
_______________________________________________
es-discuss mailing list
es-discuss at mozilla.org
https://mail.mozilla.org/listinfo/es-discuss
_______________________________________________
es-discuss mailing list
es-discuss at mozilla.org
https://mail.mozilla.org/listinfo/es-discuss
--
Cheers,
--MarkM
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141001/ee5370cd/attachment-0001.html>
Allen Wirfs-Brock
2014-10-01 15:59:00 UTC
Permalink
On Oct 1, 2014, at 8:39 AM, Mark S. Miller wrote:

...

I was with you until you got to the following point
If there is an intervening "with" or sloppy direct eval, then there is not a statically apparent assignment to a const variable. Since this can only occur in sloppy code anyway, it seems more consistent with the rest of sloppy mode for this failed assignment to be silent, rather than dynamically throwing an error.
const is a new kind of declaration unlike any declaration form that previous existed in ES, so Ii don't think its handling introduces any legacy consistency issues. If somebody is using const, regard less of mode, they pretty clearly expect assignments to any const bindings to be illegal. And, I don't think any body wants new silent failure errors, even in sloppy mode. The most consistent thing is for runtime detected assignments to const bindings to always be noisy errors. Early where possible, at runtime in the rest of the cases.

Allen
Mark S. Miller
2014-10-01 16:05:40 UTC
Permalink
Good point. If we can require all such assignments to be rejected
statically, why is a runtime assignment to a const variable even possible?
Can't we just assert that this cannot occur?


On Wed, Oct 1, 2014 at 8:59 AM, Allen Wirfs-Brock <allen at wirfs-brock.com>
Post by Allen Wirfs-Brock
...
I was with you until you got to the following point
Post by Mark S. Miller
If there is an intervening "with" or sloppy direct eval, then there is
not a statically apparent assignment to a const variable. Since this can
only occur in sloppy code anyway, it seems more consistent with the rest of
sloppy mode for this failed assignment to be silent, rather than
dynamically throwing an error.
const is a new kind of declaration unlike any declaration form that
previous existed in ES, so Ii don't think its handling introduces any
legacy consistency issues. If somebody is using const, regard less of
mode, they pretty clearly expect assignments to any const bindings to be
illegal. And, I don't think any body wants new silent failure errors, even
in sloppy mode. The most consistent thing is for runtime detected
assignments to const bindings to always be noisy errors. Early where
possible, at runtime in the rest of the cases.
Allen
--
Cheers,
--MarkM
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141001/05de9669/attachment.html>
Allen Wirfs-Brock
2014-10-01 16:15:02 UTC
Permalink
Good point. If we can require all such assignments to be rejected statically, why is a runtime assignment to a const variable even possible? Can't we just assert that this cannot occur?
The runtime cases I meant are the ones you mentioned. Sloppy with or eval dynamically shadowing a sloppy a [[Set]] reference to a const binding. Can't be a early error, should be a runtime error.

Allen
...
I was with you until you got to the following point
If there is an intervening "with" or sloppy direct eval, then there is not a statically apparent assignment to a const variable. Since this can only occur in sloppy code anyway, it seems more consistent with the rest of sloppy mode for this failed assignment to be silent, rather than dynamically throwing an error.
const is a new kind of declaration unlike any declaration form that previous existed in ES, so Ii don't think its handling introduces any legacy consistency issues. If somebody is using const, regard less of mode, they pretty clearly expect assignments to any const bindings to be illegal. And, I don't think any body wants new silent failure errors, even in sloppy mode. The most consistent thing is for runtime detected assignments to const bindings to always be noisy errors. Early where possible, at runtime in the rest of the cases.
Allen
--
Cheers,
--MarkM
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141001/8cb2765a/attachment.html>
Mark Miller
2014-10-01 17:30:29 UTC
Permalink
On Wed, Oct 1, 2014 at 9:15 AM, Allen Wirfs-Brock <allen at wirfs-brock.com>
Post by Mark S. Miller
Good point. If we can require all such assignments to be rejected
statically, why is a runtime assignment to a const variable even possible?
Can't we just assert that this cannot occur?
The runtime cases I meant are the ones you mentioned. Sloppy with or eval
dynamically shadowing a sloppy a [[Set]] reference to a const binding.
Can't be a early error, should be a runtime error.
Although it is a bit late to suggest it ;) ...

Couldn't we have "with" and sloppy direct eval ignore/skip const and let
bindings? Then these errors could always be early.

I have no argument with the answer "too late", but still curious if there's
another reason.
Post by Mark S. Miller
Allen
Post by Allen Wirfs-Brock
...
I was with you until you got to the following point
Post by Mark S. Miller
If there is an intervening "with" or sloppy direct eval, then there is
not a statically apparent assignment to a const variable. Since this can
only occur in sloppy code anyway, it seems more consistent with the rest of
sloppy mode for this failed assignment to be silent, rather than
dynamically throwing an error.
const is a new kind of declaration unlike any declaration form that
previous existed in ES, so Ii don't think its handling introduces any
legacy consistency issues. If somebody is using const, regard less of
mode, they pretty clearly expect assignments to any const bindings to be
illegal. And, I don't think any body wants new silent failure errors, even
in sloppy mode. The most consistent thing is for runtime detected
assignments to const bindings to always be noisy errors. Early where
possible, at runtime in the rest of the cases.
--
Cheers,
--MarkM
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141001/3eb3beef/attachment.html>
Allen Wirfs-Brock
2014-10-01 17:34:11 UTC
Permalink
Post by Allen Wirfs-Brock
Good point. If we can require all such assignments to be rejected statically, why is a runtime assignment to a const variable even possible? Can't we just assert that this cannot occur?
The runtime cases I meant are the ones you mentioned. Sloppy with or eval dynamically shadowing a sloppy a [[Set]] reference to a const binding. Can't be a early error, should be a runtime error.
Although it is a bit late to suggest it ;) ...
Couldn't we have "with" and sloppy direct eval ignore/skip const and let bindings? Then these errors could always be early.
Actually we've already agreed that eval puts const/let/class bindings into a separate lexical scope. But, an eval can still var shadow an outer const declaration.

Allen

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141001/98d1da02/attachment.html>
Jason Orendorff
2014-10-01 18:32:36 UTC
Permalink
I think there is a way that the error could occur at runtime even in
all-strict-mode code: when a new const is added at toplevel in a
second script.

<script>
"use strict";
function f(value) { x = value; }
</script>
<script>
"use strict";
const x = 0;
f(1);
</script>

-j


On Wed, Oct 1, 2014 at 11:15 AM, Allen Wirfs-Brock
Post by Mark S. Miller
Good point. If we can require all such assignments to be rejected
statically, why is a runtime assignment to a const variable even possible?
Can't we just assert that this cannot occur?
The runtime cases I meant are the ones you mentioned. Sloppy with or eval
dynamically shadowing a sloppy a [[Set]] reference to a const binding.
Can't be a early error, should be a runtime error.
Allen
Post by Allen Wirfs-Brock
...
I was with you until you got to the following point
Post by Mark S. Miller
If there is an intervening "with" or sloppy direct eval, then there is
not a statically apparent assignment to a const variable. Since this can
only occur in sloppy code anyway, it seems more consistent with the rest of
sloppy mode for this failed assignment to be silent, rather than dynamically
throwing an error.
const is a new kind of declaration unlike any declaration form that
previous existed in ES, so Ii don't think its handling introduces any legacy
consistency issues. If somebody is using const, regard less of mode, they
pretty clearly expect assignments to any const bindings to be illegal. And,
I don't think any body wants new silent failure errors, even in sloppy mode.
The most consistent thing is for runtime detected assignments to const
bindings to always be noisy errors. Early where possible, at runtime in the
rest of the cases.
Allen
--
Cheers,
--MarkM
_______________________________________________
es-discuss mailing list
es-discuss at mozilla.org
https://mail.mozilla.org/listinfo/es-discuss
Brendan Eich
2014-10-01 22:08:43 UTC
Permalink
Right.

Need a complete semantics: static and runtime. News at 11,

/be
Post by Jason Orendorff
I think there is a way that the error could occur at runtime even in
all-strict-mode code: when a new const is added at toplevel in a
second script.
<script>
"use strict";
function f(value) { x = value; }
</script>
<script>
"use strict";
const x = 0;
f(1);
</script>
-j
Andreas Rossberg
2014-10-02 07:24:33 UTC
Permalink
Post by Jason Orendorff
I think there is a way that the error could occur at runtime even in
all-strict-mode code: when a new const is added at toplevel in a
second script.
<script>
"use strict";
function f(value) { x = value; }
</script>
That's an early ReferenceError right there, AFAICT, regardless of what
follows in a later script.

/Andreas
Post by Jason Orendorff
<script>
"use strict";
const x = 0;
f(1);
</script>
-j
Andreas Rossberg
2014-10-02 07:31:24 UTC
Permalink
Post by Andreas Rossberg
Post by Jason Orendorff
I think there is a way that the error could occur at runtime even in
all-strict-mode code: when a new const is added at toplevel in a
second script.
<script>
"use strict";
function f(value) { x = value; }
</script>
That's an early ReferenceError right there, AFAICT, regardless of what
follows in a later script.
I take that back, I just realised it's not. I somehow thought that
strict mode would also fix unbound assignments, but apparently that
was wishful thinking on my part. Not happy. :(

/Andreas
Andreas Rossberg
2014-10-02 15:31:15 UTC
Permalink
I believe that the module champions have always wanted static unresolvablse reference rejection to be part of the module semantics. But we've never had actual specification for that semantics.
Yes, I had always hoped for a "stricter mode" applied to modules. But
I don't think that's something that's still possible at this (or a
later) point. If it had been, then it would have made total sense for
it to check against const assignments as well, along with a number of
other things.
Personally, I think it would be fine for all of these sort of conditions to be specified as runtime errors and leave it to linters to find them early and JS implementations to optimize the runtime checks away.
Outside modules, with the language being as it is, adding one minor
new static check definitely does not seem worth the trouble and the
cost risk.

/Andreas
David Herman
2014-10-02 20:07:33 UTC
Permalink
Post by Andreas Rossberg
I believe that the module champions have always wanted static unresolvablse reference rejection to be part of the module semantics. But we've never had actual specification for that semantics.
Yes, I had always hoped for a "stricter mode" applied to modules. But
I don't think that's something that's still possible at this (or a
later) point. If it had been, then it would have made total sense for
it to check against const assignments as well, along with a number of
other things.
Yeah, I did wish for it, but we conceded a long time ago that it wasn't going to work out to have additional checks in modules. It's especially problematic because whether something is unbound or global is entirely dynamic (globals can be added and removed), scripts can be loaded at dynamically unpredictable times (so predicting the state of the global at the time code will be loaded is hard), in some modes HTML even throws element ids on the global and removes them again ...
Post by Andreas Rossberg
Personally, I think it would be fine for all of these sort of conditions to be specified as runtime errors and leave it to linters to find them early and JS implementations to optimize the runtime checks away.
Outside modules, with the language being as it is, adding one minor
new static check definitely does not seem worth the trouble and the
cost risk.
Agreed. The consolation prize is that modules now make it possible for linters to do free variable analysis and errors without any user-configuration at all. (By contrast, JSHint has historically required programmers to annotate the set of globals they intend to use so that they aren't reported as free variable errors.)

Dave
Oliver Hunt
2014-10-01 16:43:09 UTC
Permalink
Good point. If we can require all such assignments to be rejected statically, why is a runtime assignment to a const variable even possible? Can't we just assert that this cannot occur?
You mean duplicate assignment? IIRC the problem is code that does

const x;
x=blah;

or

if (foo)
const x = bar
else
const x = wiffle

etc

Whether this is actually still something that exists - I recall in JSC many years ago we had to allow:
for (......) {
const x = ...
...
}

Even though that is technically duplicate assignment.

But i guess that last case is less relevant as const is now always block scoped isn't it?

--Oliver
...
I was with you until you got to the following point
If there is an intervening "with" or sloppy direct eval, then there is not a statically apparent assignment to a const variable. Since this can only occur in sloppy code anyway, it seems more consistent with the rest of sloppy mode for this failed assignment to be silent, rather than dynamically throwing an error.
const is a new kind of declaration unlike any declaration form that previous existed in ES, so Ii don't think its handling introduces any legacy consistency issues. If somebody is using const, regard less of mode, they pretty clearly expect assignments to any const bindings to be illegal. And, I don't think any body wants new silent failure errors, even in sloppy mode. The most consistent thing is for runtime detected assignments to const bindings to always be noisy errors. Early where possible, at runtime in the rest of the cases.
Allen
--
Cheers,
--MarkM
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141001/562297ea/attachment-0001.html>
Allen Wirfs-Brock
2014-10-01 17:00:46 UTC
Permalink
Post by Oliver Hunt
Good point. If we can require all such assignments to be rejected statically, why is a runtime assignment to a const variable even possible? Can't we just assert that this cannot occur?
You mean duplicate assignment? IIRC the problem is code that does
const x;
syntax error: no initializer
Post by Oliver Hunt
x=blah;
or
if (foo)
const x = bar
syntax error: not a statement
Post by Oliver Hunt
else
const x = wiffle
syntax error: not a statement
Post by Oliver Hunt
etc
Allen

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141001/848b4b68/attachment.html>
Till Schneidereit
2014-10-01 16:02:13 UTC
Permalink
Post by Mark S. Miller
Post by Oliver Hunt
JSC does lazy parsing of nested functions and we have no problem
reporting static errors, so i?m not sure what you believe is the problem
here.
Hi Oliver,
First, I agree with you. This shouldn't be a problem.
However, both JSC and v8 cause confusion when they use the term "lazy
parsing". There are plenty of static errors that could not be reported with
a truly lazy parser, i.e., one which actually postpones parsing. v8 for
example uses a lightweight but accurate parser that is supposed to catch
early errors early, but, afaik, doesn't construct an actual ast.
SpiderMonkey in some places uses the term "lazy parsing", too, somewhat
unfortunately. Mostly, though, we refer to bytecode-less functions as lazy,
and call the initial parsing "syntax parsing". Which should be more
indicative of what it actually does.

Also, I'm pretty sure that we correctly report these static errors even
when they occur in syntax-only parsed functions (modulo bugs in the WIP
implementation in bug 611388[1]).

[1] https://bugzilla.mozilla.org/show_bug.cgi?id=611388
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141001/9bb6c593/attachment.html>
Allen Wirfs-Brock
2014-10-01 15:50:47 UTC
Permalink
The static error is problematic. I'm pretty sure that engines that do lazy parsing of functions is not going to report static errors before doing a full parse of the function.
I think we need to either enforce this or remove this restriction. Anything in between will lead to inconsistent behavior between engines.
There is nothing in between about the intent of the early error in the specification. It is mandatory, just like all other specified early errors. We do need to improve the specification language and ideally we should have an algorithmic specification of the error check. However, the latter is probably not going to be done for ES6 unless somebody has time to make a suitable contribution to the spec.

Allen
Brendan Eich
2014-10-01 22:08:13 UTC
Permalink
Yup, and we've talked about this at TC39 meetings. We need a
simple-enough static analysis, and runtime errors for the residue that
escapes that analysis. I hope this is not controversial!

/be
Post by Erik Arvidsson
The static error is problematic. I'm pretty sure that engines that do
lazy parsing of functions is not going to report static errors before
doing a full parse of the function.
I think we need to either enforce this or remove this restriction.
Anything in between will lead to inconsistent behavior between engines.
Andreas Rossberg
2014-10-02 07:22:12 UTC
Permalink
Post by Erik Arvidsson
The static error is problematic. I'm pretty sure that engines that do lazy
parsing of functions is not going to report static errors before doing a
full parse of the function.
Well, it is no harder than reporting reference errors for unbound
variables in strict mode, which is already required for ES5.
...However, at least V8 does not report that correctly either, as soon
as lazy parsing kicks in.
Post by Erik Arvidsson
So the question is not "Can this early error be reported accurately without
parsing?", since essentially none can, so no engines postpone parsing. The
question is whether a particular new early error would require these
lightweight early parsers to do something that they can't do while
preserving their current efficiency.
Statically apparent assignment to const variables does not seem like a
burden.
This analysis (as well as diagnosing unbound variables in strict mode)
requires more than just parsing -- it requires full binding analysis.
You have to construct all scope environments, analyse all
declarations, and track all variable uses, even during "lazy" parsing.
That has non-trivial complexity (although I don't know how costly it
is).

/Andreas
Mark Miller
2014-10-02 08:45:45 UTC
Permalink
On Thu, Oct 2, 2014 at 12:22 AM, Andreas Rossberg <rossberg at google.com>
Post by Andreas Rossberg
Post by Erik Arvidsson
The static error is problematic. I'm pretty sure that engines that do
lazy
Post by Erik Arvidsson
parsing of functions is not going to report static errors before doing a
full parse of the function.
Well, it is no harder than reporting reference errors for unbound
variables in strict mode, which is already required for ES5.
...However, at least V8 does not report that correctly either, as soon
as lazy parsing kicks in.
Hi Andreas, can you show an example where v8 observably does the wrong
thing here? Thanks.
--
Cheers,
--MarkM
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141002/f34d0208/attachment.html>
Andreas Rossberg
2014-10-02 14:06:59 UTC
Permalink
Post by Mark Miller
On Thu, Oct 2, 2014 at 12:22 AM, Andreas Rossberg <rossberg at google.com>
Post by Andreas Rossberg
Post by Erik Arvidsson
The static error is problematic. I'm pretty sure that engines that do lazy
parsing of functions is not going to report static errors before doing a
full parse of the function.
Well, it is no harder than reporting reference errors for unbound
variables in strict mode, which is already required for ES5.
...However, at least V8 does not report that correctly either, as soon
as lazy parsing kicks in.
Hi Andreas, can you show an example where v8 observably does the wrong thing
here? Thanks.
No, I was confused, again. :) To set that straight:

- ES5 of course does _not_ require early errors for unbound variables.
- So the good news is that V8 is currently correct.
- The bad news is that requiring const assignments to be early errors
would indeed be a big new hurdle for lazy compilation.

And I now remember that I have brought this up myself at some meeting. :)

So, yes, please let us remove the requirement to make const
assignments an early error. A single, relatively unimportant
diagnostics like that is not worth the considerable complication for
VMs, especially given that all similar errors are not early errors
either.

/Andreas
Andreas Rossberg
2014-10-02 14:17:44 UTC
Permalink
Post by Andreas Rossberg
So, yes, please let us remove the requirement to make const
assignments an early error. A single, relatively unimportant
diagnostics like that is not worth the considerable complication for
VMs, especially given that all similar errors are not early errors
either.
And to be sure, this is not just a problem that implementers can deal
with somehow. It might very well have measurable impact on startup
times for all larger JavaScript programs on all VMs. (AFAIK, nobody
has tried to implement and measure it, though, so we cannot be sure
either way.)

/Andreas
Isiah Meadows
2014-10-02 22:36:40 UTC
Permalink
From: Andreas Rossberg <rossberg at google.com>
To: Allen Wirfs-Brock <allen at wirfs-brock.com>
Cc: "Mark S. Miller" <erights at google.com>, Erik Arvidsson <
erik.arvidsson at gmail.com>, "es-discuss at mozilla.org list" <
es-discuss at mozilla.org>
Date: Thu, 2 Oct 2014 17:31:15 +0200
Subject: Re: Throwing errors on mutating immutable bindings
I believe that the module champions have always wanted static
unresolvablse reference rejection to be part of the module semantics. But
we've never had actual specification for that semantics.
Yes, I had always hoped for a "stricter mode" applied to modules. But
I don't think that's something that's still possible at this (or a
later) point. If it had been, then it would have made total sense for
it to check against const assignments as well, along with a number of
other things.
Personally, I think it would be fine for all of these sort of
conditions to be specified as runtime errors and leave it to linters to
find them early and JS implementations to optimize the runtime checks away.

More likely, they will be even more quickly optimized and potentially
inlined as static constants.
Outside modules, with the language being as it is, adding one minor
new static check definitely does not seem worth the trouble and the
cost risk.
True, and I don't know of a decently fast ES3/5 parser. ES6 will be even
more complicated, and thus, slower than an equivalence ES3/5 one
/Andreas
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141002/a577c353/attachment.html>
Brendan Eich
2014-10-02 22:51:07 UTC
Permalink
Post by Isiah Meadows
True, and I don't know of a decently fast ES3/5 parser.
What is your use-case? For a parser in JS, is
http://marijnhaverbeke.nl/blog/acorn.html too slow? For C++ hand-coded
parser, what's your not-decently-fast benchmark basis code?
Post by Isiah Meadows
ES6 will be even more complicated, and thus, slower than an
equivalence ES3/5 one
I don't think ES6 adds anything that changes asymptotic complexity.
Recognizing keywords is still O(k), for example (and k is small and
stays small in ES6 -- number of leading characters to distinguish,
e.g.). More productions in a grammar does not make parsers for that
grammar slower, apart from indirect effects in small memory systems.

What do you mean here?

/be
Isiah Meadows
2014-10-03 08:24:49 UTC
Permalink
I mean that I wasn't thinking straight when I sent that. I'm incorrect
in every detail of that email.
Post by Brendan Eich
Post by Isiah Meadows
True, and I don't know of a decently fast ES3/5 parser.
What is your use-case? For a parser in JS, is
http://marijnhaverbeke.nl/blog/acorn.html too slow? For C++ hand-coded
parser, what's your not-decently-fast benchmark basis code?
Post by Isiah Meadows
ES6 will be even more complicated, and thus, slower than an equivalence
ES3/5 one
I don't think ES6 adds anything that changes asymptotic complexity.
Recognizing keywords is still O(k), for example (and k is small and stays
small in ES6 -- number of leading characters to distinguish, e.g.). More
productions in a grammar does not make parsers for that grammar slower,
apart from indirect effects in small memory systems.
What do you mean here?
/be
--
Isiah Meadows
Brendan Eich
2014-10-03 18:23:00 UTC
Permalink
Post by Isiah Meadows
I mean that I wasn't thinking straight when I sent that. I'm incorrect
in every detail of that email.
No worries -- I'm interested in parser benchmarks, both in-JS and
C++-to-the-metal. Anyone else have any?

/be
Isiah Meadows
2014-10-05 05:24:18 UTC
Permalink
If I can find a native parser (that only parses), I would run benchmarks.
Shouldn't take that long to time a few rounds of parsing a large library
like jQuery or React. I would be more than willing to accept pointers on
where to find one.

I know one exists in Java (Closure Compiler), but I'm not sure about truly
native ones.
Post by Brendan Eich
Post by Isiah Meadows
I mean that I wasn't thinking straight when I sent that. I'm incorrect
in every detail of that email.
No worries -- I'm interested in parser benchmarks, both in-JS and
C++-to-the-metal. Anyone else have any?
/be
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141005/83c84c3d/attachment.html>
Till Schneidereit
2014-10-05 23:32:15 UTC
Permalink
Post by Isiah Meadows
If I can find a native parser (that only parses), I would run benchmarks.
Shouldn't take that long to time a few rounds of parsing a large library
like jQuery or React. I would be more than willing to accept pointers on
where to find one.
The SpiderMonkey shell lets you do both syntax parsing - the fast initial
parse we do to detect static errors, and full parsing; both using the same
parser Firefox uses. You can download the latest version here:
http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/latest-trunk/

Or, for non-dev build versions, check one directory up.

The shell has a `parse` function for full parsing and a `parseSyntax` for,
well, syntax parsing. More information available using `help()`.


hth,
till
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141006/3f91dd47/attachment.html>
Isiah Meadows
2014-10-06 07:02:53 UTC
Permalink
CC the list... :(


---------- Forwarded message ----------
From: Isiah Meadows <impinball at gmail.com>
Date: Mon, Oct 6, 2014 at 2:19 AM
Subject: Re: Throwing errors on mutating immutable bindings
To: Till Schneidereit <till at tillschneidereit.net>


Thank you. I was looking for something I could benchmark in native
code, but this works. It shouldn't be hard to test side by side. I'll
get back with my results.

On Sun, Oct 5, 2014 at 7:32 PM, Till Schneidereit
Post by Till Schneidereit
Post by Isiah Meadows
If I can find a native parser (that only parses), I would run benchmarks.
Shouldn't take that long to time a few rounds of parsing a large library
like jQuery or React. I would be more than willing to accept pointers on
where to find one.
The SpiderMonkey shell lets you do both syntax parsing - the fast initial
parse we do to detect static errors, and full parsing; both using the same
http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/latest-trunk/
Or, for non-dev build versions, check one directory up.
The shell has a `parse` function for full parsing and a `parseSyntax` for,
well, syntax parsing. More information available using `help()`.
hth,
till
--
Isiah Meadows
--
Isiah Meadows
Isiah Meadows
2014-10-06 07:22:44 UTC
Permalink
Here's what I got from 4 consecutive runs (label prefixes self-explanatory):

```
(SM Native) Time to complete: 15584 microseconds.
(SM Shell) Time to complete: 512053 microseconds.
(Node V8) Time to complete: 238188 microseconds.
(SM Native) Time to complete: 17418 microseconds.
(SM Shell) Time to complete: 556189 microseconds.
(Node V8) Time to complete: 179156 microseconds.
(SM Native) Time to complete: 16119 microseconds.
(SM Shell) Time to complete: 528309 microseconds.
(Node V8) Time to complete: 157015 microseconds.
(SM Native) Time to complete: 16457 microseconds.
(SM Shell) Time to complete: 470544 microseconds.
(Node V8) Time to complete: 166123 microseconds.
```

Clearly, we still have a long way to go before beating a C++ parser.
Pretty interesting to think about, though.

(Also, as a side note, should this be profiled for SpiderMonkey? V8 is
averaging about 10x native, SpiderMonkey about 3-5x V8. My spidey
senses sense a growing gap...;-))

-----

Here's the source for each:
```js
// sm-parser.js
if (typeof require !== 'undefined' && typeof process !== 'undefined') {
throw new Error('Not for Node. Try with SpiderMonkey.');
}

load('./acorn.js');

function getMicroseconds(end, start) {
return end * 1000 - start * 1000;
}

var jQuerySrc = read('./jquery-2.1.1.min.js');
var parsed, end;
var start = dateNow();
parsed = parse(jQuerySrc);
end = dateNow();
print('(SM Native) Time to complete: ' + getMicroseconds(end, start) +
' microseconds.');
```

```js
// js-parser.js
if (typeof require !== 'undefined' && typeof process !== 'undefined') {
// Boilerplate for Node.js
var acorn = require('./acorn.js');
var dateNow = process.hrtime;
var read = require('fs').readFileSync;
var print = console.log;
var getMicroseconds = function (end, start) {
// Microsecond precision to match SpiderMonkey
end = end[0] * 1e9 + end[1];
start = start[0] * 1e9 + start[1];
return Math.floor((end - start) / 1000);
};
var prefix = '(Node V8) ';
} else {
load('./acorn.js');
var getMicroseconds = function (end, start) {
return end * 1000 - start * 1000;
};
var prefix = '(SM Shell) ';
}

var jsParse = acorn.parse;
var jQuerySrc = read('./jquery-2.1.1.min.js');
var parsed, end;
var start = dateNow();
parsed = jsParse(jQuerySrc);
end = dateNow();
print(prefix + ' Time to complete: ' + getMicroseconds(end, start) + '
microseconds.');
```
Post by Isiah Meadows
CC the list... :(
---------- Forwarded message ----------
From: Isiah Meadows <impinball at gmail.com>
Date: Mon, Oct 6, 2014 at 2:19 AM
Subject: Re: Throwing errors on mutating immutable bindings
To: Till Schneidereit <till at tillschneidereit.net>
Thank you. I was looking for something I could benchmark in native
code, but this works. It shouldn't be hard to test side by side. I'll
get back with my results.
On Sun, Oct 5, 2014 at 7:32 PM, Till Schneidereit
Post by Till Schneidereit
Post by Isiah Meadows
If I can find a native parser (that only parses), I would run benchmarks.
Shouldn't take that long to time a few rounds of parsing a large library
like jQuery or React. I would be more than willing to accept pointers on
where to find one.
The SpiderMonkey shell lets you do both syntax parsing - the fast initial
parse we do to detect static errors, and full parsing; both using the same
http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/latest-trunk/
Or, for non-dev build versions, check one directory up.
The shell has a `parse` function for full parsing and a `parseSyntax` for,
well, syntax parsing. More information available using `help()`.
hth,
till
--
Isiah Meadows
--
Isiah Meadows
--
Isiah Meadows
Till Schneidereit
2014-10-06 13:58:04 UTC
Permalink
Post by Isiah Meadows
Clearly, we still have a long way to go before beating a C++ parser.
Pretty interesting to think about, though.
This comparison, while quite informative, isn't a full answer to the
question at hand, for at least two reasons:

For startup speed, the syntax parsing mode is more important, because that
is the one that gets run over the whole file before anything executes. The
full parser only kicks in for functions that are actually executed. (Well,
there are some features that force us on a slow, full-parsing path, but
they don't apply here.) I'm pretty sure if you use `syntaxParse` instead of
`parse`, that'll somewhat widen the gap. (In a quick test, `syntaxParse` is
about 40% faster for the test file.)

On the other hand, the Acorn parser probably does a lot more work than the
builtin one: I didn't look into it, but my guess is that it builds and
returns an AST, so it has to create a substantial graph of JS objects in
addition to parsing the source. I'm not sure how easy it'd be to hack Acorn
to just do the parsing, but if doable, that'd probably close the gap
considerably.
Post by Isiah Meadows
(Also, as a side note, should this be profiled for SpiderMonkey? V8 is
averaging about 10x native, SpiderMonkey about 3-5x V8. My spidey
senses sense a growing gap...;-))
By all our tests (and external ones that I'm aware of) we've all but closed
the gap; check http://arewefastyet.com. This test case is quite
interesting, though - I filed
https://bugzilla.mozilla.org/show_bug.cgi?id=1078273 to investigate.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141006/bbff3a3c/attachment.html>
Isiah Meadows
2014-10-07 10:08:30 UTC
Permalink
On Oct 6, 2014 9:58 AM, "Till Schneidereit" <till at tillschneidereit.net>
Post by Till Schneidereit
Post by Isiah Meadows
Clearly, we still have a long way to go before beating a C++ parser.
Pretty interesting to think about, though.
This comparison, while quite informative, isn't a full answer to the
For startup speed, the syntax parsing mode is more important, because
that is the one that gets run over the whole file before anything executes.
The full parser only kicks in for functions that are actually executed.
(Well, there are some features that force us on a slow, full-parsing path,
but they don't apply here.) I'm pretty sure if you use `syntaxParse`
instead of `parse`, that'll somewhat widen the gap. (In a quick test,
`syntaxParse` is about 40% faster for the test file.)

That is effectively access to the tokenizer. Most dedicated parsers out
there have such access.
Post by Till Schneidereit
On the other hand, the Acorn parser probably does a lot more work than
the builtin one: I didn't look into it, but my guess is that it builds and
returns an AST, so it has to create a substantial graph of JS objects in
addition to parsing the source. I'm not sure how easy it'd be to hack Acorn
to just do the parsing, but if doable, that'd probably close the gap
considerably.

They both return the same exact format. And Acorn does the least out of
most of the JS parsers (which is also why it has the most ES6 support). If
you simply want to check out the tokenizer, Acorn has a `.tokenize()`
method.
Post by Till Schneidereit
Post by Isiah Meadows
(Also, as a side note, should this be profiled for SpiderMonkey? V8 is
averaging about 10x native, SpiderMonkey about 3-5x V8. My spidey
senses sense a growing gap...;-))
By all our tests (and external ones that I'm aware of) we've all but
closed the gap; check http://arewefastyet.com. This test case is quite
interesting, though - I filed
https://bugzilla.mozilla.org/show_bug.cgi?id=1078273 to investigate.

Yeah...part of why I mentioned it.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141007/22b6d6ce/attachment.html>
Loading...