Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
Sorry about this. c464c1bd4781ca650a5397ff6a2cfd574c1877f0 made it possibly used uninitialized when argc was 0 and return value was not used.
dlls/jscript/string.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/jscript/string.c b/dlls/jscript/string.c index e73b9e8..dac89c9 100644 --- a/dlls/jscript/string.c +++ b/dlls/jscript/string.c @@ -1628,7 +1628,7 @@ static HRESULT StringConstr_fromCharCode(script_ctx_t *ctx, jsval_t vthis, WORD static HRESULT StringConstr_value(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { - HRESULT hres; + HRESULT hres = S_OK;
TRACE("\n");
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
Tests are basic because it depends on the user's locale, but observations have been noted in comments.
dlls/jscript/number.c | 77 ++++++++++++++++++++++++++++++++++++++- dlls/jscript/tests/api.js | 7 ++++ 2 files changed, 82 insertions(+), 2 deletions(-)
diff --git a/dlls/jscript/number.c b/dlls/jscript/number.c index be733fb..c487f10 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" @@ -344,8 +345,80 @@ static HRESULT Number_toString(script_ctx_t *ctx, jsval_t vthis, WORD flags, uns 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; + WCHAR buf[314], *numstr, *p, *frac = NULL; + BOOL remove_fraction = FALSE; + jsstr_t *str, *tmp; + _locale_t locale; + unsigned convlen; + HRESULT hres; + DOUBLE val; + int len; + + TRACE("\n"); + + hres = numberval_this(vthis, &val); + if(FAILED(hres)) + return hres; + + if(!r) + return S_OK; + + /* FIXME: Localize this? */ + if(!isfinite(val)) { + hres = to_string(ctx, jsval_number(val), &str); + if(FAILED(hres)) + return hres; + goto done; + } + + /* 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; + } + } + +done: + *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");
On 4/15/22 15:00, Gabriel Ivăncescu wrote:
- 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;
}
- }
This seems hackish, could we have share a helper with toString() and pass its result through GetNumberFormatW instead?
Thanks,
Jacek
On 19/04/2022 17:45, Jacek Caban wrote:
On 4/15/22 15:00, Gabriel Ivăncescu wrote:
+ 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; + } + }
This seems hackish, could we have share a helper with toString() and pass its result through GetNumberFormatW instead?
Thanks,
Jacek
I don't think sharing with toString will work, since toString seems to add some logic around displaying exponents (scientific notation), which GetNumberFormatW can't handle. And the result in toLocaleString never displays an exponent even for very large numbers (but displays a ton of digits with thousands separators).
If that's referring to the _swprintf_l part, that is.
I agree the removing fractions part is not exactly pretty, but I'm not sure how to do it, since GetNumberFormatW adds fraction digits even if they are zeros (if the locale settings are that way) while on native mshtml they get stripped even with same locale settings! jscript doesn't, though. Do you have better idea?
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
Useful for further patches (and the js proxies too).
dlls/jscript/dispex.c | 30 ++++++++++++++++++------------ dlls/jscript/jscript.h | 1 + 2 files changed, 19 insertions(+), 12 deletions(-)
diff --git a/dlls/jscript/dispex.c b/dlls/jscript/dispex.c index 8682766..beede34 100644 --- a/dlls/jscript/dispex.c +++ b/dlls/jscript/dispex.c @@ -2056,22 +2056,28 @@ static HRESULT disp_invoke(script_ctx_t *ctx, IDispatch *disp, DISPID id, WORD f hres = IDispatch_Invoke(disp, id, &IID_NULL, ctx->lcid, flags, params, r, &ei, &err); }
- if(hres == DISP_E_EXCEPTION) { - TRACE("DISP_E_EXCEPTION: %08lx %s %s\n", ei.scode, debugstr_w(ei.bstrSource), debugstr_w(ei.bstrDescription)); - reset_ei(ctx->ei); - ctx->ei->error = (SUCCEEDED(ei.scode) || ei.scode == DISP_E_EXCEPTION) ? E_FAIL : ei.scode; - if(ei.bstrSource) - ctx->ei->source = jsstr_alloc_len(ei.bstrSource, SysStringLen(ei.bstrSource)); - if(ei.bstrDescription) - ctx->ei->message = jsstr_alloc_len(ei.bstrDescription, SysStringLen(ei.bstrDescription)); - SysFreeString(ei.bstrSource); - SysFreeString(ei.bstrDescription); - SysFreeString(ei.bstrHelpFile); - } + if(hres == DISP_E_EXCEPTION) + disp_fill_exception(ctx, &ei);
return hres; }
+void disp_fill_exception(script_ctx_t *ctx, EXCEPINFO *ei) +{ + TRACE("DISP_E_EXCEPTION: %08lx %s %s\n", ei->scode, debugstr_w(ei->bstrSource), debugstr_w(ei->bstrDescription)); + reset_ei(ctx->ei); + if(ei->pfnDeferredFillIn) + ei->pfnDeferredFillIn(ei); + ctx->ei->error = (SUCCEEDED(ei->scode) || ei->scode == DISP_E_EXCEPTION) ? E_FAIL : ei->scode; + if(ei->bstrSource) + ctx->ei->source = jsstr_alloc_len(ei->bstrSource, SysStringLen(ei->bstrSource)); + if(ei->bstrDescription) + ctx->ei->message = jsstr_alloc_len(ei->bstrDescription, SysStringLen(ei->bstrDescription)); + SysFreeString(ei->bstrSource); + SysFreeString(ei->bstrDescription); + SysFreeString(ei->bstrHelpFile); +} + HRESULT disp_call(script_ctx_t *ctx, IDispatch *disp, DISPID id, WORD flags, unsigned argc, jsval_t *argv, jsval_t *ret) { VARIANT buf[6], retv; diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index 7ed4425..8cf386d 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -240,6 +240,7 @@ HRESULT create_dispex(script_ctx_t*,const builtin_info_t*,jsdisp_t*,jsdisp_t**) HRESULT init_dispex(jsdisp_t*,script_ctx_t*,const builtin_info_t*,jsdisp_t*) DECLSPEC_HIDDEN; HRESULT init_dispex_from_constr(jsdisp_t*,script_ctx_t*,const builtin_info_t*,jsdisp_t*) DECLSPEC_HIDDEN;
+void disp_fill_exception(script_ctx_t*,EXCEPINFO*) DECLSPEC_HIDDEN; HRESULT disp_call(script_ctx_t*,IDispatch*,DISPID,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;
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/array.c | 112 ++++++++++++++++++++++++++++++++++++-- dlls/jscript/jscript.h | 1 + dlls/jscript/number.c | 2 +- dlls/jscript/tests/api.js | 1 + dlls/jscript/tests/run.c | 49 +++++++++++++++++ dlls/mshtml/tests/es5.js | 53 ++++++++++++++++++ 6 files changed, 211 insertions(+), 7 deletions(-)
diff --git a/dlls/jscript/array.c b/dlls/jscript/array.c index ee72579..b9faa5a 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,114 @@ 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) +{ + DISPPARAMS dp = { 0 }; + IDispatchEx *dispex; + jsdisp_t *jsdisp; + IDispatch *obj; + EXCEPINFO ei; + HRESULT hres; + UINT err = 0; + VARIANT var; + DISPID id; + BSTR bstr; + + switch(jsval_type(val)) { + case JSV_OBJECT: + obj = get_object(val); + if((jsdisp = to_jsdisp(obj))) { + hres = jsdisp_call_name(jsdisp, 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; + } + + if(!(bstr = SysAllocString(L"toLocaleString"))) + return E_OUTOFMEMORY; + + V_VT(&var) = VT_EMPTY; + hres = IDispatch_QueryInterface(obj, &IID_IDispatchEx, (void**)&dispex); + if(SUCCEEDED(hres) && dispex) { + hres = IDispatchEx_GetDispID(dispex, bstr, make_grfdex(ctx, fdexNameCaseSensitive), &id); + if(SUCCEEDED(hres)) { + hres = IDispatchEx_InvokeEx(dispex, id, ctx->lcid, DISPATCH_METHOD, &dp, &var, &ei, &ctx->jscaller->IServiceProvider_iface); + if(hres == DISP_E_EXCEPTION) + disp_fill_exception(ctx, &ei); + } + IDispatchEx_Release(dispex); + }else { + hres = IDispatch_GetIDsOfNames(obj, &IID_NULL, &bstr, 1, 0, &id); + if(SUCCEEDED(hres)) { + hres = IDispatch_Invoke(obj, id, &IID_NULL, ctx->lcid, DISPATCH_METHOD, &dp, &var, &ei, &err); + if(hres == DISP_E_EXCEPTION) + disp_fill_exception(ctx, &ei); + } + } + SysFreeString(bstr); + if(FAILED(hres)) + return hres; + + hres = variant_to_jsval(ctx, &var, &val); + VariantClear(&var); + break; + case JSV_NUMBER: + if(ctx->version >= SCRIPTLANGUAGEVERSION_ES5) { + hres = Number_toLocaleString(ctx, val, 0, 0, NULL, &val); + if(SUCCEEDED(hres)) + *str = get_string(val); + return hres; + } + /* 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; + + 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; + } + + hres = array_join(ctx, jsthis, length, L", ", 2, 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/jscript.h b/dlls/jscript/jscript.h index 8cf386d..a9b56fb 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -452,6 +452,7 @@ BOOL bool_obj_value(jsdisp_t*) DECLSPEC_HIDDEN; unsigned array_get_length(jsdisp_t*) DECLSPEC_HIDDEN;
HRESULT JSGlobal_eval(script_ctx_t*,jsval_t,WORD,unsigned,jsval_t*,jsval_t*) DECLSPEC_HIDDEN; +HRESULT Number_toLocaleString(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; HRESULT Object_set_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 c487f10..cc0f224 100644 --- a/dlls/jscript/number.c +++ b/dlls/jscript/number.c @@ -342,7 +342,7 @@ static HRESULT Number_toString(script_ctx_t *ctx, jsval_t vthis, WORD flags, uns return S_OK; }
-static HRESULT Number_toLocaleString(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, +HRESULT Number_toLocaleString(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { WCHAR buf[314], *numstr, *p, *frac = NULL; 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..baa52a8 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,23 @@ 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->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_I4; + V_I4(pvarRes) = 1234; + return S_OK; }
@@ -3060,6 +3086,23 @@ 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);
+ 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), L"12, 12, undefined undefined undefined, 12"), "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 +3699,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..c57d85f 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -68,6 +68,59 @@ 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; + 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, undefined undefined undefined, 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, 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, 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); + } +}); + sync_test("indexOf", function() { function expect(array, args, exr) { var r = Array.prototype.indexOf.apply(array, args);
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=112662
Your paranoid android.
=== w7u_el (32 bit report) ===
jscript: run.c:3094: Test failed: V_BSTR(v) = L"12; 12; undefined undefined undefined; 12" run.c:3094: Test failed: V_BSTR(v) = L"12; 12; undefined undefined undefined; 12"
=== w10pro64_ar (64 bit report) ===
jscript: run.c:3094: Test failed: V_BSTR(v) = L"12; 12; undefined undefined undefined; 12" run.c:3094: Test failed: V_BSTR(v) = L"12; 12; undefined undefined undefined; 12"
=== w8adm (32 bit report) ===
mshtml: events.c:1089: Test failed: unexpected call img_onerror events: Timeout
=== 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_el (32 bit report) ===
mshtml: script.c:634: Test failed: L"/index.html?es5.js:Array toLocaleString: toLocaleString returned aBc; undefined undefined undefined; aBc" script.c:634: Test failed: L"/index.html?es5.js:Array toLocaleString: toLocaleString on array-like object returned foo; bar" script.c:634: Test failed: L"/index.html?es5.js:Array toLocaleString: toLocaleString on 'ab' returned a; b"
=== w10pro64_ar (64 bit report) ===
mshtml: script.c:634: Test failed: L"/index.html?es5.js:Array toLocaleString: toLocaleString returned aBc; undefined undefined undefined; aBc" script.c:634: Test failed: L"/index.html?es5.js:Array toLocaleString: toLocaleString on array-like object returned foo; bar" script.c:634: Test failed: L"/index.html?es5.js:Array toLocaleString: toLocaleString on 'ab' returned a; b"
Meh, looks like some locales have "; " as delimiter instead of ", "
Is there something I can query for them to obtain that? Or should I just adjust the tests to not check even this?
On 15/04/2022 18:21, Marvin wrote:
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=112662
Your paranoid android.
=== w7u_el (32 bit report) ===
jscript: run.c:3094: Test failed: V_BSTR(v) = L"12; 12; undefined undefined undefined; 12" run.c:3094: Test failed: V_BSTR(v) = L"12; 12; undefined undefined undefined; 12"
=== w10pro64_ar (64 bit report) ===
jscript: run.c:3094: Test failed: V_BSTR(v) = L"12; 12; undefined undefined undefined; 12" run.c:3094: Test failed: V_BSTR(v) = L"12; 12; undefined undefined undefined; 12"
=== w8adm (32 bit report) ===
mshtml: events.c:1089: Test failed: unexpected call img_onerror events: Timeout
=== 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_el (32 bit report) ===
mshtml: script.c:634: Test failed: L"/index.html?es5.js:Array toLocaleString: toLocaleString returned aBc; undefined undefined undefined; aBc" script.c:634: Test failed: L"/index.html?es5.js:Array toLocaleString: toLocaleString on array-like object returned foo; bar" script.c:634: Test failed: L"/index.html?es5.js:Array toLocaleString: toLocaleString on 'ab' returned a; b"
=== w10pro64_ar (64 bit report) ===
mshtml: script.c:634: Test failed: L"/index.html?es5.js:Array toLocaleString: toLocaleString returned aBc; undefined undefined undefined; aBc" script.c:634: Test failed: L"/index.html?es5.js:Array toLocaleString: toLocaleString on array-like object returned foo; bar" script.c:634: Test failed: L"/index.html?es5.js:Array toLocaleString: toLocaleString on 'ab' returned a; b"
On 4/15/22 19:13, Gabriel Ivăncescu wrote:
Meh, looks like some locales have "; " as delimiter instead of ", "
Is there something I can query for them to obtain that? Or should I just adjust the tests to not check even this?
I don't think there is JS API for that, but vbscript have a similar thing, see isEnglishLang in vbscript/tests.
Thanks,
Jacek
On 15/04/2022 21:07, Jacek Caban wrote:
On 4/15/22 19:13, Gabriel Ivăncescu wrote:
Meh, looks like some locales have "; " as delimiter instead of ", "
Is there something I can query for them to obtain that? Or should I just adjust the tests to not check even this?
I don't think there is JS API for that, but vbscript have a similar thing, see isEnglishLang in vbscript/tests.
Thanks,
Jacek
Looks like using LOCALE_SLIST (with GetLocaleInfo) matches all the testbot locales, but it appends a space character afterwards.
Hi Gabriel,
On 4/15/22 15:00, Gabriel Ivăncescu wrote:
+static HRESULT to_locale_string(script_ctx_t *ctx, jsval_t val, jsstr_t **str) +{
- DISPPARAMS dp = { 0 };
- IDispatchEx *dispex;
- jsdisp_t *jsdisp;
- IDispatch *obj;
- EXCEPINFO ei;
- HRESULT hres;
- UINT err = 0;
- VARIANT var;
- DISPID id;
- BSTR bstr;
- switch(jsval_type(val)) {
- case JSV_OBJECT:
obj = get_object(val);
if((jsdisp = to_jsdisp(obj))) {
hres = jsdisp_call_name(jsdisp, 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;
}
if(!(bstr = SysAllocString(L"toLocaleString")))
return E_OUTOFMEMORY;
V_VT(&var) = VT_EMPTY;
hres = IDispatch_QueryInterface(obj, &IID_IDispatchEx, (void**)&dispex);
if(SUCCEEDED(hres) && dispex) {
hres = IDispatchEx_GetDispID(dispex, bstr, make_grfdex(ctx, fdexNameCaseSensitive), &id);
if(SUCCEEDED(hres)) {
hres = IDispatchEx_InvokeEx(dispex, id, ctx->lcid, DISPATCH_METHOD, &dp, &var, &ei, &ctx->jscaller->IServiceProvider_iface);
if(hres == DISP_E_EXCEPTION)
disp_fill_exception(ctx, &ei);
}
IDispatchEx_Release(dispex);
}else {
hres = IDispatch_GetIDsOfNames(obj, &IID_NULL, &bstr, 1, 0, &id);
if(SUCCEEDED(hres)) {
hres = IDispatch_Invoke(obj, id, &IID_NULL, ctx->lcid, DISPATCH_METHOD, &dp, &var, &ei, &err);
if(hres == DISP_E_EXCEPTION)
disp_fill_exception(ctx, &ei);
}
}
SysFreeString(bstr);
if(FAILED(hres))
return hres;
hres = variant_to_jsval(ctx, &var, &val);
VariantClear(&var);
break;
Shouldn't it just use disp_call();
- case JSV_NUMBER:
if(ctx->version >= SCRIPTLANGUAGEVERSION_ES5) {
hres = Number_toLocaleString(ctx, val, 0, 0, NULL, &val);
if(SUCCEEDED(hres))
*str = get_string(val);
return hres;
}
/* fall through */
Should it just use the default case? If not, then please don't do direct calls to functions like Number_toLocaleString like this, a separated helper would be better.
Thanks,
Jacek
Hi Jacek,
Sorry for delay on this, been a bit swamped.
On 15/04/2022 21:02, Jacek Caban wrote:
Hi Gabriel,
On 4/15/22 15:00, Gabriel Ivăncescu wrote:
+static HRESULT to_locale_string(script_ctx_t *ctx, jsval_t val, jsstr_t **str) +{ + DISPPARAMS dp = { 0 }; + IDispatchEx *dispex; + jsdisp_t *jsdisp; + IDispatch *obj; + EXCEPINFO ei; + HRESULT hres; + UINT err = 0; + VARIANT var; + DISPID id; + BSTR bstr;
+ switch(jsval_type(val)) { + case JSV_OBJECT: + obj = get_object(val); + if((jsdisp = to_jsdisp(obj))) { + hres = jsdisp_call_name(jsdisp, 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; + }
+ if(!(bstr = SysAllocString(L"toLocaleString"))) + return E_OUTOFMEMORY;
+ V_VT(&var) = VT_EMPTY; + hres = IDispatch_QueryInterface(obj, &IID_IDispatchEx, (void**)&dispex); + if(SUCCEEDED(hres) && dispex) { + hres = IDispatchEx_GetDispID(dispex, bstr, make_grfdex(ctx, fdexNameCaseSensitive), &id); + if(SUCCEEDED(hres)) { + hres = IDispatchEx_InvokeEx(dispex, id, ctx->lcid, DISPATCH_METHOD, &dp, &var, &ei, &ctx->jscaller->IServiceProvider_iface); + if(hres == DISP_E_EXCEPTION) + disp_fill_exception(ctx, &ei); + } + IDispatchEx_Release(dispex); + }else { + hres = IDispatch_GetIDsOfNames(obj, &IID_NULL, &bstr, 1, 0, &id); + if(SUCCEEDED(hres)) { + hres = IDispatch_Invoke(obj, id, &IID_NULL, ctx->lcid, DISPATCH_METHOD, &dp, &var, &ei, &err); + if(hres == DISP_E_EXCEPTION) + disp_fill_exception(ctx, &ei); + } + } + SysFreeString(bstr); + if(FAILED(hres)) + return hres;
+ hres = variant_to_jsval(ctx, &var, &val); + VariantClear(&var); + break;
Shouldn't it just use disp_call();
disp_call needs a DISPID though. I still need to obtain the DISPID, so just using Invoke after doesn't seem like much, considering I already QueryInterface for IDispatchEx for the DISPID...
+ case JSV_NUMBER: + if(ctx->version >= SCRIPTLANGUAGEVERSION_ES5) { + hres = Number_toLocaleString(ctx, val, 0, 0, NULL, &val); + if(SUCCEEDED(hres)) + *str = get_string(val); + return hres; + } + /* fall through */
Should it just use the default case? If not, then please don't do direct calls to functions like Number_toLocaleString like this, a separated helper would be better.
According to the tests, it uses Number.prototype.toLocaleString on ES5 for some reason. See the /* primitive number value not affected */ part.
On 4/18/22 16:42, Gabriel Ivăncescu wrote:
Hi Jacek,
Sorry for delay on this, been a bit swamped.
On 15/04/2022 21:02, Jacek Caban wrote:
Hi Gabriel,
On 4/15/22 15:00, Gabriel Ivăncescu wrote:
+static HRESULT to_locale_string(script_ctx_t *ctx, jsval_t val, jsstr_t **str) +{ + DISPPARAMS dp = { 0 }; + IDispatchEx *dispex; + jsdisp_t *jsdisp; + IDispatch *obj; + EXCEPINFO ei; + HRESULT hres; + UINT err = 0; + VARIANT var; + DISPID id; + BSTR bstr;
+ switch(jsval_type(val)) { + case JSV_OBJECT: + obj = get_object(val); + if((jsdisp = to_jsdisp(obj))) { + hres = jsdisp_call_name(jsdisp, 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; + }
+ if(!(bstr = SysAllocString(L"toLocaleString"))) + return E_OUTOFMEMORY;
+ V_VT(&var) = VT_EMPTY; + hres = IDispatch_QueryInterface(obj, &IID_IDispatchEx, (void**)&dispex); + if(SUCCEEDED(hres) && dispex) { + hres = IDispatchEx_GetDispID(dispex, bstr, make_grfdex(ctx, fdexNameCaseSensitive), &id); + if(SUCCEEDED(hres)) { + hres = IDispatchEx_InvokeEx(dispex, id, ctx->lcid, DISPATCH_METHOD, &dp, &var, &ei, &ctx->jscaller->IServiceProvider_iface); + if(hres == DISP_E_EXCEPTION) + disp_fill_exception(ctx, &ei); + } + IDispatchEx_Release(dispex); + }else { + hres = IDispatch_GetIDsOfNames(obj, &IID_NULL, &bstr, 1, 0, &id); + if(SUCCEEDED(hres)) { + hres = IDispatch_Invoke(obj, id, &IID_NULL, ctx->lcid, DISPATCH_METHOD, &dp, &var, &ei, &err); + if(hres == DISP_E_EXCEPTION) + disp_fill_exception(ctx, &ei); + } + } + SysFreeString(bstr); + if(FAILED(hres)) + return hres;
+ hres = variant_to_jsval(ctx, &var, &val); + VariantClear(&var); + break;
Shouldn't it just use disp_call();
disp_call needs a DISPID though. I still need to obtain the DISPID, so just using Invoke after doesn't seem like much, considering I already QueryInterface for IDispatchEx for the DISPID...
I don't think it's justified and I generally don't like spreading direct IDispach[Ex] calls over the code base, especially non-trivial ones like this one. In this case, we could simply introduce disp_call_name() helper that could use disp_invoke().
+ case JSV_NUMBER: + if(ctx->version >= SCRIPTLANGUAGEVERSION_ES5) { + hres = Number_toLocaleString(ctx, val, 0, 0, NULL, &val); + if(SUCCEEDED(hres)) + *str = get_string(val); + return hres; + } + /* fall through */
Should it just use the default case? If not, then please don't do direct calls to functions like Number_toLocaleString like this, a separated helper would be better.
According to the tests, it uses Number.prototype.toLocaleString on ES5 for some reason. See the /* primitive number value not affected */ part.
I see. Still, it could use a separated helper shared with Number_toLocaleString instead of calling Number_toLocaleString like that.
Thanks,
Jacek
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 b9faa5a..309c9c4 100644 --- a/dlls/jscript/array.c +++ b/dlls/jscript/array.c @@ -1057,6 +1057,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) { @@ -1399,6 +1473,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 c57d85f..32b60c9 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -147,6 +147,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=112663
Your paranoid android.
=== w7u_el (32 bit report) ===
mshtml: script.c:634: Test failed: L"/index.html?es5.js:date_now: unexpected Date.now() result 1650033473361 expected 1650033473476" script.c:634: Test failed: L"/index.html?es5.js:Array toLocaleString: toLocaleString returned aBc; undefined undefined undefined; aBc" script.c:634: Test failed: L"/index.html?es5.js:Array toLocaleString: toLocaleString on array-like object returned foo; bar" script.c:634: Test failed: L"/index.html?es5.js:Array toLocaleString: toLocaleString on 'ab' returned a; b"
=== w10pro64_ar (64 bit report) ===
mshtml: script.c:634: Test failed: L"/index.html?es5.js:Array toLocaleString: toLocaleString returned aBc; undefined undefined undefined; aBc" script.c:634: Test failed: L"/index.html?es5.js:Array toLocaleString: toLocaleString on array-like object returned foo; bar" script.c:634: Test failed: L"/index.html?es5.js:Array toLocaleString: toLocaleString on 'ab' returned a; b"
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 309c9c4..17c2c6f 100644 --- a/dlls/jscript/array.c +++ b/dlls/jscript/array.c @@ -1057,6 +1057,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) { @@ -1473,6 +1539,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 17c2c6f..525b3a3 100644 --- a/dlls/jscript/array.c +++ b/dlls/jscript/array.c @@ -1448,6 +1448,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) @@ -1552,6 +1618,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 32b60c9..ceb9c4a 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -175,6 +175,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=112665
Your paranoid android.
=== w8 (32 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:634: Test failed: L"/index.html?es5.js:date_now: unexpected Date.now() result 1650035898594 expected 1650035898647"
=== w7u_el (32 bit report) ===
mshtml: script.c:634: Test failed: L"/index.html?es5.js:Array toLocaleString: toLocaleString returned aBc; undefined undefined undefined; aBc" script.c:634: Test failed: L"/index.html?es5.js:Array toLocaleString: toLocaleString on array-like object returned foo; bar" script.c:634: Test failed: L"/index.html?es5.js:Array toLocaleString: toLocaleString on 'ab' returned a; b"
=== w10pro64_ar (64 bit report) ===
mshtml: script.c:634: Test failed: L"/index.html?es5.js:Array toLocaleString: toLocaleString returned aBc; undefined undefined undefined; aBc" script.c:634: Test failed: L"/index.html?es5.js:Array toLocaleString: toLocaleString on array-like object returned foo; bar" script.c:634: Test failed: L"/index.html?es5.js:Array toLocaleString: toLocaleString on 'ab' returned a; b"
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 525b3a3..6aa1a69 100644 --- a/dlls/jscript/array.c +++ b/dlls/jscript/array.c @@ -1311,6 +1311,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; @@ -1610,6 +1672,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 ceb9c4a..d77c69c 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -147,6 +147,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=112666
Your paranoid android.
=== w7u_adm (32 bit report) ===
mshtml: script.c:634: Test failed: L"/index.html?es5.js:date_now: unexpected Date.now() result 1650038056564 expected 1650038056626"
=== w7u_el (32 bit report) ===
mshtml: script.c:634: Test failed: L"/index.html?es5.js:Array toLocaleString: toLocaleString returned aBc; undefined undefined undefined; aBc" script.c:634: Test failed: L"/index.html?es5.js:Array toLocaleString: toLocaleString on array-like object returned foo; bar" script.c:634: Test failed: L"/index.html?es5.js:Array toLocaleString: toLocaleString on 'ab' returned a; b"
=== w10pro64_ar (64 bit report) ===
mshtml: script.c:634: Test failed: L"/index.html?es5.js:Array toLocaleString: toLocaleString returned aBc; undefined undefined undefined; aBc" script.c:634: Test failed: L"/index.html?es5.js:Array toLocaleString: toLocaleString on array-like object returned foo; bar" script.c:634: Test failed: L"/index.html?es5.js:Array toLocaleString: toLocaleString on 'ab' returned a; b"