From: Gabriel Ivăncescu gabrielopcode@gmail.com
Behavior depends on mode. Javascript used inside mshtml allows non-JS objects that expose "length" to be used in apply(). For ES5 and above, even JS objects that expose "length" are allowed. Javascript not in HTML mode does not, however.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/dispex.c | 37 +++++++++++++++++ dlls/jscript/function.c | 41 ++++++++++--------- dlls/jscript/jscript.h | 2 + dlls/jscript/tests/api.js | 2 + dlls/mshtml/tests/documentmode.js | 66 +++++++++++++++++++++++++++++++ 5 files changed, 129 insertions(+), 19 deletions(-)
diff --git a/dlls/jscript/dispex.c b/dlls/jscript/dispex.c index 3b475237970..c5e1fb7e8a9 100644 --- a/dlls/jscript/dispex.c +++ b/dlls/jscript/dispex.c @@ -2975,6 +2975,35 @@ HRESULT jsdisp_propget_name(jsdisp_t *obj, const WCHAR *name, jsval_t *val) return prop_get(obj, to_disp(obj), prop, val); }
+HRESULT disp_propget_name(script_ctx_t *ctx, IDispatch *disp, const WCHAR *name, jsval_t *val) +{ + IDispatchEx *dispex; + jsdisp_t *jsdisp; + DISPID dispid; + HRESULT hres; + BSTR str; + + jsdisp = to_jsdisp(disp); + if(jsdisp && jsdisp->ctx == ctx) + return jsdisp_propget_name(jsdisp, name, val); + + if(!(str = SysAllocString(name))) + return E_OUTOFMEMORY; + hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex); + if(hres != S_OK) + hres = IDispatch_GetIDsOfNames(disp, &IID_NULL, &str, 1, 0, &dispid); + else { + hres = IDispatchEx_GetDispID(dispex, str, fdexNameCaseSensitive, &dispid); + IDispatchEx_Release(dispex); + } + SysFreeString(str); + + if(SUCCEEDED(hres)) + hres = disp_propget(ctx, disp, dispid, val); + + return hres; +} + HRESULT jsdisp_get_idx(jsdisp_t *obj, DWORD idx, jsval_t *r) { WCHAR name[12]; @@ -2995,6 +3024,14 @@ HRESULT jsdisp_get_idx(jsdisp_t *obj, DWORD idx, jsval_t *r) return prop_get(obj, to_disp(obj), prop, r); }
+HRESULT disp_propget_idx(script_ctx_t *ctx, IDispatch *disp, DWORD idx, jsval_t *r) +{ + WCHAR buf[12]; + + swprintf(buf, ARRAY_SIZE(buf), L"%u", idx); + return disp_propget_name(ctx, disp, buf, r); +} + HRESULT jsdisp_propget(jsdisp_t *jsdisp, DISPID id, jsval_t *val) { dispex_prop_t *prop; diff --git a/dlls/jscript/function.c b/dlls/jscript/function.c index ffb55af0922..59384d1f0a2 100644 --- a/dlls/jscript/function.c +++ b/dlls/jscript/function.c @@ -417,27 +417,30 @@ static HRESULT Function_toString(script_ctx_t *ctx, jsval_t vthis, WORD flags, u return S_OK; }
-static HRESULT array_to_args(script_ctx_t *ctx, jsdisp_t *arg_array, unsigned *argc, jsval_t **ret) +static HRESULT array_to_args(script_ctx_t *ctx, IDispatch *disp, unsigned *argc, jsval_t **ret) { jsval_t *argv, val; - UINT32 length, i; HRESULT hres; + INT32 length; + UINT32 i;
- hres = jsdisp_propget_name(arg_array, L"length", &val); + hres = disp_propget_name(ctx, disp, L"length", &val); if(FAILED(hres)) - return hres; + return (hres == DISP_E_UNKNOWNNAME) ? JS_E_JSCRIPT_EXPECTED : hres;
- hres = to_uint32(ctx, val, &length); + hres = to_int32(ctx, val, &length); jsval_release(val); if(FAILED(hres)) return hres; + if(length < 0) + return JS_E_JSCRIPT_EXPECTED;
argv = malloc(length * sizeof(*argv)); if(!argv) return E_OUTOFMEMORY;
for(i=0; i<length; i++) { - hres = jsdisp_get_idx(arg_array, i, argv+i); + hres = disp_propget_idx(ctx, disp, i, argv+i); if(hres == DISP_E_UNKNOWNNAME) { argv[i] = jsval_undefined(); }else if(FAILED(hres)) { @@ -483,24 +486,24 @@ static HRESULT Function_apply(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsi }
if(argc >= 2) { - jsdisp_t *arg_array = NULL; + IDispatch *obj = NULL; + jsdisp_t *arg_array;
if(is_object_instance(argv[1])) { - arg_array = iface_to_jsdisp(get_object(argv[1])); - if(arg_array && - (!is_class(arg_array, JSCLASS_ARRAY) && !is_class(arg_array, JSCLASS_ARGUMENTS) )) { - jsdisp_release(arg_array); - arg_array = NULL; + obj = get_object(argv[1]); + arg_array = to_jsdisp(obj); + + if(ctx->version < SCRIPTLANGUAGEVERSION_ES5) { + if(!arg_array) { + if(!ctx->html_mode) + obj = NULL; + }else if(!is_class(arg_array, JSCLASS_ARRAY) && !is_class(arg_array, JSCLASS_ARGUMENTS)) { + obj = NULL; + } } }
- if(arg_array) { - hres = array_to_args(ctx, arg_array, &cnt, &args); - jsdisp_release(arg_array); - }else { - FIXME("throw TypeError\n"); - hres = E_FAIL; - } + hres = obj ? array_to_args(ctx, obj, &cnt, &args) : JS_E_JSCRIPT_EXPECTED; }
if(SUCCEEDED(hres)) { diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index a3009239ba1..5660ee48435 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -253,7 +253,9 @@ HRESULT jsdisp_call(jsdisp_t*,DISPID,WORD,unsigned,jsval_t*,jsval_t*); HRESULT jsdisp_call_name(jsdisp_t*,const WCHAR*,WORD,unsigned,jsval_t*,jsval_t*); HRESULT disp_propget(script_ctx_t*,IDispatch*,DISPID,jsval_t*); HRESULT disp_propput(script_ctx_t*,IDispatch*,DISPID,jsval_t); +HRESULT disp_propget_name(script_ctx_t*,IDispatch*,const WCHAR*,jsval_t*); HRESULT disp_propput_name(script_ctx_t*,IDispatch*,const WCHAR*,jsval_t); +HRESULT disp_propget_idx(script_ctx_t*,IDispatch*,DWORD,jsval_t*); HRESULT jsdisp_propget(jsdisp_t*,DISPID,jsval_t*); HRESULT jsdisp_propput(jsdisp_t*,const WCHAR*,DWORD,BOOL,jsval_t); HRESULT jsdisp_propput_name(jsdisp_t*,const WCHAR*,jsval_t); diff --git a/dlls/jscript/tests/api.js b/dlls/jscript/tests/api.js index aea7cac63dc..8c054fff058 100644 --- a/dlls/jscript/tests/api.js +++ b/dlls/jscript/tests/api.js @@ -2918,6 +2918,8 @@ testFunctionThis("toString"); testFunctionThis("call"); testFunctionThis("apply");
+testException(function() {(function (a, b) {}).apply(null, testObj)}, "E_JSCRIPT_EXPECTED"); + function testArrayHostThis(func) { testException(function() { Array.prototype[func].call(testObj); }, "E_JSCRIPT_EXPECTED"); } diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js index 8e8b87d7e36..562761a022f 100644 --- a/dlls/mshtml/tests/documentmode.js +++ b/dlls/mshtml/tests/documentmode.js @@ -502,6 +502,72 @@ sync_test("builtin_obj", function() { f.call = function() { }; ok(f.apply === 0, "changed f.apply = ", f.apply); ok(f.call instanceof Function, "changed f.call not instance of Function"); + + e = Array.isArray(document.body.childNodes); + ok(e === false, "isArray(childNodes) returned " + e); + } + + (function(a, b, c) { + ok(a === document.body.childNodes[0], "a = " + a); + ok(b === document.body.childNodes[1], "b = " + b); + ok(c === document.body.childNodes[2], "c = " + c); + }).apply(null, document.body.childNodes); + + elem1[0] = "a"; + elem1[1] = "b"; + if(v < 9) { + try { + (function(a, b) {}).apply(null, elem1); + }catch(ex) { + e = ex.number; + } + todo_wine. + ok(e === 0xa13a4 - 0x80000000, "[function.apply with elem without length] e = " + e); + }else { + (function(a, b) { + ok(a === undefined, "a = " + a); + ok(b === undefined, "b = " + b); + }).apply(null, elem1); + } + + elem1.length = 2; + (function(a, b) { + ok(a === "a", "a = " + a); + ok(b === "b", "b = " + b); + }).apply(null, elem1); + + elem1 = new Object; + elem1[0] = "c"; + elem1[1] = "d"; + if(v < 9) { + try { + (function(c, d) {}).apply(null, elem1); + }catch(ex) { + e = ex.number; + } + todo_wine. + ok(e === 0xa13a4 - 0x80000000, "[function.apply with Object without length] e = " + e); + }else { + (function(c, d) { + ok(c === undefined, "c = " + c); + ok(d === undefined, "d = " + d); + }).apply(null, elem1); + } + + elem1.length = 2; + if(v < 9) { + try { + (function(c, d) {}).apply(null, elem1); + }catch(ex) { + e = ex.number; + } + todo_wine. + ok(e === 0xa13a4 - 0x80000000, "[function.apply with Object with length] e = " + e); + }else { + (function(c, d) { + ok(c === "c", "c = " + c); + ok(d === "d", "d = " + d); + }).apply(null, elem1); } });