-- v2: jscript: Implement Uint8ClampedArray. jscript: Implement 'set' for Typed Arrays. jscript: Implement 'subarray' for Typed Arrays. jscript: Implement Typed Array construction from objects. jscript: Implement ArrayBuffer.isView. jscript: Implement Typed Array construction on ArrayBuffers. jscript: Add initial implementation of Typed Arrays. jscript: Use DISP_E_UNKNOWNNAME when looking up a prop that doesn't exist. jscript: Avoid allocating props for indexed props unless necessary. jscript: Move the indexed prop idx lookup into a helper. mshtml/tests: Test redefining a writable indexed prop.
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/tests/es5.js | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index 353d0b0b5c0..57fde4d7c9d 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -829,6 +829,16 @@ sync_test("defineProperty", function() { test_accessor_prop_desc(child.prototype, "funcprop_prot", desc); ok(obj.funcprop_prot(100) === 10, "obj.funcprop_prot() = " + obj.funcprop_prot(100));
+ (function() { + ok(arguments.length === 3, "arguments.length = " + arguments.length); + ok(arguments[0] === 1, "arguments[0] = " + arguments[0]); + ok(arguments[1] === 2, "arguments[1] = " + arguments[1]); + ok(arguments[2] === 3, "arguments[2] = " + arguments[2]); + Object.defineProperty(arguments, "1", {value: "foobar", writable: false, enumerable: true, configurable: false}); + test_own_data_prop_desc(arguments, "1", false, true, false); + ok(arguments[1] === "foobar", "arguments[1] after defineProperty = " + arguments[1]); + })(1, 2, 3); + expect_exception(function() { Object.defineProperty(null, "funcprop", desc); }, JS_E_OBJECT_EXPECTED);
From: Gabriel Ivăncescu gabrielopcode@gmail.com
And handle it up to the int max, which will be useful for Typed Arrays.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/dispex.c | 45 +++++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 17 deletions(-)
diff --git a/dlls/jscript/dispex.c b/dlls/jscript/dispex.c index 57882bf499e..fc9e50414a1 100644 --- a/dlls/jscript/dispex.c +++ b/dlls/jscript/dispex.c @@ -244,11 +244,29 @@ static dispex_prop_t *alloc_protref(jsdisp_t *This, const WCHAR *name, DWORD ref return ret; }
+static BOOL get_indexed_prop_idx(jsdisp_t *This, const WCHAR *name, unsigned *ret_idx) +{ + const WCHAR *ptr; + unsigned idx = 0; + + if(!This->builtin_info->idx_length) + return FALSE; + + for(ptr = name; is_digit(*ptr) && idx <= (UINT_MAX-9 / 10); ptr++) + idx = idx*10 + (*ptr-'0'); + if(*ptr) + return FALSE; + + *ret_idx = idx; + return TRUE; +} + static HRESULT find_prop_name(jsdisp_t *This, unsigned hash, const WCHAR *name, BOOL case_insens, dispex_prop_t **ret) { const builtin_prop_t *builtin; unsigned bucket, pos, prev = ~0; dispex_prop_t *prop; + unsigned idx; HRESULT hres;
bucket = get_props_idx(This, hash); @@ -301,24 +319,17 @@ static HRESULT find_prop_name(jsdisp_t *This, unsigned hash, const WCHAR *name, return S_OK; }
- if(This->builtin_info->idx_length) { - const WCHAR *ptr; - unsigned idx = 0; - - for(ptr = name; is_digit(*ptr) && idx < 0x10000; ptr++) - idx = idx*10 + (*ptr-'0'); - if(!*ptr && idx < This->builtin_info->idx_length(This)) { - unsigned flags = PROPF_ENUMERABLE; - if(This->builtin_info->idx_put) - flags |= PROPF_WRITABLE; - prop = alloc_prop(This, name, PROP_IDX, flags); - if(!prop) - return E_OUTOFMEMORY; + if(get_indexed_prop_idx(This, name, &idx) && idx < This->builtin_info->idx_length(This)) { + unsigned flags = PROPF_ENUMERABLE; + if(This->builtin_info->idx_put) + flags |= PROPF_WRITABLE; + prop = alloc_prop(This, name, PROP_IDX, flags); + if(!prop) + return E_OUTOFMEMORY;
- prop->u.idx = idx; - *ret = prop; - return S_OK; - } + prop->u.idx = idx; + *ret = prop; + return S_OK; }
*ret = NULL;
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Pre-requisite optimization for Typed Arrays. The only time we still create a PROP_IDX on them is when the DISPID is requested, when enumerating (same reason), or when it's used as a prototype (very weird case, probably not found in practice, but still).
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/dispex.c | 105 +++++++++++++++++++++++++++++------------- 1 file changed, 74 insertions(+), 31 deletions(-)
diff --git a/dlls/jscript/dispex.c b/dlls/jscript/dispex.c index fc9e50414a1..11ad00456d9 100644 --- a/dlls/jscript/dispex.c +++ b/dlls/jscript/dispex.c @@ -261,7 +261,8 @@ static BOOL get_indexed_prop_idx(jsdisp_t *This, const WCHAR *name, unsigned *re return TRUE; }
-static HRESULT find_prop_name(jsdisp_t *This, unsigned hash, const WCHAR *name, BOOL case_insens, dispex_prop_t **ret) +/* Looks up the prop given the name, or its index in case of an indexed prop that we can optimize (in which case it returns S_FALSE and index into ret_idx) */ +static HRESULT find_prop_name(jsdisp_t *This, unsigned hash, const WCHAR *name, BOOL case_insens, dispex_prop_t **ret, unsigned *ret_idx) { const builtin_prop_t *builtin; unsigned bucket, pos, prev = ~0; @@ -321,6 +322,12 @@ static HRESULT find_prop_name(jsdisp_t *This, unsigned hash, const WCHAR *name,
if(get_indexed_prop_idx(This, name, &idx) && idx < This->builtin_info->idx_length(This)) { unsigned flags = PROPF_ENUMERABLE; + + if(ret_idx) { + *ret_idx = idx; + return S_FALSE; + } + if(This->builtin_info->idx_put) flags |= PROPF_WRITABLE; prop = alloc_prop(This, name, PROP_IDX, flags); @@ -336,13 +343,13 @@ static HRESULT find_prop_name(jsdisp_t *This, unsigned hash, const WCHAR *name, return S_OK; }
-static HRESULT find_prop_name_prot(jsdisp_t *This, unsigned hash, const WCHAR *name, BOOL case_insens, dispex_prop_t **ret) +static HRESULT find_prop_name_prot(jsdisp_t *This, unsigned hash, const WCHAR *name, BOOL case_insens, dispex_prop_t **ret, unsigned *ret_idx) { dispex_prop_t *prop, *del=NULL; HRESULT hres;
- hres = find_prop_name(This, hash, name, case_insens, &prop); - if(FAILED(hres)) + hres = find_prop_name(This, hash, name, case_insens, &prop, ret_idx); + if(hres != S_OK) return hres; if(prop && prop->type==PROP_DELETED) { del = prop; @@ -353,7 +360,7 @@ static HRESULT find_prop_name_prot(jsdisp_t *This, unsigned hash, const WCHAR *n }
if(This->prototype) { - hres = find_prop_name_prot(This->prototype, hash, name, case_insens, &prop); + hres = find_prop_name_prot(This->prototype, hash, name, case_insens, &prop, NULL); if(FAILED(hres)) return hres; if(prop && prop->type != PROP_DELETED) { @@ -376,13 +383,15 @@ static HRESULT find_prop_name_prot(jsdisp_t *This, unsigned hash, const WCHAR *n return S_OK; }
-static HRESULT ensure_prop_name(jsdisp_t *This, const WCHAR *name, DWORD create_flags, BOOL case_insens, dispex_prop_t **ret) +static HRESULT ensure_prop_name(jsdisp_t *This, const WCHAR *name, DWORD create_flags, BOOL case_insens, dispex_prop_t **ret, unsigned *ret_idx) { dispex_prop_t *prop; HRESULT hres;
- hres = find_prop_name_prot(This, string_hash(name), name, case_insens, &prop); - if(SUCCEEDED(hres) && (!prop || prop->type == PROP_DELETED)) { + hres = find_prop_name_prot(This, string_hash(name), name, case_insens, &prop, ret_idx); + if(hres != S_OK) + return hres; + if(!prop || prop->type == PROP_DELETED) { TRACE("creating prop %s flags %lx\n", debugstr_w(name), create_flags);
if(prop) { @@ -631,7 +640,7 @@ static HRESULT fill_props(jsdisp_t *obj)
for(i = 0; i < len; i++) { swprintf(name, ARRAY_SIZE(name), L"%u", i); - hres = find_prop_name(obj, string_hash(name), name, FALSE, &prop); + hres = find_prop_name(obj, string_hash(name), name, FALSE, &prop, NULL); if(FAILED(hres)) return hres; } @@ -657,9 +666,13 @@ static HRESULT fill_protrefs(jsdisp_t *This) return hres;
for(iter = This->prototype->props; iter < This->prototype->props+This->prototype->prop_cnt; iter++) { - hres = find_prop_name(This, iter->hash, iter->name, FALSE, &prop); - if(FAILED(hres)) - return hres; + unsigned idx; + hres = find_prop_name(This, iter->hash, iter->name, FALSE, &prop, &idx); + if(hres != S_OK) { + if(FAILED(hres)) + return hres; + continue; + } if(!prop || prop->type==PROP_DELETED) { if(prop) { prop->type = PROP_PROTREF; @@ -2068,6 +2081,7 @@ static HRESULT WINAPI DispatchEx_DeleteMemberByName(IDispatchEx *iface, BSTR bst { jsdisp_t *This = impl_from_IDispatchEx(iface); dispex_prop_t *prop; + unsigned idx; BOOL b; HRESULT hres;
@@ -2076,9 +2090,9 @@ static HRESULT WINAPI DispatchEx_DeleteMemberByName(IDispatchEx *iface, BSTR bst if(grfdex & ~(fdexNameCaseSensitive|fdexNameCaseInsensitive|fdexNameEnsure|fdexNameImplicit|FDEX_VERSION_MASK)) FIXME("Unsupported grfdex %lx\n", grfdex);
- hres = find_prop_name(This, string_hash(bstrName), bstrName, grfdex & fdexNameCaseInsensitive, &prop); - if(FAILED(hres)) - return hres; + hres = find_prop_name(This, string_hash(bstrName), bstrName, grfdex & fdexNameCaseInsensitive, &prop, &idx); + if(hres != S_OK) + return FAILED(hres) ? hres : S_OK; if(!prop) { TRACE("not found\n"); return S_OK; @@ -2311,7 +2325,7 @@ HRESULT init_dispex_from_constr(jsdisp_t *dispex, script_ctx_t *ctx, const built dispex_prop_t *prop; HRESULT hres;
- hres = find_prop_name_prot(constr, string_hash(L"prototype"), L"prototype", FALSE, &prop); + hres = find_prop_name_prot(constr, string_hash(L"prototype"), L"prototype", FALSE, &prop, NULL); if(SUCCEEDED(hres) && prop && prop->type!=PROP_DELETED) { jsval_t val;
@@ -2350,9 +2364,9 @@ HRESULT jsdisp_get_id(jsdisp_t *jsdisp, const WCHAR *name, DWORD flags, DISPID *
if(jsdisp->extensible && (flags & fdexNameEnsure)) hres = ensure_prop_name(jsdisp, name, PROPF_ENUMERABLE | PROPF_CONFIGURABLE | PROPF_WRITABLE, - flags & fdexNameCaseInsensitive, &prop); + flags & fdexNameCaseInsensitive, &prop, NULL); else - hres = find_prop_name_prot(jsdisp, string_hash(name), name, flags & fdexNameCaseInsensitive, &prop); + hres = find_prop_name_prot(jsdisp, string_hash(name), name, flags & fdexNameCaseInsensitive, &prop, NULL); if(FAILED(hres)) return hres;
@@ -2413,7 +2427,7 @@ HRESULT jsdisp_call_name(jsdisp_t *disp, const WCHAR *name, WORD flags, unsigned dispex_prop_t *prop; HRESULT hres;
- hres = find_prop_name_prot(disp, string_hash(name), name, FALSE, &prop); + hres = find_prop_name_prot(disp, string_hash(name), name, FALSE, &prop, NULL); if(FAILED(hres)) return hres;
@@ -2649,14 +2663,17 @@ HRESULT disp_call_value_with_caller(script_ctx_t *ctx, IDispatch *disp, jsval_t HRESULT jsdisp_propput(jsdisp_t *obj, const WCHAR *name, DWORD flags, BOOL throw, jsval_t val) { dispex_prop_t *prop; + unsigned idx; HRESULT hres;
if(obj->extensible) - hres = ensure_prop_name(obj, name, flags, FALSE, &prop); + hres = ensure_prop_name(obj, name, flags, FALSE, &prop, &idx); else - hres = find_prop_name(obj, string_hash(name), name, FALSE, &prop); + hres = find_prop_name(obj, string_hash(name), name, FALSE, &prop, &idx); if(FAILED(hres)) return hres; + if(hres == S_FALSE) + return obj->builtin_info->idx_put ? obj->builtin_info->idx_put(obj, idx, val) : S_OK; if(!prop || (prop->type == PROP_DELETED && !obj->extensible)) return throw ? JS_E_INVALID_ACTION : S_OK;
@@ -2753,11 +2770,14 @@ HRESULT disp_propput_name(script_ctx_t *ctx, IDispatch *disp, const WCHAR *name, HRESULT jsdisp_propget_name(jsdisp_t *obj, const WCHAR *name, jsval_t *val) { dispex_prop_t *prop; + unsigned idx; HRESULT hres;
- hres = find_prop_name_prot(obj, string_hash(name), name, FALSE, &prop); + hres = find_prop_name_prot(obj, string_hash(name), name, FALSE, &prop, &idx); if(FAILED(hres)) return hres; + if(hres == S_FALSE) + return obj->builtin_info->idx_get(obj, idx, val);
if(!prop || prop->type==PROP_DELETED) { *val = jsval_undefined(); @@ -2771,13 +2791,16 @@ HRESULT jsdisp_get_idx(jsdisp_t *obj, DWORD idx, jsval_t *r) { WCHAR name[12]; dispex_prop_t *prop; + unsigned tmp; HRESULT hres;
swprintf(name, ARRAY_SIZE(name), L"%d", idx);
- hres = find_prop_name_prot(obj, string_hash(name), name, FALSE, &prop); + hres = find_prop_name_prot(obj, string_hash(name), name, FALSE, &prop, &tmp); if(FAILED(hres)) return hres; + if(hres == S_FALSE) + return obj->builtin_info->idx_get(obj, idx, r);
if(!prop || prop->type==PROP_DELETED) { *r = jsval_undefined(); @@ -2827,14 +2850,17 @@ HRESULT jsdisp_delete_idx(jsdisp_t *obj, DWORD idx) { WCHAR buf[12]; dispex_prop_t *prop; + unsigned tmp; BOOL b; HRESULT hres;
swprintf(buf, ARRAY_SIZE(buf), L"%d", idx);
- hres = find_prop_name(obj, string_hash(buf), buf, FALSE, &prop); - if(FAILED(hres) || !prop) - return hres; + hres = find_prop_name(obj, string_hash(buf), buf, FALSE, &prop, &tmp); + if(hres != S_OK) + return FAILED(hres) ? hres : S_OK; + if(!prop) + return S_OK;
hres = delete_prop(prop, &b); if(FAILED(hres)) @@ -2921,6 +2947,7 @@ HRESULT disp_delete_name(script_ctx_t *ctx, IDispatch *disp, jsstr_t *name, BOOL if(jsdisp) { dispex_prop_t *prop; const WCHAR *ptr; + unsigned idx;
ptr = jsstr_flatten(name); if(!ptr) { @@ -2928,12 +2955,14 @@ HRESULT disp_delete_name(script_ctx_t *ctx, IDispatch *disp, jsstr_t *name, BOOL return E_OUTOFMEMORY; }
- hres = find_prop_name(jsdisp, string_hash(ptr), ptr, FALSE, &prop); - if(prop) { + hres = find_prop_name(jsdisp, string_hash(ptr), ptr, FALSE, &prop, &idx); + if(hres != S_OK) { + *ret = FALSE; + hres = S_OK; + }else if(prop) { hres = delete_prop(prop, ret); }else { *ret = TRUE; - hres = S_OK; }
jsdisp_release(jsdisp); @@ -2973,12 +3002,26 @@ HRESULT jsdisp_get_own_property(jsdisp_t *obj, const WCHAR *name, BOOL flags_onl property_desc_t *desc) { dispex_prop_t *prop; + unsigned idx; HRESULT hres;
- hres = find_prop_name(obj, string_hash(name), name, FALSE, &prop); + hres = find_prop_name(obj, string_hash(name), name, FALSE, &prop, &idx); if(FAILED(hres)) return hres;
+ if(hres == S_FALSE) { + memset(desc, 0, sizeof(*desc)); + if(!flags_only) { + hres = obj->builtin_info->idx_get(obj, idx, &desc->value); + if(FAILED(hres)) + return hres; + } + desc->flags = PROPF_ENUMERABLE | (obj->builtin_info->idx_put ? PROPF_WRITABLE : 0); + desc->mask = PROPF_ENUMERABLE | PROPF_WRITABLE | PROPF_CONFIGURABLE; + desc->explicit_value = TRUE; + return S_OK; + } + if(!prop) return DISP_E_UNKNOWNNAME;
@@ -3019,7 +3062,7 @@ HRESULT jsdisp_define_property(jsdisp_t *obj, const WCHAR *name, property_desc_t dispex_prop_t *prop; HRESULT hres;
- hres = find_prop_name(obj, string_hash(name), name, FALSE, &prop); + hres = find_prop_name(obj, string_hash(name), name, FALSE, &prop, NULL); if(FAILED(hres)) return hres;
From: Gabriel Ivăncescu gabrielopcode@gmail.com
This is necessary for Typed Arrays, because they need to avoid allocating a prop when an indexed prop is used (not just for optimization purposes, but it's actually required for correctness per the spec), but must also allow the caller to know when the resulting index is "out of bounds". In other words, a Typed Array overrides every positive index lookup, even if it's larger than the Typed Array's size! But it does act like the prop doesn't exist in those circumstances, so its actual bounds are still needed.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/dispex.c | 151 +++++++++++++++++++++--------------------- 1 file changed, 76 insertions(+), 75 deletions(-)
diff --git a/dlls/jscript/dispex.c b/dlls/jscript/dispex.c index 11ad00456d9..e4981912c83 100644 --- a/dlls/jscript/dispex.c +++ b/dlls/jscript/dispex.c @@ -339,8 +339,7 @@ static HRESULT find_prop_name(jsdisp_t *This, unsigned hash, const WCHAR *name, return S_OK; }
- *ret = NULL; - return S_OK; + return DISP_E_UNKNOWNNAME; }
static HRESULT find_prop_name_prot(jsdisp_t *This, unsigned hash, const WCHAR *name, BOOL case_insens, dispex_prop_t **ret, unsigned *ret_idx) @@ -349,11 +348,12 @@ static HRESULT find_prop_name_prot(jsdisp_t *This, unsigned hash, const WCHAR *n HRESULT hres;
hres = find_prop_name(This, hash, name, case_insens, &prop, ret_idx); - if(hres != S_OK) - return hres; - if(prop && prop->type==PROP_DELETED) { + if(hres != S_OK) { + if(hres != DISP_E_UNKNOWNNAME) + return hres; + }else if(prop->type==PROP_DELETED) { del = prop; - } else if(prop) { + }else { fix_protref_prop(This, prop); *ret = prop; return S_OK; @@ -361,9 +361,10 @@ static HRESULT find_prop_name_prot(jsdisp_t *This, unsigned hash, const WCHAR *n
if(This->prototype) { hres = find_prop_name_prot(This->prototype, hash, name, case_insens, &prop, NULL); - if(FAILED(hres)) - return hres; - if(prop && prop->type != PROP_DELETED) { + if(FAILED(hres)) { + if(hres != DISP_E_UNKNOWNNAME) + return hres; + }else if(prop->type != PROP_DELETED) { if(del) { del->type = PROP_PROTREF; del->u.ref = prop - This->prototype->props; @@ -379,6 +380,8 @@ static HRESULT find_prop_name_prot(jsdisp_t *This, unsigned hash, const WCHAR *n } }
+ if(!del) + return DISP_E_UNKNOWNNAME; *ret = del; return S_OK; } @@ -389,26 +392,23 @@ static HRESULT ensure_prop_name(jsdisp_t *This, const WCHAR *name, DWORD create_ HRESULT hres;
hres = find_prop_name_prot(This, string_hash(name), name, case_insens, &prop, ret_idx); - if(hres != S_OK) - return hres; - if(!prop || prop->type == PROP_DELETED) { - TRACE("creating prop %s flags %lx\n", debugstr_w(name), create_flags); - - if(prop) { - prop->type = PROP_JSVAL; - prop->flags = create_flags; - prop->u.val = jsval_undefined(); - }else { - prop = alloc_prop(This, name, PROP_JSVAL, create_flags); - if(!prop) - return E_OUTOFMEMORY; - } + if(hres != S_OK) { + if(hres != DISP_E_UNKNOWNNAME) + return hres;
+ TRACE("creating new prop %s flags %lx\n", debugstr_w(name), create_flags); + if(!(prop = alloc_prop(This, name, PROP_JSVAL, create_flags))) + return E_OUTOFMEMORY; + prop->u.val = jsval_undefined(); + }else if(prop->type == PROP_DELETED) { + TRACE("creating prop %s flags %lx\n", debugstr_w(name), create_flags); + prop->type = PROP_JSVAL; + prop->flags = create_flags; prop->u.val = jsval_undefined(); }
*ret = prop; - return hres; + return S_OK; }
static IDispatch *get_this(DISPPARAMS *dp) @@ -669,20 +669,17 @@ static HRESULT fill_protrefs(jsdisp_t *This) unsigned idx; hres = find_prop_name(This, iter->hash, iter->name, FALSE, &prop, &idx); if(hres != S_OK) { - if(FAILED(hres)) - return hres; - continue; - } - if(!prop || prop->type==PROP_DELETED) { - if(prop) { - prop->type = PROP_PROTREF; - prop->flags = 0; - prop->u.ref = iter - This->prototype->props; - }else { - prop = alloc_protref(This, iter->name, iter - This->prototype->props); - if(!prop) - return E_OUTOFMEMORY; + if(hres != DISP_E_UNKNOWNNAME) { + if(FAILED(hres)) + return hres; + continue; } + if(!alloc_protref(This, iter->name, iter - This->prototype->props)) + return E_OUTOFMEMORY; + }else if(prop->type==PROP_DELETED) { + prop->type = PROP_PROTREF; + prop->flags = 0; + prop->u.ref = iter - This->prototype->props; } }
@@ -2091,9 +2088,9 @@ static HRESULT WINAPI DispatchEx_DeleteMemberByName(IDispatchEx *iface, BSTR bst FIXME("Unsupported grfdex %lx\n", grfdex);
hres = find_prop_name(This, string_hash(bstrName), bstrName, grfdex & fdexNameCaseInsensitive, &prop, &idx); - if(hres != S_OK) - return FAILED(hres) ? hres : S_OK; - if(!prop) { + if(hres != S_OK) { + if(hres != DISP_E_UNKNOWNNAME) + return FAILED(hres) ? hres : S_OK; TRACE("not found\n"); return S_OK; } @@ -2326,7 +2323,7 @@ HRESULT init_dispex_from_constr(jsdisp_t *dispex, script_ctx_t *ctx, const built HRESULT hres;
hres = find_prop_name_prot(constr, string_hash(L"prototype"), L"prototype", FALSE, &prop, NULL); - if(SUCCEEDED(hres) && prop && prop->type!=PROP_DELETED) { + if(hres == S_OK && prop->type != PROP_DELETED) { jsval_t val;
hres = prop_get(constr, to_disp(constr), prop, &val); @@ -2367,12 +2364,13 @@ HRESULT jsdisp_get_id(jsdisp_t *jsdisp, const WCHAR *name, DWORD flags, DISPID * flags & fdexNameCaseInsensitive, &prop, NULL); else hres = find_prop_name_prot(jsdisp, string_hash(name), name, flags & fdexNameCaseInsensitive, &prop, NULL); - if(FAILED(hres)) + if(hres == S_OK) { + if(prop->type != PROP_DELETED) { + *id = prop_to_id(jsdisp, prop); + return S_OK; + } + }else if(hres != DISP_E_UNKNOWNNAME) { return hres; - - if(prop && prop->type!=PROP_DELETED) { - *id = prop_to_id(jsdisp, prop); - return S_OK; }
TRACE("not found %s\n", debugstr_w(name)); @@ -2429,9 +2427,9 @@ HRESULT jsdisp_call_name(jsdisp_t *disp, const WCHAR *name, WORD flags, unsigned
hres = find_prop_name_prot(disp, string_hash(name), name, FALSE, &prop, NULL); if(FAILED(hres)) - return hres; + return hres != DISP_E_UNKNOWNNAME ? hres : JS_E_INVALID_PROPERTY;
- if(!prop || prop->type == PROP_DELETED) + if(prop->type == PROP_DELETED) return JS_E_INVALID_PROPERTY;
return invoke_prop_func(disp, to_disp(disp), prop, flags, argc, argv, r, &disp->ctx->jscaller->IServiceProvider_iface); @@ -2671,10 +2669,10 @@ HRESULT jsdisp_propput(jsdisp_t *obj, const WCHAR *name, DWORD flags, BOOL throw else hres = find_prop_name(obj, string_hash(name), name, FALSE, &prop, &idx); if(FAILED(hres)) - return hres; + return hres != DISP_E_UNKNOWNNAME ? hres : (throw ? JS_E_INVALID_ACTION : S_OK); if(hres == S_FALSE) return obj->builtin_info->idx_put ? obj->builtin_info->idx_put(obj, idx, val) : S_OK; - if(!prop || (prop->type == PROP_DELETED && !obj->extensible)) + if(prop->type == PROP_DELETED && !obj->extensible) return throw ? JS_E_INVALID_ACTION : S_OK;
return prop_put(obj, prop, val); @@ -2774,12 +2772,16 @@ HRESULT jsdisp_propget_name(jsdisp_t *obj, const WCHAR *name, jsval_t *val) HRESULT hres;
hres = find_prop_name_prot(obj, string_hash(name), name, FALSE, &prop, &idx); - if(FAILED(hres)) - return hres; + if(FAILED(hres)) { + if(hres != DISP_E_UNKNOWNNAME) + return hres; + *val = jsval_undefined(); + return S_OK; + } if(hres == S_FALSE) return obj->builtin_info->idx_get(obj, idx, val);
- if(!prop || prop->type==PROP_DELETED) { + if(prop->type == PROP_DELETED) { *val = jsval_undefined(); return S_OK; } @@ -2797,12 +2799,16 @@ HRESULT jsdisp_get_idx(jsdisp_t *obj, DWORD idx, jsval_t *r) swprintf(name, ARRAY_SIZE(name), L"%d", idx);
hres = find_prop_name_prot(obj, string_hash(name), name, FALSE, &prop, &tmp); - if(FAILED(hres)) - return hres; + if(FAILED(hres)) { + if(hres != DISP_E_UNKNOWNNAME) + return hres; + *r = jsval_undefined(); + return DISP_E_UNKNOWNNAME; + } if(hres == S_FALSE) return obj->builtin_info->idx_get(obj, idx, r);
- if(!prop || prop->type==PROP_DELETED) { + if(prop->type == PROP_DELETED) { *r = jsval_undefined(); return DISP_E_UNKNOWNNAME; } @@ -2858,9 +2864,7 @@ HRESULT jsdisp_delete_idx(jsdisp_t *obj, DWORD idx)
hres = find_prop_name(obj, string_hash(buf), buf, FALSE, &prop, &tmp); if(hres != S_OK) - return FAILED(hres) ? hres : S_OK; - if(!prop) - return S_OK; + return FAILED(hres) && hres != DISP_E_UNKNOWNNAME ? hres : S_OK;
hres = delete_prop(prop, &b); if(FAILED(hres)) @@ -2956,13 +2960,11 @@ HRESULT disp_delete_name(script_ctx_t *ctx, IDispatch *disp, jsstr_t *name, BOOL }
hres = find_prop_name(jsdisp, string_hash(ptr), ptr, FALSE, &prop, &idx); - if(hres != S_OK) { - *ret = FALSE; - hres = S_OK; - }else if(prop) { + if(hres == S_OK) hres = delete_prop(prop, ret); - }else { - *ret = TRUE; + else { + *ret = (hres == DISP_E_UNKNOWNNAME); + hres = S_OK; }
jsdisp_release(jsdisp); @@ -3022,9 +3024,6 @@ HRESULT jsdisp_get_own_property(jsdisp_t *obj, const WCHAR *name, BOOL flags_onl return S_OK; }
- if(!prop) - return DISP_E_UNKNOWNNAME; - memset(desc, 0, sizeof(*desc));
switch(prop->type) { @@ -3063,15 +3062,17 @@ HRESULT jsdisp_define_property(jsdisp_t *obj, const WCHAR *name, property_desc_t HRESULT hres;
hres = find_prop_name(obj, string_hash(name), name, FALSE, &prop, NULL); - if(FAILED(hres)) - return hres; - - if((!prop || prop->type == PROP_DELETED || prop->type == PROP_PROTREF) && !obj->extensible) + if(FAILED(hres)) { + if(hres != DISP_E_UNKNOWNNAME) + return hres; + if(!obj->extensible) + return throw_error(obj->ctx, JS_E_OBJECT_NONEXTENSIBLE, name); + if(!(prop = alloc_prop(obj, name, PROP_DELETED, 0))) + return E_OUTOFMEMORY; + } + else if((prop->type == PROP_DELETED || prop->type == PROP_PROTREF) && !obj->extensible) return throw_error(obj->ctx, JS_E_OBJECT_NONEXTENSIBLE, name);
- if(!prop && !(prop = alloc_prop(obj, name, PROP_DELETED, 0))) - return E_OUTOFMEMORY; - if(prop->type == PROP_DELETED || prop->type == PROP_PROTREF) { prop->flags = desc->flags; if(desc->explicit_getter || desc->explicit_setter) {
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Typed Arrays have special properties (even according to the spec); they override all positive indices, and operations on them must be special cased. Presumably (on native) this is done for performance reasons, as they are used as actual data arrays which are expected to perform well with many elements (e.g. using them to store and manipulate an image). The spec is even stricter, where any number is intercepted, but for us it's simpler since native doesn't follow the spec.
Note that even the behavior is special; defining an indexed prop on a typed array for indices out of bounds of the array doesn't do anything, although it does throw if flags don't match (since it's not configurable). Redefining an index within bounds throws if flags don't match, but otherwise acts like a normal setter (coerces it into the underlying data type), which is NOT how defineProperty usually works, even on other indexed props. delete always returns false, even for out of bounds indices, despite them not being props (hasOwnProperty returns false), which is another special case.
The prototype is also not consulted for any positive indices, but is for negative (unlike the spec says, native differs here), i.e. the positive indices behavior is completely overridden, even for those out of bounds.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/arraybuf.c | 347 +++++++++++++++++++++++++++++++++++++++ dlls/jscript/dispex.c | 45 ++++- dlls/jscript/error.c | 3 + dlls/jscript/jscript.h | 19 ++- dlls/jscript/jscript.rc | 3 + dlls/jscript/object.c | 8 + dlls/jscript/resource.h | 3 + dlls/mshtml/tests/es5.js | 189 ++++++++++++++++++++- 8 files changed, 611 insertions(+), 6 deletions(-)
diff --git a/dlls/jscript/arraybuf.c b/dlls/jscript/arraybuf.c index 62dfe614d65..351f7449bd5 100644 --- a/dlls/jscript/arraybuf.c +++ b/dlls/jscript/arraybuf.c @@ -39,6 +39,14 @@ typedef struct { DWORD size; } DataViewInstance;
+typedef struct { + jsdisp_t dispex; + + ArrayBufferInstance *buffer; + DWORD offset; + DWORD length; +} TypedArrayInstance; + static inline ArrayBufferInstance *arraybuf_from_jsdisp(jsdisp_t *jsdisp) { return CONTAINING_RECORD(jsdisp, ArrayBufferInstance, dispex); @@ -49,6 +57,11 @@ static inline DataViewInstance *dataview_from_jsdisp(jsdisp_t *jsdisp) return CONTAINING_RECORD(jsdisp, DataViewInstance, dispex); }
+static inline TypedArrayInstance *typedarr_from_jsdisp(jsdisp_t *jsdisp) +{ + return CONTAINING_RECORD(jsdisp, TypedArrayInstance, dispex); +} + static inline ArrayBufferInstance *arraybuf_this(jsval_t vthis) { jsdisp_t *jsdisp = is_object_instance(vthis) ? to_jsdisp(get_object(vthis)) : NULL; @@ -709,6 +722,303 @@ static const builtin_info_t DataViewConstr_info = { NULL };
+#define TYPEDARRAY_LIST \ +X(Int8Array, JSCLASS_INT8ARRAY, INT8, to_int32, INT) \ +X(Int16Array, JSCLASS_INT16ARRAY, INT16, to_int32, INT) \ +X(Int32Array, JSCLASS_INT32ARRAY, INT32, to_int32, INT) \ +X(Uint8Array, JSCLASS_UINT8ARRAY, UINT8, to_int32, INT) \ +X(Uint16Array, JSCLASS_UINT16ARRAY, UINT16, to_int32, INT) \ +X(Uint32Array, JSCLASS_UINT32ARRAY, UINT32, to_int32, INT) \ +X(Float32Array, JSCLASS_FLOAT32ARRAY, float, to_number, double) \ +X(Float64Array, JSCLASS_FLOAT64ARRAY, double, to_number, double) + +#define TYPEDARRAY_INDEX(JSCLASS) ((JSCLASS) - FIRST_TYPEDARRAY_JSCLASS) + +#define X(NAME, JSCLASS, TYPE, CONVERT, NUM_TYPE) [TYPEDARRAY_INDEX(JSCLASS)] = L"" #NAME, +static const WCHAR *const TypedArray_name[] = { TYPEDARRAY_LIST }; +#undef X + +#define X(NAME, JSCLASS, TYPE, CONVERT, NUM_TYPE) [TYPEDARRAY_INDEX(JSCLASS)] = sizeof(TYPE), +static const unsigned TypedArray_elem_size[] = { TYPEDARRAY_LIST }; +#undef X + +static inline TypedArrayInstance *typedarr_this(jsval_t vthis, jsclass_t jsclass) +{ + jsdisp_t *jsdisp = is_object_instance(vthis) ? to_jsdisp(get_object(vthis)) : NULL; + return (jsdisp && is_class(jsdisp, jsclass)) ? typedarr_from_jsdisp(jsdisp) : NULL; +} + +static HRESULT TypedArray_get_buffer(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) +{ + TRACE("%p\n", jsthis); + + *r = jsval_obj(jsdisp_addref(&typedarr_from_jsdisp(jsthis)->buffer->dispex)); + return S_OK; +} + +static HRESULT TypedArray_get_byteLength(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) +{ + TRACE("%p\n", jsthis); + + *r = jsval_number(typedarr_from_jsdisp(jsthis)->length * TypedArray_elem_size[TYPEDARRAY_INDEX(jsthis->builtin_info->class)]); + return S_OK; +} + +static HRESULT TypedArray_get_byteOffset(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) +{ + TRACE("%p\n", jsthis); + + *r = jsval_number(typedarr_from_jsdisp(jsthis)->offset); + return S_OK; +} + +static HRESULT TypedArray_get_length(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) +{ + TRACE("%p\n", jsthis); + + *r = jsval_number(typedarr_from_jsdisp(jsthis)->length); + return S_OK; +} + +static HRESULT TypedArray_set(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, + jsval_t *r, jsclass_t jsclass) +{ + TypedArrayInstance *typedarr; + + FIXME("not implemented\n"); + + if(!(typedarr = typedarr_this(vthis, jsclass))) + return JS_E_NOT_TYPEDARRAY; + return E_NOTIMPL; +} + +static HRESULT TypedArray_subarray(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, + jsval_t *r, jsclass_t jsclass) +{ + TypedArrayInstance *typedarr; + + FIXME("not implemented\n"); + + if(!(typedarr = typedarr_this(vthis, jsclass))) + return JS_E_NOT_TYPEDARRAY; + return E_NOTIMPL; +} + +static unsigned TypedArray_idx_length(jsdisp_t *jsdisp) +{ + TypedArrayInstance *typedarr = typedarr_from_jsdisp(jsdisp); + return typedarr->length; +} + +static void TypedArray_destructor(jsdisp_t *dispex) +{ + TypedArrayInstance *typedarr = typedarr_from_jsdisp(dispex); + if(typedarr->buffer) + jsdisp_release(&typedarr->buffer->dispex); + free(typedarr); +} + +static HRESULT TypedArray_gc_traverse(struct gc_ctx *gc_ctx, enum gc_traverse_op op, jsdisp_t *dispex) +{ + TypedArrayInstance *typedarr = typedarr_from_jsdisp(dispex); + return gc_process_linked_obj(gc_ctx, op, dispex, &typedarr->buffer->dispex, (void**)&typedarr->buffer); +} + +static const builtin_prop_t TypedArrayInst_props[] = { + {L"buffer", NULL, 0, TypedArray_get_buffer}, + {L"byteLength", NULL, 0, TypedArray_get_byteLength}, + {L"byteOffset", NULL, 0, TypedArray_get_byteOffset}, + {L"length", NULL, 0, TypedArray_get_length}, +}; + +#define X(NAME, JSCLASS, TYPE, CONVERT, NUM_TYPE) \ +static HRESULT NAME ##_idx_get(jsdisp_t *jsdisp, unsigned idx, jsval_t *r) \ +{ \ + TypedArrayInstance *typedarr = typedarr_from_jsdisp(jsdisp); \ + \ + TRACE("%p[%u]\n", typedarr, idx); \ + \ + *r = jsval_number(*(TYPE*)&typedarr->buffer->buf[typedarr->offset + idx * sizeof(TYPE)]); \ + return S_OK; \ +} \ + \ +static HRESULT NAME ##_idx_put(jsdisp_t *jsdisp, unsigned idx, jsval_t val) \ +{ \ + TypedArrayInstance *typedarr = typedarr_from_jsdisp(jsdisp); \ + HRESULT hres; \ + NUM_TYPE n; \ + \ + TRACE("%p[%u] = %s\n", typedarr, idx, debugstr_jsval(val)); \ + \ + hres = CONVERT(jsdisp->ctx, val, &n); \ + if(SUCCEEDED(hres)) \ + *(TYPE*)&typedarr->buffer->buf[typedarr->offset + idx * sizeof(TYPE)] = n; \ + return hres; \ +} \ + \ +static HRESULT NAME ##_set(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) \ +{ \ + return TypedArray_set(ctx, vthis, flags, argc, argv, r, JSCLASS); \ +} \ + \ +static HRESULT NAME ##_subarray(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) \ +{ \ + return TypedArray_subarray(ctx, vthis, flags, argc, argv, r, JSCLASS); \ +} \ + \ +static const builtin_prop_t NAME ##_props[] = { \ + {L"buffer", NULL, 0, TypedArray_get_buffer}, \ + {L"byteLength", NULL, 0, TypedArray_get_byteLength}, \ + {L"byteOffset", NULL, 0, TypedArray_get_byteOffset}, \ + {L"length", NULL, 0, TypedArray_get_length}, \ + {L"set", NAME ##_set, PROPF_METHOD|2}, \ + {L"subarray", NAME ##_subarray, PROPF_METHOD|2}, \ +}; +TYPEDARRAY_LIST +#undef X + +#define X(NAME, JSCLASS, TYPE, CONVERT, NUM_TYPE) \ +[TYPEDARRAY_INDEX(JSCLASS)] = \ +{ \ + JSCLASS, \ + NULL, \ + ARRAY_SIZE(NAME ##_props), \ + NAME ##_props, \ + TypedArray_destructor, \ + NULL, \ + TypedArray_idx_length, \ + NAME ##_idx_get, \ + NAME ##_idx_put, \ + TypedArray_gc_traverse \ +}, +static const builtin_info_t TypedArray_info[] = { TYPEDARRAY_LIST }; +#undef X + +#define X(NAME, JSCLASS, TYPE, CONVERT, NUM_TYPE) \ +[TYPEDARRAY_INDEX(JSCLASS)] = \ +{ \ + JSCLASS, \ + NULL, \ + ARRAY_SIZE(TypedArrayInst_props), \ + TypedArrayInst_props, \ + TypedArray_destructor, \ + NULL, \ + TypedArray_idx_length, \ + NAME ##_idx_get, \ + NAME ##_idx_put, \ + TypedArray_gc_traverse \ +}, +static const builtin_info_t TypedArrayInst_info[] = { TYPEDARRAY_LIST }; +#undef X + +static HRESULT create_typedarr(script_ctx_t *ctx, jsclass_t jsclass, ArrayBufferInstance *buffer, DWORD offset, DWORD length, jsdisp_t **ret) +{ + TypedArrayInstance *typedarr; + HRESULT hres; + + if(!(typedarr = calloc(1, sizeof(TypedArrayInstance)))) + return E_OUTOFMEMORY; + + hres = init_dispex_from_constr(&typedarr->dispex, ctx, &TypedArrayInst_info[TYPEDARRAY_INDEX(jsclass)], + ctx->typedarr_constr[TYPEDARRAY_INDEX(jsclass)]); + if(FAILED(hres)) { + free(typedarr); + return hres; + } + + jsdisp_addref(&buffer->dispex); + typedarr->buffer = buffer; + typedarr->offset = offset; + typedarr->length = length; + + *ret = &typedarr->dispex; + return S_OK; +} + +static HRESULT TypedArrayConstr_value(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, + jsval_t *r, jsclass_t jsclass) +{ + const unsigned typedarr_idx = TYPEDARRAY_INDEX(jsclass); + unsigned elem_size = TypedArray_elem_size[typedarr_idx]; + ArrayBufferInstance *buffer = NULL; + DWORD offset = 0, length = 0; + jsdisp_t *typedarr; + HRESULT hres; + double n; + + TRACE("\n"); + + switch(flags) { + case DISPATCH_METHOD: + case DISPATCH_CONSTRUCT: { + if(argc) { + if(is_object_instance(argv[0])) { + jsdisp_t *obj = to_jsdisp(get_object(argv[0])); + + if(!obj) + return JS_E_TYPEDARRAY_BAD_CTOR_ARG; + + FIXME("Construction from object not implemented\n"); + return E_NOTIMPL; + }else if(is_number(argv[0])) { + hres = to_integer(ctx, argv[0], &n); + if(FAILED(hres)) + return hres; + if(n < 0.0) + return JS_E_TYPEDARRAY_INVALID_OFFSLEN; + if(n * elem_size > (UINT_MAX - FIELD_OFFSET(ArrayBufferInstance, buf[0]))) + return E_OUTOFMEMORY; + length = n; + }else + return JS_E_TYPEDARRAY_BAD_CTOR_ARG; + } + + if(!r) + return S_OK; + + if(!buffer) { + hres = create_arraybuf(ctx, length * elem_size, &buffer); + if(FAILED(hres)) + return hres; + } + + hres = create_typedarr(ctx, jsclass, buffer, offset, length, &typedarr); + jsdisp_release(&buffer->dispex); + if(FAILED(hres)) + return hres; + + *r = jsval_obj(typedarr); + break; + } + default: + FIXME("unimplemented flags: %x\n", flags); + return E_NOTIMPL; + } + + return S_OK; +} + +#define X(NAME, JSCLASS, TYPE, CONVERT, NUM_TYPE) \ +static HRESULT NAME ## Constr_value(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) \ +{ \ + return TypedArrayConstr_value(ctx, vthis, flags, argc, argv, r, JSCLASS); \ +} +TYPEDARRAY_LIST +#undef X + +#define X(NAME, JSCLASS, TYPE, CONVERT, NUM_TYPE) [TYPEDARRAY_INDEX(JSCLASS)] = NAME ## Constr_value, +static const builtin_invoke_t TypedArray_constr[] = { TYPEDARRAY_LIST }; +#undef X + +static const builtin_info_t TypedArrayConstr_info = { + JSCLASS_FUNCTION, + Function_value, + 0, + NULL, + NULL, + NULL +}; + HRESULT init_arraybuf_constructors(script_ctx_t *ctx) { static const struct { @@ -720,6 +1030,7 @@ HRESULT init_arraybuf_constructors(script_ctx_t *ctx) { L"byteOffset", DataView_get_byteOffset }, }; ArrayBufferInstance *arraybuf; + TypedArrayInstance *typedarr; DataViewInstance *view; property_desc_t desc; HRESULT hres; @@ -791,6 +1102,42 @@ HRESULT init_arraybuf_constructors(script_ctx_t *ctx)
hres = jsdisp_define_data_property(ctx->global, L"DataView", PROPF_CONFIGURABLE | PROPF_WRITABLE, jsval_obj(ctx->dataview_constr)); + if(FAILED(hres)) + return hres; + + for(i = 0; i < ARRAY_SIZE(TypedArray_info); i++) { + if(!(typedarr = calloc(1, sizeof(TypedArrayInstance)))) + return E_OUTOFMEMORY; + + hres = create_arraybuf(ctx, 0, &typedarr->buffer); + if(FAILED(hres)) { + free(typedarr); + return hres; + } + + hres = init_dispex(&typedarr->dispex, ctx, &TypedArray_info[i], ctx->object_prototype); + if(FAILED(hres)) { + jsdisp_release(&typedarr->buffer->dispex); + free(typedarr); + return hres; + } + + hres = create_builtin_constructor(ctx, TypedArray_constr[i], TypedArray_name[i], &TypedArrayConstr_info, + PROPF_CONSTR|1, &typedarr->dispex, &ctx->typedarr_constr[i]); + jsdisp_release(&typedarr->dispex); + if(FAILED(hres)) + return hres; + + hres = jsdisp_define_data_property(ctx->typedarr_constr[i], L"BYTES_PER_ELEMENT", 0, + jsval_number(TypedArray_elem_size[i])); + if(FAILED(hres)) + return hres; + + hres = jsdisp_define_data_property(ctx->global, TypedArray_name[i], PROPF_CONFIGURABLE | PROPF_WRITABLE, + jsval_obj(ctx->typedarr_constr[i])); + if(FAILED(hres)) + return hres; + }
return hres; } diff --git a/dlls/jscript/dispex.c b/dlls/jscript/dispex.c index e4981912c83..da7d9255ab7 100644 --- a/dlls/jscript/dispex.c +++ b/dlls/jscript/dispex.c @@ -254,8 +254,11 @@ static BOOL get_indexed_prop_idx(jsdisp_t *This, const WCHAR *name, unsigned *re
for(ptr = name; is_digit(*ptr) && idx <= (UINT_MAX-9 / 10); ptr++) idx = idx*10 + (*ptr-'0'); - if(*ptr) - return FALSE; + if(*ptr) { + while(is_digit(*ptr)) ptr++; + if(*ptr) return FALSE; + idx = UINT_MAX; + }
*ret_idx = idx; return TRUE; @@ -264,12 +267,27 @@ static BOOL get_indexed_prop_idx(jsdisp_t *This, const WCHAR *name, unsigned *re /* Looks up the prop given the name, or its index in case of an indexed prop that we can optimize (in which case it returns S_FALSE and index into ret_idx) */ static HRESULT find_prop_name(jsdisp_t *This, unsigned hash, const WCHAR *name, BOOL case_insens, dispex_prop_t **ret, unsigned *ret_idx) { + BOOL is_typedarr = (This->builtin_info->class >= FIRST_TYPEDARRAY_JSCLASS && This->builtin_info->class <= LAST_TYPEDARRAY_JSCLASS); const builtin_prop_t *builtin; unsigned bucket, pos, prev = ~0; dispex_prop_t *prop; unsigned idx; HRESULT hres;
+ /* Typed Arrays are very much special, they override every positive index no matter what (not even consulting prototype), even if out of bounds! */ + if(is_typedarr && get_indexed_prop_idx(This, name, &idx)) { + if(idx >= This->builtin_info->idx_length(This)) { + if(ret_idx) + *ret_idx = UINT_MAX; + return DISP_E_UNKNOWNNAME; + } + if(ret_idx) { + *ret_idx = idx; + return S_FALSE; + } + is_typedarr = FALSE; /* allow PROP_IDX allocation (e.g. for enumeration) */ + } + bucket = get_props_idx(This, hash); pos = This->props[bucket].bucket_head; while(pos != ~0) { @@ -320,7 +338,7 @@ static HRESULT find_prop_name(jsdisp_t *This, unsigned hash, const WCHAR *name, return S_OK; }
- if(get_indexed_prop_idx(This, name, &idx) && idx < This->builtin_info->idx_length(This)) { + if(!is_typedarr && get_indexed_prop_idx(This, name, &idx) && idx < This->builtin_info->idx_length(This)) { unsigned flags = PROPF_ENUMERABLE;
if(ret_idx) { @@ -339,6 +357,8 @@ static HRESULT find_prop_name(jsdisp_t *This, unsigned hash, const WCHAR *name, return S_OK; }
+ if(ret_idx) + *ret_idx = 0; return DISP_E_UNKNOWNNAME; }
@@ -395,6 +415,8 @@ static HRESULT ensure_prop_name(jsdisp_t *This, const WCHAR *name, DWORD create_ if(hres != S_OK) { if(hres != DISP_E_UNKNOWNNAME) return hres; + if(ret_idx && *ret_idx == UINT_MAX) + return DISP_E_UNKNOWNNAME;
TRACE("creating new prop %s flags %lx\n", debugstr_w(name), create_flags); if(!(prop = alloc_prop(This, name, PROP_JSVAL, create_flags))) @@ -674,6 +696,8 @@ static HRESULT fill_protrefs(jsdisp_t *This) return hres; continue; } + if(idx == UINT_MAX) + continue; if(!alloc_protref(This, iter->name, iter - This->prototype->props)) return E_OUTOFMEMORY; }else if(prop->type==PROP_DELETED) { @@ -2963,7 +2987,7 @@ HRESULT disp_delete_name(script_ctx_t *ctx, IDispatch *disp, jsstr_t *name, BOOL if(hres == S_OK) hres = delete_prop(prop, ret); else { - *ret = (hres == DISP_E_UNKNOWNNAME); + *ret = (hres == DISP_E_UNKNOWNNAME && idx != UINT_MAX); hres = S_OK; }
@@ -3059,8 +3083,21 @@ HRESULT jsdisp_get_own_property(jsdisp_t *obj, const WCHAR *name, BOOL flags_onl HRESULT jsdisp_define_property(jsdisp_t *obj, const WCHAR *name, property_desc_t *desc) { dispex_prop_t *prop; + unsigned idx; HRESULT hres;
+ /* Typed Arrays have special rules here; any attempt to change their properties fails, and defining them acts like a setter! */ + if(obj->builtin_info->class >= FIRST_TYPEDARRAY_JSCLASS && obj->builtin_info->class <= LAST_TYPEDARRAY_JSCLASS && + get_indexed_prop_idx(obj, name, &idx)) { + if((desc->flags & desc->mask) != (desc->mask & (PROPF_WRITABLE | PROPF_ENUMERABLE))) + return throw_error(obj->ctx, JS_E_NONCONFIGURABLE_REDEFINED, name); + if(desc->explicit_value) + return idx < obj->builtin_info->idx_length(obj) ? obj->builtin_info->idx_put(obj, idx, desc->value) : S_OK; + if(desc->explicit_getter || desc->explicit_setter) + return throw_error(obj->ctx, JS_E_NONCONFIGURABLE_REDEFINED, name); + return idx < obj->builtin_info->idx_length(obj) ? obj->builtin_info->idx_put(obj, idx, jsval_undefined()) : S_OK; + } + hres = find_prop_name(obj, string_hash(name), name, FALSE, &prop, NULL); if(FAILED(hres)) { if(hres != DISP_E_UNKNOWNNAME) diff --git a/dlls/jscript/error.c b/dlls/jscript/error.c index 81e3a3a9739..aa5399d02c3 100644 --- a/dlls/jscript/error.c +++ b/dlls/jscript/error.c @@ -483,6 +483,8 @@ jsdisp_t *create_builtin_error(script_ctx_t *ctx) case JS_E_OBJECT_NONEXTENSIBLE: case JS_E_NONCONFIGURABLE_REDEFINED: case JS_E_NONWRITABLE_MODIFIED: + case JS_E_TYPEDARRAY_BAD_CTOR_ARG: + case JS_E_NOT_TYPEDARRAY: case JS_E_NOT_DATAVIEW: case JS_E_DATAVIEW_NO_ARGUMENT: case JS_E_WRONG_THIS: @@ -497,6 +499,7 @@ jsdisp_t *create_builtin_error(script_ctx_t *ctx) case JS_E_FRACTION_DIGITS_OUT_OF_RANGE: case JS_E_PRECISION_OUT_OF_RANGE: case JS_E_INVALID_LENGTH: + case JS_E_TYPEDARRAY_INVALID_OFFSLEN: case JS_E_DATAVIEW_INVALID_ACCESS: case JS_E_DATAVIEW_INVALID_OFFSET: constr = ctx->range_error_constr; diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index 2453cba4939..11193992f3d 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -118,11 +118,24 @@ typedef enum { JSCLASS_JSON, JSCLASS_ARRAYBUFFER, JSCLASS_DATAVIEW, + JSCLASS_INT8ARRAY, + JSCLASS_INT16ARRAY, + JSCLASS_INT32ARRAY, + JSCLASS_UINT8ARRAY, + JSCLASS_UINT16ARRAY, + JSCLASS_UINT32ARRAY, + JSCLASS_FLOAT32ARRAY, + JSCLASS_FLOAT64ARRAY, JSCLASS_MAP, JSCLASS_SET, JSCLASS_WEAKMAP, + + FIRST_TYPEDARRAY_JSCLASS = JSCLASS_INT8ARRAY, + LAST_TYPEDARRAY_JSCLASS = JSCLASS_FLOAT64ARRAY, } jsclass_t;
+enum { NUM_TYPEDARRAY_TYPES = LAST_TYPEDARRAY_JSCLASS - FIRST_TYPEDARRAY_JSCLASS + 1 }; + jsdisp_t *iface_to_jsdisp(IDispatch*);
typedef HRESULT (*builtin_invoke_t)(script_ctx_t*,jsval_t,WORD,unsigned,jsval_t*,jsval_t*); @@ -439,11 +452,12 @@ struct _script_ctx_t { jsdisp_t *vbarray_constr; jsdisp_t *arraybuf_constr; jsdisp_t *dataview_constr; + jsdisp_t *typedarr_constr[NUM_TYPEDARRAY_TYPES]; jsdisp_t *map_prototype; jsdisp_t *set_prototype; jsdisp_t *weakmap_prototype; }; - jsdisp_t *global_objects[25]; + jsdisp_t *global_objects[25 + NUM_TYPEDARRAY_TYPES]; }; }; C_ASSERT(RTL_SIZEOF_THROUGH_FIELD(script_ctx_t, weakmap_prototype) == RTL_SIZEOF_THROUGH_FIELD(script_ctx_t, global_objects)); @@ -581,6 +595,9 @@ static inline HRESULT disp_call_value(script_ctx_t *ctx, IDispatch *disp, jsval_ #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_TYPEDARRAY_BAD_CTOR_ARG MAKE_JSERROR(IDS_TYPEDARRAY_BAD_CTOR_ARG) +#define JS_E_NOT_TYPEDARRAY MAKE_JSERROR(IDS_NOT_TYPEDARRAY) +#define JS_E_TYPEDARRAY_INVALID_OFFSLEN MAKE_JSERROR(IDS_TYPEDARRAY_INVALID_OFFSLEN) #define JS_E_NOT_DATAVIEW MAKE_JSERROR(IDS_NOT_DATAVIEW) #define JS_E_DATAVIEW_NO_ARGUMENT MAKE_JSERROR(IDS_DATAVIEW_NO_ARGUMENT) #define JS_E_DATAVIEW_INVALID_ACCESS MAKE_JSERROR(IDS_DATAVIEW_INVALID_ACCESS) diff --git a/dlls/jscript/jscript.rc b/dlls/jscript/jscript.rc index cbdfde507b7..a224b6cd6f3 100644 --- a/dlls/jscript/jscript.rc +++ b/dlls/jscript/jscript.rc @@ -76,6 +76,9 @@ 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_NOT_TYPEDARRAY "'this' is not a typed array object" + IDS_TYPEDARRAY_BAD_CTOR_ARG "Typed array constructor argument is invalid" + IDS_TYPEDARRAY_INVALID_OFFSLEN "Invalid offset/length when creating typed array" IDS_NOT_DATAVIEW "'this' is not a DataView object" IDS_DATAVIEW_NO_ARGUMENT "Required argument offset or value in DataView method is not specified" IDS_DATAVIEW_INVALID_ACCESS "DataView operation access beyond specified buffer length" diff --git a/dlls/jscript/object.c b/dlls/jscript/object.c index 16ecc3dea3c..1b33f481b3b 100644 --- a/dlls/jscript/object.c +++ b/dlls/jscript/object.c @@ -52,6 +52,14 @@ static HRESULT Object_toString(script_ctx_t *ctx, jsval_t vthis, WORD flags, uns L"[object Object]", L"[object ArrayBuffer]", L"[object Object]", + L"[object Int8Array]", + L"[object Int16Array]", + L"[object Int32Array]", + L"[object Uint8Array]", + L"[object Uint16Array]", + L"[object Uint32Array]", + L"[object Float32Array]", + L"[object Float64Array]", L"[object Object]", L"[object Object]", L"[object Object]" diff --git a/dlls/jscript/resource.h b/dlls/jscript/resource.h index 801e940b95e..feb2593e723 100644 --- a/dlls/jscript/resource.h +++ b/dlls/jscript/resource.h @@ -74,6 +74,9 @@ #define IDS_OBJECT_NONEXTENSIBLE 0x13D5 #define IDS_NONCONFIGURABLE_REDEFINED 0x13D6 #define IDS_NONWRITABLE_MODIFIED 0x13D7 +#define IDS_TYPEDARRAY_BAD_CTOR_ARG 0x13DA +#define IDS_NOT_TYPEDARRAY 0x13DB +#define IDS_TYPEDARRAY_INVALID_OFFSLEN 0x13DC #define IDS_NOT_DATAVIEW 0x13DF #define IDS_DATAVIEW_NO_ARGUMENT 0x13E0 #define IDS_DATAVIEW_INVALID_ACCESS 0x13E1 diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index 57fde4d7c9d..9f7a7eab9ed 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -32,6 +32,9 @@ var JS_E_INVALID_LENGTH = 0x800a13a5; var JS_E_INVALID_WRITABLE_PROP_DESC = 0x800a13ac; var JS_E_NONCONFIGURABLE_REDEFINED = 0x800a13d6; var JS_E_NONWRITABLE_MODIFIED = 0x800a13d7; +var JS_E_TYPEDARRAY_BAD_CTOR_ARG = 0x800a13da; +var JS_E_NOT_TYPEDARRAY = 0x800a13db; +var JS_E_TYPEDARRAY_INVALID_OFFSLEN = 0x800a13dc; var JS_E_NOT_DATAVIEW = 0x800a13df; var JS_E_DATAVIEW_NO_ARGUMENT = 0x800a13e0; var JS_E_DATAVIEW_INVALID_ACCESS = 0x800a13e1; @@ -1698,7 +1701,7 @@ sync_test("RegExp", function() { });
sync_test("ArrayBuffers & Views", function() { - var i, r, buf, buf2, view, view2, arr; + var i, r, buf, buf2, view, view2, arr, arr2;
var types = [ [ "Int8", 1 ], @@ -2041,6 +2044,190 @@ sync_test("ArrayBuffers & Views", function() { ok(r === undefined, "buf.slice(-9) view(1).setFloat64(0, 11.875) returned " + r); r = view2.getFloat64(0); ok(r === 11.875, "buf.slice(-9) view(1).getFloat64(0) returned " + r); + + for(i = 0; i < types.length; i++) { + var arrType = types[i][0] + "Array", typeSz = types[i][1]; + test_own_props(arrType, [ "BYTES_PER_ELEMENT" ]); + test_not_own_props(arrType, [ "from", "of" ]); + test_own_props(arrType + ".prototype", [ "buffer", "byteLength", "byteOffset", "length", "set", "subarray" ]); + test_not_own_props(arrType + ".prototype", [ + "at", "copyWithin", "entries", "every", "fill", "filter", "find", "findIndex", "forEach", + "includes", "indexOf", "join", "keys", "lastIndexOf", "map", "reduce", "reduceRight", + "reverse", "slice", "some", "sort", "toLocaleString", "toString", "values" + ]); + + arr = eval(arrType); + test_own_data_prop_desc(arr, "BYTES_PER_ELEMENT", false, false, false); + ok(arr.BYTES_PER_ELEMENT === typeSz, arrType + ".BYTES_PER_ELEMENT = " + arr.BYTES_PER_ELEMENT); + r = arr.length; + ok(r === 1, arrType + ".length = " + r); + r = arr.prototype.set.length; + ok(r === 2, arrType + ".prototype.set.length = " + r); + r = arr.prototype.subarray.length; + ok(r === 2, arrType + ".prototype.subarray.length = " + r); + + r = eval("Object.getPrototypeOf(" + arrType + ")"); + ok(r === Function.prototype, arrType + "'s prototype is not Function.prototype: " + r); + r = eval("Object.getPrototypeOf(" + arrType + ".prototype)"); + ok(r === Object.prototype, arrType + ".prototype's prototype is not Object.prototype: " + r); + r = eval("Object.prototype.toString.call(new " + arrType + "(3))"); + ok(r === "[object " + arrType + "]", "Object toString(new " + arrType + "(3)) = " + r); + r = eval(arrType + ".prototype"); + test_own_data_prop_desc(r, "byteLength", false, false, false); + test_own_data_prop_desc(r, "byteOffset", false, false, false); + test_own_data_prop_desc(r, "length", false, false, false); + test_own_data_prop_desc(r, "buffer", false, false, false); + + try { + eval("new " + arrType + "(-1)"); + ok(false, "new " + arrType + "(-1) did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_TYPEDARRAY_INVALID_OFFSLEN, "new " + arrType + "(-1) threw " + n); + } + try { + eval("new " + arrType + "('9')"); + ok(false, "new " + arrType + "('9') did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_TYPEDARRAY_BAD_CTOR_ARG, "new " + arrType + "('9') threw " + n); + } + try { + eval("new " + arrType + "(null)"); + ok(false, "new " + arrType + "(null) did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_TYPEDARRAY_BAD_CTOR_ARG, "new " + arrType + "(null) threw " + n); + } + + arr = eval("new " + arrType + "()"); + ok(arr.byteLength === 0, arrType + "().byteLength = " + arr.byteLength); + ok(arr.byteOffset === 0, arrType + "().byteOffset = " + arr.byteOffset); + ok(arr.length === 0, arrType + "().length = " + arr.length); + ok(arr.buffer.byteLength === 0, arrType + "().buffer.byteLength = " + arr.buffer.byteLength); + test_readonly(arr, "byteLength", 0); + test_readonly(arr, "byteOffset", 0); + test_readonly(arr, "length", 0); + test_own_data_prop_desc(arr, "byteLength", false, false, false); + test_own_data_prop_desc(arr, "byteOffset", false, false, false); + test_own_data_prop_desc(arr, "length", false, false, false); + test_own_data_prop_desc(arr, "buffer", false, false, false); + + Object.freeze(arr); + ok(Object.isFrozen(arr) === true, arrType + "() not frozen"); + + arr = eval(arrType + "(9.1)"); + ok(arr.byteLength === 9 * typeSz, arrType + "(9.1).byteLength = " + arr.byteLength); + ok(arr.byteOffset === 0, arrType + "(9.1).byteOffset = " + arr.byteOffset); + ok(arr.length === 9, arrType + "(9.1).length = " + arr.length); + ok(arr.buffer.byteLength === arr.byteLength, arrType + "(9.1).buffer.byteLength = " + arr.buffer.byteLength); + for(var j = 0; j < 9; j++) + ok(arr[j] === 0, "arr[" + j + "] = " + arr[j]); + arr[5] = 42; + ok(arr[5] === 42, arrType + "(9.1)[5] = " + arr[5]); + arr[9] = 50; + ok(arr[9] === undefined, arrType + "(9.1)[9] = " + arr[9]); + + eval(arrType + ".prototype[6] = 'foo'"); + r = eval(arrType + ".prototype[6]"); + ok(r === undefined, arrType + ".prototype[6] = " + r); + ok(arr[6] === 0, arrType + "(9.1)[6] after set in prototype = " + arr[6]); + arr[6] = 0; + ok(Object.prototype.hasOwnProperty.call(arr, "6"), "'6' not a property of " + arrType + "(9.1)[6]"); + test_own_data_prop_desc(arr, "6", true, true, false); + r = (delete arr[6]); + ok(r === false, "delete " + arrType + "(9.1)[6] returned " + r); + try { + Object.defineProperty(arr, "6", {writable: false, enumerable: false, configurable: true, value: 10}); + ok(false, "redefining " + arrType + "(9.1)[6] with different flags did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_NONCONFIGURABLE_REDEFINED, "redefining " + arrType + "(9.1)[6] with different flags threw " + n); + } + Object.defineProperty(arr, "6", {writable: true, enumerable: true, configurable: false, value: 10}); + ok(arr[6] === 10, arrType + "(9.1)[6] after definition = " + arr[6]); + Object.defineProperty(arr, "6", {writable: true, enumerable: true, configurable: false, value: "foo"}); + if(arrType.substr(0, 5) === "Float") + ok(arr[6] !== arr[6] /* NaN */, arrType + "(9.1)[6] after definition to string = " + arr[6]); + else + ok(arr[6] === 0, arrType + "(9.1)[6] after definition to string = " + arr[6]); + + eval(arrType + ".prototype[100] = 'foobar'"); + r = eval(arrType + ".prototype[100]"); + ok(r === undefined, arrType + ".prototype[100] = " + r); + ok(arr[100] === undefined, arrType + "(9.1)[100] after set in prototype = " + arr[100]); + arr[100] = 0; + ok(arr[100] === undefined, arrType + "(9.1)[100] after set to zero = " + arr[100]); + ok(!Object.prototype.hasOwnProperty.call(arr, "100"), "'100' is a property of " + arrType + "(9.1)[100]"); + r = (delete arr[100]); + ok(r === false, "delete " + arrType + "(9.1)[100] returned " + r); + try { + Object.defineProperty(arr, "100", {writable: false, enumerable: false, configurable: true, value: 10}); + ok(false, "redefining " + arrType + "(9.1)[100] with different flags did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_NONCONFIGURABLE_REDEFINED, "redefining " + arrType + "(9.1)[100] with different flags threw " + n); + } + Object.defineProperty(arr, "100", {writable: true, enumerable: true, configurable: false, value: 10}); + ok(arr[100] === undefined, arrType + "(9.1)[100] after defined to 10 = " + arr[100]); + ok(!Object.prototype.hasOwnProperty.call(arr, "100"), "'100' is a property of " + arrType + "(9.1)[100] after definition"); + ok(arr[100] === undefined, arrType + "(9.1)[100] after definition = " + arr[100]); + + r = 0; + for(var idx in arr) { + ok(idx === ""+r, arrType + "(9.1) enum idx " + r + " = " + idx); + r++; + } + ok(r === 9, arrType + "(9.1) enum did " + r + " iterations"); + + eval(arrType + ".prototype[-1] = 'barfoo'"); + r = eval(arrType + ".prototype[-1]"); + ok(r === "barfoo", arrType + ".prototype[-1] = " + r); + ok(arr[-1] === "barfoo", arrType + "(9.1)[-1] after set in prototype = " + arr[-1]); + + eval(arrType + ".prototype.foo = 'bar'"); + r = eval(arrType + ".prototype.foo = 'bar'"); + ok(r === "bar", arrType + ".prototype.foo = " + r); + ok(arr.foo === "bar", arrType + "(9.1).foo after set in prototype = " + arr.foo); + Object.freeze(arr); + ok(Object.isFrozen(arr) === true, arrType + "(9.1) not frozen"); + arr = eval(arrType + ".prototype"); + delete arr[-1]; + delete arr.foo; + } + + arr = new Int16Array(2); + arr[0] = 65535; + arr[1] = -65535; + ok(arr[0] == -1, "16-bit arr[0] after overflow = " + arr[0]); + ok(arr[1] == 1, "16-bit arr[1] after overflow = " + arr[1]); + + arr = new Uint8Array(2); + arr[0] = -2; + arr[1] = 258; + ok(arr[0] == 254, "8-bit arr[0] after overflow = " + arr[0]); + ok(arr[1] == 2, "8-bit arr[1] after overflow = " + arr[1]); + + arr = new Int8Array(); + arr2 = new Int32Array(); + + /* methods are incompatible, even though thrown error is not explicit */ + ok(Uint16Array.prototype.subarray !== Int32Array.prototype.subarray, "Uint16Array and Int32Array have same subarray methods"); + ok(Int8Array.prototype.set !== Float32Array.prototype.set, "Int8Array and Float32Array have same set methods"); + try { + Uint8Array.prototype.set.call(arr, [12, 50]); + ok(false, "calling Uint8Array's set with Int8Array context did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_NOT_TYPEDARRAY, "calling Uint8Array's set with Int8Array context threw " + n); + } + try { + Uint32Array.prototype.subarray.call(arr2, 0); + ok(false, "calling Uint32Array's subarray with Int32Array context did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_NOT_TYPEDARRAY, "calling Uint32Array's subarray with Int32Array context threw " + n); + } });
sync_test("builtin_context", function() {
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/arraybuf.c | 34 ++++++++++++++++++++++-- dlls/mshtml/tests/es5.js | 57 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 2 deletions(-)
diff --git a/dlls/jscript/arraybuf.c b/dlls/jscript/arraybuf.c index 351f7449bd5..52d7db567b9 100644 --- a/dlls/jscript/arraybuf.c +++ b/dlls/jscript/arraybuf.c @@ -958,8 +958,38 @@ static HRESULT TypedArrayConstr_value(script_ctx_t *ctx, jsval_t vthis, WORD fla if(!obj) return JS_E_TYPEDARRAY_BAD_CTOR_ARG;
- FIXME("Construction from object not implemented\n"); - return E_NOTIMPL; + if(obj->builtin_info->class == JSCLASS_ARRAYBUFFER) { + buffer = arraybuf_from_jsdisp(obj); + if(argc > 1) { + hres = to_integer(ctx, argv[1], &n); + if(FAILED(hres)) + return hres; + if(n < 0.0 || n > buffer->size) + return JS_E_TYPEDARRAY_INVALID_OFFSLEN; + offset = n; + if(offset % elem_size) + return JS_E_TYPEDARRAY_INVALID_OFFSLEN; + } + if(argc > 2 && !is_undefined(argv[2])) { + hres = to_integer(ctx, argv[2], &n); + if(FAILED(hres)) + return hres; + if(n < 0.0 || n > UINT_MAX) + return JS_E_TYPEDARRAY_INVALID_OFFSLEN; + length = n; + if(offset + length * elem_size > buffer->size) + return JS_E_TYPEDARRAY_INVALID_OFFSLEN; + }else { + length = buffer->size - offset; + if(length % elem_size) + return JS_E_TYPEDARRAY_INVALID_OFFSLEN; + length /= elem_size; + } + jsdisp_addref(&buffer->dispex); + }else { + FIXME("Construction from object not implemented\n"); + return E_NOTIMPL; + } }else if(is_number(argv[0])) { hres = to_integer(ctx, argv[0], &n); if(FAILED(hres)) diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index 9f7a7eab9ed..56566ee84de 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -2078,6 +2078,7 @@ sync_test("ArrayBuffers & Views", function() { test_own_data_prop_desc(r, "length", false, false, false); test_own_data_prop_desc(r, "buffer", false, false, false);
+ buf = ArrayBuffer(34); try { eval("new " + arrType + "(-1)"); ok(false, "new " + arrType + "(-1) did not throw exception"); @@ -2085,6 +2086,27 @@ sync_test("ArrayBuffers & Views", function() { var n = ex.number >>> 0; ok(n === JS_E_TYPEDARRAY_INVALID_OFFSLEN, "new " + arrType + "(-1) threw " + n); } + try { + eval("new " + arrType + "(buf, -1)"); + ok(false, "new " + arrType + "(buf, -1) did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_TYPEDARRAY_INVALID_OFFSLEN, "new " + arrType + "(buf, -1) threw " + n); + } + try { + eval("new " + arrType + "(buf, 36)"); + ok(false, "new " + arrType + "(buf, 36) did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_TYPEDARRAY_INVALID_OFFSLEN, "new " + arrType + "(buf, 36) threw " + n); + } + try { + eval("new " + arrType + "(buf, 32, 4)"); + ok(false, "new " + arrType + "(buf, 32, 4) did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_TYPEDARRAY_INVALID_OFFSLEN, "new " + arrType + "(buf, 32, 4) threw " + n); + } try { eval("new " + arrType + "('9')"); ok(false, "new " + arrType + "('9') did not throw exception"); @@ -2099,6 +2121,26 @@ sync_test("ArrayBuffers & Views", function() { var n = ex.number >>> 0; ok(n === JS_E_TYPEDARRAY_BAD_CTOR_ARG, "new " + arrType + "(null) threw " + n); } + if(typeSz > 1) { + /* test misalignment */ + var a = typeSz >>> 1; + try { + eval("new " + arrType + "(buf, a, 1)"); + ok(false, "new " + arrType + "(buf, " + a + ", 1) did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_TYPEDARRAY_INVALID_OFFSLEN, "new " + arrType + "(buf, " + a + ", 1) threw " + n); + } + a += typeSz; + var b = new ArrayBuffer(a); + try { + eval("new " + arrType + "(b)"); + ok(false, "new " + arrType + "(new ArrayBuffer(" + a + ")) did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_TYPEDARRAY_INVALID_OFFSLEN, "new " + arrType + "(new ArrayBuffer(" + a + ")) threw " + n); + } + }
arr = eval("new " + arrType + "()"); ok(arr.byteLength === 0, arrType + "().byteLength = " + arr.byteLength); @@ -2194,6 +2236,21 @@ sync_test("ArrayBuffers & Views", function() { arr = eval(arrType + ".prototype"); delete arr[-1]; delete arr.foo; + + name = arrType + "(buf, " + typeSz + ", 2)"; + arr = eval(name); + ok(arr.byteLength === 2 * typeSz, name + ".byteLength = " + arr.byteLength); + ok(arr.byteOffset === typeSz, name + ".byteOffset = " + arr.byteOffset); + ok(arr.length === 2, name + ".length = " + arr.length); + ok(arr.buffer === buf, name + ".buffer = " + arr.buffer); + view = DataView(buf); + view["set" + types[i][0]](typeSz, 10, true); + ok(arr[0] === 10, "arr[0] after DataView(buf).set" + types[i][0] + " = " + arr[0]); + arr[0] = 12; + r = view["get" + types[i][0]](typeSz, true); + ok(r === 12, "DataView(buf).get" + types[i][0] + " after arr[0] set = " + r); + Object.freeze(arr); + ok(Object.isFrozen(arr) === true, name + " not frozen"); }
arr = new Int16Array(2);
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/arraybuf.c | 15 +++++++++++++-- dlls/jscript/jscript.h | 2 ++ dlls/mshtml/tests/es5.js | 13 +++++++++++++ 3 files changed, 28 insertions(+), 2 deletions(-)
diff --git a/dlls/jscript/arraybuf.c b/dlls/jscript/arraybuf.c index 52d7db567b9..a4d11e1763f 100644 --- a/dlls/jscript/arraybuf.c +++ b/dlls/jscript/arraybuf.c @@ -177,9 +177,20 @@ static HRESULT create_arraybuf(script_ctx_t *ctx, DWORD size, ArrayBufferInstanc static HRESULT ArrayBufferConstr_isView(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { - FIXME("not implemented\n"); + BOOL ret = FALSE; + jsdisp_t *obj;
- return E_NOTIMPL; + TRACE("\n"); + + if(!r) + return S_OK; + + if(argc && is_object_instance(argv[0]) && (obj = to_jsdisp(get_object(argv[0]))) && + obj->builtin_info->class >= FIRST_VIEW_JSCLASS && obj->builtin_info->class <= LAST_VIEW_JSCLASS) + ret = TRUE; + + *r = jsval_bool(ret); + return S_OK; }
static HRESULT ArrayBufferConstr_value(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 11193992f3d..9bf25630597 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -132,6 +132,8 @@ typedef enum {
FIRST_TYPEDARRAY_JSCLASS = JSCLASS_INT8ARRAY, LAST_TYPEDARRAY_JSCLASS = JSCLASS_FLOAT64ARRAY, + FIRST_VIEW_JSCLASS = JSCLASS_DATAVIEW, + LAST_VIEW_JSCLASS = JSCLASS_FLOAT64ARRAY, } jsclass_t;
enum { NUM_TYPEDARRAY_TYPES = LAST_TYPEDARRAY_JSCLASS - FIRST_TYPEDARRAY_JSCLASS + 1 }; diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index 56566ee84de..63bc8e1700e 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -1777,6 +1777,13 @@ sync_test("ArrayBuffers & Views", function() { test_readonly(buf, "byteLength", 10); test_own_data_prop_desc(buf, "byteLength", false, false, false);
+ ok(ArrayBuffer.isView() === false, "ArrayBuffer.isView() returned true"); + ok(ArrayBuffer.isView([]) === false, "ArrayBuffer.isView([]) returned true"); + ok(ArrayBuffer.isView({}) === false, "ArrayBuffer.isView({}) returned true"); + ok(ArrayBuffer.isView(undefined) === false, "ArrayBuffer.isView(undefined) returned true"); + ok(ArrayBuffer.isView(null) === false, "ArrayBuffer.isView(null) returned true"); + ok(ArrayBuffer.isView(buf) === false, "ArrayBuffer.isView(ArrayBuffer) returned true"); + test_own_props("DataView.prototype", [ "buffer", "byteLength", "byteOffset", "getInt8", "setInt8", "getUint8", "setUint8", @@ -1897,6 +1904,9 @@ sync_test("ArrayBuffers & Views", function() { ok(view.byteLength === 10, "DataView(buf).byteLength = " + view.byteLength); ok(view.byteOffset === 0, "DataView(buf).byteOffset = " + view.byteOffset);
+ ok(ArrayBuffer.isView(DataView) === false, "ArrayBuffer.isView(DataView) returned true"); + ok(ArrayBuffer.isView(view) === true, "ArrayBuffer.isView(DataView(buf)) returned false"); + for(i = 0; i < 10; i++) { r = view.getInt8(i); ok(r === 0, "view byte " + i + " = " + r); @@ -2031,6 +2041,8 @@ sync_test("ArrayBuffers & Views", function() { ok(buf2.byteLength === 9, "buf.slice(-9).byteLength = " + buf2.byteLength); view2 = DataView(buf2, 1); ok(view2.byteLength === 8, "buf.slice(-9) view(1).byteLength = " + view2.byteLength); + ok(ArrayBuffer.isView(buf2) === false, "ArrayBuffer.isView(buf.slice(-9)) returned true"); + ok(ArrayBuffer.isView(view2) === true, "ArrayBuffer.isView(DataView(buf.slice(-9))) returned false");
r = view2.getUint32(0); ok(r === 4294967040, "buf.slice(-9) view(1).getUint32(0) returned " + r); @@ -2155,6 +2167,7 @@ sync_test("ArrayBuffers & Views", function() { test_own_data_prop_desc(arr, "length", false, false, false); test_own_data_prop_desc(arr, "buffer", false, false, false);
+ ok(ArrayBuffer.isView(arr) === true, "ArrayBuffer.isView(" + arrType + "()) returned false"); Object.freeze(arr); ok(Object.isFrozen(arr) === true, arrType + "() not frozen");
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
fill_typedarr_data_from_object will also be used for 'set'. --- dlls/jscript/arraybuf.c | 66 ++++++++++++++++++++++++++++++++++++++-- dlls/mshtml/tests/es5.js | 47 +++++++++++++++++++++++++++- 2 files changed, 110 insertions(+), 3 deletions(-)
diff --git a/dlls/jscript/arraybuf.c b/dlls/jscript/arraybuf.c index a4d11e1763f..1478b4affef 100644 --- a/dlls/jscript/arraybuf.c +++ b/dlls/jscript/arraybuf.c @@ -18,6 +18,7 @@
#include <limits.h> +#include <assert.h>
#include "jscript.h"
@@ -791,6 +792,40 @@ static HRESULT TypedArray_get_length(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_ return S_OK; }
+static HRESULT fill_typedarr_data_from_object(script_ctx_t *ctx, BYTE *data, jsdisp_t *obj, DWORD length, jsclass_t jsclass) +{ + HRESULT hres = S_OK; + jsval_t val; + UINT32 i; + + switch(jsclass) { +#define X(NAME, JSCLASS, TYPE, CONVERT, NUM_TYPE) \ + case JSCLASS: \ + for(i = 0; i < length; i++) { \ + NUM_TYPE n; \ + \ + hres = jsdisp_get_idx(obj, i, &val); \ + if(FAILED(hres)) { \ + if(hres != DISP_E_UNKNOWNNAME) \ + break; \ + val = jsval_undefined(); \ + } \ + \ + hres = CONVERT(ctx, val, &n); \ + jsval_release(val); \ + if(FAILED(hres)) \ + break; \ + *(TYPE*)&data[i * sizeof(TYPE)] = n; \ + } \ + break; + TYPEDARRAY_LIST + DEFAULT_UNREACHABLE; +#undef X + } + + return hres; +} + static HRESULT TypedArray_set(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r, jsclass_t jsclass) { @@ -998,8 +1033,35 @@ static HRESULT TypedArrayConstr_value(script_ctx_t *ctx, jsval_t vthis, WORD fla } jsdisp_addref(&buffer->dispex); }else { - FIXME("Construction from object not implemented\n"); - return E_NOTIMPL; + jsval_t val; + UINT32 len; + DWORD size; + + hres = jsdisp_propget_name(obj, L"length", &val); + if(FAILED(hres)) + return hres; + if(is_undefined(val)) + return JS_E_TYPEDARRAY_BAD_CTOR_ARG; + + hres = to_uint32(ctx, val, &len); + jsval_release(val); + if(FAILED(hres)) + return hres; + + length = len; + size = length * elem_size; + if(size < length || size > (UINT_MAX - FIELD_OFFSET(ArrayBufferInstance, buf[0]))) + return E_OUTOFMEMORY; + + hres = create_arraybuf(ctx, size, &buffer); + if(FAILED(hres)) + return hres; + + hres = fill_typedarr_data_from_object(ctx, buffer->buf, obj, length, jsclass); + if(FAILED(hres)) { + jsdisp_release(&buffer->dispex); + return hres; + } } }else if(is_number(argv[0])) { hres = to_integer(ctx, argv[0], &n); diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index 63bc8e1700e..bc6acaee70e 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -2133,6 +2133,13 @@ sync_test("ArrayBuffers & Views", function() { var n = ex.number >>> 0; ok(n === JS_E_TYPEDARRAY_BAD_CTOR_ARG, "new " + arrType + "(null) threw " + n); } + try { + eval("new " + arrType + "({})"); + ok(false, "new " + arrType + "({}) did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_TYPEDARRAY_BAD_CTOR_ARG, "new " + arrType + "({}) threw " + n); + } if(typeSz > 1) { /* test misalignment */ var a = typeSz >>> 1; @@ -2250,6 +2257,23 @@ sync_test("ArrayBuffers & Views", function() { delete arr[-1]; delete arr.foo;
+ arr2 = { length: 4 }; + arr2[0] = 1.5; + arr2[1] = '3'; + arr2[3] = 12; + var name = arrType + "(array-like object)"; + arr = eval(arrType + "(arr2)"); + ok(arr.byteLength === 4 * typeSz, name + ".byteLength = " + arr.byteLength); + ok(arr.byteOffset === 0, name + ".byteOffset = " + arr.byteOffset); + ok(arr.length === 4, name + ".length = " + arr.length); + if(isNaN(arr[2])) { + ok(arr[0] === 1.5, name + "[0] = " + arr[0]); + ok(arr[1] === 3, name + "[1] = " + arr[1]); + ok(arr[3] === 12, name + "[3] = " + arr[3]); + }else + for(var j = 0; j < 4; j++) + ok(arr[j] === [1, 3, 0, 12][j], name + "[" + j + "] = " + arr[j]); + name = arrType + "(buf, " + typeSz + ", 2)"; arr = eval(name); ok(arr.byteLength === 2 * typeSz, name + ".byteLength = " + arr.byteLength); @@ -2264,7 +2288,28 @@ sync_test("ArrayBuffers & Views", function() { ok(r === 12, "DataView(buf).get" + types[i][0] + " after arr[0] set = " + r); Object.freeze(arr); ok(Object.isFrozen(arr) === true, name + " not frozen"); - } + + arr2 = eval(arrType + "(arr)"); + ok(arr2.byteLength === arr.byteLength, name + " copy.byteLength = " + arr2.byteLength); + ok(arr2.byteOffset === 0, name + " copy.byteOffset = " + arr2.byteOffset); + ok(arr2.length === arr.length, name + " copy.length = " + arr2.length); + ok(arr2.buffer !== arr.buffer, name + " copy.buffer = " + arr2.buffer); + } + + arr = new Float32Array(3); + arr[0] = 1.125; + arr[1] = 2.25; + arr[2] = 3.375; + arr2 = new Uint16Array(arr); + ok(arr[0] === 1.125, "arr[0] = " + arr[0]); + ok(arr[1] === 2.25, "arr[1] = " + arr[1]); + ok(arr[2] === 3.375, "arr[2] = " + arr[2]); + ok(arr2[0] === 1, "arr2[0] = " + arr2[0]); + ok(arr2[1] === 2, "arr2[1] = " + arr2[1]); + ok(arr2[2] === 3, "arr2[2] = " + arr2[2]); + arr2[0] = 100; + ok(arr[0] === 1.125, "arr[0] after arr2[0] changed = " + arr[0]); + ok(arr2[0] === 100, "arr2[0] after change = " + arr2[0]);
arr = new Int16Array(2); arr[0] = 65535;
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/arraybuf.c | 44 ++++++++++++++++++++++++++++++-- dlls/jscript/error.c | 1 + dlls/jscript/jscript.h | 1 + dlls/jscript/jscript.rc | 1 + dlls/jscript/resource.h | 1 + dlls/mshtml/tests/es5.js | 55 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 101 insertions(+), 2 deletions(-)
diff --git a/dlls/jscript/arraybuf.c b/dlls/jscript/arraybuf.c index 1478b4affef..9e8cb17a7c4 100644 --- a/dlls/jscript/arraybuf.c +++ b/dlls/jscript/arraybuf.c @@ -760,6 +760,8 @@ static inline TypedArrayInstance *typedarr_this(jsval_t vthis, jsclass_t jsclass return (jsdisp && is_class(jsdisp, jsclass)) ? typedarr_from_jsdisp(jsdisp) : NULL; }
+static HRESULT create_typedarr(script_ctx_t*,jsclass_t,ArrayBufferInstance*,DWORD,DWORD,jsdisp_t**); + static HRESULT TypedArray_get_buffer(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) { TRACE("%p\n", jsthis); @@ -842,12 +844,50 @@ static HRESULT TypedArray_subarray(script_ctx_t *ctx, jsval_t vthis, WORD flags, jsval_t *r, jsclass_t jsclass) { TypedArrayInstance *typedarr; + DWORD begin = 0, end; + jsdisp_t *obj; + HRESULT hres; + double n;
- FIXME("not implemented\n"); + TRACE("\n");
if(!(typedarr = typedarr_this(vthis, jsclass))) return JS_E_NOT_TYPEDARRAY; - return E_NOTIMPL; + if(!argc) + return JS_E_TYPEDARRAY_INVALID_SUBARRAY; + if(!r) + return S_OK; + + hres = to_integer(ctx, argv[0], &n); + if(FAILED(hres)) + return hres; + end = typedarr->length; + if(n < 0.0) + n += typedarr->length; + if(n >= 0.0) + begin = n < typedarr->length ? n : typedarr->length; + + if(argc > 1 && !is_undefined(argv[1])) { + hres = to_integer(ctx, argv[1], &n); + if(FAILED(hres)) + return hres; + if(n < 0.0) + n += typedarr->length; + if(n >= 0.0) { + end = n < typedarr->length ? n : typedarr->length; + end = end < begin ? begin : end; + }else + end = begin; + } + + hres = create_typedarr(ctx, jsclass, typedarr->buffer, + typedarr->offset + begin * TypedArray_elem_size[TYPEDARRAY_INDEX(jsclass)], + end - begin, &obj); + if(FAILED(hres)) + return hres; + + *r = jsval_obj(obj); + return S_OK; }
static unsigned TypedArray_idx_length(jsdisp_t *jsdisp) diff --git a/dlls/jscript/error.c b/dlls/jscript/error.c index aa5399d02c3..ca45697e74a 100644 --- a/dlls/jscript/error.c +++ b/dlls/jscript/error.c @@ -500,6 +500,7 @@ jsdisp_t *create_builtin_error(script_ctx_t *ctx) case JS_E_PRECISION_OUT_OF_RANGE: case JS_E_INVALID_LENGTH: case JS_E_TYPEDARRAY_INVALID_OFFSLEN: + case JS_E_TYPEDARRAY_INVALID_SUBARRAY: case JS_E_DATAVIEW_INVALID_ACCESS: case JS_E_DATAVIEW_INVALID_OFFSET: constr = ctx->range_error_constr; diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index 9bf25630597..594531ff031 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -600,6 +600,7 @@ static inline HRESULT disp_call_value(script_ctx_t *ctx, IDispatch *disp, jsval_ #define JS_E_TYPEDARRAY_BAD_CTOR_ARG MAKE_JSERROR(IDS_TYPEDARRAY_BAD_CTOR_ARG) #define JS_E_NOT_TYPEDARRAY MAKE_JSERROR(IDS_NOT_TYPEDARRAY) #define JS_E_TYPEDARRAY_INVALID_OFFSLEN MAKE_JSERROR(IDS_TYPEDARRAY_INVALID_OFFSLEN) +#define JS_E_TYPEDARRAY_INVALID_SUBARRAY MAKE_JSERROR(IDS_TYPEDARRAY_INVALID_SUBARRAY) #define JS_E_NOT_DATAVIEW MAKE_JSERROR(IDS_NOT_DATAVIEW) #define JS_E_DATAVIEW_NO_ARGUMENT MAKE_JSERROR(IDS_DATAVIEW_NO_ARGUMENT) #define JS_E_DATAVIEW_INVALID_ACCESS MAKE_JSERROR(IDS_DATAVIEW_INVALID_ACCESS) diff --git a/dlls/jscript/jscript.rc b/dlls/jscript/jscript.rc index a224b6cd6f3..b3036ffe435 100644 --- a/dlls/jscript/jscript.rc +++ b/dlls/jscript/jscript.rc @@ -79,6 +79,7 @@ STRINGTABLE IDS_NOT_TYPEDARRAY "'this' is not a typed array object" IDS_TYPEDARRAY_BAD_CTOR_ARG "Typed array constructor argument is invalid" IDS_TYPEDARRAY_INVALID_OFFSLEN "Invalid offset/length when creating typed array" + IDS_TYPEDARRAY_INVALID_SUBARRAY "Invalid begin/end value in typed array subarray method" IDS_NOT_DATAVIEW "'this' is not a DataView object" IDS_DATAVIEW_NO_ARGUMENT "Required argument offset or value in DataView method is not specified" IDS_DATAVIEW_INVALID_ACCESS "DataView operation access beyond specified buffer length" diff --git a/dlls/jscript/resource.h b/dlls/jscript/resource.h index feb2593e723..91d99048858 100644 --- a/dlls/jscript/resource.h +++ b/dlls/jscript/resource.h @@ -77,6 +77,7 @@ #define IDS_TYPEDARRAY_BAD_CTOR_ARG 0x13DA #define IDS_NOT_TYPEDARRAY 0x13DB #define IDS_TYPEDARRAY_INVALID_OFFSLEN 0x13DC +#define IDS_TYPEDARRAY_INVALID_SUBARRAY 0x13DD #define IDS_NOT_DATAVIEW 0x13DF #define IDS_DATAVIEW_NO_ARGUMENT 0x13E0 #define IDS_DATAVIEW_INVALID_ACCESS 0x13E1 diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index bc6acaee70e..f416f26da15 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -35,6 +35,7 @@ var JS_E_NONWRITABLE_MODIFIED = 0x800a13d7; var JS_E_TYPEDARRAY_BAD_CTOR_ARG = 0x800a13da; var JS_E_NOT_TYPEDARRAY = 0x800a13db; var JS_E_TYPEDARRAY_INVALID_OFFSLEN = 0x800a13dc; +var JS_E_TYPEDARRAY_INVALID_SUBARRAY = 0x800a13dd; var JS_E_NOT_DATAVIEW = 0x800a13df; var JS_E_DATAVIEW_NO_ARGUMENT = 0x800a13e0; var JS_E_DATAVIEW_INVALID_ACCESS = 0x800a13e1; @@ -2294,6 +2295,60 @@ sync_test("ArrayBuffers & Views", function() { ok(arr2.byteOffset === 0, name + " copy.byteOffset = " + arr2.byteOffset); ok(arr2.length === arr.length, name + " copy.length = " + arr2.length); ok(arr2.buffer !== arr.buffer, name + " copy.buffer = " + arr2.buffer); + arr2 = arr.subarray(undefined, "1"); + ok(arr2.byteLength === typeSz, name + " subarray(undefined, '1').byteLength = " + arr2.byteLength); + ok(arr2.byteOffset === arr.byteOffset, name + " subarray(undefined, '1').byteOffset = " + arr2.byteOffset); + ok(arr2.length === 1, name + " subarray(undefined, '1').length = " + arr2.length); + ok(arr2.buffer === arr.buffer, name + " subarray(undefined, '1').buffer = " + arr2.buffer); + + name = arrType + "(10)"; + arr = eval(name); + try { + arr.subarray.call(null, 0); + ok(false, arrType + ": calling subarray with null context did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_NOT_TYPEDARRAY, arrType + ": calling subarray with null context threw " + n); + } + try { + arr.subarray.call({}, 0); + ok(false, arrType + ": calling subarray with an object context did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_NOT_TYPEDARRAY, arrType + ": calling subarray with an object context threw " + n); + } + try { + arr.subarray(); + ok(false, name + " subarray() did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_TYPEDARRAY_INVALID_SUBARRAY, name + " subarray() threw " + n); + } + arr2 = arr.subarray(4); + ok(arr2.byteLength === 6 * typeSz, name + ".subarray(4).byteLength = " + arr2.byteLength); + ok(arr2.byteOffset === 4 * typeSz, name + ".subarray(4).byteOffset = " + arr2.byteOffset); + ok(arr2.length === 6, name + ".subarray(4).length = " + arr2.length); + ok(arr2.buffer === arr.buffer, name + ".subarray(4).buffer = " + arr2.buffer); + arr2 = arr.subarray(4, 2); + ok(arr2.byteLength === 0, name + ".subarray(4, 2).byteLength = " + arr2.byteLength); + ok(arr2.byteOffset === 4 * typeSz, name + ".subarray(4, 2).byteOffset = " + arr2.byteOffset); + ok(arr2.length === 0, name + ".subarray(4, 2).length = " + arr2.length); + ok(arr2.buffer === arr.buffer, name + ".subarray(4, 2).buffer = " + arr2.buffer); + arr2 = arr.subarray(-3, 100); + ok(arr2.byteLength === 3 * typeSz, name + ".subarray(-3, 100).byteLength = " + arr2.byteLength); + ok(arr2.byteOffset === 7 * typeSz, name + ".subarray(-3, 100).byteOffset = " + arr2.byteOffset); + ok(arr2.length === 3, name + ".subarray(-3, 100).length = " + arr2.length); + ok(arr2.buffer === arr.buffer, name + ".subarray(-3, 100).buffer = " + arr2.buffer); + arr2 = arr.subarray(42, -1); + ok(arr2.byteLength === 0, name + ".subarray(42, -1).byteLength = " + arr2.byteLength); + ok(arr2.byteOffset === 10 * typeSz, name + ".subarray(42, -1).byteOffset = " + arr2.byteOffset); + ok(arr2.length === 0, name + ".subarray(42, -1).length = " + arr2.length); + ok(arr2.buffer === arr.buffer, name + ".subarray(42, -1).buffer = " + arr2.buffer); + arr2 = arr.subarray(2, -3); + ok(arr2.byteLength === 5 * typeSz, name + ".subarray(2, -3).byteLength = " + arr2.byteLength); + ok(arr2.byteOffset === 2 * typeSz, name + ".subarray(2, -3).byteOffset = " + arr2.byteOffset); + ok(arr2.length === 5, name + ".subarray(2, -3).length = " + arr2.length); + ok(arr2.buffer === arr.buffer, name + ".subarray(2, -3).buffer = " + arr2.buffer); }
arr = new Float32Array(3);
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/arraybuf.c | 75 ++++++++++++++++++++++++++++++- dlls/jscript/error.c | 1 + dlls/jscript/jscript.h | 1 + dlls/jscript/jscript.rc | 1 + dlls/jscript/resource.h | 1 + dlls/mshtml/tests/es5.js | 97 +++++++++++++++++++++++++++++++++++++++- 6 files changed, 172 insertions(+), 4 deletions(-)
diff --git a/dlls/jscript/arraybuf.c b/dlls/jscript/arraybuf.c index 9e8cb17a7c4..b11691c7053 100644 --- a/dlls/jscript/arraybuf.c +++ b/dlls/jscript/arraybuf.c @@ -831,13 +831,84 @@ static HRESULT fill_typedarr_data_from_object(script_ctx_t *ctx, BYTE *data, jsd static HRESULT TypedArray_set(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r, jsclass_t jsclass) { + const unsigned elem_size = TypedArray_elem_size[TYPEDARRAY_INDEX(jsclass)]; TypedArrayInstance *typedarr; + DWORD begin = 0, size; + BYTE *dest, *data; + IDispatch *disp; + jsdisp_t *obj; + HRESULT hres; + jsval_t val; + UINT32 len; + double n;
- FIXME("not implemented\n"); + TRACE("\n");
if(!(typedarr = typedarr_this(vthis, jsclass))) return JS_E_NOT_TYPEDARRAY; - return E_NOTIMPL; + if(!argc) + return JS_E_TYPEDARRAY_INVALID_SOURCE; + + hres = to_object(ctx, argv[0], &disp); + if(FAILED(hres)) + return JS_E_TYPEDARRAY_INVALID_SOURCE; + + if(!(obj = to_jsdisp(disp))) { + FIXME("Non-JS array object\n"); + hres = JS_E_TYPEDARRAY_INVALID_SOURCE; + goto done; + } + + hres = jsdisp_propget_name(obj, L"length", &val); + if(FAILED(hres)) + goto done; + + hres = to_uint32(ctx, val, &len); + jsval_release(val); + if(FAILED(hres)) + goto done; + + if(argc > 1) { + hres = to_integer(ctx, argv[1], &n); + if(FAILED(hres)) + goto done; + if(n < 0.0 || n > typedarr->length) { + hres = JS_E_TYPEDARRAY_INVALID_OFFSLEN; + goto done; + } + begin = n; + } + + if(len > typedarr->length - begin) { + hres = JS_E_TYPEDARRAY_INVALID_OFFSLEN; + goto done; + } + size = len * elem_size; + dest = data = &typedarr->buffer->buf[typedarr->offset + begin * elem_size]; + + /* If they overlap, make a temporary copy */ + if(obj->builtin_info->class >= FIRST_TYPEDARRAY_JSCLASS && obj->builtin_info->class <= LAST_TYPEDARRAY_JSCLASS) { + TypedArrayInstance *src_arr = typedarr_from_jsdisp(obj); + const BYTE *src = src_arr->buffer->buf + src_arr->offset; + + if(dest < src + len * TypedArray_elem_size[TYPEDARRAY_INDEX(obj->builtin_info->class)] && + dest + size > src) { + if(!(data = malloc(size))) { + hres = E_OUTOFMEMORY; + goto done; + } + } + } + + hres = fill_typedarr_data_from_object(ctx, data, obj, len, jsclass); + if(SUCCEEDED(hres) && dest != data) { + memcpy(dest, data, size); + free(data); + } + +done: + IDispatch_Release(disp); + return hres; }
static HRESULT TypedArray_subarray(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, diff --git a/dlls/jscript/error.c b/dlls/jscript/error.c index ca45697e74a..4902940517c 100644 --- a/dlls/jscript/error.c +++ b/dlls/jscript/error.c @@ -485,6 +485,7 @@ jsdisp_t *create_builtin_error(script_ctx_t *ctx) case JS_E_NONWRITABLE_MODIFIED: case JS_E_TYPEDARRAY_BAD_CTOR_ARG: case JS_E_NOT_TYPEDARRAY: + case JS_E_TYPEDARRAY_INVALID_SOURCE: case JS_E_NOT_DATAVIEW: case JS_E_DATAVIEW_NO_ARGUMENT: case JS_E_WRONG_THIS: diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index 594531ff031..8c2b4103825 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -601,6 +601,7 @@ static inline HRESULT disp_call_value(script_ctx_t *ctx, IDispatch *disp, jsval_ #define JS_E_NOT_TYPEDARRAY MAKE_JSERROR(IDS_NOT_TYPEDARRAY) #define JS_E_TYPEDARRAY_INVALID_OFFSLEN MAKE_JSERROR(IDS_TYPEDARRAY_INVALID_OFFSLEN) #define JS_E_TYPEDARRAY_INVALID_SUBARRAY MAKE_JSERROR(IDS_TYPEDARRAY_INVALID_SUBARRAY) +#define JS_E_TYPEDARRAY_INVALID_SOURCE MAKE_JSERROR(IDS_TYPEDARRAY_INVALID_SOURCE) #define JS_E_NOT_DATAVIEW MAKE_JSERROR(IDS_NOT_DATAVIEW) #define JS_E_DATAVIEW_NO_ARGUMENT MAKE_JSERROR(IDS_DATAVIEW_NO_ARGUMENT) #define JS_E_DATAVIEW_INVALID_ACCESS MAKE_JSERROR(IDS_DATAVIEW_INVALID_ACCESS) diff --git a/dlls/jscript/jscript.rc b/dlls/jscript/jscript.rc index b3036ffe435..6feec39d084 100644 --- a/dlls/jscript/jscript.rc +++ b/dlls/jscript/jscript.rc @@ -80,6 +80,7 @@ STRINGTABLE IDS_TYPEDARRAY_BAD_CTOR_ARG "Typed array constructor argument is invalid" IDS_TYPEDARRAY_INVALID_OFFSLEN "Invalid offset/length when creating typed array" IDS_TYPEDARRAY_INVALID_SUBARRAY "Invalid begin/end value in typed array subarray method" + IDS_TYPEDARRAY_INVALID_SOURCE "Invalid source in typed array set" IDS_NOT_DATAVIEW "'this' is not a DataView object" IDS_DATAVIEW_NO_ARGUMENT "Required argument offset or value in DataView method is not specified" IDS_DATAVIEW_INVALID_ACCESS "DataView operation access beyond specified buffer length" diff --git a/dlls/jscript/resource.h b/dlls/jscript/resource.h index 91d99048858..48572bd4992 100644 --- a/dlls/jscript/resource.h +++ b/dlls/jscript/resource.h @@ -78,6 +78,7 @@ #define IDS_NOT_TYPEDARRAY 0x13DB #define IDS_TYPEDARRAY_INVALID_OFFSLEN 0x13DC #define IDS_TYPEDARRAY_INVALID_SUBARRAY 0x13DD +#define IDS_TYPEDARRAY_INVALID_SOURCE 0x13DE #define IDS_NOT_DATAVIEW 0x13DF #define IDS_DATAVIEW_NO_ARGUMENT 0x13E0 #define IDS_DATAVIEW_INVALID_ACCESS 0x13E1 diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index f416f26da15..b344061ef5c 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -36,6 +36,7 @@ var JS_E_TYPEDARRAY_BAD_CTOR_ARG = 0x800a13da; var JS_E_NOT_TYPEDARRAY = 0x800a13db; var JS_E_TYPEDARRAY_INVALID_OFFSLEN = 0x800a13dc; var JS_E_TYPEDARRAY_INVALID_SUBARRAY = 0x800a13dd; +var JS_E_TYPEDARRAY_INVALID_SOURCE = 0x800a13de; var JS_E_NOT_DATAVIEW = 0x800a13df; var JS_E_DATAVIEW_NO_ARGUMENT = 0x800a13e0; var JS_E_DATAVIEW_INVALID_ACCESS = 0x800a13e1; @@ -2349,6 +2350,87 @@ sync_test("ArrayBuffers & Views", function() { ok(arr2.byteOffset === 2 * typeSz, name + ".subarray(2, -3).byteOffset = " + arr2.byteOffset); ok(arr2.length === 5, name + ".subarray(2, -3).length = " + arr2.length); ok(arr2.buffer === arr.buffer, name + ".subarray(2, -3).buffer = " + arr2.buffer); + + try { + arr.set.call(null, [1]); + ok(false, arrType + ": calling set with null context did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_NOT_TYPEDARRAY, arrType + ": calling set with null context threw " + n); + } + try { + arr.set.call({}, [1]); + ok(false, arrType + ": calling set with an object context did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_NOT_TYPEDARRAY, arrType + ": calling set with an object context threw " + n); + } + try { + arr.set(); + ok(false, name + ".set() did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_TYPEDARRAY_INVALID_SOURCE, name + ".set() threw " + n); + } + try { + arr.set(null); + ok(false, name + ".set(null) did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_TYPEDARRAY_INVALID_SOURCE, name + ".set(null) threw " + n); + } + try { + arr.set([1,2,3], 8); + ok(false, name + ".set([1,2,3], 8) did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_TYPEDARRAY_INVALID_OFFSLEN, name + ".set([1,2,3], 8) threw " + n); + } + try { + arr.set([99], -3); + ok(false, name + ".set([99], -3) did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_TYPEDARRAY_INVALID_OFFSLEN, name + ".set([99], -3) threw " + n); + } + + r = arr.set(5); + ok(r === undefined, name + ".set(5) returned " + r); + for(var j = 0; j < 10; j++) + ok(arr[j] === 0, name + ".set(5): arr[" + j + "] = " + arr[j]); + + r = arr.set({}); + ok(r === undefined, name + ".set({}) returned " + r); + for(var j = 0; j < 10; j++) + ok(arr[j] === 0, name + ".set({}): arr[" + j + "] = " + arr[j]); + + r = arr.set("12"); + ok(r === undefined, name + ".set('12') returned " + r); + for(var j = 0; j < 10; j++) + ok(arr[j] === [ 1, 2, 0, 0, 0, 0, 0, 0, 0, 0 ][j], name + ".set('12'): arr[" + j + "] = " + arr[j]); + + arr2 = { length: 2 }; + arr2[0] = 9; + arr2[1] = 7; + r = arr.set(arr2); + ok(r === undefined, name + ".set(array-like obj) returned " + r); + for(var j = 0; j < 10; j++) + ok(arr[j] === [ 9, 7, 0, 0, 0, 0, 0, 0, 0, 0 ][j], name + ".set(array-like obj): arr[" + j + "] = " + arr[j]); + + r = arr.set([12, 10, 11], 3); + ok(r === undefined, name + ".set([12, 10, 11], 3) returned " + r); + for(var j = 0; j < 10; j++) + ok(arr[j] === [ 9, 7, 0, 12, 10, 11, 0, 0, 0, 0 ][j], name + ".set([12, 10, 11], 3): arr[" + j + "] = " + arr[j]); + + r = arr.set(arr.subarray(4, 6), 5); + ok(r === undefined, name + ".set(arr.subarray(4, 2), 5) returned " + r); + for(var j = 0; j < 10; j++) + ok(arr[j] === [ 9, 7, 0, 12, 10, 10, 11, 0, 0, 0 ][j], name + ".set(arr.subarray(4, 2), 5): arr[" + j + "] = " + arr[j]); + + r = arr.set(arr.subarray(3, 7), 2); + ok(r === undefined, name + ".set(arr.subarray(3, 7), 2) returned " + r); + for(var j = 0; j < 10; j++) + ok(arr[j] === [ 9, 7, 12, 10, 10, 11, 11, 0, 0, 0 ][j], name + ".set(arr.subarray(3, 7), 2): arr[" + j + "] = " + arr[j]); }
arr = new Float32Array(3); @@ -2378,8 +2460,19 @@ sync_test("ArrayBuffers & Views", function() { ok(arr[0] == 254, "8-bit arr[0] after overflow = " + arr[0]); ok(arr[1] == 2, "8-bit arr[1] after overflow = " + arr[1]);
- arr = new Int8Array(); - arr2 = new Int32Array(); + arr = new Int8Array(12); + arr.set([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); + for(var j = 0; j < 12; j++) + ok(arr[j] === j + 1, "sequential arr[" + j + "] = " + arr[j]); + arr2 = new Int32Array(arr.buffer); + ok(arr2.buffer === arr.buffer, "arr2.buffer = " + arr2.buffer); + for(var j = 0; j < 3; j++) + ok(arr2[j] === [ 0x04030201, 0x08070605, 0x0c0b0a09 ][j], "sequential 32-bit arr[" + j + "] = " + arr2[j]); + + /* test overlap */ + arr2.set(arr.subarray(1, 4)); + for(var j = 0; j < 3; j++) + ok(arr2[j] === j + 2, "arr with overlap[" + j + "] = " + arr[j]);
/* methods are incompatible, even though thrown error is not explicit */ ok(Uint16Array.prototype.subarray !== Int32Array.prototype.subarray, "Uint16Array and Int32Array have same subarray methods");
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/arraybuf.c | 18 ++++++++++++++++++ dlls/jscript/jscript.h | 1 + dlls/jscript/object.c | 1 + dlls/mshtml/tests/es5.js | 12 ++++++++++++ 4 files changed, 32 insertions(+)
diff --git a/dlls/jscript/arraybuf.c b/dlls/jscript/arraybuf.c index b11691c7053..09a332bb0a3 100644 --- a/dlls/jscript/arraybuf.c +++ b/dlls/jscript/arraybuf.c @@ -17,6 +17,7 @@ */
+#include <math.h> #include <limits.h> #include <assert.h>
@@ -734,11 +735,28 @@ static const builtin_info_t DataViewConstr_info = { NULL };
+static HRESULT clamped_u8(script_ctx_t *ctx, jsval_t v, UINT8 *ret) +{ + HRESULT hres; + double n; + + hres = to_number(ctx, v, &n); + if(FAILED(hres)) + return hres; + + if(!isfinite(n)) + *ret = (n == INFINITY ? 255 : 0); + else + *ret = (n >= 255.0 ? 255 : n <= 0 ? 0 : lround(n)); + return S_OK; +} + #define TYPEDARRAY_LIST \ X(Int8Array, JSCLASS_INT8ARRAY, INT8, to_int32, INT) \ X(Int16Array, JSCLASS_INT16ARRAY, INT16, to_int32, INT) \ X(Int32Array, JSCLASS_INT32ARRAY, INT32, to_int32, INT) \ X(Uint8Array, JSCLASS_UINT8ARRAY, UINT8, to_int32, INT) \ +X(Uint8ClampedArray, JSCLASS_UINT8CLAMPEDARRAY, UINT8, clamped_u8, UINT8) \ X(Uint16Array, JSCLASS_UINT16ARRAY, UINT16, to_int32, INT) \ X(Uint32Array, JSCLASS_UINT32ARRAY, UINT32, to_int32, INT) \ X(Float32Array, JSCLASS_FLOAT32ARRAY, float, to_number, double) \ diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index 8c2b4103825..a955f34600b 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -122,6 +122,7 @@ typedef enum { JSCLASS_INT16ARRAY, JSCLASS_INT32ARRAY, JSCLASS_UINT8ARRAY, + JSCLASS_UINT8CLAMPEDARRAY, JSCLASS_UINT16ARRAY, JSCLASS_UINT32ARRAY, JSCLASS_FLOAT32ARRAY, diff --git a/dlls/jscript/object.c b/dlls/jscript/object.c index 1b33f481b3b..a918b55f020 100644 --- a/dlls/jscript/object.c +++ b/dlls/jscript/object.c @@ -56,6 +56,7 @@ static HRESULT Object_toString(script_ctx_t *ctx, jsval_t vthis, WORD flags, uns L"[object Int16Array]", L"[object Int32Array]", L"[object Uint8Array]", + L"[object Uint8ClampedArray]", L"[object Uint16Array]", L"[object Uint32Array]", L"[object Float32Array]", diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index b344061ef5c..fe3ee2dfc78 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -2491,6 +2491,18 @@ sync_test("ArrayBuffers & Views", function() { var n = ex.number >>> 0; ok(n === JS_E_NOT_TYPEDARRAY, "calling Uint32Array's subarray with Int32Array context threw " + n); } + + /* clamped array */ + arr = new Uint8ClampedArray(7); + arr2 = new Uint8Array(7); + arr.set ([42, -1, 999, 0.9, NaN, Infinity, -Infinity]); + arr2.set([42, -1, 999, 0.9, NaN, Infinity, -Infinity]); + for(var j = 0; j < 7; j++) { + ok(arr[j] === [42, 0, 255, 1, 0, 255, 0][j], "clamped arr[" + j + "] = " + arr[j]); + ok(arr2[j] === [42, 255, 231, 0, 0, 0, 0][j], "non-clamped arr[" + j + "] = " + arr2[j]); + } + r = Object.prototype.toString.call(arr); + ok(r === "[object Uint8ClampedArray]", "Object toString for Uint8ClampedArray = " + r); });
sync_test("builtin_context", function() {
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=143577
Your paranoid android.
=== w11pro64_amd (64 bit report) ===
mshtml: script.c:1223: Test failed: L"/index.html?dom.js:document_lastModified: lastModified too far from navigationStart: 1476"
Ok, I've made it somewhat more generic, but this was harder than expected to deal with all cases and not break something, while also a lot more complex than I would have liked. I'm also not completely happy with how it turned out, but since I've spent so much time rewriting it over and over to deal with corner cases, I figured some input would help.
More importantly, though, is that it's now mostly split from the Typed Array patch, except for the true typed array special cases needed.
Note that I did **not** add a new member in the vtbl: I couldn't find a use for it. I mean, sure, I can use it instead of checking for typed arrays but I think that's overkill since it's only two places (find_prop_name and jsdisp_define_property), and both would need two separate member functions in the vtbl in this regard. I don't think it's worth it.
Here's the main list of reasons it's complicated:
* Normal indexed props can be redefined if writable (e.g. `arguments`) to some arbitrary jsval. This means they won't even be `PROP_IDX` anymore, and so to handle them correctly (see first patch which adds a test) we need to lookup props first *before* attempting any optimization by avoiding `PROP_IDX` allocation. Which is why I had to integrate this whole thing in find_prop_name.
* Typed Arrays are special in three different ways, unlike any other indexed prop. This is actually per spec, and they're supposed to override **any** number as index, but native only does that for positive integers (not even negative), which simplifies it a bit for us. But still, they are actually special.
* The first special case is that they override any positive index, even if out of bounds. However, they **do** have bounds. The suggested override of `idx_length` to handle all indices does not work, because for instance when obtaining an ID or when testing if a prop "exists" (hasOwnProperty), they would be reported as available, even though they're **not**. A Typed Array with 5 elements does **not** have the property "10", even though you cannot redefine it. You also cannot access a prototype's "10" prop with it because, even if it doesn't exist and is out of bounds, it's still intercepted!
* The second special case is that they have differing behavior when a prop is out of bounds in some cases (deleting them, attempting to create props with them, or enumerating with prototypes, since they override them). They don't behave as normal "deleted" or "non-existing" props in those cases. This makes my patches have a bit of a kludge: I use `ret_idx` to signify this with an UINT_MAX despite returning DISP_E_UNKNOWNNAME (the latter works in most other cases directly), but it still needs special handling in those 3 special cases I mentioned (deletion, ensure_prop_name and enumeration). Sigh. I'm not too happy about this kludge, let me know if you have a better idea.
* The third special case is when defining a Typed Array prop. For out of bounds, as previously mentioned, it's simply ignored. But for props that do exist, they act as *setters*, which is completely unlike normal props. That is, defining a prop does coerce the value into the typed array's type! And defining accessors simply throws an error. This is handle in jsdisp_define_property as a special case.
Of course, all of those behaviors above are tested. And this is the best I could do for now while still passing all tests.
Actually while reworking my jscript-mshtml proxy patches I realized we could conceivably use two extra methods in the builtin info vtbl, because proxies also need special cases there (I'm talking about find_prop_name and jsdisp_define_property special cases), so they could use the same mechanism as Typed Arrays on their own jsclass. But I'll wait for feedback first to see if this is good way forward to begin with.
Note that I did **not** add a new member in the vtbl: I couldn't find a use for it. I mean, sure, I can use it instead of checking for typed arrays but I think that's overkill since it's only two places (find_prop_name and jsdisp_define_property), and both would need two separate member functions in the vtbl in this regard. I don't think it's worth it.
It depends how far do we want to go with it. Ideally we wouldn't allocate `dispex_prop_t` even for properties that are queried with `GetDispID` and it may be easier to just never allocate them at all. In that case, it would be an alternative to `find_prop_name`, not something to use in it. We usually need it to get some information about the property and in that sense, an interface similar to `jsdisp_get_own_property` should be enough for most cases. We don't need the actual `dispex_prop_t` when the object is able to provide its description and get/set operations. Enumeration may be a bit tricky, but it seems doable as well.
It could be limited to indexed properties or not, if it's helpful for MSHTML bindings too. I can't really comment on that part without seeing the code.
For DISPIDs, we could allocate a pool for 'indexed' or 'custom' properties (like we do in MSHTML) or something along those lines. To handle cases which allow overriding properties, the whole thing may indeed be a fallback for `find_prop_name`.
- The first special case is that they override any positive index, even if out of bounds. However, they **do** have bounds. The suggested override of `idx_length` to handle all indices does not work, because for instance when obtaining an ID or when testing if a prop "exists" (hasOwnProperty), they would be reported as available, even though they're **not**. A Typed Array with 5 elements does **not** have the property "10", even though you cannot redefine it. You also cannot access a prototype's "10" prop with it because, even if it doesn't exist and is out of bounds, it's still intercepted!
Yes, that's why I suggested a new function querying the property: you could just return an error from it for invalid indexes.
The third special case is when defining a Typed Array prop. For out of bounds, as previously mentioned, it's simply ignored. But for props that do exist, they act as _setters_, which is completely unlike normal props. That is, defining a prop does coerce the value into the typed array's type! And defining accessors simply throws an error. This is handle in jsdisp_define_property as a special case.
It's not that special. If you consider those properties as a non-configurable properties, then the same checks as we do in `jsdisp_define_property` would explain it. For changing the type, even the existing `idx_put` should be enough.
It depends how far do we want to go with it. Ideally we wouldn't allocate `dispex_prop_t` even for properties that are queried with `GetDispID` and it may be easier to just never allocate them at all. In that case, it would be an alternative to `find_prop_name`, not something to use in it. We usually need it to get some information about the property and in that sense, an interface similar to `jsdisp_get_own_property` should be enough for most cases. We don't need the actual `dispex_prop_t` when the object is able to provide its description and get/set operations. Enumeration may be a bit tricky, but it seems doable as well.
It could be limited to indexed properties or not, if it's helpful for MSHTML bindings too. I can't really comment on that part without seeing the code.
For DISPIDs, we could allocate a pool for 'indexed' or 'custom' properties (like we do in MSHTML) or something along those lines. To handle cases which allow overriding properties, the whole thing may indeed be a fallback for `find_prop_name`.
I see. Since we map DISPIDs to indices in the props list, maybe I should place these at the upper end (signed), so first one is i.e. `2147483648 - idx_length` and so on. Not 100% sure how it will work yet since it needs a larger revamp, we'll see.
I still don't fully understand how overriding props should work, but we only really have `arguments` so far that needs it. Maybe just special casing it in there would be enough? (handling any jsval only, because it's not configurable in the first place)
It's not that special. If you consider those properties as a non-configurable properties, then the same checks as we do in `jsdisp_define_property` would explain it. For changing the type, even the existing `idx_put` should be enough.
I tried and it doesn't work. The current checks basically replace a writable non-configurable jsval with another jsval (it can be anything, not related to the original; i.e. if original was a number, it can now be a string). It never calls prop_put or anything of the sort. For typed arrays we need strings to be coerced into the underlying type in this case, as if prop_put was called on it, instead of define_property.
I see. Since we map DISPIDs to indices in the props list, maybe I should place these at the upper end (signed), so first one is i.e. `2147483648 - idx_length` and so on. Not 100% sure how it will work yet since it needs a larger revamp, we'll see.
I guess that something like `UINT_MAX/2` for the first indexed property would be nicer to work with.
I still don't fully understand how overriding props should work, but we only really have `arguments` so far that needs it. Maybe just special casing it in there would be enough? (handling any jsval only, because it's not configurable in the first place)
I'm not sure I follow, can we structure the code to use `Arguments_idx_put` in this case?
I tried and it doesn't work. The current checks basically replace a writable non-configurable jsval with another jsval (it can be anything, not related to the original; i.e. if original was a number, it can now be a string). It never calls prop_put or anything of the sort. For typed arrays we need strings to be coerced into the underlying type in this case, as if prop_put was called on it, instead of define_property.
I meant it for check and error handling. For the actual value setting, it sounds to me like the could use `idx_put` instead of `copy_jsval` when it's dealing with builtin indexed props.
Created !5444 for the indexed props changes, I will update this in whatever shape once that's in.
This merge request was closed by Jacek Caban.
MSHTML bindings will need something very similar for MSHTML collection objects. It probably makes sense to extend the existing mechanism to support that and then use it for typed arrays too.