Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
I kept the fraction removal since I couldn't think of a better way for now. However, the other improvements have been done.
dlls/jscript/jscript.h | 1 + dlls/jscript/number.c | 82 ++++++++++++++++++++++++++++++++++++++- dlls/jscript/tests/api.js | 7 ++++ 3 files changed, 88 insertions(+), 2 deletions(-)
diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index 7ed4425..406efa9 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -449,6 +449,7 @@ HRESULT regexp_string_match(script_ctx_t*,jsdisp_t*,jsstr_t*,jsval_t*) DECLSPEC_
BOOL bool_obj_value(jsdisp_t*) DECLSPEC_HIDDEN; unsigned array_get_length(jsdisp_t*) DECLSPEC_HIDDEN; +HRESULT localize_number(script_ctx_t*,DOUBLE,jsstr_t**) DECLSPEC_HIDDEN;
HRESULT JSGlobal_eval(script_ctx_t*,jsval_t,WORD,unsigned,jsval_t*,jsval_t*) DECLSPEC_HIDDEN; HRESULT Object_get_proto_(script_ctx_t*,jsval_t,WORD,unsigned,jsval_t*,jsval_t*) DECLSPEC_HIDDEN; diff --git a/dlls/jscript/number.c b/dlls/jscript/number.c index be733fb..64404ba 100644 --- a/dlls/jscript/number.c +++ b/dlls/jscript/number.c @@ -17,6 +17,7 @@ */
#include <math.h> +#include <locale.h> #include <assert.h>
#include "jscript.h" @@ -341,11 +342,88 @@ static HRESULT Number_toString(script_ctx_t *ctx, jsval_t vthis, WORD flags, uns return S_OK; }
+HRESULT localize_number(script_ctx_t *ctx, DOUBLE val, jsstr_t **ret) +{ + WCHAR buf[314], *numstr, *p, *frac = NULL; + BOOL remove_fraction = FALSE; + jsstr_t *str, *tmp; + _locale_t locale; + unsigned convlen; + int len; + + /* FIXME: Localize this? */ + if(!isfinite(val)) + return to_string(ctx, jsval_number(val), ret); + + /* Native never uses an exponent, even if the number is very large, it will in fact + return all the digits (with thousands separators). jscript.dll uses two digits for + fraction even if they are zero (likely default numDigits) and always returns them, + while mshtml's jscript only if they are non-zero (on same locale settings); this + applies even for very small numbers, such as 0.0000999, which will simply be 0 */ + if(!(locale = _create_locale(LC_ALL, "C"))) + return E_OUTOFMEMORY; + len = _swprintf_l(buf, ARRAY_SIZE(buf), L"%.2f", locale, val); + _free_locale(locale); + + if(!(convlen = GetNumberFormatW(ctx->lcid, 0, buf, NULL, NULL, 0)) || + !(str = jsstr_alloc_buf(convlen - 1, &numstr))) + return E_OUTOFMEMORY; + + if(!GetNumberFormatW(ctx->lcid, 0, buf, NULL, numstr, convlen)) { + jsstr_release(str); + return E_OUTOFMEMORY; + } + + if(ctx->version >= SCRIPTLANGUAGEVERSION_ES5) { + while(len--) { + if(buf[len] != '0') { + if(buf[len] == '.') + remove_fraction = TRUE; + break; + } + } + } + + if(remove_fraction && GetLocaleInfoW(ctx->lcid, LOCALE_SDECIMAL, buf, ARRAY_SIZE(buf))) { + p = numstr; + while(*p) { + if(!(p = wcsstr(p, buf))) + break; + frac = p++; + } + if(frac) { + tmp = jsstr_alloc_len(numstr, frac - numstr); + jsstr_release(str); + if(!tmp) + return E_OUTOFMEMORY; + str = tmp; + } + } + + *ret = str; + return S_OK; +} + static HRESULT Number_toLocaleString(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { - FIXME("\n"); - return E_NOTIMPL; + jsstr_t *str; + HRESULT hres; + DOUBLE val; + + TRACE("\n"); + + hres = numberval_this(vthis, &val); + if(FAILED(hres)) + return hres; + + if(r) { + hres = localize_number(ctx, val, &str); + if(FAILED(hres)) + return hres; + *r = jsval_string(str); + } + return S_OK; }
static HRESULT Number_toFixed(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, diff --git a/dlls/jscript/tests/api.js b/dlls/jscript/tests/api.js index 1efc023..8653946 100644 --- a/dlls/jscript/tests/api.js +++ b/dlls/jscript/tests/api.js @@ -1365,6 +1365,11 @@ ok(tmp === "0", "num().toString = " + tmp); tmp = (new Number(5.5)).toString(2); ok(tmp === "101.1", "num(5.5).toString(2) = " + tmp);
+tmp = (new Number(12)).toLocaleString(); +ok(tmp.indexOf(String.fromCharCode(0)) == -1, "invalid null byte"); +tmp = Number.prototype.toLocaleString.call(NaN); +ok(tmp.indexOf(String.fromCharCode(0)) == -1, "invalid null byte"); + tmp = (new Number(3)).toFixed(3); ok(tmp === "3.000", "num(3).toFixed(3) = " + tmp); tmp = (new Number(3)).toFixed(); @@ -2594,6 +2599,8 @@ testException(function() {arr.test();}, "E_NO_PROPERTY"); testException(function() {[1,2,3].sort(nullDisp);}, "E_JSCRIPT_EXPECTED"); testException(function() {Number.prototype.toString.call(arr);}, "E_NOT_NUM"); testException(function() {Number.prototype.toFixed.call(arr);}, "E_NOT_NUM"); +testException(function() {Number.prototype.toLocaleString.call(arr);}, "E_NOT_NUM"); +testException(function() {Number.prototype.toLocaleString.call(null);}, "E_NOT_NUM"); testException(function() {(new Number(3)).toString(1);}, "E_INVALID_CALL_ARG"); testException(function() {(new Number(3)).toFixed(21);}, "E_FRACTION_DIGITS_OUT_OF_RANGE"); testException(function() {(new Number(1)).toPrecision(0);}, "E_PRECISION_OUT_OF_RANGE");