From 371def9626c630c1419011fd82cc1d9d6afadd25 Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Mon, 28 Jan 2013 13:08:32 -0800 Subject: [PATCH] Bug 786135 - Make parseInt(stringStartingWith0ButNot0xOr0X) parse as decimal to comply with ES5. r=dmandelin --HG-- rename : js/src/tests/js1_8_5/extensions/parseInt-octal.js => js/src/tests/ecma_5/Global/parseInt-default-to-decimal.js extra : rebase_source : 824fe7e00fbb6ffa38ebba5c43f6c0801f387309 --- js/src/jit-test/tests/basic/parseIntTests.js | 4 +- .../tests/basic/strictParseIntOctal.js | 16 +-- js/src/jsnum.cpp | 112 +++++++----------- js/src/tests/ecma/GlobalObject/15.1.2.2-1.js | 56 ++++----- js/src/tests/ecma/GlobalObject/15.1.2.2-2.js | 11 +- js/src/tests/ecma/TypeConversion/9.3.1-3.js | 7 +- .../Global/parseInt-default-to-decimal.js} | 15 +-- .../components/places/tests/head_common.js | 2 +- 8 files changed, 106 insertions(+), 117 deletions(-) rename js/src/tests/{js1_8_5/extensions/parseInt-octal.js => ecma_5/Global/parseInt-default-to-decimal.js} (63%) diff --git a/js/src/jit-test/tests/basic/parseIntTests.js b/js/src/jit-test/tests/basic/parseIntTests.js index c8ecb366556c..852dd9021a41 100644 --- a/js/src/jit-test/tests/basic/parseIntTests.js +++ b/js/src/jit-test/tests/basic/parseIntTests.js @@ -19,5 +19,5 @@ function doParseIntTests() { } doParseIntTests(); -assertEq(parseInt("08"), 0); -assertEq(parseInt("09"), 0); +assertEq(parseInt("08"), 8); +assertEq(parseInt("09"), 9); diff --git a/js/src/jit-test/tests/basic/strictParseIntOctal.js b/js/src/jit-test/tests/basic/strictParseIntOctal.js index 536d2d7dd0c3..59412f317936 100644 --- a/js/src/jit-test/tests/basic/strictParseIntOctal.js +++ b/js/src/jit-test/tests/basic/strictParseIntOctal.js @@ -1,16 +1,16 @@ "use strict"; -assertEq(parseInt("08"), 0); -assertEq(parseInt("09"), 0); -assertEq(parseInt("014"), 12); +assertEq(parseInt("08"), 8); +assertEq(parseInt("09"), 9); +assertEq(parseInt("014"), 14); assertEq(parseInt("0xA"), 10); -assertEq(parseInt("00123"), 83); +assertEq(parseInt("00123"), 123); for (var i = 0; i < 5; i++) { - assertEq(parseInt("08"), 0); - assertEq(parseInt("09"), 0); - assertEq(parseInt("014"), 12); + assertEq(parseInt("08"), 8); + assertEq(parseInt("09"), 9); + assertEq(parseInt("014"), 14); assertEq(parseInt("0xA"), 10); - assertEq(parseInt("00123"), 83); + assertEq(parseInt("00123"), 123); } diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp index e60eb204f698..7d1368f5233c 100644 --- a/js/src/jsnum.cpp +++ b/js/src/jsnum.cpp @@ -281,60 +281,7 @@ num_parseFloat(JSContext *cx, unsigned argc, Value *vp) return JS_TRUE; } -static bool -ParseIntStringHelper(JSContext *cx, const jschar *ws, const jschar *end, int maybeRadix, - bool stripPrefix, double *dp) -{ - JS_ASSERT(maybeRadix == 0 || (2 <= maybeRadix && maybeRadix <= 36)); - JS_ASSERT(ws <= end); - - const jschar *s = SkipSpace(ws, end); - JS_ASSERT(ws <= s); - JS_ASSERT(s <= end); - - /* 15.1.2.2 steps 3-4. */ - bool negative = (s != end && s[0] == '-'); - - /* 15.1.2.2 step 5. */ - if (s != end && (s[0] == '-' || s[0] == '+')) - s++; - - /* 15.1.2.2 step 9. */ - int radix = maybeRadix; - if (radix == 0) { - if (end - s >= 2 && s[0] == '0' && (s[1] != 'x' && s[1] != 'X')) { - /* - * Non-standard: ES5 requires that parseInt interpret leading-zero - * strings not starting with "0x" or "0X" as decimal (absent an - * explicitly specified non-zero radix), but we continue to - * interpret such strings as octal, as per ES3 and web practice. - */ - radix = 8; - } else { - radix = 10; - } - } - - /* 15.1.2.2 step 10. */ - if (stripPrefix) { - if (end - s >= 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) { - s += 2; - radix = 16; - } - } - - /* 15.1.2.2 steps 11-14. */ - const jschar *actualEnd; - if (!GetPrefixInteger(cx, s, end, radix, &actualEnd, dp)) - return false; - if (s == actualEnd) - *dp = js_NaN; - else if (negative) - *dp = -*dp; - return true; -} - -/* See ECMA 15.1.2.2. */ +/* ES5 15.1.2.2. */ JSBool js::num_parseInt(JSContext *cx, unsigned argc, Value *vp) { @@ -352,6 +299,7 @@ js::num_parseInt(JSContext *cx, unsigned argc, Value *vp) args.rval().set(args[0]); return true; } + /* * Step 1 is |inputString = ToString(string)|. When string >= * 1e21, ToString(string) is in the form "NeM". 'e' marks the end of @@ -386,13 +334,17 @@ js::num_parseInt(JSContext *cx, unsigned argc, Value *vp) return false; args[0].setString(inputString); - /* 15.1.2.2 steps 6-8. */ + /* Steps 6-9. */ bool stripPrefix = true; - int32_t radix = 0; - if (args.length() > 1) { + int32_t radix; + if (!args.hasDefined(1)) { + radix = 10; + } else { if (!ToInt32(cx, args[1], &radix)) return false; - if (radix != 0) { + if (radix == 0) { + radix = 10; + } else { if (radix < 2 || radix > 36) { args.rval().setDouble(js_NaN); return true; @@ -402,18 +354,44 @@ js::num_parseInt(JSContext *cx, unsigned argc, Value *vp) } } - /* Steps 2-5, 9-14. */ - const jschar *ws = inputString->getChars(cx); - if (!ws) - return false; - const jschar *end = ws + inputString->length(); + /* Step 2. */ + const jschar *s; + const jschar *end; + { + const jschar *ws = inputString->getChars(cx); + if (!ws) + return false; + end = ws + inputString->length(); + s = SkipSpace(ws, end); + MOZ_ASSERT(ws <= s); + MOZ_ASSERT(s <= end); + } + + /* Steps 3-4. */ + bool negative = (s != end && s[0] == '-'); + + /* Step 5. */ + if (s != end && (s[0] == '-' || s[0] == '+')) + s++; + + /* Step 10. */ + if (stripPrefix) { + if (end - s >= 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) { + s += 2; + radix = 16; + } + } + + /* Steps 11-15. */ + const jschar *actualEnd; double number; - if (!ParseIntStringHelper(cx, ws, end, radix, stripPrefix, &number)) + if (!GetPrefixInteger(cx, s, end, radix, &actualEnd, &number)) return false; - - /* Step 15. */ - args.rval().setNumber(number); + if (s == actualEnd) + args.rval().setNumber(js_NaN); + else + args.rval().setNumber(negative ? -number : number); return true; } diff --git a/js/src/tests/ecma/GlobalObject/15.1.2.2-1.js b/js/src/tests/ecma/GlobalObject/15.1.2.2-1.js index 9b575fe44cc0..2a862127429f 100644 --- a/js/src/tests/ecma/GlobalObject/15.1.2.2-1.js +++ b/js/src/tests/ecma/GlobalObject/15.1.2.2-1.js @@ -150,27 +150,27 @@ new TestCase( SECTION, parseInt( "0XABCDEF") ); for ( HEX_STRING = "0x0", HEX_VALUE = 0, POWER = 0; POWER < 15; POWER++, HEX_STRING = HEX_STRING +"f" ) { - new TestCase( SECTION, "parseInt("+HEX_STRING+")", HEX_VALUE, parseInt(HEX_STRING) ); + new TestCase( SECTION, "parseInt('"+HEX_STRING+"')", HEX_VALUE, parseInt(HEX_STRING) ); HEX_VALUE += Math.pow(16,POWER)*15; } for ( HEX_STRING = "0X0", HEX_VALUE = 0, POWER = 0; POWER < 15; POWER++, HEX_STRING = HEX_STRING +"f" ) { - new TestCase( SECTION, "parseInt("+HEX_STRING+")", HEX_VALUE, parseInt(HEX_STRING) ); + new TestCase( SECTION, "parseInt('"+HEX_STRING+"')", HEX_VALUE, parseInt(HEX_STRING) ); HEX_VALUE += Math.pow(16,POWER)*15; } for ( HEX_STRING = "0x0", HEX_VALUE = 0, POWER = 0; POWER < 15; POWER++, HEX_STRING = HEX_STRING +"f" ) { - new TestCase( SECTION, "parseInt("+HEX_STRING+",16)", HEX_VALUE, parseInt(HEX_STRING,16) ); + new TestCase( SECTION, "parseInt('"+HEX_STRING+"', 16)", HEX_VALUE, parseInt(HEX_STRING,16) ); HEX_VALUE += Math.pow(16,POWER)*15; } for ( HEX_STRING = "0x0", HEX_VALUE = 0, POWER = 0; POWER < 15; POWER++, HEX_STRING = HEX_STRING +"f" ) { - new TestCase( SECTION, "parseInt("+HEX_STRING+",16)", HEX_VALUE, parseInt(HEX_STRING,16) ); + new TestCase( SECTION, "parseInt('"+HEX_STRING+"', 16)", HEX_VALUE, parseInt(HEX_STRING,16) ); HEX_VALUE += Math.pow(16,POWER)*15; } for ( HEX_STRING = "0x0", HEX_VALUE = 0, POWER = 0; POWER < 15; POWER++, HEX_STRING = HEX_STRING +"f" ) { - new TestCase( SECTION, "parseInt("+HEX_STRING+",null)", HEX_VALUE, parseInt(HEX_STRING,null) ); + new TestCase( SECTION, "parseInt('"+HEX_STRING+"', null)", HEX_VALUE, parseInt(HEX_STRING,null) ); HEX_VALUE += Math.pow(16,POWER)*15; } for ( HEX_STRING = "0x0", HEX_VALUE = 0, POWER = 0; POWER < 15; POWER++, HEX_STRING = HEX_STRING +"f" ) { - new TestCase( SECTION, "parseInt("+HEX_STRING+", void 0)", HEX_VALUE, parseInt(HEX_STRING, void 0) ); + new TestCase( SECTION, "parseInt('"+HEX_STRING+"', void 0)", HEX_VALUE, parseInt(HEX_STRING, void 0) ); HEX_VALUE += Math.pow(16,POWER)*15; } @@ -180,7 +180,7 @@ for ( var space = " ", HEX_STRING = "0x0", HEX_VALUE = 0, POWER = 0; POWER < 15; POWER++, HEX_STRING = HEX_STRING +"f", space += " ") { - new TestCase( SECTION, "parseInt("+space+HEX_STRING+space+", void 0)", HEX_VALUE, parseInt(space+HEX_STRING+space, void 0) ); + new TestCase( SECTION, "parseInt('"+space+HEX_STRING+space+"', void 0)", HEX_VALUE, parseInt(space+HEX_STRING+space, void 0) ); HEX_VALUE += Math.pow(16,POWER)*15; } @@ -188,73 +188,73 @@ new TestCase(SECTION, "parseInt(BOM + '123', 10)", 123, parseInt("\uFEFF" + "123 // a few tests with negative numbers for ( HEX_STRING = "-0x0", HEX_VALUE = 0, POWER = 0; POWER < 15; POWER++, HEX_STRING = HEX_STRING +"f" ) { - new TestCase( SECTION, "parseInt("+HEX_STRING+")", HEX_VALUE, parseInt(HEX_STRING) ); + new TestCase( SECTION, "parseInt('"+HEX_STRING+"')", HEX_VALUE, parseInt(HEX_STRING) ); HEX_VALUE -= Math.pow(16,POWER)*15; } // we should stop parsing when we get to a value that is not a numeric literal for the type we expect for ( HEX_STRING = "0x0", HEX_VALUE = 0, POWER = 0; POWER < 15; POWER++, HEX_STRING = HEX_STRING +"f" ) { - new TestCase( SECTION, "parseInt("+HEX_STRING+"g,16)", HEX_VALUE, parseInt(HEX_STRING+"g",16) ); + new TestCase( SECTION, "parseInt('"+HEX_STRING+"g', 16)", HEX_VALUE, parseInt(HEX_STRING+"g",16) ); HEX_VALUE += Math.pow(16,POWER)*15; } for ( HEX_STRING = "0x0", HEX_VALUE = 0, POWER = 0; POWER < 15; POWER++, HEX_STRING = HEX_STRING +"f" ) { - new TestCase( SECTION, "parseInt("+HEX_STRING+"g,16)", HEX_VALUE, parseInt(HEX_STRING+"G",16) ); + new TestCase( SECTION, "parseInt('"+HEX_STRING+"G', 16)", HEX_VALUE, parseInt(HEX_STRING+"G",16) ); HEX_VALUE += Math.pow(16,POWER)*15; } for ( HEX_STRING = "-0x0", HEX_VALUE = 0, POWER = 0; POWER < 15; POWER++, HEX_STRING = HEX_STRING +"f" ) { - new TestCase( SECTION, "parseInt("+HEX_STRING+")", HEX_VALUE, parseInt(HEX_STRING) ); + new TestCase( SECTION, "parseInt('"+HEX_STRING+"')", HEX_VALUE, parseInt(HEX_STRING) ); HEX_VALUE -= Math.pow(16,POWER)*15; } for ( HEX_STRING = "-0X0", HEX_VALUE = 0, POWER = 0; POWER < 15; POWER++, HEX_STRING = HEX_STRING +"f" ) { - new TestCase( SECTION, "parseInt("+HEX_STRING+")", HEX_VALUE, parseInt(HEX_STRING) ); + new TestCase( SECTION, "parseInt('"+HEX_STRING+"')", HEX_VALUE, parseInt(HEX_STRING) ); HEX_VALUE -= Math.pow(16,POWER)*15; } for ( HEX_STRING = "-0x0", HEX_VALUE = 0, POWER = 0; POWER < 15; POWER++, HEX_STRING = HEX_STRING +"f" ) { - new TestCase( SECTION, "parseInt("+HEX_STRING+",16)", HEX_VALUE, parseInt(HEX_STRING,16) ); + new TestCase( SECTION, "parseInt('"+HEX_STRING+"', 16)", HEX_VALUE, parseInt(HEX_STRING,16) ); HEX_VALUE -= Math.pow(16,POWER)*15; } for ( HEX_STRING = "-0x0", HEX_VALUE = 0, POWER = 0; POWER < 15; POWER++, HEX_STRING = HEX_STRING +"f" ) { - new TestCase( SECTION, "parseInt("+HEX_STRING+",16)", HEX_VALUE, parseInt(HEX_STRING,16) ); + new TestCase( SECTION, "parseInt('"+HEX_STRING+"', 16)", HEX_VALUE, parseInt(HEX_STRING,16) ); HEX_VALUE -= Math.pow(16,POWER)*15; } -// let us do some octal tests. numbers that start with 0 and do not provid a radix should -// default to using "0" as a radix. +// Numbers that start with 0 and do not provide a radix should use 10 as radix +// per ES5, not octal (as it was in ES3). var OCT_STRING = "0"; var OCT_VALUE = 0; for ( OCT_STRING = "0", OCT_VALUE = 0, POWER = 0; POWER < 15; POWER++, OCT_STRING = OCT_STRING +"7" ) { - new TestCase( SECTION, "parseInt("+OCT_STRING+")", OCT_VALUE, parseInt(OCT_STRING) ); - OCT_VALUE += Math.pow(8,POWER)*7; + new TestCase( SECTION, "parseInt('"+OCT_STRING+"')", OCT_VALUE, parseInt(OCT_STRING) ); + OCT_VALUE += Math.pow(10,POWER)*7; } for ( OCT_STRING = "-0", OCT_VALUE = 0, POWER = 0; POWER < 15; POWER++, OCT_STRING = OCT_STRING +"7" ) { - new TestCase( SECTION, "parseInt("+OCT_STRING+")", OCT_VALUE, parseInt(OCT_STRING) ); - OCT_VALUE -= Math.pow(8,POWER)*7; + new TestCase( SECTION, "parseInt('"+OCT_STRING+"')", OCT_VALUE, parseInt(OCT_STRING) ); + OCT_VALUE -= Math.pow(10,POWER)*7; } -// should get the same results as above if we provid the radix of 8 (or 010) +// should get octal-based results if we provid the radix of 8 (or 010) for ( OCT_STRING = "0", OCT_VALUE = 0, POWER = 0; POWER < 15; POWER++, OCT_STRING = OCT_STRING +"7" ) { - new TestCase( SECTION, "parseInt("+OCT_STRING+",8)", OCT_VALUE, parseInt(OCT_STRING,8) ); + new TestCase( SECTION, "parseInt('"+OCT_STRING+"', 8)", OCT_VALUE, parseInt(OCT_STRING,8) ); OCT_VALUE += Math.pow(8,POWER)*7; } for ( OCT_STRING = "-0", OCT_VALUE = 0, POWER = 0; POWER < 15; POWER++, OCT_STRING = OCT_STRING +"7" ) { - new TestCase( SECTION, "parseInt("+OCT_STRING+",010)", OCT_VALUE, parseInt(OCT_STRING,010) ); + new TestCase( SECTION, "parseInt('"+OCT_STRING+"', 010)", OCT_VALUE, parseInt(OCT_STRING,010) ); OCT_VALUE -= Math.pow(8,POWER)*7; } // we shall stop parsing digits when we get one that isn't a numeric literal of the type we think // it should be. for ( OCT_STRING = "0", OCT_VALUE = 0, POWER = 0; POWER < 15; POWER++, OCT_STRING = OCT_STRING +"7" ) { - new TestCase( SECTION, "parseInt("+OCT_STRING+"8,8)", OCT_VALUE, parseInt(OCT_STRING+"8",8) ); + new TestCase( SECTION, "parseInt('"+OCT_STRING+"8', 8)", OCT_VALUE, parseInt(OCT_STRING+"8",8) ); OCT_VALUE += Math.pow(8,POWER)*7; } for ( OCT_STRING = "-0", OCT_VALUE = 0, POWER = 0; POWER < 15; POWER++, OCT_STRING = OCT_STRING +"7" ) { - new TestCase( SECTION, "parseInt("+OCT_STRING+"8,010)", OCT_VALUE, parseInt(OCT_STRING+"8",010) ); + new TestCase( SECTION, "parseInt('"+OCT_STRING+"8', 010)", OCT_VALUE, parseInt(OCT_STRING+"8",010) ); OCT_VALUE -= Math.pow(8,POWER)*7; } @@ -295,8 +295,8 @@ new TestCase( SECTION, new TestCase( SECTION, "parseInt( '01234567890', 0 )", - Number.NaN, - parseInt("01234567890",1) ); + 1234567890, + parseInt("01234567890", 0) ); new TestCase( SECTION, "parseInt( '01234567890', 1 )", @@ -345,7 +345,7 @@ new TestCase( SECTION, new TestCase( SECTION, "parseInt( '01234567890', 10 )", - 1234567890, + 1234567890, parseInt("01234567890",10) ); // need more test cases with hex radix diff --git a/js/src/tests/ecma/GlobalObject/15.1.2.2-2.js b/js/src/tests/ecma/GlobalObject/15.1.2.2-2.js index 0136aa2fd4df..1a24e3414973 100644 --- a/js/src/tests/ecma/GlobalObject/15.1.2.2-2.js +++ b/js/src/tests/ecma/GlobalObject/15.1.2.2-2.js @@ -122,13 +122,18 @@ new TestCase( SECTION, new TestCase( SECTION, 'parseInt("0022")', - 18, + 22, parseInt("0022")); new TestCase( SECTION, - 'parseInt("0022",10)', + 'parseInt("0022", 8)', + 18, + parseInt("0022", 8)); + +new TestCase( SECTION, + 'parseInt("0022", 10)', 22, - parseInt("0022",10) ); + parseInt("0022", 10) ); new TestCase( SECTION, 'parseInt("0x1000000000000080")', diff --git a/js/src/tests/ecma/TypeConversion/9.3.1-3.js b/js/src/tests/ecma/TypeConversion/9.3.1-3.js index 0d4d7202ec5f..8fb37b6790fe 100644 --- a/js/src/tests/ecma/TypeConversion/9.3.1-3.js +++ b/js/src/tests/ecma/TypeConversion/9.3.1-3.js @@ -474,9 +474,14 @@ new TestCase( SECTION, new TestCase( SECTION, "parseInt(\"0022\")", - 18, + 22, parseInt("0022") ); +new TestCase( SECTION, + "parseInt(\"0022\", 8)", + 18, + parseInt("0022", 8) ); + new TestCase( SECTION, "parseInt(\"0022\",10)", 22, diff --git a/js/src/tests/js1_8_5/extensions/parseInt-octal.js b/js/src/tests/ecma_5/Global/parseInt-default-to-decimal.js similarity index 63% rename from js/src/tests/js1_8_5/extensions/parseInt-octal.js rename to js/src/tests/ecma_5/Global/parseInt-default-to-decimal.js index dd55665ae183..b2e1378a4fd6 100644 --- a/js/src/tests/js1_8_5/extensions/parseInt-octal.js +++ b/js/src/tests/ecma_5/Global/parseInt-default-to-decimal.js @@ -4,7 +4,8 @@ //----------------------------------------------------------------------------- var BUGNUMBER = 583925; var summary = - "parseInt should treat leading-zero inputs as octal regardless of whether caller is strict or laissez-faire mode code"; + "parseInt should treat leading-zero inputs (with radix unspecified) as " + + "decimal, not octal (this changed in bug 786135)"; print(BUGNUMBER + ": " + summary); @@ -12,15 +13,15 @@ print(BUGNUMBER + ": " + summary); * BEGIN TEST * **************/ -assertEq(parseInt("08"), 0); -assertEq(parseInt("09"), 0); -assertEq(parseInt("014"), 12); +assertEq(parseInt("08"), 8); +assertEq(parseInt("09"), 9); +assertEq(parseInt("014"), 14); function strictParseInt(s) { "use strict"; return parseInt(s); } -assertEq(strictParseInt("08"), 0); -assertEq(strictParseInt("09"), 0); -assertEq(strictParseInt("014"), 12); +assertEq(strictParseInt("08"), 8); +assertEq(strictParseInt("09"), 9); +assertEq(strictParseInt("014"), 14); /******************************************************************************/ diff --git a/toolkit/components/places/tests/head_common.js b/toolkit/components/places/tests/head_common.js index 45d4c9e5378f..64743182bb01 100644 --- a/toolkit/components/places/tests/head_common.js +++ b/toolkit/components/places/tests/head_common.js @@ -473,7 +473,7 @@ function create_JSON_backup(aFilename) { let bookmarksBackupDir = gProfD.clone(); bookmarksBackupDir.append("bookmarkbackups"); if (!bookmarksBackupDir.exists()) { - bookmarksBackupDir.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("0755")); + bookmarksBackupDir.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("0755", 8)); do_check_true(bookmarksBackupDir.exists()); } let bookmarksJSONFile = gTestDir.clone();