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");
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/array.c | 78 +++++++++++++++++++++++++++++++++++--- dlls/jscript/dispex.c | 27 +++++++++++++ dlls/jscript/jscript.h | 1 + dlls/jscript/tests/api.js | 1 + dlls/jscript/tests/run.c | 51 +++++++++++++++++++++++++ dlls/mshtml/tests/es5.js | 61 +++++++++++++++++++++++++++++ dlls/mshtml/tests/script.c | 29 ++++++++++++++ 7 files changed, 242 insertions(+), 6 deletions(-)
diff --git a/dlls/jscript/array.c b/dlls/jscript/array.c index ee72579..291f032 100644 --- a/dlls/jscript/array.c +++ b/dlls/jscript/array.c @@ -229,7 +229,7 @@ done: }
static HRESULT array_join(script_ctx_t *ctx, jsdisp_t *array, DWORD length, const WCHAR *sep, - unsigned seplen, jsval_t *r) + unsigned seplen, HRESULT (*to_string)(script_ctx_t*,jsval_t,jsstr_t**), jsval_t *r) { jsstr_t **str_tab, *ret = NULL; jsval_t val; @@ -339,11 +339,11 @@ static HRESULT Array_join(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned if(FAILED(hres)) goto done;
- hres = array_join(ctx, jsthis, length, sep, jsstr_length(sep_str), r); + hres = array_join(ctx, jsthis, length, sep, jsstr_length(sep_str), to_string, r);
jsstr_release(sep_str); }else { - hres = array_join(ctx, jsthis, length, L",", 1, r); + hres = array_join(ctx, jsthis, length, L",", 1, to_string, r); }
done: @@ -947,14 +947,80 @@ static HRESULT Array_toString(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsi if(!array) return JS_E_ARRAY_EXPECTED;
- return array_join(ctx, &array->dispex, array->length, L",", 1, r); + return array_join(ctx, &array->dispex, array->length, L",", 1, to_string, r); +} + +static HRESULT to_locale_string(script_ctx_t *ctx, jsval_t val, jsstr_t **str) +{ + jsdisp_t *jsdisp; + IDispatch *obj; + HRESULT hres; + + switch(jsval_type(val)) { + case JSV_OBJECT: + hres = disp_call_name(ctx, get_object(val), L"toLocaleString", DISPATCH_METHOD, 0, NULL, &val); + if(FAILED(hres)) { + if(hres == JS_E_INVALID_PROPERTY && ctx->version >= SCRIPTLANGUAGEVERSION_ES5) + hres = JS_E_FUNCTION_EXPECTED; + return hres; + } + break; + case JSV_NUMBER: + if(ctx->version >= SCRIPTLANGUAGEVERSION_ES5) + return localize_number(ctx, get_number(val), str); + /* fall through */ + default: + if(ctx->version >= SCRIPTLANGUAGEVERSION_ES5) + break; + + hres = to_object(ctx, val, &obj); + if(FAILED(hres)) + return hres; + + jsdisp = as_jsdisp(obj); + hres = jsdisp_call_name(jsdisp, L"toLocaleString", DISPATCH_METHOD, 0, NULL, &val); + jsdisp_release(jsdisp); + if(FAILED(hres)) + return hres; + break; + } + + return to_string(ctx, val, str); }
static HRESULT Array_toLocaleString(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { - FIXME("\n"); - return E_NOTIMPL; + jsdisp_t *jsthis; + UINT32 length; + HRESULT hres; + WCHAR buf[5]; + int len; + + TRACE("\n"); + + if(ctx->version < SCRIPTLANGUAGEVERSION_ES5) { + ArrayInstance *array = array_this(vthis); + if(!array) + return JS_E_ARRAY_EXPECTED; + jsthis = jsdisp_addref(&array->dispex); + length = array->length; + }else { + hres = get_length(ctx, vthis, &jsthis, &length); + if(FAILED(hres)) + return hres; + } + + if(!(len = GetLocaleInfoW(ctx->lcid, LOCALE_SLIST, buf, ARRAY_SIZE(buf) - 1))) { + buf[len++] = ','; + len++; + } + buf[len - 1] = ' '; + buf[len] = '\0'; + + hres = array_join(ctx, jsthis, length, buf, len, to_locale_string, r); + jsdisp_release(jsthis); + return hres; }
static HRESULT Array_forEach(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, diff --git a/dlls/jscript/dispex.c b/dlls/jscript/dispex.c index 8682766..468a0dd 100644 --- a/dlls/jscript/dispex.c +++ b/dlls/jscript/dispex.c @@ -2146,6 +2146,33 @@ HRESULT disp_call(script_ctx_t *ctx, IDispatch *disp, DISPID id, WORD flags, uns return hres; }
+HRESULT disp_call_name(script_ctx_t *ctx, IDispatch *disp, const WCHAR *name, WORD flags, unsigned argc, jsval_t *argv, jsval_t *ret) +{ + IDispatchEx *dispex; + jsdisp_t *jsdisp; + HRESULT hres; + DISPID id; + BSTR bstr; + + if((jsdisp = to_jsdisp(disp)) && jsdisp->ctx == ctx) + return jsdisp_call_name(jsdisp, name, flags, argc, argv, ret); + + if(!(bstr = SysAllocString(name))) + return E_OUTOFMEMORY; + hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex); + if(SUCCEEDED(hres) && dispex) { + hres = IDispatchEx_GetDispID(dispex, bstr, make_grfdex(ctx, fdexNameCaseSensitive), &id); + IDispatchEx_Release(dispex); + }else { + hres = IDispatch_GetIDsOfNames(disp, &IID_NULL, &bstr, 1, 0, &id); + } + SysFreeString(bstr); + if(FAILED(hres)) + return hres; + + return disp_call(ctx, disp, id, flags, argc, argv, ret); +} + HRESULT disp_call_value(script_ctx_t *ctx, IDispatch *disp, IDispatch *jsthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index 406efa9..f594b38 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -241,6 +241,7 @@ HRESULT init_dispex(jsdisp_t*,script_ctx_t*,const builtin_info_t*,jsdisp_t*) DEC HRESULT init_dispex_from_constr(jsdisp_t*,script_ctx_t*,const builtin_info_t*,jsdisp_t*) DECLSPEC_HIDDEN;
HRESULT disp_call(script_ctx_t*,IDispatch*,DISPID,WORD,unsigned,jsval_t*,jsval_t*) DECLSPEC_HIDDEN; +HRESULT disp_call_name(script_ctx_t*,IDispatch*,const WCHAR*,WORD,unsigned,jsval_t*,jsval_t*) DECLSPEC_HIDDEN; HRESULT disp_call_value(script_ctx_t*,IDispatch*,IDispatch*,WORD,unsigned,jsval_t*,jsval_t*) DECLSPEC_HIDDEN; HRESULT jsdisp_call_value(jsdisp_t*,IDispatch*,WORD,unsigned,jsval_t*,jsval_t*) DECLSPEC_HIDDEN; HRESULT jsdisp_call(jsdisp_t*,DISPID,WORD,unsigned,jsval_t*,jsval_t*) DECLSPEC_HIDDEN; diff --git a/dlls/jscript/tests/api.js b/dlls/jscript/tests/api.js index 8653946..e81bbea 100644 --- a/dlls/jscript/tests/api.js +++ b/dlls/jscript/tests/api.js @@ -2597,6 +2597,7 @@ testException(function() {date.setTime();}, "E_ARG_NOT_OPT"); testException(function() {date.setYear();}, "E_ARG_NOT_OPT"); testException(function() {arr.test();}, "E_NO_PROPERTY"); testException(function() {[1,2,3].sort(nullDisp);}, "E_JSCRIPT_EXPECTED"); +testException(function() {var o = new Object(); o.length = 1; o[0] = "a"; Array.prototype.toLocaleString.call(o);}, "E_NOT_ARRAY"); 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"); diff --git a/dlls/jscript/tests/run.c b/dlls/jscript/tests/run.c index e4408be..35d81ec 100644 --- a/dlls/jscript/tests/run.c +++ b/dlls/jscript/tests/run.c @@ -112,6 +112,8 @@ DEFINE_EXPECT(testobj_onlydispid_i); DEFINE_EXPECT(testobj_notexists_d); DEFINE_EXPECT(testobj_newenum); DEFINE_EXPECT(testobj_getidfail_d); +DEFINE_EXPECT(testobj_tolocalestr_d); +DEFINE_EXPECT(testobj_tolocalestr_i); DEFINE_EXPECT(enumvariant_next_0); DEFINE_EXPECT(enumvariant_next_1); DEFINE_EXPECT(enumvariant_reset); @@ -178,6 +180,7 @@ DEFINE_EXPECT(BindHandler); #define DISPID_TESTOBJ_PROP 0x2000 #define DISPID_TESTOBJ_ONLYDISPID 0x2001 #define DISPID_TESTOBJ_WITHPROP 0x2002 +#define DISPID_TESTOBJ_TOLOCALESTR 0x2003
#define JS_E_OUT_OF_MEMORY 0x800a03ec #define JS_E_INVALID_CHAR 0x800a03f6 @@ -476,6 +479,12 @@ static HRESULT WINAPI testObj_GetDispID(IDispatchEx *iface, BSTR bstrName, DWORD *pid = DISPID_TESTOBJ_ONLYDISPID; return S_OK; } + if(!lstrcmpW(bstrName, L"toLocaleString")) { + CHECK_EXPECT(testobj_tolocalestr_d); + test_grfdex(grfdex, fdexNameCaseSensitive); + *pid = DISPID_TESTOBJ_TOLOCALESTR; + return S_OK; + } if(!lstrcmpW(bstrName, L"notExists")) { CHECK_EXPECT(testobj_notexists_d); test_grfdex(grfdex, fdexNameCaseSensitive); @@ -556,6 +565,22 @@ static HRESULT WINAPI testObj_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid, V_VT(pvarRes) = VT_I4; V_I4(pvarRes) = 1;
+ return S_OK; + case DISPID_TESTOBJ_TOLOCALESTR: + CHECK_EXPECT(testobj_tolocalestr_i); + + ok(wFlags == DISPATCH_METHOD, "wFlags = %x\n", wFlags); + ok(pdp != NULL, "pdp == NULL\n"); + ok(!pdp->rgdispidNamedArgs, "rgdispidNamedArgs != NULL\n"); + ok(!pdp->cArgs, "cArgs = %d\n", pdp->cArgs); + ok(!pdp->cNamedArgs, "cNamedArgs = %d\n", pdp->cNamedArgs); + ok(pvarRes != NULL, "pvarRes == NULL\n"); + ok(V_VT(pvarRes) == VT_EMPTY, "V_VT(pvarRes) = %d\n", V_VT(pvarRes)); + ok(pei != NULL, "pei == NULL\n"); + + V_VT(pvarRes) = VT_I4; + V_I4(pvarRes) = 1234; + return S_OK; }
@@ -2998,6 +3023,7 @@ static void test_default_value(void)
static void test_script_exprs(void) { + WCHAR buf[64], sep[4]; VARIANT v; HRESULT hres;
@@ -3060,6 +3086,25 @@ static void test_script_exprs(void) ok(!lstrcmpW(V_BSTR(&v), L"wine"), "V_BSTR(v) = %s\n", wine_dbgstr_w(V_BSTR(&v))); VariantClear(&v);
+ if(!GetLocaleInfoW(GetUserDefaultLCID(), LOCALE_SLIST, sep, ARRAY_SIZE(sep))) wcscpy(sep, L","); + swprintf(buf, ARRAY_SIZE(buf), L"12%s 12%s undefined undefined undefined%s 12", sep, sep, sep); + hres = parse_script_expr(L"var arr = [5]; arr.toLocaleString = function(a,b,c) {return a+' '+b+' '+c;};" + L"Number.prototype.toLocaleString = function() {return 12;};" + L"[1,2,arr,3].toLocaleString('foo','bar','baz')", &v, NULL); + ok(hres == S_OK, "parse_script_expr failed: %08lx\n", hres); + ok(V_VT(&v) == VT_BSTR, "V_VT(v) = %d\n", V_VT(&v)); + ok(!lstrcmpW(V_BSTR(&v), buf), "V_BSTR(v) = %s\n", wine_dbgstr_w(V_BSTR(&v))); + VariantClear(&v); + + hres = parse_script_expr(L"delete Object.prototype.toLocaleString; Array.prototype.toLocaleString.call([])", &v, NULL); + ok(hres == S_OK, "parse_script_expr failed: %08lx\n", hres); + ok(V_VT(&v) == VT_BSTR, "V_VT(v) = %d\n", V_VT(&v)); + ok(!lstrcmpW(V_BSTR(&v), L""), "V_BSTR(v) = %s\n", wine_dbgstr_w(V_BSTR(&v))); + VariantClear(&v); + + hres = parse_script_expr(L"delete Object.prototype.toLocaleString; Array.prototype.toLocaleString.call(['a'])", &v, NULL); + ok(hres == 0x800a01b6, "parse_script_expr failed: %08lx\n", hres); + test_default_value(); test_propputref(); test_retval(); @@ -3656,6 +3701,12 @@ static BOOL run_tests(void) CHECK_CALLED(testobj_withprop_d); CHECK_CALLED(testobj_withprop_i);
+ SET_EXPECT(testobj_tolocalestr_d); + SET_EXPECT(testobj_tolocalestr_i); + run_script(L"var t = [testObj].toLocaleString(); ok(t === '1234', 't = ' + t);"); + CHECK_CALLED(testobj_tolocalestr_d); + CHECK_CALLED(testobj_tolocalestr_i); + run_script(L"@set @t=2\nok(@t === 2, '@t = ' + @t);");
SET_EXPECT(global_success_d); diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index 4892207..e6fdf03 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -68,6 +68,67 @@ sync_test("toISOString", function() { expect_exception(function() { new Date(31494784780800001).toISOString(); }); });
+sync_test("Array toLocaleString", function() { + var r = Array.prototype.toLocaleString.length, old = Number.prototype.toLocaleString; + var s = external.listSeparator + ' '; + ok(r === 0, "length = " + r); + + r = [5]; + r.toLocaleString = function(a, b, c) { return a + " " + b + " " + c; }; + Number.prototype.toLocaleString = function() { return "aBc"; }; + + r = [new Number(3), r, new Number(12)].toLocaleString("foo", "bar", "baz"); + ok(r === "aBc"+s+"undefined undefined undefined"+s+"aBc", "toLocaleString returned " + r); + + r = [3].toLocaleString(); /* primitive number value not affected */ + ok(r !== "aBc", "toLocaleString returned " + r); + Number.prototype.toLocaleString = old; + + r = Object.create(null); + r.toString = function() { return "foo"; } + try { + Array.prototype.toLocaleString.call([r]); + ok(false, "expected exception calling it on array with object without toLocaleString"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_FUNCTION_EXPECTED, "called on array with object without toLocaleString threw " + n); + } + + r = { length: 2 }; + r[0] = { toLocaleString: function() { return "foo"; } } + r[1] = { toLocaleString: function() { return "bar"; } } + r = Array.prototype.toLocaleString.call(r); + ok(r === "foo"+s+"bar", "toLocaleString on array-like object returned " + r); + + r = Array.prototype.toLocaleString.call({}); + ok(r === "", "toLocaleString on {} returned " + r); + + r = Array.prototype.toLocaleString.call("ab"); + ok(r === "a"+s+"b", "toLocaleString on 'ab' returned " + r); + + try { + Array.prototype.toLocaleString.call(undefined); + ok(false, "expected exception calling it on undefined"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_OBJECT_EXPECTED, "called on undefined threw " + n); + } + try { + Array.prototype.toLocaleString.call(null); + ok(false, "expected exception calling it on null"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_OBJECT_EXPECTED, "called on null threw " + n); + } + try { + Array.prototype.toLocaleString.call(external.nullDisp); + ok(false, "expected exception calling it on nullDisp"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_OBJECT_EXPECTED, "called on nullDisp threw " + n); + } +}); + sync_test("indexOf", function() { function expect(array, args, exr) { var r = Array.prototype.indexOf.apply(array, args); diff --git a/dlls/mshtml/tests/script.c b/dlls/mshtml/tests/script.c index ceb4fc7..1226d1a 100644 --- a/dlls/mshtml/tests/script.c +++ b/dlls/mshtml/tests/script.c @@ -153,6 +153,7 @@ DEFINE_EXPECT(GetTypeInfo); #define DISPID_EXTERNAL_WRITESTREAM 0x300006 #define DISPID_EXTERNAL_GETVT 0x300007 #define DISPID_EXTERNAL_NULL_DISP 0x300008 +#define DISPID_EXTERNAL_LIST_SEP 0x300009
static const GUID CLSID_TestScript = {0x178fc163,0xf585,0x4e24,{0x9c,0x13,0x4b,0xb7,0xfa,0xf8,0x07,0x46}}; @@ -599,6 +600,10 @@ static HRESULT WINAPI externalDisp_GetDispID(IDispatchEx *iface, BSTR bstrName, *pid = DISPID_EXTERNAL_NULL_DISP; return S_OK; } + if(!lstrcmpW(bstrName, L"listSeparator")) { + *pid = DISPID_EXTERNAL_LIST_SEP; + return S_OK; + }
ok(0, "unexpected name %s\n", wine_dbgstr_w(bstrName)); return DISP_E_UNKNOWNNAME; @@ -784,6 +789,30 @@ static HRESULT WINAPI externalDisp_InvokeEx(IDispatchEx *iface, DISPID id, LCID V_DISPATCH(pvarRes) = NULL; return S_OK;
+ case DISPID_EXTERNAL_LIST_SEP: { + WCHAR buf[4]; + int len; + + ok(wFlags == INVOKE_PROPERTYGET, "wFlags = %x\n", wFlags); + ok(pdp != NULL, "pdp == NULL\n"); + ok(!pdp->rgvarg, "rgvarg != NULL\n"); + ok(!pdp->rgdispidNamedArgs, "rgdispidNamedArgs != NULL\n"); + ok(!pdp->cArgs, "cArgs = %d\n", pdp->cArgs); + ok(!pdp->cNamedArgs, "cNamedArgs = %d\n", pdp->cNamedArgs); + ok(pvarRes != NULL, "pvarRes == NULL\n"); + ok(V_VT(pvarRes) == VT_EMPTY, "V_VT(pvarRes) = %d\n", V_VT(pvarRes)); + ok(pei != NULL, "pei == NULL\n"); + + if(!(len = GetLocaleInfoW(GetUserDefaultLCID(), LOCALE_SLIST, buf, ARRAY_SIZE(buf)))) + buf[len++] = ','; + else + len--; + + V_VT(pvarRes) = VT_BSTR; + V_BSTR(pvarRes) = SysAllocStringLen(buf, len); + return S_OK; + } + default: ok(0, "unexpected call\n"); return E_NOTIMPL;
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=112975
Your paranoid android.
=== w1064_tsign (64 bit report) ===
mshtml: htmldoc.c:2541: Test failed: unexpected call UpdateUI htmldoc.c:2853: Test failed: unexpected call Exec_UPDATECOMMANDS htmldoc.c:2541: Test failed: unexpected call UpdateUI htmldoc.c:2853: Test failed: unexpected call Exec_UPDATECOMMANDS htmldoc.c:350: Test failed: expected Exec_SETTITLE htmldoc.c:2859: Test failed: unexpected call Exec_SETTITLE
=== w7u_adm (32 bit report) ===
mshtml: script.c:639: Test failed: L"/index.html?es5.js:date_now: unexpected Date.now() result 1650479334624 expected 1650479334686"
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/array.c | 75 ++++++++++++++++++++++++++++++++++++++++ dlls/mshtml/tests/es5.js | 28 +++++++++++++++ 2 files changed, 103 insertions(+)
diff --git a/dlls/jscript/array.c b/dlls/jscript/array.c index 291f032..3923bb3 100644 --- a/dlls/jscript/array.c +++ b/dlls/jscript/array.c @@ -1023,6 +1023,80 @@ static HRESULT Array_toLocaleString(script_ctx_t *ctx, jsval_t vthis, WORD flags return hres; }
+static HRESULT Array_filter(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, + jsval_t *r) +{ + IDispatch *context_obj = NULL, *callback; + jsval_t value, args[3], res; + unsigned length, i, j = 0; + jsdisp_t *jsthis, *arr; + HRESULT hres; + BOOL boolval; + + TRACE("\n"); + + hres = get_length(ctx, vthis, &jsthis, &length); + if(FAILED(hres)) + return hres; + + /* FIXME: check IsCallable */ + if(!argc || !is_object_instance(argv[0])) { + FIXME("Invalid arg %s\n", debugstr_jsval(argc ? argv[0] : jsval_undefined())); + hres = E_INVALIDARG; + goto done; + } + callback = get_object(argv[0]); + + if(argc > 1 && !is_undefined(argv[1])) { + if(!is_object_instance(argv[1])) { + FIXME("Unsupported context this %s\n", debugstr_jsval(argv[1])); + hres = E_NOTIMPL; + goto done; + } + context_obj = get_object(argv[1]); + } + + hres = create_array(ctx, 0, &arr); + if(FAILED(hres)) + goto done; + + for(i = 0; i < length; i++) { + hres = jsdisp_get_idx(jsthis, i, &value); + if(FAILED(hres)) { + if(hres == DISP_E_UNKNOWNNAME) { + hres = S_OK; + continue; + } + break; + } + args[0] = value; + args[1] = jsval_number(i); + args[2] = jsval_obj(jsthis); + hres = disp_call_value(ctx, callback, context_obj, DISPATCH_METHOD, ARRAY_SIZE(args), args, &res); + if(SUCCEEDED(hres)) { + hres = to_boolean(res, &boolval); + jsval_release(res); + if(SUCCEEDED(hres) && boolval) + hres = jsdisp_propput_idx(arr, j++, value); + } + jsval_release(value); + if(FAILED(hres)) + break; + } + + if(FAILED(hres)) { + jsdisp_release(arr); + goto done; + } + set_length(arr, j); + + if(r) + *r = jsval_obj(arr); +done: + jsdisp_release(jsthis); + return hres; +} + static HRESULT Array_forEach(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { @@ -1365,6 +1439,7 @@ static void Array_on_put(jsdisp_t *dispex, const WCHAR *name)
static const builtin_prop_t Array_props[] = { {L"concat", Array_concat, PROPF_METHOD|1}, + {L"filter", Array_filter, PROPF_METHOD|PROPF_ES5|1}, {L"forEach", Array_forEach, PROPF_METHOD|PROPF_ES5|1}, {L"indexOf", Array_indexOf, PROPF_METHOD|PROPF_ES5|1}, {L"join", Array_join, PROPF_METHOD|1}, diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index e6fdf03..32064d3 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -155,6 +155,34 @@ sync_test("indexOf", function() { expect([1,2,3], [2, 1.9], 1); });
+sync_test("filter", function() { + ok(Array.prototype.filter.length === 1, "filter.length = " + Array.prototype.filter.length); + + var arr = ["a","foobar",true,"b",42,0,Math,null,undefined,[1,2,3,"4"]]; + delete arr[1]; + + function test(expect, fn, expect_this) { + var mismatch = false, r = function(v, i, a) { + ok(a === arr, "unexpected array " + arr); + ok(v === arr[i], "value = " + v + ", expected " + arr[i]); + ok(this === (expect_this ? expect_this : window), "this = " + this + ", expected " + expect_this); + return fn(v); + }; + r = expect_this ? Array.prototype.filter.call(arr, r, expect_this) : Array.prototype.filter.call(arr, r); + ok(r.length === expect.length, "filtered array length = " + r.length + ", expected " + expect.length); + for(var i = 0; i < r.length; i++) + if(r[i] !== expect[i]) + mismatch = true; + ok(!mismatch, "filtered array = " + r + ", expected " + expect); + } + + test([], function(v) { return false; }); + test(["a",true,"b",42,0,Math,null,undefined,arr[9]], function(v) { if(arr[1] === "foobar") delete arr[1]; return true; }); + test(["a","b"], function(v) { if(v === "b") delete arr[0]; return typeof v === "string"; }); + test(["b"], function(v) { if(arr[arr.length - 1] !== "c") arr.push("c"); return typeof v === "string"; }); + test([true,"b",42,Math,arr[9],"c"], function(v) { return v; }, Object); +}); + sync_test("forEach", function() { ok(Array.prototype.forEach.length === 1, "forEach.length = " + Array.prototype.forEach.length);
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=112976
Your paranoid android.
=== w7u_adm (32 bit report) ===
mshtml: script.c:639: Test failed: L"/index.html?es5.js:date_now: unexpected Date.now() result 1650481301754 expected 1650481301815"
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
Tested together with Array.some since they are very similar.
dlls/jscript/array.c | 67 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+)
diff --git a/dlls/jscript/array.c b/dlls/jscript/array.c index 3923bb3..52ae647 100644 --- a/dlls/jscript/array.c +++ b/dlls/jscript/array.c @@ -1023,6 +1023,72 @@ static HRESULT Array_toLocaleString(script_ctx_t *ctx, jsval_t vthis, WORD flags return hres; }
+static HRESULT Array_every(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, + jsval_t *r) +{ + IDispatch *context_obj = NULL, *callback; + jsval_t value, args[3], res; + BOOL boolval, ret = TRUE; + unsigned length, i; + jsdisp_t *jsthis; + HRESULT hres; + + TRACE("\n"); + + hres = get_length(ctx, vthis, &jsthis, &length); + if(FAILED(hres)) + return hres; + + /* FIXME: check IsCallable */ + if(!argc || !is_object_instance(argv[0])) { + FIXME("Invalid arg %s\n", debugstr_jsval(argc ? argv[0] : jsval_undefined())); + hres = E_INVALIDARG; + goto done; + } + callback = get_object(argv[0]); + + if(argc > 1 && !is_undefined(argv[1])) { + if(!is_object_instance(argv[1])) { + FIXME("Unsupported context this %s\n", debugstr_jsval(argv[1])); + hres = E_NOTIMPL; + goto done; + } + context_obj = get_object(argv[1]); + } + + for(i = 0; i < length; i++) { + hres = jsdisp_get_idx(jsthis, i, &value); + if(FAILED(hres)) { + if(hres == DISP_E_UNKNOWNNAME) + continue; + goto done; + } + args[0] = value; + args[1] = jsval_number(i); + args[2] = jsval_obj(jsthis); + hres = disp_call_value(ctx, callback, context_obj, DISPATCH_METHOD, ARRAY_SIZE(args), args, &res); + jsval_release(value); + if(FAILED(hres)) + goto done; + + hres = to_boolean(res, &boolval); + jsval_release(res); + if(FAILED(hres)) + goto done; + if(!boolval) { + ret = FALSE; + break; + } + } + + if(r) + *r = jsval_bool(ret); + hres = S_OK; +done: + jsdisp_release(jsthis); + return hres; +} + static HRESULT Array_filter(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { @@ -1439,6 +1505,7 @@ static void Array_on_put(jsdisp_t *dispex, const WCHAR *name)
static const builtin_prop_t Array_props[] = { {L"concat", Array_concat, PROPF_METHOD|1}, + {L"every", Array_every, PROPF_METHOD|PROPF_ES5|1}, {L"filter", Array_filter, PROPF_METHOD|PROPF_ES5|1}, {L"forEach", Array_forEach, PROPF_METHOD|PROPF_ES5|1}, {L"indexOf", Array_indexOf, PROPF_METHOD|PROPF_ES5|1},
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/array.c | 67 ++++++++++++++++++++++++++++++++++++++++ dlls/mshtml/tests/es5.js | 34 ++++++++++++++++++++ 2 files changed, 101 insertions(+)
diff --git a/dlls/jscript/array.c b/dlls/jscript/array.c index 52ae647..3ee687a 100644 --- a/dlls/jscript/array.c +++ b/dlls/jscript/array.c @@ -1414,6 +1414,72 @@ done: return hres; }
+static HRESULT Array_some(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, + jsval_t *r) +{ + IDispatch *context_obj = NULL, *callback; + jsval_t value, args[3], res; + BOOL boolval, ret = FALSE; + unsigned length, i; + jsdisp_t *jsthis; + HRESULT hres; + + TRACE("\n"); + + hres = get_length(ctx, vthis, &jsthis, &length); + if(FAILED(hres)) + return hres; + + /* FIXME: check IsCallable */ + if(!argc || !is_object_instance(argv[0])) { + FIXME("Invalid arg %s\n", debugstr_jsval(argc ? argv[0] : jsval_undefined())); + hres = E_INVALIDARG; + goto done; + } + callback = get_object(argv[0]); + + if(argc > 1 && !is_undefined(argv[1])) { + if(!is_object_instance(argv[1])) { + FIXME("Unsupported context this %s\n", debugstr_jsval(argv[1])); + hres = E_NOTIMPL; + goto done; + } + context_obj = get_object(argv[1]); + } + + for(i = 0; i < length; i++) { + hres = jsdisp_get_idx(jsthis, i, &value); + if(FAILED(hres)) { + if(hres == DISP_E_UNKNOWNNAME) + continue; + goto done; + } + args[0] = value; + args[1] = jsval_number(i); + args[2] = jsval_obj(jsthis); + hres = disp_call_value(ctx, callback, context_obj, DISPATCH_METHOD, ARRAY_SIZE(args), args, &res); + jsval_release(value); + if(FAILED(hres)) + goto done; + + hres = to_boolean(res, &boolval); + jsval_release(res); + if(FAILED(hres)) + goto done; + if(boolval) { + ret = TRUE; + break; + } + } + + if(r) + *r = jsval_bool(ret); + hres = S_OK; +done: + jsdisp_release(jsthis); + return hres; +} + /* ECMA-262 3rd Edition 15.4.4.13 */ static HRESULT Array_unshift(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) @@ -1518,6 +1584,7 @@ static const builtin_prop_t Array_props[] = { {L"reverse", Array_reverse, PROPF_METHOD}, {L"shift", Array_shift, PROPF_METHOD}, {L"slice", Array_slice, PROPF_METHOD|2}, + {L"some", Array_some, PROPF_METHOD|PROPF_ES5|1}, {L"sort", Array_sort, PROPF_METHOD|1}, {L"splice", Array_splice, PROPF_METHOD|2}, {L"toLocaleString", Array_toLocaleString, PROPF_METHOD}, diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index 32064d3..487e3de 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -183,6 +183,40 @@ sync_test("filter", function() { test([true,"b",42,Math,arr[9],"c"], function(v) { return v; }, Object); });
+sync_test("every & some", function() { + ok(Array.prototype.every.length === 1, "every.length = " + Array.prototype.every.length); + ok(Array.prototype.some.length === 1, "some.length = " + Array.prototype.some.length); + var arr = ["foobar"]; + + function test(expect_every, expect_some, fn, expect_this) { + var cb = function(v, i, a) { + ok(a === arr, "unexpected array " + arr); + ok(v === arr[i], "value = " + v + ", expected " + arr[i]); + ok(this === (expect_this ? expect_this : window), "this = " + this + ", expected " + expect_this); + return fn(v); + }; + r = expect_this ? Array.prototype.every.call(arr, cb, expect_this) : Array.prototype.every.call(arr, cb); + ok(r === expect_every, "'every' = " + r); + r = expect_this ? Array.prototype.some.call(arr, cb, expect_this) : Array.prototype.some.call(arr, cb); + ok(r === expect_some, "'some' = " + r); + } + + delete arr[0]; + test(true, false, function(v) { return false; }); + test(true, false, function(v) { return true; }); + + arr = [1,"2",3]; + test(true, true, function(v) { return true; }); + test(true, true, function(v) { if(arr[1] === "2") delete arr[1]; return typeof v === "number"; }); + test(true, true, function(v) { if(arr[arr.length - 1] !== "a") arr.push("a"); return typeof v === "number"; }, Object); + test(false, true, function(v) { return typeof v === "number"; }, Object); + + arr = [0,null,undefined,false]; + test(false, false, function(v) { return v; }); + arr.push(1); + test(false, true, function(v) { return v; }); +}); + sync_test("forEach", function() { ok(Array.prototype.forEach.length === 1, "forEach.length = " + Array.prototype.forEach.length);
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=112978
Your paranoid android.
=== w1064_tsign (64 bit report) ===
mshtml: htmldoc.c:2541: Test failed: unexpected call UpdateUI htmldoc.c:2853: Test failed: unexpected call Exec_UPDATECOMMANDS
=== w10pro64_he (64 bit report) ===
mshtml: htmldoc.c:2541: Test failed: unexpected call UpdateUI htmldoc.c:2853: Test failed: unexpected call Exec_UPDATECOMMANDS
=== w7u_adm (32 bit report) ===
mshtml: script.c:639: Test failed: L"/index.html?es5.js:date_now: unexpected Date.now() result 1650486562435 expected 1650486562496"
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/array.c | 63 ++++++++++++++++++++++++++++++++++++++++ dlls/mshtml/tests/es5.js | 29 ++++++++++++++++++ 2 files changed, 92 insertions(+)
diff --git a/dlls/jscript/array.c b/dlls/jscript/array.c index 3ee687a..c8caaeb 100644 --- a/dlls/jscript/array.c +++ b/dlls/jscript/array.c @@ -1277,6 +1277,68 @@ done: return hres; }
+static HRESULT Array_lastIndexOf(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, + jsval_t *r) +{ + jsval_t search, value; + unsigned i, length; + jsdisp_t *jsthis; + HRESULT hres; + BOOL eq; + + TRACE("\n"); + + hres = get_length(ctx, vthis, &jsthis, &length); + if(FAILED(hres)) + return hres; + if(!length) + goto notfound; + + search = argc ? argv[0] : jsval_undefined(); + + i = length - 1; + if(argc > 1) { + double from_arg; + + hres = to_integer(ctx, argv[1], &from_arg); + if(FAILED(hres)) + goto done; + + if(from_arg >= 0.0) + i = min(from_arg, i); + else { + from_arg += length; + if(from_arg < 0.0) + goto notfound; + i = from_arg; + } + } + + do { + hres = jsdisp_get_idx(jsthis, i, &value); + if(hres == DISP_E_UNKNOWNNAME) + continue; + if(FAILED(hres)) + goto done; + + hres = jsval_strict_equal(value, search, &eq); + jsval_release(value); + if(FAILED(hres)) + goto done; + if(eq) { + if(r) *r = jsval_number(i); + goto done; + } + } while(i--); + +notfound: + if(r) *r = jsval_number(-1); + hres = S_OK; +done: + jsdisp_release(jsthis); + return hres; +} + static HRESULT Array_map(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { IDispatch *context_this = NULL, *callback; @@ -1576,6 +1638,7 @@ static const builtin_prop_t Array_props[] = { {L"forEach", Array_forEach, PROPF_METHOD|PROPF_ES5|1}, {L"indexOf", Array_indexOf, PROPF_METHOD|PROPF_ES5|1}, {L"join", Array_join, PROPF_METHOD|1}, + {L"lastIndexOf", Array_lastIndexOf, PROPF_METHOD|PROPF_ES5|1}, {L"length", NULL,0, Array_get_length, Array_set_length}, {L"map", Array_map, PROPF_METHOD|PROPF_ES5|1}, {L"pop", Array_pop, PROPF_METHOD}, diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index 487e3de..11f8db2 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -155,6 +155,35 @@ sync_test("indexOf", function() { expect([1,2,3], [2, 1.9], 1); });
+sync_test("lastIndexOf", function() { + function expect(array, args, exr) { + var r = Array.prototype.lastIndexOf.apply(array, args); + ok(r == exr, "lastIndexOf returned " + r + " expected " + exr); + } + + ok(Array.prototype.lastIndexOf.length == 1, "lastIndexOf.length = " + Array.prototype.lastIndexOf.length); + + expect([1,2,3], [2], 1); + expect([1,undefined,3], [undefined], 1); + expect([1,undefined,3], [], 1); + expect([1,,3], [undefined], -1); + expect([1,undefined,undefined], [undefined], 2); + expect([1,2,3,2,5,6], [2, 2], 1); + expect([1,2,3,2,5,6], [2], 3); + expect([1,2,3,2,5,6], [2, -3], 3); + expect([1,2,3,2,5,6], [2, -4], 1); + expect([1,2,3,2,5,6], [1, -100], -1); + expect([1,2,3,2,5,6], [2, 100], 3); + expect("abcba", ["b"], 3); + expect(true, [true], -1); + expect({"4": 4, length: 5}, [4], 4); + expect({"4": 4, length: 5}, [undefined], -1); + expect({"4": 4, length: 3}, [4], -1); + expect({"test": true}, [true], -1); + expect([1,2,3], [2, 1.9], 1); + expect([1,2,3], [2, 0.9], -1); +}); + sync_test("filter", function() { ok(Array.prototype.filter.length === 1, "filter.length = " + Array.prototype.filter.length);
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=112979
Your paranoid android.
=== w10pro64_he (64 bit report) ===
mshtml: htmldoc.c:2541: Test failed: unexpected call UpdateUI htmldoc.c:2853: Test failed: unexpected call Exec_UPDATECOMMANDS
Hi Gabriel,
On 4/20/22 18:56, Gabriel Ivăncescu wrote:
Signed-off-by: Gabriel Ivăncescugabrielopcode@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.
Could we just modify the existing toString() code to optionally take into account localized separators and required customizations? It seems to me that avoiding exponential form is simply a matter of using a larger buffer for localized string.
Also, it would be nice to have better tests. For jscript mode, you should be able to change GetLCID() to force using English locale. For HTML mode, you may run those tests only for English locale.
Thanks,
Jacek
Hi Jacek,
On 21/04/2022 15:45, Jacek Caban wrote:
Hi Gabriel,
On 4/20/22 18:56, Gabriel Ivăncescu wrote:
Signed-off-by: Gabriel Ivăncescugabrielopcode@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.
Could we just modify the existing toString() code to optionally take into account localized separators and required customizations? It seems to me that avoiding exponential form is simply a matter of using a larger buffer for localized string.
Do you mean to localize the entire number ourselves in toString? Or just the sprintf part? (so it can be passed to GetNumberFormatW)
I don't know if localizing the number in toString without using an API like GetNumberFormatW is a good idea—I certainly don't know the required customizations for each locale, having to implement them sounds like a lot of duplication, and is it possible they could even change in the future?
Also, it would be nice to have better tests. For jscript mode, you should be able to change GetLCID() to force using English locale. For HTML mode, you may run those tests only for English locale.
OK, I'll see what I can do about the English tests at least.
Thanks, Gabriel
On 4/21/22 20:11, Gabriel Ivăncescu wrote:
Hi Jacek,
On 21/04/2022 15:45, Jacek Caban wrote:
Hi Gabriel,
On 4/20/22 18:56, Gabriel Ivăncescu wrote:
Signed-off-by: Gabriel Ivăncescugabrielopcode@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.
Could we just modify the existing toString() code to optionally take into account localized separators and required customizations? It seems to me that avoiding exponential form is simply a matter of using a larger buffer for localized string.
Do you mean to localize the entire number ourselves in toString? Or just the sprintf part? (so it can be passed to GetNumberFormatW)
I don't know if localizing the number in toString without using an API like GetNumberFormatW is a good idea—I certainly don't know the required customizations for each locale, having to implement them sounds like a lot of duplication, and is it possible they could even change in the future?
You seem to assume that GetNumberFormatW does what we need, but the differences that you found with native suggests otherwise. If something theoretically changes about it in the future, it not clear if that it would be desirable for jscript behaviour to change.
Anyway, using toString() may indeed be not really be convenient. There is also NUMBERFMTW parameter to GetNumberFormatW, which has NumDigits that does what you need. The unfortunate thing is that preparing NUMBERFORMATW is not really convenient. It may be worth to try to use that. If it doesn't work out, I guess we may need to use something like your patch after all.
Thanks,
Jacek