Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
VariantChangeTypeEx from VT_R8 to VT_BSTR uses the same method with _create_locale and _swprintf_l, but with unusable format for this, so I think it should be fine.
dlls/jscript/jscript.h | 1 + dlls/jscript/number.c | 84 +++++++++++++++++++++++++++++++++++++- dlls/jscript/tests/api.js | 7 ++++ dlls/jscript/tests/run.c | 37 ++++++++++++++++- dlls/mshtml/tests/es5.js | 50 +++++++++++++++++++++++ dlls/mshtml/tests/script.c | 36 +++++++++++++++- 6 files changed, 211 insertions(+), 4 deletions(-)
diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index 7ed4425..90d8142 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,BOOL,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..9470fbf 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,90 @@ 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, BOOL new_format, jsstr_t **ret) +{ + WCHAR buf[316], decimal[8], thousands[8], *numstr; + NUMBERFMTW *format = NULL, format_buf; + LCID lcid = ctx->lcid; + _locale_t locale; + unsigned convlen; + jsstr_t *str; + 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 uses 3 digits and trims trailing zeros (on same locale). + This is 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"%.3f", locale, val); + _free_locale(locale); + + if(new_format) { + WCHAR grouping[10]; + + format = &format_buf; + format->NumDigits = 3; + while(buf[--len] == '0') + format->NumDigits--; + + /* same logic as VarFormatNumber */ + grouping[2] = '\0'; + if(!GetLocaleInfoW(lcid, LOCALE_SGROUPING, grouping, ARRAY_SIZE(grouping))) + format->Grouping = 3; + else + format->Grouping = (grouping[2] == '2' ? 32 : grouping[0] - '0'); + + if(!GetLocaleInfoW(lcid, LOCALE_ILZERO | LOCALE_RETURN_NUMBER, (WCHAR*)&format->LeadingZero, 2)) + format->LeadingZero = 0; + if(!GetLocaleInfoW(lcid, LOCALE_INEGNUMBER | LOCALE_RETURN_NUMBER, (WCHAR*)&format->NegativeOrder, 2)) + format->NegativeOrder = 1; + format->lpDecimalSep = decimal; + if(!GetLocaleInfoW(lcid, LOCALE_SDECIMAL, decimal, ARRAY_SIZE(decimal))) + wcscpy(decimal, L"."); + format->lpThousandSep = thousands; + if(!GetLocaleInfoW(lcid, LOCALE_STHOUSAND, thousands, ARRAY_SIZE(thousands))) + wcscpy(thousands, L","); + } + + if(!(convlen = GetNumberFormatW(lcid, 0, buf, format, NULL, 0)) || + !(str = jsstr_alloc_buf(convlen - 1, &numstr))) + return E_OUTOFMEMORY; + + if(!GetNumberFormatW(lcid, 0, buf, format, numstr, convlen)) { + jsstr_release(str); + return E_OUTOFMEMORY; + } + + *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, ctx->version >= SCRIPTLANGUAGEVERSION_ES5, &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"); diff --git a/dlls/jscript/tests/run.c b/dlls/jscript/tests/run.c index e4408be..ebdc8dd 100644 --- a/dlls/jscript/tests/run.c +++ b/dlls/jscript/tests/run.c @@ -186,6 +186,7 @@ static BOOL strict_dispid_check, testing_expr; static const char *test_name = "(null)"; static IDispatch *script_disp; static int invoke_version; +static BOOL use_english; static IActiveScriptError *script_error; static IActiveScript *script_engine; static const CLSID *engine_clsid = &CLSID_JScript; @@ -1834,7 +1835,7 @@ static ULONG WINAPI ActiveScriptSite_Release(IActiveScriptSite *iface)
static HRESULT WINAPI ActiveScriptSite_GetLCID(IActiveScriptSite *iface, LCID *plcid) { - *plcid = GetUserDefaultLCID(); + *plcid = use_english ? MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) : GetUserDefaultLCID(); return S_OK; }
@@ -2996,6 +2997,39 @@ static void test_default_value(void) close_script(script); }
+static void test_number_localization(void) +{ + static struct { + const WCHAR *num; + const WCHAR *expect; + } tests[] = { + { L"0", L"0.00" }, + { L"+1234.5", L"1,234.50" }, + { L"-1337.7331", L"-1,337.73" }, + { L"-0.0123", L"-0.01" }, + { L"-0.0198", L"-0.02" }, + { L"0.004", L"0.00" }, + { L"65536.5", L"65,536.50" }, + { L"NaN", L"NaN" } + }; + static const WCHAR fmt[] = L"Number.prototype.toLocaleString.call(%s)"; + WCHAR script_buf[ARRAY_SIZE(fmt) + 32]; + HRESULT hres; + unsigned i; + VARIANT v; + + use_english = TRUE; + for(i = 0; i < ARRAY_SIZE(tests); i++) { + swprintf(script_buf, ARRAY_SIZE(script_buf), fmt, tests[i].num); + hres = parse_script_expr(script_buf, &v, NULL); + ok(hres == S_OK, "[%u] parse_script_expr failed: %08lx\n", i, hres); + ok(V_VT(&v) == VT_BSTR, "[%u] V_VT(v) = %d\n", i, V_VT(&v)); + ok(!lstrcmpW(V_BSTR(&v), tests[i].expect), "[%u] got %s\n", i, wine_dbgstr_w(V_BSTR(&v))); + VariantClear(&v); + } + use_english = FALSE; +} + static void test_script_exprs(void) { VARIANT v; @@ -3060,6 +3094,7 @@ 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);
+ test_number_localization(); test_default_value(); test_propputref(); test_retval(); diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index 4892207..4364054 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -28,6 +28,7 @@ var JS_E_REGEXP_EXPECTED = 0x800a1398; var JS_E_INVALID_WRITABLE_PROP_DESC = 0x800a13ac; var JS_E_NONCONFIGURABLE_REDEFINED = 0x800a13d6; var JS_E_NONWRITABLE_MODIFIED = 0x800a13d7; +var JS_E_WRONG_THIS = 0x800a13fc;
var tests = [];
@@ -68,6 +69,55 @@ sync_test("toISOString", function() { expect_exception(function() { new Date(31494784780800001).toISOString(); }); });
+sync_test("Number toLocaleString", function() { + var r = Number.prototype.toLocaleString.length; + ok(r === 0, "length = " + r); + var tests = [ + [ 0.0, "0" ], + [ 1234.5, "1,234.5" ], + [ -1337.7331, "-1,337.733" ], + [ -0.0123, "-0.012" ], + [-0.0198, "-0.02" ], + [ 0.004, "0.004" ], + [ 99.004, "99.004" ], + [ 99.0004, "99" ], + [ 65536.5, "65,536.5" ], + [ NaN, "NaN" ] + ]; + + if(external.isEnglish) { + for(var i = 0; i < tests.length; i++) { + r = Number.prototype.toLocaleString.call(tests[i][0]); + ok(r === tests[i][1], "[" + i + "] got " + r); + } + } + + try { + Number.prototype.toLocaleString.call("50"); + ok(false, "expected exception calling it on string"); + }catch(ex) { + var n = ex.number >>> 0; + todo_wine. + ok(n === JS_E_WRONG_THIS, "called on string threw " + n); + } + try { + Number.prototype.toLocaleString.call(undefined); + ok(false, "expected exception calling it on undefined"); + }catch(ex) { + var n = ex.number >>> 0; + todo_wine. + ok(n === JS_E_WRONG_THIS, "called on undefined threw " + n); + } + try { + Number.prototype.toLocaleString.call(external.nullDisp); + ok(false, "expected exception calling it on nullDisp"); + }catch(ex) { + var n = ex.number >>> 0; + todo_wine. + ok(n === JS_E_WRONG_THIS, "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..7bf2eb6 100644 --- a/dlls/mshtml/tests/script.c +++ b/dlls/mshtml/tests/script.c @@ -153,13 +153,14 @@ DEFINE_EXPECT(GetTypeInfo); #define DISPID_EXTERNAL_WRITESTREAM 0x300006 #define DISPID_EXTERNAL_GETVT 0x300007 #define DISPID_EXTERNAL_NULL_DISP 0x300008 +#define DISPID_EXTERNAL_IS_ENGLISH 0x300009
static const GUID CLSID_TestScript = {0x178fc163,0xf585,0x4e24,{0x9c,0x13,0x4b,0xb7,0xfa,0xf8,0x07,0x46}}; static const GUID CLSID_TestActiveX = {0x178fc163,0xf585,0x4e24,{0x9c,0x13,0x4b,0xb7,0xfa,0xf8,0x06,0x46}};
-static BOOL is_ie9plus; +static BOOL is_ie9plus, is_english; static IHTMLDocument2 *notif_doc; static IOleDocumentView *view; static IDispatchEx *window_dispex; @@ -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"isEnglish")) { + *pid = DISPID_EXTERNAL_IS_ENGLISH; + return S_OK; + }
ok(0, "unexpected name %s\n", wine_dbgstr_w(bstrName)); return DISP_E_UNKNOWNNAME; @@ -784,6 +789,21 @@ static HRESULT WINAPI externalDisp_InvokeEx(IDispatchEx *iface, DISPID id, LCID V_DISPATCH(pvarRes) = NULL; return S_OK;
+ case DISPID_EXTERNAL_IS_ENGLISH: + 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"); + + V_VT(pvarRes) = VT_BOOL; + V_BOOL(pvarRes) = is_english ? VARIANT_TRUE : VARIANT_FALSE; + return S_OK; + default: ok(0, "unexpected call\n"); return E_NOTIMPL; @@ -3743,6 +3763,19 @@ static HWND create_container_window(void) 300, 300, NULL, NULL, NULL, NULL); }
+static void detect_locale(void) +{ + HMODULE kernel32 = GetModuleHandleA("kernel32.dll"); + LANGID (WINAPI *pGetThreadUILanguage)(void) = (void*)GetProcAddress(kernel32, "GetThreadUILanguage"); + + is_english = ((!pGetThreadUILanguage || PRIMARYLANGID(pGetThreadUILanguage()) == LANG_ENGLISH) && + PRIMARYLANGID(GetUserDefaultUILanguage()) == LANG_ENGLISH && + PRIMARYLANGID(GetUserDefaultLangID()) == LANG_ENGLISH); + + if(!is_english) + skip("Skipping some tests in non-English locale\n"); +} + static BOOL check_ie(void) { IHTMLDocument2 *doc; @@ -3779,6 +3812,7 @@ START_TEST(script) CoInitialize(NULL); container_hwnd = create_container_window();
+ detect_locale(); if(argc > 2) { init_protocol_handler(); run_script_as_http_with_mode(argv[2], NULL, "11");
Note that, for example, Number.toFixed still returns JS_E_NUMBER_EXPECTED even in ES5 mode (this is already tested).
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/jscript.h | 2 +- dlls/jscript/jscript.rc | 2 +- dlls/jscript/number.c | 5 ++++- dlls/jscript/resource.h | 2 +- dlls/jscript/set.c | 4 ++-- dlls/mshtml/tests/es5.js | 3 --- 6 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index 90d8142..513f886 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -524,7 +524,7 @@ static inline DWORD make_grfdex(script_ctx_t *ctx, DWORD flags) #define JS_E_OBJECT_NONEXTENSIBLE MAKE_JSERROR(IDS_OBJECT_NONEXTENSIBLE) #define JS_E_NONCONFIGURABLE_REDEFINED MAKE_JSERROR(IDS_NONCONFIGURABLE_REDEFINED) #define JS_E_NONWRITABLE_MODIFIED MAKE_JSERROR(IDS_NONWRITABLE_MODIFIED) -#define JS_E_MAP_EXPECTED MAKE_JSERROR(IDS_MAP_EXPECTED) +#define JS_E_WRONG_THIS MAKE_JSERROR(IDS_WRONG_THIS) #define JS_E_PROP_DESC_MISMATCH MAKE_JSERROR(IDS_PROP_DESC_MISMATCH) #define JS_E_INVALID_WRITABLE_PROP_DESC MAKE_JSERROR(IDS_INVALID_WRITABLE_PROP_DESC)
diff --git a/dlls/jscript/jscript.rc b/dlls/jscript/jscript.rc index 2fd0b33..de21a4a 100644 --- a/dlls/jscript/jscript.rc +++ b/dlls/jscript/jscript.rc @@ -75,7 +75,7 @@ STRINGTABLE IDS_OBJECT_NONEXTENSIBLE "Cannot define property '|': object is not extensible" IDS_NONCONFIGURABLE_REDEFINED "Cannot redefine non-configurable property '|'" IDS_NONWRITABLE_MODIFIED "Cannot modify non-writable property '|'" - IDS_MAP_EXPECTED "'this' is not a | object" + IDS_WRONG_THIS "'this' is not a | object" IDS_PROP_DESC_MISMATCH "Property cannot have both accessors and a value"
IDS_COMPILATION_ERROR "Microsoft JScript compilation error" diff --git a/dlls/jscript/number.c b/dlls/jscript/number.c index 9470fbf..90c1401 100644 --- a/dlls/jscript/number.c +++ b/dlls/jscript/number.c @@ -416,8 +416,11 @@ static HRESULT Number_toLocaleString(script_ctx_t *ctx, jsval_t vthis, WORD flag TRACE("\n");
hres = numberval_this(vthis, &val); - if(FAILED(hres)) + if(FAILED(hres)) { + if(hres == JS_E_NUMBER_EXPECTED && ctx->version >= SCRIPTLANGUAGEVERSION_ES5) + return throw_error(ctx, JS_E_WRONG_THIS, L"Number"); return hres; + }
if(r) { hres = localize_number(ctx, val, ctx->version >= SCRIPTLANGUAGEVERSION_ES5, &str); diff --git a/dlls/jscript/resource.h b/dlls/jscript/resource.h index 1338ac5..f84d77c 100644 --- a/dlls/jscript/resource.h +++ b/dlls/jscript/resource.h @@ -73,7 +73,7 @@ #define IDS_OBJECT_NONEXTENSIBLE 0x13D5 #define IDS_NONCONFIGURABLE_REDEFINED 0x13D6 #define IDS_NONWRITABLE_MODIFIED 0x13D7 -#define IDS_MAP_EXPECTED 0x13FC +#define IDS_WRONG_THIS 0x13FC /* FIXME: This is not compatible with native, but we would * conflict with IDS_UNSUPPORTED_ACTION otherwise */ #define IDS_PROP_DESC_MISMATCH 0x1F00 diff --git a/dlls/jscript/set.c b/dlls/jscript/set.c index dbb4a5d..d1ea663 100644 --- a/dlls/jscript/set.c +++ b/dlls/jscript/set.c @@ -94,7 +94,7 @@ static HRESULT get_map_this(script_ctx_t *ctx, jsval_t vthis, MapInstance **ret) return JS_E_OBJECT_EXPECTED; if(!(jsdisp = to_jsdisp(get_object(vthis))) || !is_class(jsdisp, JSCLASS_MAP)) { WARN("not a Map object passed as 'this'\n"); - return throw_error(ctx, JS_E_MAP_EXPECTED, L"Map"); + return throw_error(ctx, JS_E_WRONG_THIS, L"Map"); }
*ret = CONTAINING_RECORD(jsdisp, MapInstance, dispex); @@ -109,7 +109,7 @@ static HRESULT get_set_this(script_ctx_t *ctx, jsval_t vthis, MapInstance **ret) return JS_E_OBJECT_EXPECTED; if(!(jsdisp = to_jsdisp(get_object(vthis))) || !is_class(jsdisp, JSCLASS_SET)) { WARN("not a Set object passed as 'this'\n"); - return throw_error(ctx, JS_E_MAP_EXPECTED, L"Set"); + return throw_error(ctx, JS_E_WRONG_THIS, L"Set"); }
*ret = CONTAINING_RECORD(jsdisp, MapInstance, dispex); diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index 4364054..3ac099b 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -97,7 +97,6 @@ sync_test("Number toLocaleString", function() { ok(false, "expected exception calling it on string"); }catch(ex) { var n = ex.number >>> 0; - todo_wine. ok(n === JS_E_WRONG_THIS, "called on string threw " + n); } try { @@ -105,7 +104,6 @@ sync_test("Number toLocaleString", function() { ok(false, "expected exception calling it on undefined"); }catch(ex) { var n = ex.number >>> 0; - todo_wine. ok(n === JS_E_WRONG_THIS, "called on undefined threw " + n); } try { @@ -113,7 +111,6 @@ sync_test("Number toLocaleString", function() { ok(false, "expected exception calling it on nullDisp"); }catch(ex) { var n = ex.number >>> 0; - todo_wine. ok(n === JS_E_WRONG_THIS, "called on nullDisp threw " + n); } });
Signed-off-by: Jacek Caban jacek@codeweavers.com
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=114027
Your paranoid android.
=== w10pro64_zh_CN (64 bit report) ===
mshtml: 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 1651597483451 expected 1651597483512"
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 | 64 +++++++++++++++++++++++++++++++ dlls/mshtml/tests/script.c | 29 ++++++++++++++ 7 files changed, 245 insertions(+), 6 deletions(-)
diff --git a/dlls/jscript/array.c b/dlls/jscript/array.c index ee72579..e9a8fb5 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), FALSE, 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 513f886..000bcc2 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 ebdc8dd..94dc2e1 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 @@ -477,6 +480,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); @@ -557,6 +566,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; }
@@ -3032,6 +3057,7 @@ static void test_number_localization(void)
static void test_script_exprs(void) { + WCHAR buf[64], sep[4]; VARIANT v; HRESULT hres;
@@ -3094,6 +3120,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_number_localization(); test_default_value(); test_propputref(); @@ -3691,6 +3736,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 3ac099b..8b1c307 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -69,6 +69,70 @@ 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 */ + if(external.isEnglish) + ok(r === "3.00", "[3].toLocaleString returned " + r); + else + ok(r !== "aBc", "[3].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("Number toLocaleString", function() { var r = Number.prototype.toLocaleString.length; ok(r === 0, "length = " + r); diff --git a/dlls/mshtml/tests/script.c b/dlls/mshtml/tests/script.c index 7bf2eb6..da31c4d 100644 --- a/dlls/mshtml/tests/script.c +++ b/dlls/mshtml/tests/script.c @@ -154,6 +154,7 @@ DEFINE_EXPECT(GetTypeInfo); #define DISPID_EXTERNAL_GETVT 0x300007 #define DISPID_EXTERNAL_NULL_DISP 0x300008 #define DISPID_EXTERNAL_IS_ENGLISH 0x300009 +#define DISPID_EXTERNAL_LIST_SEP 0x30000A
static const GUID CLSID_TestScript = {0x178fc163,0xf585,0x4e24,{0x9c,0x13,0x4b,0xb7,0xfa,0xf8,0x07,0x46}}; @@ -604,6 +605,10 @@ static HRESULT WINAPI externalDisp_GetDispID(IDispatchEx *iface, BSTR bstrName, *pid = DISPID_EXTERNAL_IS_ENGLISH; 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; @@ -804,6 +809,30 @@ static HRESULT WINAPI externalDisp_InvokeEx(IDispatchEx *iface, DISPID id, LCID V_BOOL(pvarRes) = is_english ? VARIANT_TRUE : VARIANT_FALSE; 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;
Signed-off-by: Jacek Caban jacek@codeweavers.com
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=114028
Your paranoid android.
=== debian11 (64 bit WoW report) ===
mshtml: htmllocation: Timeout
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 e9a8fb5..fae761a 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 8b1c307..6bef293 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -205,6 +205,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);
Signed-off-by: Jacek Caban jacek@codeweavers.com
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=114029
Your paranoid android.
=== w10pro64 (32 bit report) ===
mshtml: htmldoc.c:2541: Test failed: unexpected call UpdateUI htmldoc.c:2853: Test failed: unexpected call Exec_UPDATECOMMANDS
=== w10pro64_ar (64 bit report) ===
mshtml: htmldoc.c:2541: Test failed: unexpected call UpdateUI htmldoc.c:2853: Test failed: unexpected call Exec_UPDATECOMMANDS
=== w10pro64_ja (64 bit report) ===
mshtml: htmldoc.c:2541: Test failed: unexpected call UpdateUI htmldoc.c:2853: Test failed: unexpected call Exec_UPDATECOMMANDS
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 fae761a..5373610 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: Jacek Caban jacek@codeweavers.com
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 5373610..c88b391 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 6bef293..1b49826 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -233,6 +233,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);
Signed-off-by: Jacek Caban jacek@codeweavers.com
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=114031
Your paranoid android.
=== w10pro64 (32 bit report) ===
mshtml: htmldoc.c:2541: Test failed: unexpected call UpdateUI htmldoc.c:2853: Test failed: unexpected call Exec_UPDATECOMMANDS
=== w7u_2qxl (32 bit report) ===
mshtml: script.c:644: Test failed: L"/index.html?es5.js:date_now: unexpected Date.now() result 1651609167627 expected 1651609167679"
=== w7u_adm (32 bit report) ===
mshtml: script.c:644: Test failed: L"/index.html?es5.js:date_now: unexpected Date.now() result 1651608749417 expected 1651608749470"
=== w7u_el (32 bit report) ===
mshtml: script.c:644: Test failed: L"/index.html?es5.js:date_now: unexpected Date.now() result 1651609351690 expected 1651609351744"
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 c88b391..5a8131f 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 1b49826..8238e5d 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -205,6 +205,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);
Signed-off-by: Jacek Caban jacek@codeweavers.com
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=114032
Your paranoid android.
=== w10pro64_ar (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
=== w10pro64_ja (64 bit report) ===
mshtml: htmldoc.c:2541: Test failed: unexpected call UpdateUI htmldoc.c:2853: Test failed: unexpected call Exec_UPDATECOMMANDS
=== debian11 (32 bit Arabic:Morocco report) ===
mshtml: htmllocation: Timeout
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=114026
Your paranoid android.
=== w10pro64 (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
=== w10pro64_ar (64 bit report) ===
mshtml: 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
=== w10pro64_ja (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 1651595095852 expected 1651595095903"