ES5 left-to-right evaluation order issues

Allen Wirfs-Brock Allen.Wirfs-Brock at microsoft.com
Fri Nov 20 12:05:29 PST 2009


We generally claim that ECMAScript uses a left-to-right evaluation order and in ES5 we even fixed what was consider a bug in the ES3 spec (second item ES5 Annex D) regarding evaluation order of relational operators.  Consider:

var a={valueOf: function () {alert(" a valueOf");return 1}};
var b={valueOf: function () {alert(" b valueOf");return 2}};
a<=b;

A strict implementation of the ES3 spec. would alert: "b valueOf" followed by "a valueOf" indicating what appears to  be a right-to-left evaluation order.  Chrome actually does this while at least IE and FF alert "a valueOf" followed by "b valueOf".  The ES5 changes say that "a valueOf" should occur first as this is consistent with the general left-to-right rule.

However, when accessor properties were added to the specification we did not consider the observable ordering interaction between accessor get functions with side-effects and ToPrimitive operations (ie valueOf) with side-effects.  A typical binary operator algorithm from section 11 looks like:

1.            Let left be the result of evaluating MultiplicativeExpression.
2.            Let leftValue be GetValue(left).
3.            Let right be the result of evaluating UnaryExpression.
4.            Let rightValue be GetValue(right).
5.            Let leftNum be ToNumber(leftValue).
6.            Let rightNum be ToNumber(rightValue).
7.            Return the result of applying the specified operation (*, /, or %) to leftNum and rightNum. See the Notes below 11.5.1, 11.5.2, 11.5.3.

Note that the two calls to GetValue precede the two calls to ToNumber.  Generally in ES3 GetValue doesn't have side-effects so left-to-right order is maintained (however the ordering is observable if the right argument throws a ReferenceError).  The interweaved evaluation order is much easier to observe if accessor properties are implemented.  Consider executing the following in the context of the declarations of a and b above:

var o={get a() {alert("get a");return a}, get b() {alert ("get b");return b}};
o.a*o.b;

GetValue calls the accessor get functions, so based upon the above algorithm, you would expect to see the alert sequence: "get a", "get b", "a valueOf", "b valueOf".  This is exactly what happens for existing getter/setter implementations in at least FF and Chrome.

So, the ES5 specification of operators is consistent with existing implementations of accessor properties.  However, this is not a strict left-to-right order of evaluation order.  We don't have many options at this point.  Here are the alternatives I see:
1.  Abandon the idea that ECMAScript  does strict left-to-right evaluation (it never really did, anyway) and be content with the story that it generally evaluates left to right with assorted special case exceptions to this rule.
2.  Consider all existing violation of strict left-to-right evaluation as specification bugs and make a big note of that in the errata.  Maybe they can be fixed in the ISO version of the ES5 spec.

It would take substantial work to fix this throughout the specification and actual implementation of the strict left-to-right rule would introduce numerous potentially observable changes to existing implementations.  Based upon this, I suspect that we have to choose alternative #1 although I would personally be open to considering alternative #2.

Thoughts?

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20091120/86c05fb0/attachment.html>


More information about the es-discuss mailing list