Revision: 13160
Author:   [email protected]
Date:     Fri Dec  7 02:20:35 2012
Log:      Fix spec violations in methods of Number.prototype.

[email protected]
BUG=v8:2443

Review URL: https://chromiumcodereview.appspot.com/11465005
http://code.google.com/p/v8/source/detail?r=13160

Added:
 /branches/bleeding_edge/test/mjsunit/regress/regress-2443.js
Modified:
 /branches/bleeding_edge/src/runtime.cc
 /branches/bleeding_edge/src/v8natives.js
 /branches/bleeding_edge/test/mjsunit/function-call.js
 /branches/bleeding_edge/test/mjsunit/regress/regress-crbug-18639.js

=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/regress/regress-2443.js Fri Dec 7 02:20:35 2012
@@ -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);
=======================================
--- /branches/bleeding_edge/src/runtime.cc      Fri Dec  7 00:55:06 2012
+++ /branches/bleeding_edge/src/runtime.cc      Fri Dec  7 02:20:35 2012
@@ -3807,15 +3807,6 @@
   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 @@
   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 @@
   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);
=======================================
--- /branches/bleeding_edge/src/v8natives.js    Fri Nov 16 01:32:39 2012
+++ /branches/bleeding_edge/src/v8natives.js    Fri Dec  7 02:20:35 2012
@@ -1413,11 +1413,7 @@

 // 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 @@

 // 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);
 }

=======================================
--- /branches/bleeding_edge/test/mjsunit/function-call.js Wed Nov 14 01:14:47 2012 +++ /branches/bleeding_edge/test/mjsunit/function-call.js Fri Dec 7 02:20:35 2012
@@ -67,8 +67,7 @@
      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
=======================================
--- /branches/bleeding_edge/test/mjsunit/regress/regress-crbug-18639.js Tue Dec 7 03:01:02 2010 +++ /branches/bleeding_edge/test/mjsunit/regress/regress-crbug-18639.js Fri Dec 7 02:20:35 2012
@@ -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);
+}

--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev

Reply via email to