Module: wine Branch: master Commit: 1cffc0eb73dc479ea83da847c342926d55028b45 URL: http://source.winehq.org/git/wine.git/?a=commit;h=1cffc0eb73dc479ea83da847c3...
Author: Piotr Caban piotr.caban@gmail.com Date: Wed Jul 15 12:51:21 2009 +0200
jscript: Improve Number_toString implementation.
---
dlls/jscript/number.c | 113 ++++++++++++++++++++++++++++++++++++++++++-- dlls/jscript/tests/api.js | 50 +++++++++++++++++-- 2 files changed, 151 insertions(+), 12 deletions(-)
diff --git a/dlls/jscript/number.c b/dlls/jscript/number.c index d6591d0..5399c91 100644 --- a/dlls/jscript/number.c +++ b/dlls/jscript/number.c @@ -16,6 +16,8 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#include <math.h> + #include "jscript.h"
#include "wine/debug.h" @@ -39,11 +41,14 @@ static const WCHAR propertyIsEnumerableW[] = {'p','r','o','p','e','r','t','y','I','s','E','n','u','m','e','r','a','b','l','e',0}; static const WCHAR isPrototypeOfW[] = {'i','s','P','r','o','t','o','t','y','p','e','O','f',0};
+#define NUMBER_TOSTRING_BUF_SIZE 64 /* ECMA-262 3rd Edition 15.7.4.2 */ static HRESULT Number_toString(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) { NumberInstance *number; + INT radix = 10; + DOUBLE val; BSTR str; HRESULT hres;
@@ -56,14 +61,110 @@ static HRESULT Number_toString(DispatchEx *dispex, LCID lcid, WORD flags, DISPPA
number = (NumberInstance*)dispex;
- if(arg_cnt(dp) != 0) { - FIXME("unsupported args\n"); - return E_NOTIMPL; + if(arg_cnt(dp)) { + hres = to_int32(dispex->ctx, get_arg(dp, 0), ei, &radix); + if(FAILED(hres)) + return hres; + + if(radix<2 || radix>36) { + FIXME("throw TypeError\n"); + return E_FAIL; + } }
- hres = to_string(dispex->ctx, &number->num, ei, &str); - if(FAILED(hres)) - return hres; + if(V_VT(&number->num) == VT_I4) + val = V_I4(&number->num); + else + val = V_R8(&number->num); + + if(radix==10 || isnan(val) || isinf(val)) { + hres = to_string(dispex->ctx, &number->num, ei, &str); + if(FAILED(hres)) + return hres; + } + else { + INT idx = 0; + DOUBLE integ, frac, log_radix = 0; + WCHAR buf[NUMBER_TOSTRING_BUF_SIZE+16]; + BOOL exp = FALSE; + + if(val<0) { + val = -val; + buf[idx++] = '-'; + } + + while(1) { + integ = floor(val); + frac = val-integ; + + if(integ == 0) + buf[idx++] = '0'; + while(integ>=1 && idx<NUMBER_TOSTRING_BUF_SIZE) { + buf[idx] = fmod(integ, radix); + if(buf[idx]<10) buf[idx] += '0'; + else buf[idx] += 'a'-10; + integ /= radix; + idx++; + } + + if(idx<NUMBER_TOSTRING_BUF_SIZE) { + INT beg = buf[0]=='-'?1:0; + INT end = idx-1; + WCHAR wch; + + while(end > beg) { + wch = buf[beg]; + buf[beg++] = buf[end]; + buf[end--] = wch; + } + } + + if(idx != NUMBER_TOSTRING_BUF_SIZE) buf[idx++] = '.'; + + while(frac>0 && idx<NUMBER_TOSTRING_BUF_SIZE) { + frac *= radix; + buf[idx] = fmod(frac, radix); + frac -= buf[idx]; + if(buf[idx]<10) buf[idx] += '0'; + else buf[idx] += 'a'-10; + idx++; + } + + if(idx==NUMBER_TOSTRING_BUF_SIZE && !exp) { + exp = TRUE; + idx = (buf[0]=='-') ? 1 : 0; + log_radix = floor(log(val)/log(radix)); + val *= pow(radix, -log_radix); + continue; + } + + break; + } + + while(buf[idx-1] == '0') idx--; + if(buf[idx-1] == '.') idx--; + + if(exp) { + if(log_radix==0) + buf[idx++] = '\0'; + else { + static const WCHAR formatW[] = {'(','e','%','c','%','d',')',0}; + WCHAR ch; + + if(log_radix<0) { + log_radix = -log_radix; + ch = '-'; + } + else ch = '+'; + sprintfW(&buf[idx], formatW, ch, (int)log_radix); + } + } + else buf[idx] = '\0'; + + str = SysAllocString(buf); + if(!str) + return E_OUTOFMEMORY; + }
if(retv) { V_VT(retv) = VT_BSTR; diff --git a/dlls/jscript/tests/api.js b/dlls/jscript/tests/api.js index f4dd74b..b347846 100644 --- a/dlls/jscript/tests/api.js +++ b/dlls/jscript/tests/api.js @@ -566,12 +566,50 @@ ok(tmp === 0, "(new Number()).valueOf = " + tmp); tmp = Number.prototype.valueOf(); ok(tmp === 0, "Number.prototype.valueOf = " + tmp);
-num = new Number(NaN); -ok(num.toString() === "NaN", "num.toString() = " + num.toString()); -num = new Number(-Infinity); -ok(num.toString() === "-Infinity", "num.toString() = " + num.toString()); -num = new Number(Infinity); -ok(num.toString() === "Infinity", "num.toString() = " + num.toString()); +function equals(val, base) { + var i; + var num = 0; + var str = val.toString(base); + + for(i=0; i<str.length; i++) { + if(str.substring(i, i+1) == '(') break; + if(str.substring(i, i+1) == '.') break; + num = num*base + parseInt(str.substring(i, i+1)); + } + + if(str.substring(i, i+1) == '.') { + var mult = base; + for(i++; i<str.length; i++) { + if(str.substring(i, i+1) == '(') break; + num += parseInt(str.substring(i, i+1))/mult; + mult *= base; + } + } + + if(str.substring(i, i+1) == '(') { + exp = parseInt(str.substring(i+2)); + num *= Math.pow(base, exp); + } + + ok(num>val-val/1000 && num<val+val/1000, "equals: num = " + num); +} + +ok((10).toString(11) === "a", "(10).toString(11) = " + (10).toString(11)); +ok((213213433).toString(17) === "8e2ddcb", "(213213433).toString(17) = " + (213213433).toString(17)); +ok((-3254343).toString(33) === "-2oicf", "(-3254343).toString(33) = " + (-3254343).toString(33)); +ok((NaN).toString(12) === "NaN", "(NaN).toString(11) = " + (NaN).toString(11)); +ok((Infinity).toString(13) === "Infinity", "(Infinity).toString(11) = " + (Infinity).toString(11)); +for(i=2; i<10; i++) { + equals(1.123, i); + equals(2305843009200000000, i); + equals(5.123, i); + equals(1/(1024*1024*1024*1024*1024*1024*1.9), i); + equals(1024*1024*1024*1024*1024*1024*1.9999, i); + equals(0.0000000000000000001, i); + equals(0.6, i); + equals(4.65661287308e-10, i); + ok((0).toString(i) === "0", "(0).toString("+i+") = " + (0).toString(i)); +}
tmp = Math.min(1); ok(tmp === 1, "Math.min(1) = " + tmp);