Fix spec violations in methods of Number.prototype.

R=svenpanne@chromium.org
BUG=v8:2443

Review URL: https://chromiumcodereview.appspot.com/11465005

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@13160 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
yangguo@chromium.org 2012-12-07 10:20:35 +00:00
parent eeca7c7759
commit 3388f92e63
5 changed files with 185 additions and 58 deletions

View File

@ -3807,15 +3807,6 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberToFixed) {
ASSERT(args.length() == 2);
CONVERT_DOUBLE_ARG_CHECKED(value, 0);
if (isnan(value)) {
return *isolate->factory()->nan_symbol();
}
if (isinf(value)) {
if (value < 0) {
return *isolate->factory()->minus_infinity_symbol();
}
return *isolate->factory()->infinity_symbol();
}
CONVERT_DOUBLE_ARG_CHECKED(f_number, 1);
int f = FastD2IChecked(f_number);
RUNTIME_ASSERT(f >= 0);
@ -3832,15 +3823,6 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberToExponential) {
ASSERT(args.length() == 2);
CONVERT_DOUBLE_ARG_CHECKED(value, 0);
if (isnan(value)) {
return *isolate->factory()->nan_symbol();
}
if (isinf(value)) {
if (value < 0) {
return *isolate->factory()->minus_infinity_symbol();
}
return *isolate->factory()->infinity_symbol();
}
CONVERT_DOUBLE_ARG_CHECKED(f_number, 1);
int f = FastD2IChecked(f_number);
RUNTIME_ASSERT(f >= -1 && f <= 20);
@ -3857,15 +3839,6 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberToPrecision) {
ASSERT(args.length() == 2);
CONVERT_DOUBLE_ARG_CHECKED(value, 0);
if (isnan(value)) {
return *isolate->factory()->nan_symbol();
}
if (isinf(value)) {
if (value < 0) {
return *isolate->factory()->minus_infinity_symbol();
}
return *isolate->factory()->infinity_symbol();
}
CONVERT_DOUBLE_ARG_CHECKED(f_number, 1);
int f = FastD2IChecked(f_number);
RUNTIME_ASSERT(f >= 1 && f <= 21);

View File

@ -1413,11 +1413,7 @@ function NumberToString(radix) {
// ECMA-262 section 15.7.4.3
function NumberToLocaleString() {
if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
throw MakeTypeError("called_on_null_or_undefined",
["Number.prototype.toLocaleString"]);
}
return this.toString();
return NumberToString();
}
@ -1434,50 +1430,76 @@ function NumberValueOf() {
// ECMA-262 section 15.7.4.5
function NumberToFixed(fractionDigits) {
var x = this;
if (!IS_NUMBER(this)) {
if (!IS_NUMBER_WRAPPER(this)) {
throw MakeTypeError("incompatible_method_receiver",
["Number.prototype.toFixed", this]);
}
// Get the value of this number in case it's an object.
x = %_ValueOf(this);
}
var f = TO_INTEGER(fractionDigits);
if (f < 0 || f > 20) {
throw new $RangeError("toFixed() digits argument must be between 0 and 20");
}
if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
throw MakeTypeError("called_on_null_or_undefined",
["Number.prototype.toFixed"]);
}
var x = ToNumber(this);
if (NUMBER_IS_NAN(x)) return "NaN";
if (x == 1/0) return "Infinity";
if (x == -1/0) return "-Infinity";
return %NumberToFixed(x, f);
}
// ECMA-262 section 15.7.4.6
function NumberToExponential(fractionDigits) {
var f = -1;
if (!IS_UNDEFINED(fractionDigits)) {
f = TO_INTEGER(fractionDigits);
if (f < 0 || f > 20) {
throw new $RangeError(
"toExponential() argument must be between 0 and 20");
var x = this;
if (!IS_NUMBER(this)) {
if (!IS_NUMBER_WRAPPER(this)) {
throw MakeTypeError("incompatible_method_receiver",
["Number.prototype.toExponential", this]);
}
// Get the value of this number in case it's an object.
x = %_ValueOf(this);
}
if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
throw MakeTypeError("called_on_null_or_undefined",
["Number.prototype.toExponential"]);
var f = IS_UNDEFINED(fractionDigits) ? void 0 : TO_INTEGER(fractionDigits);
if (NUMBER_IS_NAN(x)) return "NaN";
if (x == 1/0) return "Infinity";
if (x == -1/0) return "-Infinity";
if (IS_UNDEFINED(f)) {
f = -1; // Signal for runtime function that f is not defined.
} else if (f < 0 || f > 20) {
throw new $RangeError("toExponential() argument must be between 0 and 20");
}
var x = ToNumber(this);
return %NumberToExponential(x, f);
}
// ECMA-262 section 15.7.4.7
function NumberToPrecision(precision) {
if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
throw MakeTypeError("called_on_null_or_undefined",
["Number.prototype.toPrecision"]);
var x = this;
if (!IS_NUMBER(this)) {
if (!IS_NUMBER_WRAPPER(this)) {
throw MakeTypeError("incompatible_method_receiver",
["Number.prototype.toPrecision", this]);
}
// Get the value of this number in case it's an object.
x = %_ValueOf(this);
}
if (IS_UNDEFINED(precision)) return ToString(%_ValueOf(this));
var p = TO_INTEGER(precision);
if (NUMBER_IS_NAN(x)) return "NaN";
if (x == 1/0) return "Infinity";
if (x == -1/0) return "-Infinity";
if (p < 1 || p > 21) {
throw new $RangeError("toPrecision() argument must be between 1 and 21");
}
var x = ToNumber(this);
return %NumberToPrecision(x, p);
}

View File

@ -67,8 +67,7 @@ var should_throw_on_null_and_undefined =
String.prototype.toLocaleLowerCase,
String.prototype.toUpperCase,
String.prototype.toLocaleUpperCase,
String.prototype.trim,
Number.prototype.toLocaleString];
String.prototype.trim];
// Non generic natives do not work on any input other than the specific
// type, but since this change will allow call to be invoked with undefined

View File

@ -0,0 +1,129 @@
// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Number.prototype methods on non-Numbers.
assertThrows(function() { Number.prototype.toExponential.call({}) },
TypeError);
assertThrows(function() { Number.prototype.toPrecision.call({}) },
TypeError);
assertThrows(function() { Number.prototype.toFixed.call({}) },
TypeError);
assertThrows(function() { Number.prototype.toString.call({}) },
TypeError);
assertThrows(function() { Number.prototype.toLocaleString.call({}) },
TypeError);
assertThrows(function() { Number.prototype.ValueOf.call({}) },
TypeError);
// Call on Number objects with custom valueOf method.
var x_obj = new Number(1);
x_obj.valueOf = function() { assertUnreachable(); };
assertEquals("1.00e+0",
Number.prototype.toExponential.call(x_obj, 2));
assertEquals("1.0",
Number.prototype.toPrecision.call(x_obj, 2));
assertEquals("1.00",
Number.prototype.toFixed.call(x_obj, 2));
// Call on primitive numbers.
assertEquals("1.00e+0",
Number.prototype.toExponential.call(1, 2));
assertEquals("1.0",
Number.prototype.toPrecision.call(1, 2));
assertEquals("1.00",
Number.prototype.toFixed.call(1, 2));
// toExponential and toPrecision does following steps in order
// 1) convert the argument using ToInteger
// 2) check for non-finite receiver, on which it returns,
// 3) check argument range and throw exception if out of range.
// Note that the the last two steps are reversed for toFixed.
// Luckily, the receiver is expected to be a number or number
// wrapper, so that getting its value is not observable.
var f_flag = false;
var f_obj = { valueOf: function() { f_flag = true; return 1000; } };
assertEquals("NaN",
Number.prototype.toExponential.call(NaN, f_obj));
assertTrue(f_flag);
f_flag = false;
assertEquals("Infinity",
Number.prototype.toExponential.call(1/0, f_obj));
assertTrue(f_flag);
f_flag = false;
assertEquals("-Infinity",
Number.prototype.toExponential.call(-1/0, f_obj));
assertTrue(f_flag);
f_flag = false;
assertEquals("NaN",
Number.prototype.toPrecision.call(NaN, f_obj));
assertTrue(f_flag);
f_flag = false;
assertEquals("Infinity",
Number.prototype.toPrecision.call(1/0, f_obj));
assertTrue(f_flag);
f_flag = false;
assertEquals("-Infinity",
Number.prototype.toPrecision.call(-1/0, f_obj));
assertTrue(f_flag);
// The odd man out: toFixed.
f_flag = false;
assertThrows(function() { Number.prototype.toFixed.call(NaN, f_obj) },
RangeError);
assertTrue(f_flag);
f_flag = false;
assertThrows(function() { Number.prototype.toFixed.call(1/0, f_obj) },
RangeError);
assertTrue(f_flag);
f_flag = false;
assertThrows(function() { Number.prototype.toFixed.call(-1/0, f_obj) },
RangeError);
assertTrue(f_flag);

View File

@ -27,8 +27,12 @@
// See http://crbug.com/18639
toString = toString;
__defineGetter__("z", (0).toLocaleString);
z;
z;
((0).toLocaleString)();
try {
toString = toString;
__defineGetter__("z", (0).toLocaleString);
z;
z;
((0).toLocaleString)();
} catch (e) {
assertInstanceof(e, TypeError);
}