Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/array.c | 78 +++++++++++++++++++++++++++++++++++--- dlls/jscript/dispex.c | 27 +++++++++++++ dlls/jscript/jscript.h | 1 + dlls/jscript/tests/api.js | 1 + dlls/jscript/tests/run.c | 51 +++++++++++++++++++++++++ dlls/mshtml/tests/es5.js | 61 +++++++++++++++++++++++++++++ dlls/mshtml/tests/script.c | 29 ++++++++++++++ 7 files changed, 242 insertions(+), 6 deletions(-)
diff --git a/dlls/jscript/array.c b/dlls/jscript/array.c index ee72579..291f032 100644 --- a/dlls/jscript/array.c +++ b/dlls/jscript/array.c @@ -229,7 +229,7 @@ done: }
static HRESULT array_join(script_ctx_t *ctx, jsdisp_t *array, DWORD length, const WCHAR *sep, - unsigned seplen, jsval_t *r) + unsigned seplen, HRESULT (*to_string)(script_ctx_t*,jsval_t,jsstr_t**), jsval_t *r) { jsstr_t **str_tab, *ret = NULL; jsval_t val; @@ -339,11 +339,11 @@ static HRESULT Array_join(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned if(FAILED(hres)) goto done;
- hres = array_join(ctx, jsthis, length, sep, jsstr_length(sep_str), r); + hres = array_join(ctx, jsthis, length, sep, jsstr_length(sep_str), to_string, r);
jsstr_release(sep_str); }else { - hres = array_join(ctx, jsthis, length, L",", 1, r); + hres = array_join(ctx, jsthis, length, L",", 1, to_string, r); }
done: @@ -947,14 +947,80 @@ static HRESULT Array_toString(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsi if(!array) return JS_E_ARRAY_EXPECTED;
- return array_join(ctx, &array->dispex, array->length, L",", 1, r); + return array_join(ctx, &array->dispex, array->length, L",", 1, to_string, r); +} + +static HRESULT to_locale_string(script_ctx_t *ctx, jsval_t val, jsstr_t **str) +{ + jsdisp_t *jsdisp; + IDispatch *obj; + HRESULT hres; + + switch(jsval_type(val)) { + case JSV_OBJECT: + hres = disp_call_name(ctx, get_object(val), L"toLocaleString", DISPATCH_METHOD, 0, NULL, &val); + if(FAILED(hres)) { + if(hres == JS_E_INVALID_PROPERTY && ctx->version >= SCRIPTLANGUAGEVERSION_ES5) + hres = JS_E_FUNCTION_EXPECTED; + return hres; + } + break; + case JSV_NUMBER: + if(ctx->version >= SCRIPTLANGUAGEVERSION_ES5) + return localize_number(ctx, get_number(val), str); + /* fall through */ + default: + if(ctx->version >= SCRIPTLANGUAGEVERSION_ES5) + break; + + hres = to_object(ctx, val, &obj); + if(FAILED(hres)) + return hres; + + jsdisp = as_jsdisp(obj); + hres = jsdisp_call_name(jsdisp, L"toLocaleString", DISPATCH_METHOD, 0, NULL, &val); + jsdisp_release(jsdisp); + if(FAILED(hres)) + return hres; + break; + } + + return to_string(ctx, val, str); }
static HRESULT Array_toLocaleString(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { - FIXME("\n"); - return E_NOTIMPL; + jsdisp_t *jsthis; + UINT32 length; + HRESULT hres; + WCHAR buf[5]; + int len; + + TRACE("\n"); + + if(ctx->version < SCRIPTLANGUAGEVERSION_ES5) { + ArrayInstance *array = array_this(vthis); + if(!array) + return JS_E_ARRAY_EXPECTED; + jsthis = jsdisp_addref(&array->dispex); + length = array->length; + }else { + hres = get_length(ctx, vthis, &jsthis, &length); + if(FAILED(hres)) + return hres; + } + + if(!(len = GetLocaleInfoW(ctx->lcid, LOCALE_SLIST, buf, ARRAY_SIZE(buf) - 1))) { + buf[len++] = ','; + len++; + } + buf[len - 1] = ' '; + buf[len] = '\0'; + + hres = array_join(ctx, jsthis, length, buf, len, to_locale_string, r); + jsdisp_release(jsthis); + return hres; }
static HRESULT Array_forEach(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, diff --git a/dlls/jscript/dispex.c b/dlls/jscript/dispex.c index 8682766..468a0dd 100644 --- a/dlls/jscript/dispex.c +++ b/dlls/jscript/dispex.c @@ -2146,6 +2146,33 @@ HRESULT disp_call(script_ctx_t *ctx, IDispatch *disp, DISPID id, WORD flags, uns return hres; }
+HRESULT disp_call_name(script_ctx_t *ctx, IDispatch *disp, const WCHAR *name, WORD flags, unsigned argc, jsval_t *argv, jsval_t *ret) +{ + IDispatchEx *dispex; + jsdisp_t *jsdisp; + HRESULT hres; + DISPID id; + BSTR bstr; + + if((jsdisp = to_jsdisp(disp)) && jsdisp->ctx == ctx) + return jsdisp_call_name(jsdisp, name, flags, argc, argv, ret); + + if(!(bstr = SysAllocString(name))) + return E_OUTOFMEMORY; + hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex); + if(SUCCEEDED(hres) && dispex) { + hres = IDispatchEx_GetDispID(dispex, bstr, make_grfdex(ctx, fdexNameCaseSensitive), &id); + IDispatchEx_Release(dispex); + }else { + hres = IDispatch_GetIDsOfNames(disp, &IID_NULL, &bstr, 1, 0, &id); + } + SysFreeString(bstr); + if(FAILED(hres)) + return hres; + + return disp_call(ctx, disp, id, flags, argc, argv, ret); +} + HRESULT disp_call_value(script_ctx_t *ctx, IDispatch *disp, IDispatch *jsthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index 406efa9..f594b38 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -241,6 +241,7 @@ HRESULT init_dispex(jsdisp_t*,script_ctx_t*,const builtin_info_t*,jsdisp_t*) DEC HRESULT init_dispex_from_constr(jsdisp_t*,script_ctx_t*,const builtin_info_t*,jsdisp_t*) DECLSPEC_HIDDEN;
HRESULT disp_call(script_ctx_t*,IDispatch*,DISPID,WORD,unsigned,jsval_t*,jsval_t*) DECLSPEC_HIDDEN; +HRESULT disp_call_name(script_ctx_t*,IDispatch*,const WCHAR*,WORD,unsigned,jsval_t*,jsval_t*) DECLSPEC_HIDDEN; HRESULT disp_call_value(script_ctx_t*,IDispatch*,IDispatch*,WORD,unsigned,jsval_t*,jsval_t*) DECLSPEC_HIDDEN; HRESULT jsdisp_call_value(jsdisp_t*,IDispatch*,WORD,unsigned,jsval_t*,jsval_t*) DECLSPEC_HIDDEN; HRESULT jsdisp_call(jsdisp_t*,DISPID,WORD,unsigned,jsval_t*,jsval_t*) DECLSPEC_HIDDEN; diff --git a/dlls/jscript/tests/api.js b/dlls/jscript/tests/api.js index 8653946..e81bbea 100644 --- a/dlls/jscript/tests/api.js +++ b/dlls/jscript/tests/api.js @@ -2597,6 +2597,7 @@ testException(function() {date.setTime();}, "E_ARG_NOT_OPT"); testException(function() {date.setYear();}, "E_ARG_NOT_OPT"); testException(function() {arr.test();}, "E_NO_PROPERTY"); testException(function() {[1,2,3].sort(nullDisp);}, "E_JSCRIPT_EXPECTED"); +testException(function() {var o = new Object(); o.length = 1; o[0] = "a"; Array.prototype.toLocaleString.call(o);}, "E_NOT_ARRAY"); testException(function() {Number.prototype.toString.call(arr);}, "E_NOT_NUM"); testException(function() {Number.prototype.toFixed.call(arr);}, "E_NOT_NUM"); testException(function() {Number.prototype.toLocaleString.call(arr);}, "E_NOT_NUM"); diff --git a/dlls/jscript/tests/run.c b/dlls/jscript/tests/run.c index e4408be..35d81ec 100644 --- a/dlls/jscript/tests/run.c +++ b/dlls/jscript/tests/run.c @@ -112,6 +112,8 @@ DEFINE_EXPECT(testobj_onlydispid_i); DEFINE_EXPECT(testobj_notexists_d); DEFINE_EXPECT(testobj_newenum); DEFINE_EXPECT(testobj_getidfail_d); +DEFINE_EXPECT(testobj_tolocalestr_d); +DEFINE_EXPECT(testobj_tolocalestr_i); DEFINE_EXPECT(enumvariant_next_0); DEFINE_EXPECT(enumvariant_next_1); DEFINE_EXPECT(enumvariant_reset); @@ -178,6 +180,7 @@ DEFINE_EXPECT(BindHandler); #define DISPID_TESTOBJ_PROP 0x2000 #define DISPID_TESTOBJ_ONLYDISPID 0x2001 #define DISPID_TESTOBJ_WITHPROP 0x2002 +#define DISPID_TESTOBJ_TOLOCALESTR 0x2003
#define JS_E_OUT_OF_MEMORY 0x800a03ec #define JS_E_INVALID_CHAR 0x800a03f6 @@ -476,6 +479,12 @@ static HRESULT WINAPI testObj_GetDispID(IDispatchEx *iface, BSTR bstrName, DWORD *pid = DISPID_TESTOBJ_ONLYDISPID; return S_OK; } + if(!lstrcmpW(bstrName, L"toLocaleString")) { + CHECK_EXPECT(testobj_tolocalestr_d); + test_grfdex(grfdex, fdexNameCaseSensitive); + *pid = DISPID_TESTOBJ_TOLOCALESTR; + return S_OK; + } if(!lstrcmpW(bstrName, L"notExists")) { CHECK_EXPECT(testobj_notexists_d); test_grfdex(grfdex, fdexNameCaseSensitive); @@ -556,6 +565,22 @@ static HRESULT WINAPI testObj_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid, V_VT(pvarRes) = VT_I4; V_I4(pvarRes) = 1;
+ return S_OK; + case DISPID_TESTOBJ_TOLOCALESTR: + CHECK_EXPECT(testobj_tolocalestr_i); + + ok(wFlags == DISPATCH_METHOD, "wFlags = %x\n", wFlags); + ok(pdp != NULL, "pdp == NULL\n"); + ok(!pdp->rgdispidNamedArgs, "rgdispidNamedArgs != NULL\n"); + ok(!pdp->cArgs, "cArgs = %d\n", pdp->cArgs); + ok(!pdp->cNamedArgs, "cNamedArgs = %d\n", pdp->cNamedArgs); + ok(pvarRes != NULL, "pvarRes == NULL\n"); + ok(V_VT(pvarRes) == VT_EMPTY, "V_VT(pvarRes) = %d\n", V_VT(pvarRes)); + ok(pei != NULL, "pei == NULL\n"); + + V_VT(pvarRes) = VT_I4; + V_I4(pvarRes) = 1234; + return S_OK; }
@@ -2998,6 +3023,7 @@ static void test_default_value(void)
static void test_script_exprs(void) { + WCHAR buf[64], sep[4]; VARIANT v; HRESULT hres;
@@ -3060,6 +3086,25 @@ static void test_script_exprs(void) ok(!lstrcmpW(V_BSTR(&v), L"wine"), "V_BSTR(v) = %s\n", wine_dbgstr_w(V_BSTR(&v))); VariantClear(&v);
+ if(!GetLocaleInfoW(GetUserDefaultLCID(), LOCALE_SLIST, sep, ARRAY_SIZE(sep))) wcscpy(sep, L","); + swprintf(buf, ARRAY_SIZE(buf), L"12%s 12%s undefined undefined undefined%s 12", sep, sep, sep); + hres = parse_script_expr(L"var arr = [5]; arr.toLocaleString = function(a,b,c) {return a+' '+b+' '+c;};" + L"Number.prototype.toLocaleString = function() {return 12;};" + L"[1,2,arr,3].toLocaleString('foo','bar','baz')", &v, NULL); + ok(hres == S_OK, "parse_script_expr failed: %08lx\n", hres); + ok(V_VT(&v) == VT_BSTR, "V_VT(v) = %d\n", V_VT(&v)); + ok(!lstrcmpW(V_BSTR(&v), buf), "V_BSTR(v) = %s\n", wine_dbgstr_w(V_BSTR(&v))); + VariantClear(&v); + + hres = parse_script_expr(L"delete Object.prototype.toLocaleString; Array.prototype.toLocaleString.call([])", &v, NULL); + ok(hres == S_OK, "parse_script_expr failed: %08lx\n", hres); + ok(V_VT(&v) == VT_BSTR, "V_VT(v) = %d\n", V_VT(&v)); + ok(!lstrcmpW(V_BSTR(&v), L""), "V_BSTR(v) = %s\n", wine_dbgstr_w(V_BSTR(&v))); + VariantClear(&v); + + hres = parse_script_expr(L"delete Object.prototype.toLocaleString; Array.prototype.toLocaleString.call(['a'])", &v, NULL); + ok(hres == 0x800a01b6, "parse_script_expr failed: %08lx\n", hres); + test_default_value(); test_propputref(); test_retval(); @@ -3656,6 +3701,12 @@ static BOOL run_tests(void) CHECK_CALLED(testobj_withprop_d); CHECK_CALLED(testobj_withprop_i);
+ SET_EXPECT(testobj_tolocalestr_d); + SET_EXPECT(testobj_tolocalestr_i); + run_script(L"var t = [testObj].toLocaleString(); ok(t === '1234', 't = ' + t);"); + CHECK_CALLED(testobj_tolocalestr_d); + CHECK_CALLED(testobj_tolocalestr_i); + run_script(L"@set @t=2\nok(@t === 2, '@t = ' + @t);");
SET_EXPECT(global_success_d); diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index 4892207..e6fdf03 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -68,6 +68,67 @@ sync_test("toISOString", function() { expect_exception(function() { new Date(31494784780800001).toISOString(); }); });
+sync_test("Array toLocaleString", function() { + var r = Array.prototype.toLocaleString.length, old = Number.prototype.toLocaleString; + var s = external.listSeparator + ' '; + ok(r === 0, "length = " + r); + + r = [5]; + r.toLocaleString = function(a, b, c) { return a + " " + b + " " + c; }; + Number.prototype.toLocaleString = function() { return "aBc"; }; + + r = [new Number(3), r, new Number(12)].toLocaleString("foo", "bar", "baz"); + ok(r === "aBc"+s+"undefined undefined undefined"+s+"aBc", "toLocaleString returned " + r); + + r = [3].toLocaleString(); /* primitive number value not affected */ + ok(r !== "aBc", "toLocaleString returned " + r); + Number.prototype.toLocaleString = old; + + r = Object.create(null); + r.toString = function() { return "foo"; } + try { + Array.prototype.toLocaleString.call([r]); + ok(false, "expected exception calling it on array with object without toLocaleString"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_FUNCTION_EXPECTED, "called on array with object without toLocaleString threw " + n); + } + + r = { length: 2 }; + r[0] = { toLocaleString: function() { return "foo"; } } + r[1] = { toLocaleString: function() { return "bar"; } } + r = Array.prototype.toLocaleString.call(r); + ok(r === "foo"+s+"bar", "toLocaleString on array-like object returned " + r); + + r = Array.prototype.toLocaleString.call({}); + ok(r === "", "toLocaleString on {} returned " + r); + + r = Array.prototype.toLocaleString.call("ab"); + ok(r === "a"+s+"b", "toLocaleString on 'ab' returned " + r); + + try { + Array.prototype.toLocaleString.call(undefined); + ok(false, "expected exception calling it on undefined"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_OBJECT_EXPECTED, "called on undefined threw " + n); + } + try { + Array.prototype.toLocaleString.call(null); + ok(false, "expected exception calling it on null"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_OBJECT_EXPECTED, "called on null threw " + n); + } + try { + Array.prototype.toLocaleString.call(external.nullDisp); + ok(false, "expected exception calling it on nullDisp"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_OBJECT_EXPECTED, "called on nullDisp threw " + n); + } +}); + sync_test("indexOf", function() { function expect(array, args, exr) { var r = Array.prototype.indexOf.apply(array, args); diff --git a/dlls/mshtml/tests/script.c b/dlls/mshtml/tests/script.c index ceb4fc7..1226d1a 100644 --- a/dlls/mshtml/tests/script.c +++ b/dlls/mshtml/tests/script.c @@ -153,6 +153,7 @@ DEFINE_EXPECT(GetTypeInfo); #define DISPID_EXTERNAL_WRITESTREAM 0x300006 #define DISPID_EXTERNAL_GETVT 0x300007 #define DISPID_EXTERNAL_NULL_DISP 0x300008 +#define DISPID_EXTERNAL_LIST_SEP 0x300009
static const GUID CLSID_TestScript = {0x178fc163,0xf585,0x4e24,{0x9c,0x13,0x4b,0xb7,0xfa,0xf8,0x07,0x46}}; @@ -599,6 +600,10 @@ static HRESULT WINAPI externalDisp_GetDispID(IDispatchEx *iface, BSTR bstrName, *pid = DISPID_EXTERNAL_NULL_DISP; return S_OK; } + if(!lstrcmpW(bstrName, L"listSeparator")) { + *pid = DISPID_EXTERNAL_LIST_SEP; + return S_OK; + }
ok(0, "unexpected name %s\n", wine_dbgstr_w(bstrName)); return DISP_E_UNKNOWNNAME; @@ -784,6 +789,30 @@ static HRESULT WINAPI externalDisp_InvokeEx(IDispatchEx *iface, DISPID id, LCID V_DISPATCH(pvarRes) = NULL; return S_OK;
+ case DISPID_EXTERNAL_LIST_SEP: { + WCHAR buf[4]; + int len; + + ok(wFlags == INVOKE_PROPERTYGET, "wFlags = %x\n", wFlags); + ok(pdp != NULL, "pdp == NULL\n"); + ok(!pdp->rgvarg, "rgvarg != NULL\n"); + ok(!pdp->rgdispidNamedArgs, "rgdispidNamedArgs != NULL\n"); + ok(!pdp->cArgs, "cArgs = %d\n", pdp->cArgs); + ok(!pdp->cNamedArgs, "cNamedArgs = %d\n", pdp->cNamedArgs); + ok(pvarRes != NULL, "pvarRes == NULL\n"); + ok(V_VT(pvarRes) == VT_EMPTY, "V_VT(pvarRes) = %d\n", V_VT(pvarRes)); + ok(pei != NULL, "pei == NULL\n"); + + if(!(len = GetLocaleInfoW(GetUserDefaultLCID(), LOCALE_SLIST, buf, ARRAY_SIZE(buf)))) + buf[len++] = ','; + else + len--; + + V_VT(pvarRes) = VT_BSTR; + V_BSTR(pvarRes) = SysAllocStringLen(buf, len); + return S_OK; + } + default: ok(0, "unexpected call\n"); return E_NOTIMPL;