archives

« Bugzilla Issues Index

#2792 — B.1.1 should specify decimal integer literals starting with 0 and containing at least one of 8 or 9


The LegacyOctalIntegerLiteral syntax in B.1.1 specifies
LegacyOctalIntegerLiteral ::
0 OctalDigit
LegacyOctalIntegerLiteral OctalDigit

However all engines that recognise LegacyOctalIntegerLiteral actually recognise

LegacyOctalIntegerLiteral ::
0 DecimalDigit
LegacyOctalIntegerLiteral DecimalDigit

And interpret the value as decimal if any digit is greater than 7.


(Changing this bug's 'Product' from 'ECMA-262' to 'Draft for 6th Edition', since LegacyOctalIntegerLiteral appears only in the 6th edition drafts.)


https://github.com/whatwg/javascript/issues/17
https://bugzilla.mozilla.org/show_bug.cgi?id=957513
https://code.google.com/p/v8/issues/detail?id=3091
https://bugs.webkit.org/show_bug.cgi?id=126618
https://connect.microsoft.com/IE/feedback/details/813193
http://esdiscuss.org/topic/early-error-on-0-followed-by-8-or-9-in-numeric-literals-does-not-seem-to-be-web-compatible


(In reply to Oliver Hunt from comment #0)
> The LegacyOctalIntegerLiteral syntax in B.1.1 specifies
> LegacyOctalIntegerLiteral ::
> 0 OctalDigit
> LegacyOctalIntegerLiteral OctalDigit
>
> However all engines that recognise LegacyOctalIntegerLiteral actually
> recognise
>
> LegacyOctalIntegerLiteral ::
> 0 DecimalDigit
> LegacyOctalIntegerLiteral DecimalDigit
>
> And interpret the value as decimal if any digit is greater than 7.

This is inaccurate. `08` for example is not a `LegacyOctalIntegerLiteral` nor is it treated as one by the majority of JavaScript engines:

> (function() { 'use strict'; return 01; }())
< SyntaxError: Octal escapes are forbidden in strict mode

But…

> (function() { 'use strict'; return 08; }())
< 8

Only SpiderMonkey seems to (incorrectly) recognize `08` as a `LegacyOctalIntegerLiteral`:

> (function() { 'use strict'; return 08; }())
< SyntaxError: octal literals and octal escape sequences are deprecated

Changing the spec for `LegacyOctalIntegerLiteral` seems like the wrong way to solve this.


(In reply to Mathias Bynens from comment #3)
> Only SpiderMonkey seems to (incorrectly) recognize `08` as a
> `LegacyOctalIntegerLiteral`:
>
> > (function() { 'use strict'; return 08; }())
> < SyntaxError: octal literals and octal escape sequences are deprecated
>
> Changing the spec for `LegacyOctalIntegerLiteral` seems like the wrong way
> to solve this.

I will defend SpiderMonkey's honor here: The error message above is incorrect in using the word "octal", but the SyntaxError is correct: `08` doesn't match any productions in ES1-5 as far as I know, or in any ES6 draft.

But there are scripts on the web that match it, so we can't drop support for it in sloppy mode.

Therefore this syntax should be banned in strict mode and specified as a special (sloppy-mode-only) legacy decimal integer literal in B.1.1. That is what implementations must do regardless; TC39 does not have a practical choice, just an editorial one.


The issue of non-octal numbers starting with 0 is not limited to integers.
First, the good news: all implementations I've tested agree on this:

07e3 // Syntax Error
07.9 // Syntax Error

So far, so good. But:

08e3 // 800
08.9 // 8.9

Yech. Let's try strict mode:

(function() { "use strict"; return 08e3 })() // 800, except FF: Syntax Error
(function() { "use strict"; return 08.9 })() // 8.9, except FF: Syntax Error

Ugh. Only FF does the Right Thing in strict mode (except maybe the wording of the error message, but error messages are not specced).

That said, it is not LegacyOctalIntegerLiteral, here. It is rather DecimalLiteralWithLeadingZeroAndNoctalDigitInIntegralPart.


(In reply to Jason Orendorff from comment #4)
> (In reply to Mathias Bynens from comment #3)
> > Only SpiderMonkey seems to (incorrectly) recognize `08` as a
> > `LegacyOctalIntegerLiteral`:
> >
> > > (function() { 'use strict'; return 08; }())
> > < SyntaxError: octal literals and octal escape sequences are deprecated
> >
> > Changing the spec for `LegacyOctalIntegerLiteral` seems like the wrong way
> > to solve this.
>
> I will defend SpiderMonkey's honor here: The error message above is
> incorrect in using the word "octal", but the SyntaxError is correct: `08`
> doesn't match any productions in ES1-5 as far as I know, or in any ES6 draft.

Correct; see comment #2. No other implementation flags `08` as a syntax error, though.

> But there are scripts on the web that match it, so we can't drop support for
> it in sloppy mode.
>
> Therefore this syntax should be banned in strict mode and specified as a
> special (sloppy-mode-only) legacy decimal integer literal in B.1.1. That is
> what implementations must do regardless; TC39 does not have a practical
> choice, just an editorial one.

That goes against Allen’s plans to make leading 0 constants legal decimal integer literals. See http://esdiscuss.org/topic/early-error-on-0-followed-by-8-or-9-in-numeric-literals-does-not-seem-to-be-web-compatible#content-19.


(In reply to Mathias Bynens from comment #6)
> That goes against Allen’s plans to make leading 0 constants legal decimal
> integer literals. See
> http://esdiscuss.org/topic/early-error-on-0-followed-by-8-or-9-in-numeric-
> literals-does-not-seem-to-be-web-compatible#content-19.

Having leading-zero decimal constants allowed is really useful only when it is allowed for *every* constant. Currently, what non-FF browsers do is at best useless and at worst harmful (e.g., some automatically generated JS might work during the test phase, then unexpected fail in production, because it wasn't tested against an account number containing only digits from 0 to 7).


(In reply to Claude Pache from comment #7)
> Having leading-zero decimal constants allowed is really useful only when it
> is allowed for *every* constant.

My proposal describes what it takes to spec the existing behavior in the majority of engines (i.e. all but SpiderMonkey). I agree that’s not the most ideal behavior, but it is what it is – there’s only so much we can do without breaking the Web.

Leading-zero decimal literals can probably only happen in strict mode (as currently octal literals throw there, so we’d go from throwing to evaluating to a value), and can never fully (i.e. for all digits) be implemented for sloppy mode (without breaking compatibility with existing Web content).


We (JSC) are incorrectly allowing 08/09 in strict mode :-/

https://bugs.webkit.org/show_bug.cgi?id=135704

This is an unintentional error and i will fix shortly.


(In reply to Oliver Hunt from comment #9)
> We (JSC) are incorrectly allowing 08/09 in strict mode :-/

Don’t feel bad – all engines except SpiderMonkey do this.


Proposed patch
==============

Explanations and notes
----------------------

* The patch adds (in sloppy mode) a NoctalDecimalIntegerLiteral production to the RHS of the DecimalIntegerLiteral production. Recall that DecimalIntegerLiteral is in turn used in DecimalLiteral, so that things like `08.5e2` will work, per Comment #5.

* NoctalDecimalIntegerLiteral describes sequences of decimal digits beginning with 0 and containing at least one occurrence of 8 or 9.

* LegacyOctalLikeDecimalIntegerLiteral describes the same sequences of digits as LegacyOctalIntegerLiteral, but the MV is different.


Stuff to be added in Section B.1.1 Numeric Literals
---------------------------------------------------
(reflecting web reality)

Syntax:

DecimalIntegerLiteral ::
0
NonZeroDigit DecimalDigits[opt]
NoctalDecimalIntegerLiteral

NoctalDecimalIntegerLiteral ::
0 NoctalDigit
LegacyOctalLikeDecimalIntegerLiteral NoctalDigit
NoctalDecimalIntegerLiteral DecimalDigit

LegacyOctalLikeDecimalIntegerLiteral ::
0 OctalDigit
LegacyOctalLikeDecimalIntegerLiteral OctalDigit

NoctalDigit :: one of 8 9

Static semantics (lengthy but trivial):

* The MV of DecimalIntegerLiteral :: NoctalDecimalIntegerLiteral is the MV of NoctalDecimalIntegerLiteral.

* The MV of NoctalDecimalIntegerLiteral :: 0 NoctalDigit is the MV of NoctalDigit.

* The MV of NoctalDecimalIntegerLiteral :: LegacyOctalLikeDecimalIntegerLiteral NoctalDigit is (the MV of LegacyOctalLikeDecimalIntegerLiteral × 10) plus the MV of NoctalDigit.

* The MV of NoctalDecimalIntegerLiteral :: NoctalDecimalIntegerLiteral DecimalDigit is (the MV of NoctalDecimalIntegerLiteral × 10) plus the MV of DecimalDigit.

* The MV of LegacyOctalLikeDecimalIntegerLiteral :: 0 OctalDigit is the MV of OctalDigit,

* The MV of LegacyOctalLikeDecimalIntegerLiteral :: LegacyOctalLikeDecimalIntegerLiteral OctalDigit is (the MV of LegacyOctalLikeDecimalIntegerLiteral × 10) plus the MV of OctalDigit.

* The MV of NoctalDigit :: 8 is 8.

* The MV of NoctalDigit :: 9 is 9.


Stuff to be changed in Section 11.8.3 if we want to ban the thing in strict mode
--------------------------------------------------------------------------------
(a breaking change for several engines)

Just after:
must not extend the syntax of NumericLiteral to include LegacyOctalIntegerLiteral
add:
, nor the syntax of DecimalIntegerLiteral to include NoctalDecimalIntegerLiteral


added some V8 and IE names to the CC list


fixed in rev28 editor's draft

using the patch from Comment 11


fixed in rev28