JavaScriptCore:
authordarin <darin@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 26 Oct 2007 07:51:25 +0000 (07:51 +0000)
committerdarin <darin@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 26 Oct 2007 07:51:25 +0000 (07:51 +0000)
        Reviewed by Maciej.

        - http://bugs.webkit.org/show_bug.cgi?id=15703
          fix numeric functions -- improve correctness and speed

        Gives about 1% gain on SunSpider.

        * kjs/value.h: Added toIntegerPreserveNan, removed toUInt16.
        (KJS::JSValue::toInt32): Changed to call getTruncatedInt32 in a way that works
        with both immediate and number values.
        (KJS::JSValue::toUInt32): Ditto.
        * kjs/value.cpp:
        (KJS::JSValue::toInteger): Moved the logic from roundValue here, with a couple
        differences. One is that it now correctly returns 0 for NaN, and another is that
        there's no special case for 0 or infinity, since the general case already handles
        those correctly.
        (KJS::JSValue::toIntegerPreserveNaN): Added. Like toInteger, but without the
        check for NaN.
        (KJS::JSValue::toInt32SlowCase): Call toNumber instead of roundValue. The
        truncation done by the typecast already does the necessary truncation that
        roundValue was doing.
        (KJS::JSValue::toUInt32SlowCase): Ditto.
        (KJS::JSValue::toUInt16): Removed.

        * kjs/internal.h: Removed roundValue.
        * kjs/internal.cpp: Ditto.

        * kjs/array_object.cpp: (KJS::ArrayProtoFunc::callAsFunction): Remove unneeded
        code to handle NaN in Array.slice; toInteger now never returns NaN as specified.

        * kjs/date_object.cpp:
        (KJS::fillStructuresUsingTimeArgs): Replaced call to roundValue with a call to
        toNumber as specified.
        (KJS::DateProtoFunc::callAsFunction): In SetTime case, replaced call to roundValue
        with a call to toNumber and timeClip as specified.
        (KJS::DateObjectImp::construct): Removed unnecessary checks of numArgs in cases
        where the default behavior of toInt32 (returning 0) was already correct. Replaced
        call to roundValue with a call to toNumber as specified.
        (KJS::DateObjectFuncImp::callAsFunction): Ditto.

        * kjs/math_object.cpp: (MathFuncImp::callAsFunction): Removed unnecessary special
        cases for the pow function that the library already handles correctly.

        * kjs/number_object.cpp: (NumberProtoFunc::callAsFunction): Changed ToString to
        call toIntegerPreserveNaN, so we can continue to handle the NaN case differently.
        The real toInteger now returns 0 for NaN. Took out unneeded special case in
        ToFixed for undefined; was only needed because our toInteger was wrong. Same
        thing in ToExponential. Changed ToPrecision to call toIntegerPreserveNaN.

        * kjs/string_object.cpp:
        (KJS::StringProtoFunc::callAsFunction): Took out CharAt and CharCodeAt special
        cases for undefined that were only needed because toInteger was wrong. Same in
        IndexOf, and was able to remove some special cases. In LastIndexOf, used
        toIntegerPreserveNaN, but was able to remove some special cases there too.
        Changed Substr implementation to preserve correct behavior with the change
        to toInteger and match the specification. Also made sure we weren't converting
        an out of range double to an int.
        (KJS::StringObjectFuncImp::callAsFunction): Changed constructor to just use
        toUInt32, because truncating toUInt32 to 16 bits is the same thing and there's
        no reason to have toUInt16 as a second, less-optimized function that's only
        called at this one call site.

        * wtf/MathExtras.h: Added trunc function for Windows.

LayoutTests:

        Reviewed by Maciej.

        - test changes for http://bugs.webkit.org/show_bug.cgi?id=15703
          fix numeric functions -- improve correctness and speed

        * fast/js/resources/char-at.js: Updated test to expect that we get the first
        character if we pass NaN to charAt and charCodeAt; it's what the specification
        asks for and matches other browsers too.
        * fast/js/char-at-expected.txt: Updated.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@27095 268f45cc-cd09-0410-ab3c-d52691b4dbfc

14 files changed:
JavaScriptCore/ChangeLog
JavaScriptCore/kjs/array_object.cpp
JavaScriptCore/kjs/date_object.cpp
JavaScriptCore/kjs/internal.cpp
JavaScriptCore/kjs/internal.h
JavaScriptCore/kjs/math_object.cpp
JavaScriptCore/kjs/number_object.cpp
JavaScriptCore/kjs/string_object.cpp
JavaScriptCore/kjs/value.cpp
JavaScriptCore/kjs/value.h
JavaScriptCore/wtf/MathExtras.h
LayoutTests/ChangeLog
LayoutTests/fast/js/char-at-expected.txt
LayoutTests/fast/js/resources/char-at.js

index 93ec716dd8f4be9ff808249934b79080ea9523aa..87ba3105d21811bdbf1cc5f522ec2564d1dcef5a 100644 (file)
@@ -1,3 +1,69 @@
+2007-10-25  Darin Adler  <darin@apple.com>
+
+        Reviewed by Maciej.
+
+        - http://bugs.webkit.org/show_bug.cgi?id=15703
+          fix numeric functions -- improve correctness and speed
+
+        Gives about 1% gain on SunSpider.
+
+        * kjs/value.h: Added toIntegerPreserveNan, removed toUInt16.
+        (KJS::JSValue::toInt32): Changed to call getTruncatedInt32 in a way that works
+        with both immediate and number values.
+        (KJS::JSValue::toUInt32): Ditto.
+        * kjs/value.cpp:
+        (KJS::JSValue::toInteger): Moved the logic from roundValue here, with a couple
+        differences. One is that it now correctly returns 0 for NaN, and another is that
+        there's no special case for 0 or infinity, since the general case already handles
+        those correctly.
+        (KJS::JSValue::toIntegerPreserveNaN): Added. Like toInteger, but without the
+        check for NaN.
+        (KJS::JSValue::toInt32SlowCase): Call toNumber instead of roundValue. The
+        truncation done by the typecast already does the necessary truncation that
+        roundValue was doing.
+        (KJS::JSValue::toUInt32SlowCase): Ditto.
+        (KJS::JSValue::toUInt16): Removed.
+
+        * kjs/internal.h: Removed roundValue.
+        * kjs/internal.cpp: Ditto.
+
+        * kjs/array_object.cpp: (KJS::ArrayProtoFunc::callAsFunction): Remove unneeded
+        code to handle NaN in Array.slice; toInteger now never returns NaN as specified.
+
+        * kjs/date_object.cpp:
+        (KJS::fillStructuresUsingTimeArgs): Replaced call to roundValue with a call to
+        toNumber as specified.
+        (KJS::DateProtoFunc::callAsFunction): In SetTime case, replaced call to roundValue
+        with a call to toNumber and timeClip as specified.
+        (KJS::DateObjectImp::construct): Removed unnecessary checks of numArgs in cases
+        where the default behavior of toInt32 (returning 0) was already correct. Replaced
+        call to roundValue with a call to toNumber as specified.
+        (KJS::DateObjectFuncImp::callAsFunction): Ditto.
+
+        * kjs/math_object.cpp: (MathFuncImp::callAsFunction): Removed unnecessary special
+        cases for the pow function that the library already handles correctly.
+
+        * kjs/number_object.cpp: (NumberProtoFunc::callAsFunction): Changed ToString to
+        call toIntegerPreserveNaN, so we can continue to handle the NaN case differently.
+        The real toInteger now returns 0 for NaN. Took out unneeded special case in
+        ToFixed for undefined; was only needed because our toInteger was wrong. Same
+        thing in ToExponential. Changed ToPrecision to call toIntegerPreserveNaN.
+
+        * kjs/string_object.cpp:
+        (KJS::StringProtoFunc::callAsFunction): Took out CharAt and CharCodeAt special
+        cases for undefined that were only needed because toInteger was wrong. Same in
+        IndexOf, and was able to remove some special cases. In LastIndexOf, used
+        toIntegerPreserveNaN, but was able to remove some special cases there too.
+        Changed Substr implementation to preserve correct behavior with the change
+        to toInteger and match the specification. Also made sure we weren't converting
+        an out of range double to an int.
+        (KJS::StringObjectFuncImp::callAsFunction): Changed constructor to just use
+        toUInt32, because truncating toUInt32 to 16 bits is the same thing and there's
+        no reason to have toUInt16 as a second, less-optimized function that's only
+        called at this one call site.
+
+        * wtf/MathExtras.h: Added trunc function for Windows.
+
 2007-10-25  Geoffrey Garen  <ggaren@apple.com>
 
         Reviewed by Maciej Stachowiak.
index 9bfcaf20bc4830e524ea71ea1cba04798b6db64d..0e222d940c7be3f0c76e0cff3793e57110e750f8 100644 (file)
@@ -253,32 +253,30 @@ JSValue* ArrayProtoFunc::callAsFunction(ExecState* exec, JSObject* thisObj, cons
     // We return a new array
     JSObject *resObj = static_cast<JSObject *>(exec->lexicalInterpreter()->builtinArray()->construct(exec,List::empty()));
     result = resObj;
-    double begin = 0;
-    if (!args[0]->isUndefined()) {
-        begin = args[0]->toInteger(exec);
-        if (begin >= 0) { // false for NaN
-            if (begin > length)
-                begin = length;
-        } else {
-            begin += length;
-            if (!(begin >= 0)) // true for NaN
-                begin = 0;
-        }
+    double begin = args[0]->toInteger(exec);
+    if (begin >= 0) {
+      if (begin > length)
+        begin = length;
+    } else {
+      begin += length;
+      if (begin < 0)
+        begin = 0;
     }
-    double end = length;
-    if (!args[1]->isUndefined()) {
+    double end;
+    if (args[1]->isUndefined())
+      end = length;
+    else {
       end = args[1]->toInteger(exec);
-      if (end < 0) { // false for NaN
+      if (end < 0) {
         end += length;
         if (end < 0)
           end = 0;
       } else {
-        if (!(end <= length)) // true for NaN
+        if (end > length)
           end = length;
       }
     }
 
-    //printf( "Slicing from %d to %d \n", begin, end );
     int n = 0;
     int b = static_cast<int>(begin);
     int e = static_cast<int>(end);
@@ -549,7 +547,7 @@ JSValue* ArrayProtoFunc::callAsFunction(ExecState* exec, JSObject* thisObj, cons
       // Documentation: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:lastIndexOf 
 
     int index = length - 1;
-    double d = args[1]->toInteger(exec);
+    double d = args[1]->toIntegerPreserveNaN(exec);
 
     if (d < 0) {
         d += length;
index 698d5c83900597bddfeaaa835a3665f2746a72ea..39b2c4a48ea915119ae34826dda742ccaa61d4ce 100644 (file)
@@ -275,7 +275,7 @@ static void fillStructuresUsingTimeArgs(ExecState *exec, const List &args, int m
     
     // milliseconds
     if (idx < numArgs) {
-        milliseconds += roundValue(exec, args[idx]);
+        milliseconds += args[idx]->toNumber(exec);
     } else {
         milliseconds += *ms;
     }
@@ -551,7 +551,7 @@ JSValue *DateProtoFunc::callAsFunction(ExecState *exec, JSObject *thisObj, const
   case GetTimezoneOffset:
     return jsNumber(-gmtoffset(t) / minutesPerHour);
   case SetTime:
-    milli = roundValue(exec, args[0]);
+    milli = timeClip(args[0]->toNumber(exec));
     result = jsNumber(milli);
     thisDateObj->setInternalValue(result);
     break;
@@ -648,11 +648,11 @@ JSObject *DateObjectImp::construct(ExecState *exec, const List &args)
       t.year = (year >= 0 && year <= 99) ? year : year - 1900;
       t.month = args[1]->toInt32(exec);
       t.monthDay = (numArgs >= 3) ? args[2]->toInt32(exec) : 1;
-      t.hour = (numArgs >= 4) ? args[3]->toInt32(exec) : 0;
-      t.minute = (numArgs >= 5) ? args[4]->toInt32(exec) : 0;
-      t.second = (numArgs >= 6) ? args[5]->toInt32(exec) : 0;
+      t.hour = args[3]->toInt32(exec);
+      t.minute = args[4]->toInt32(exec);
+      t.second = args[5]->toInt32(exec);
       t.isDST = -1;
-      double ms = (numArgs >= 7) ? roundValue(exec, args[6]) : 0;
+      double ms = (numArgs >= 7) ? args[6]->toNumber(exec) : 0;
       value = gregorianDateTimeToMS(t, ms, false);
     }
   }
@@ -702,10 +702,10 @@ JSValue *DateObjectFuncImp::callAsFunction(ExecState* exec, JSObject*, const Lis
     t.year = (year >= 0 && year <= 99) ? year : year - 1900;
     t.month = args[1]->toInt32(exec);
     t.monthDay = (n >= 3) ? args[2]->toInt32(exec) : 1;
-    t.hour = (n >= 4) ? args[3]->toInt32(exec) : 0;
-    t.minute = (n >= 5) ? args[4]->toInt32(exec) : 0;
-    t.second = (n >= 6) ? args[5]->toInt32(exec) : 0;
-    double ms = (n >= 7) ? roundValue(exec, args[6]) : 0;
+    t.hour = args[3]->toInt32(exec);
+    t.minute = args[4]->toInt32(exec);
+    t.second = args[5]->toInt32(exec);
+    double ms = (n >= 7) ? args[6]->toNumber(exec) : 0;
     return jsNumber(gregorianDateTimeToMS(t, ms, true));
   }
 }
@@ -1084,10 +1084,9 @@ double timeClip(double t)
 {
     if (!isfinite(t))
         return NaN;
-    double at = fabs(t);
-    if (at > 8.64E15)
+    if (fabs(t) > 8.64E15)
         return NaN;
-    return copysign(floor(at), t);
+    return trunc(t);
 }
 
 }
index 4041b211345e16a320f732e2d7b0ff8f529154fe..f1e7424b4beb05352cb16ee765821cd35563c5c4 100644 (file)
 
 namespace KJS {
 
-#if PLATFORM(WIN_OS)
-#define copysign _copysign
-#endif
-
 // ------------------------------ StringImp ------------------------------------
 
 JSValue* StringImp::toPrimitive(ExecState*, JSType) const
@@ -250,15 +246,6 @@ bool InternalFunctionImp::implementsHasInstance() const
 
 // ------------------------------ global functions -----------------------------
 
-double roundValue(ExecState *exec, JSValue *v)
-{
-  double d = v->toNumber(exec);
-  double ad = fabs(d);
-  if (ad == 0 || isNaN(d) || isInf(d))
-    return d;
-  return copysign(floor(ad), d);
-}
-
 #ifndef NDEBUG
 #include <stdio.h>
 void printInfo(ExecState *exec, const char *s, JSValue *o, int lineno)
index 959b886e95351073665d3f1d0002610789f92ff3..3672349703c5d8da1c93c807fc0ffb151fcc4c0f 100644 (file)
@@ -145,9 +145,6 @@ namespace KJS {
     bool isAborted;
   };
 
-  // helper function for toInteger, toInt32, toUInt32 and toUInt16
-  double roundValue(ExecState *, JSValue *);
-
 #ifndef NDEBUG
   void printInfo(ExecState *exec, const char *s, JSValue *, int lineno = -1);
 #endif
index c05e7bd2cfa5ff58490e6e925f2fd08805f363b9..a15b7d76f31c1e1921856817ea314e705d9dc834 100644 (file)
@@ -202,12 +202,8 @@ JSValue *MathFuncImp::callAsFunction(ExecState *exec, JSObject* /*thisObj*/, con
     // ECMA 15.8.2.1.13 (::pow takes care of most of the critera)
     if (isNaN(arg2))
       result = NaN;
-    else if (isNaN(arg) && arg2 != 0)
+    else if (isInf(arg2) && fabs(arg) == 1)
       result = NaN;
-    else if (fabs(arg) == 1 && isInf(arg2))
-      result = NaN;
-    else if (arg2 == 0 && arg != 0)
-      result = 1;
     else
       result = ::pow(arg, arg2);
     break;
index b8ac79e9dfc1c26abc22461f957fc8297e5169c2..e21d1ce1dd9695786799c0344aa543b9934031d4 100644 (file)
@@ -153,7 +153,7 @@ JSValue *NumberProtoFunc::callAsFunction(ExecState *exec, JSObject *thisObj, con
   case ToString: {
     double dradix = 10;
     if (!args.isEmpty())
-      dradix = args[0]->toInteger(exec);
+      dradix = args[0]->toIntegerPreserveNaN(exec);
     if (dradix >= 2 && dradix <= 36 && dradix != 10) { // false for NaN
       int radix = static_cast<int>(dradix);
       const char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
@@ -208,9 +208,7 @@ JSValue *NumberProtoFunc::callAsFunction(ExecState *exec, JSObject *thisObj, con
   {
       JSValue *fractionDigits = args[0];
       double df = fractionDigits->toInteger(exec);
-      if (fractionDigits->isUndefined())
-            df = 0;
-      if (!(df >= 0 && df <= 20)) // true for NaN
+      if (!(df >= 0 && df <= 20))
           return throwError(exec, RangeError, "toFixed() digits argument must be between 0 and 20");
       int f = (int)df;
       
@@ -255,7 +253,7 @@ JSValue *NumberProtoFunc::callAsFunction(ExecState *exec, JSObject *thisObj, con
       
       JSValue *fractionDigits = args[0];
       double df = fractionDigits->toInteger(exec);
-      if (!fractionDigits->isUndefined() && !(df >= 0 && df <= 20)) // true for NaN
+      if (!(df >= 0 && df <= 20))
           return throwError(exec, RangeError, "toExponential() argument must between 0 and 20");
       int f = (int)df;
       
@@ -342,7 +340,7 @@ JSValue *NumberProtoFunc::callAsFunction(ExecState *exec, JSObject *thisObj, con
       int e = 0;
       UString m;
       
-      double dp = args[0]->toInteger(exec);
+      double dp = args[0]->toIntegerPreserveNaN(exec);
       double x = v->toNumber(exec);
       if (isNaN(dp) || isNaN(x) || isInf(x))
           return jsString(v->toString(exec));
index 0698e112784d9b8318f4c934625943197040503b..c76f894a77eb6034d289dcef5c8cdf0f5eeb2b66 100644 (file)
@@ -457,20 +457,16 @@ JSValue* StringProtoFunc::callAsFunction(ExecState* exec, JSObject* thisObj, con
     // handled above
     break;
   case CharAt:
-    // Other browsers treat an omitted parameter as 0 rather than NaN.
-    // That doesn't match the ECMA standard, but is needed for site compatibility.
-    dpos = a0->isUndefined() ? 0 : a0->toInteger(exec);
-    if (dpos >= 0 && dpos < len) // false for NaN
+    dpos = a0->toInteger(exec);
+    if (dpos >= 0 && dpos < len)
       u = s.substr(static_cast<int>(dpos), 1);
     else
       u = "";
     result = jsString(u);
     break;
   case CharCodeAt:
-    // Other browsers treat an omitted parameter as 0 rather than NaN.
-    // That doesn't match the ECMA standard, but is needed for site compatibility.
-    dpos = a0->isUndefined() ? 0 : a0->toInteger(exec);
-    if (dpos >= 0 && dpos < len) // false for NaN
+    dpos = a0->toInteger(exec);
+    if (dpos >= 0 && dpos < len)
       result = jsNumber(s[static_cast<int>(dpos)].unicode());
     else
       result = jsNaN();
@@ -485,31 +481,21 @@ JSValue* StringProtoFunc::callAsFunction(ExecState* exec, JSObject* thisObj, con
   }
   case IndexOf:
     u2 = a0->toString(exec);
-    if (a1->isUndefined())
+    dpos = a1->toInteger(exec);
+    if (dpos < 0)
       dpos = 0;
-    else {
-      dpos = a1->toInteger(exec);
-      if (dpos >= 0) { // false for NaN
-        if (dpos > len)
-          dpos = len;
-      } else
-        dpos = 0;
-    }
+    else if (dpos > len)
+      dpos = len;
     result = jsNumber(s.find(u2, static_cast<int>(dpos)));
     break;
   case LastIndexOf:
     u2 = a0->toString(exec);
     d = a1->toNumber(exec);
-    if (a1->isUndefined() || KJS::isNaN(d))
+    dpos = a1->toIntegerPreserveNaN(exec);
+    if (dpos < 0)
+      dpos = 0;
+    else if (!(dpos <= len)) // true for NaN
       dpos = len;
-    else {
-      dpos = a1->toInteger(exec);
-      if (dpos >= 0) { // false for NaN
-        if (dpos > len)
-          dpos = len;
-      } else
-        dpos = 0;
-    }
     result = jsNumber(s.rfind(u2, static_cast<int>(dpos)));
     break;
   case Match:
@@ -658,22 +644,20 @@ JSValue* StringProtoFunc::callAsFunction(ExecState* exec, JSObject* thisObj, con
     }
     break;
   case Substr: {
-    double d = a0->toInteger(exec);
-    double d2 = a1->toInteger(exec);
-    if (!(d >= 0)) { // true for NaN
-      d += len;
-      if (!(d >= 0)) // true for NaN
-        d = 0;
-    }
-    if (isNaN(d2))
-      d2 = len - d;
-    else {
-      if (d2 < 0)
-        d2 = 0;
-      if (d2 > len - d)
-        d2 = len - d;
+    double start = a0->toInteger(exec);
+    double length = a1->isUndefined() ? len : a1->toInteger(exec);
+    if (start >= len)
+      return jsString("");
+    if (length < 0)
+      return jsString("");
+    if (start < 0) {
+      start += len;
+      if (start < 0)
+        start = 0;
     }
-    result = jsString(s.substr(static_cast<int>(d), static_cast<int>(d2)));
+    if (length > len - d)
+      length = len - d;
+    result = jsString(s.substr(static_cast<int>(start), static_cast<int>(length)));
     break;
   }
   case Substring: {
@@ -852,7 +836,7 @@ JSValue *StringObjectFuncImp::callAsFunction(ExecState *exec, JSObject* /*thisOb
     UChar *p = buf;
     ListIterator it = args.begin();
     while (it != args.end()) {
-      unsigned short u = it->toUInt16(exec);
+      unsigned short u = it->toUInt32(exec);
       *p++ = UChar(u);
       it++;
     }
index d3f20d6a2e058367c342fd3048bea78b2de69fc8..535812581175b1001cb06e38d1580cd7f3d7be3a 100644 (file)
@@ -61,14 +61,23 @@ double JSValue::toInteger(ExecState *exec) const
     int32_t i;
     if (getTruncatedInt32(i))
         return i;
-    return roundValue(exec, const_cast<JSValue*>(this));
+    double d = toNumber(exec);
+    return isNaN(d) ? 0.0 : trunc(d);
+}
+
+double JSValue::toIntegerPreserveNaN(ExecState *exec) const
+{
+    int32_t i;
+    if (getTruncatedInt32(i))
+        return i;
+    return trunc(toNumber(exec));
 }
 
 int32_t JSValue::toInt32SlowCase(ExecState* exec, bool& ok) const
 {
     ok = true;
 
-    double d = roundValue(exec, const_cast<JSValue*>(this));
+    double d = toNumber(exec);
     if (d >= -D32 / 2 && d < D32 / 2)
         return static_cast<int32_t>(d);
 
@@ -76,13 +85,12 @@ int32_t JSValue::toInt32SlowCase(ExecState* exec, bool& ok) const
         ok = false;
         return 0;
     }
-    double d32 = fmod(d, D32);
 
+    double d32 = fmod(trunc(d), D32);
     if (d32 >= D32 / 2)
         d32 -= D32;
     else if (d32 < -D32 / 2)
         d32 += D32;
-
     return static_cast<int32_t>(d32);
 }
 
@@ -90,7 +98,7 @@ uint32_t JSValue::toUInt32SlowCase(ExecState* exec, bool& ok) const
 {
     ok = true;
 
-    double d = roundValue(exec, const_cast<JSValue*>(this));
+    double d = toNumber(exec);
     if (d >= 0.0 && d < D32)
         return static_cast<uint32_t>(d);
 
@@ -98,34 +106,13 @@ uint32_t JSValue::toUInt32SlowCase(ExecState* exec, bool& ok) const
         ok = false;
         return 0;
     }
-    double d32 = fmod(d, D32);
 
+    double d32 = fmod(trunc(d), D32);
     if (d32 < 0)
         d32 += D32;
-
     return static_cast<uint32_t>(d32);
 }
 
-uint16_t JSValue::toUInt16(ExecState *exec) const
-{
-    uint32_t i;
-    if (getTruncatedUInt32(i))
-        return static_cast<uint16_t>(i);
-
-    double d = roundValue(exec, const_cast<JSValue*>(this));
-    if (d >= 0.0 && d < D16)
-        return static_cast<uint16_t>(d);
-
-    if (isNaN(d) || isInf(d))
-        return 0;
-    double d16 = fmod(d, D16);
-
-    if (d16 < 0)
-        d16 += D16;
-
-    return static_cast<uint16_t>(d16);
-}
-
 float JSValue::toFloat(ExecState* exec) const
 {
     return static_cast<float>(toNumber(exec));
index 94ad2af8801cde721f9258f6c656ea948e123899..5fb0ecd1998f6d7a6b848cd599371839b37aaeb6 100644 (file)
@@ -91,11 +91,11 @@ public:
 
     // Integer conversions.
     double toInteger(ExecState*) const;
+    double toIntegerPreserveNaN(ExecState*) const;
     int32_t toInt32(ExecState*) const;
     int32_t toInt32(ExecState*, bool& ok) const;
     uint32_t toUInt32(ExecState*) const;
     uint32_t toUInt32(ExecState*, bool& ok) const;
-    uint16_t toUInt16(ExecState*) const;
 
     // Floating point conversions.
     float toFloat(ExecState*) const;
@@ -405,7 +405,7 @@ inline JSObject* JSValue::toObject(ExecState* exec) const
 ALWAYS_INLINE int32_t JSValue::toInt32(ExecState* exec) const
 {
     int32_t i;
-    if (JSImmediate::isImmediate(this) && JSImmediate::getTruncatedInt32(this, i))
+    if (getTruncatedInt32(i))
         return i;
     bool ok;
     return toInt32SlowCase(exec, ok);
@@ -414,7 +414,7 @@ ALWAYS_INLINE int32_t JSValue::toInt32(ExecState* exec) const
 inline uint32_t JSValue::toUInt32(ExecState* exec) const
 {
     uint32_t i;
-    if (JSImmediate::isImmediate(this) && JSImmediate::getTruncatedUInt32(this, i))
+    if (getTruncatedUInt32(i))
         return i;
     bool ok;
     return toUInt32SlowCase(exec, ok);
@@ -423,7 +423,7 @@ inline uint32_t JSValue::toUInt32(ExecState* exec) const
 inline int32_t JSValue::toInt32(ExecState* exec, bool& ok) const
 {
     int32_t i;
-    if (JSImmediate::isImmediate(this) && JSImmediate::getTruncatedInt32(this, i)) {
+    if (getTruncatedInt32(i)) {
         ok = true;
         return i;
     }
@@ -433,7 +433,7 @@ inline int32_t JSValue::toInt32(ExecState* exec, bool& ok) const
 inline uint32_t JSValue::toUInt32(ExecState* exec, bool& ok) const
 {
     uint32_t i;
-    if (JSImmediate::isImmediate(this) && JSImmediate::getTruncatedUInt32(this, i)) {
+    if (getTruncatedUInt32(i)) {
         ok = true;
         return i;
     }
index 81083660c26bc8e8b847b68990d9dc2e47b0e714..204ee9ad814db72f54b2cc4cf40504b41210df1e 100644 (file)
@@ -67,6 +67,7 @@ inline long lroundf(float num) { return static_cast<long>(num > 0 ? num + 0.5f :
 inline double round(double num) { return num > 0 ? floor(num + 0.5) : ceil(num - 0.5); }
 inline float roundf(float num) { return num > 0 ? floorf(num + 0.5f) : ceilf(num - 0.5f); }
 inline bool signbit(double num) { return _copysign(1.0, num) < 0; }
+inline double trunc(double num) { return num > 0 ? floor(num) : ceil(num); }
 
 inline double nextafter(double x, double y) { return _nextafter(x, y); }
 inline float nextafterf(float x, float y) { return x > y ? x - FLT_EPSILON : x + FLT_EPSILON; }
index 880713343830ee245e163fcd65842034ec4721fb..1a620c4618afc4226ead9e533523ca745b4dcb10 100644 (file)
@@ -1,3 +1,15 @@
+2007-10-26  Darin Adler  <darin@apple.com>
+
+        Reviewed by Maciej.
+
+        - test changes for http://bugs.webkit.org/show_bug.cgi?id=15703
+          fix numeric functions -- improve correctness and speed
+
+        * fast/js/resources/char-at.js: Updated test to expect that we get the first
+        character if we pass NaN to charAt and charCodeAt; it's what the specification
+        asks for and matches other browsers too.
+        * fast/js/char-at-expected.txt: Updated.
+
 2007-10-26  Mark Rowe  <mrowe@apple.com>
 
         Update expected results.
index 3efbea5467e0a90c5e31d7ba70b588b0384fd50d..597b75a6486831ea88aa1302dc6c2a464eb8d2cc 100644 (file)
@@ -53,8 +53,8 @@ PASS "x".charAt(-1) is ""
 PASS "x".charCodeAt(-1) is NaN
 PASS "x".charAt(-Infinity) is ""
 PASS "x".charCodeAt(-Infinity) is NaN
-PASS "x".charAt(NaN) is ""
-PASS "x".charCodeAt(NaN) is NaN
+PASS "x".charAt(NaN) is "x"
+PASS "x".charCodeAt(NaN) is 120
 PASS "xy".charAt() is "x"
 PASS "xy".charCodeAt() is 120
 PASS "xy".charAt(undefined) is "x"
@@ -79,8 +79,8 @@ PASS "xy".charAt(-1) is ""
 PASS "xy".charCodeAt(-1) is NaN
 PASS "xy".charAt(-Infinity) is ""
 PASS "xy".charCodeAt(-Infinity) is NaN
-PASS "xy".charAt(NaN) is ""
-PASS "xy".charCodeAt(NaN) is NaN
+PASS "xy".charAt(NaN) is "x"
+PASS "xy".charCodeAt(NaN) is 120
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 64bb4cb62e9f211a33910d9c248cd8404b3c3ba9..039f43cbf539f97b99f1875ffb8761f615ad0584 100644 (file)
@@ -73,7 +73,7 @@ var answers = [['""', 'NaN'],
 ['""', 'NaN'],
 ['""', 'NaN'],
 ['""', 'NaN'],
-['""', 'NaN'],
+['"x"', '120'],
 ['"x"', '120'],
 ['"x"', '120'],
 ['"x"', '120'],
@@ -86,7 +86,7 @@ var answers = [['""', 'NaN'],
 ['""', 'NaN'],
 ['""', 'NaN'],
 ['""', 'NaN'],
-['""', 'NaN']];
+['"x"', '120']];
 
 for (var i = 0; i < cases.length; ++i)
 {
@@ -94,16 +94,16 @@ for (var i = 0; i < cases.length; ++i)
     var result = answers[i];
     if (item[1] == "omitted") {
         shouldBe('"' + item[0] + '".charAt()', result[0]);
-       if (result[1] == 'NaN')
-           shouldBeNaN('"' + item[0] + '".charCodeAt()');
-       else
-           shouldBe('"' + item[0] + '".charCodeAt()', result[1]);
+        if (result[1] == 'NaN')
+            shouldBeNaN('"' + item[0] + '".charCodeAt()');
+        else
+            shouldBe('"' + item[0] + '".charCodeAt()', result[1]);
     } else {
         shouldBe('"' + item[0] + '".charAt(' + item[1] + ')', result[0]);
-       if (result[1] == 'NaN')
-           shouldBeNaN('"' + item[0] + '".charCodeAt(' + item[1] + ')');
-       else
-           shouldBe('"' + item[0] + '".charCodeAt(' + item[1] + ')', result[1]);
+        if (result[1] == 'NaN')
+            shouldBeNaN('"' + item[0] + '".charCodeAt(' + item[1] + ')');
+        else
+            shouldBe('"' + item[0] + '".charCodeAt(' + item[1] + ')', result[1]);
     }
 }