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 only time we still create a PROP_IDX on them is when the DISPID is requested or when enumerating (same reason).
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). delete always returns false, even for out of bounds indices, despite them not being props (hasOwnProperty returns false).
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 | 355 +++++++++++++++++++++++++++++++++++++++ dlls/jscript/dispex.c | 99 ++++++++++- 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, 671 insertions(+), 8 deletions(-)
diff --git a/dlls/jscript/arraybuf.c b/dlls/jscript/arraybuf.c index 62dfe614d65..8d152db801a 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,311 @@ 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); \ + \ + if(idx >= typedarr->length) \ + *r = jsval_undefined(); \ + else \ + *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)); \ + \ + if(idx >= typedarr->length) \ + return S_OK; \ + \ + 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 +1038,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 +1110,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 57882bf499e..0f001739559 100644 --- a/dlls/jscript/dispex.c +++ b/dlls/jscript/dispex.c @@ -17,6 +17,7 @@ */
#include <assert.h> +#include <limits.h>
#include "jscript.h" #include "engine.h" @@ -108,6 +109,29 @@ static inline BOOL is_function_prop(dispex_prop_t *prop) return ret; }
+static inline BOOL override_idx(jsdisp_t *This, const WCHAR *name, unsigned *ret_idx) +{ + /* Typed Arrays override every positive index */ + if(This->builtin_info->class >= FIRST_TYPEDARRAY_JSCLASS && This->builtin_info->class <= LAST_TYPEDARRAY_JSCLASS) { + const WCHAR *ptr; + unsigned idx = 0; + + for(ptr = name; is_digit(*ptr) && idx <= (UINT_MAX-9 / 10); ptr++) + idx = idx*10 + (*ptr-'0'); + if(!*ptr) { + *ret_idx = idx; + return TRUE; + }else { + while(is_digit(*ptr)) ptr++; + if(!*ptr) { + *ret_idx = UINT_MAX; + return TRUE; + } + } + } + return FALSE; +} + static DWORD get_flags(jsdisp_t *This, dispex_prop_t *prop) { if(prop->type == PROP_PROTREF) { @@ -632,6 +656,7 @@ static HRESULT fill_props(jsdisp_t *obj) static HRESULT fill_protrefs(jsdisp_t *This) { dispex_prop_t *iter, *prop; + unsigned idx; HRESULT hres;
hres = fill_props(This); @@ -646,6 +671,8 @@ static HRESULT fill_protrefs(jsdisp_t *This) return hres;
for(iter = This->prototype->props; iter < This->prototype->props+This->prototype->prop_cnt; iter++) { + if(override_idx(This, iter->name, &idx)) + continue; hres = find_prop_name(This, iter->hash, iter->name, FALSE, &prop); if(FAILED(hres)) return hres; @@ -2057,6 +2084,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;
@@ -2065,6 +2093,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);
+ if(override_idx(This, bstrName, &idx)) + return S_OK; + hres = find_prop_name(This, string_hash(bstrName), bstrName, grfdex & fdexNameCaseInsensitive, &prop); if(FAILED(hres)) return hres; @@ -2335,9 +2366,17 @@ jsdisp_t *iface_to_jsdisp(IDispatch *iface) HRESULT jsdisp_get_id(jsdisp_t *jsdisp, const WCHAR *name, DWORD flags, DISPID *id) { dispex_prop_t *prop; + unsigned idx; HRESULT hres;
- if(jsdisp->extensible && (flags & fdexNameEnsure)) + if(override_idx(jsdisp, name, &idx)) { + if(idx >= jsdisp->builtin_info->idx_length(jsdisp)) { + *id = DISPID_UNKNOWN; + return DISP_E_UNKNOWNNAME; + } + hres = find_prop_name(jsdisp, string_hash(name), name, FALSE, &prop); + } + else if(jsdisp->extensible && (flags & fdexNameEnsure)) hres = ensure_prop_name(jsdisp, name, PROPF_ENUMERABLE | PROPF_CONFIGURABLE | PROPF_WRITABLE, flags & fdexNameCaseInsensitive, &prop); else @@ -2638,8 +2677,12 @@ 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(override_idx(obj, name, &idx)) + return obj->builtin_info->idx_put(obj, idx, val); + if(obj->extensible) hres = ensure_prop_name(obj, name, flags, FALSE, &prop); else @@ -2742,8 +2785,12 @@ 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;
+ if(override_idx(obj, name, &idx)) + return obj->builtin_info->idx_get(obj, idx, val); + hres = find_prop_name_prot(obj, string_hash(name), name, FALSE, &prop); if(FAILED(hres)) return hres; @@ -2762,6 +2809,9 @@ HRESULT jsdisp_get_idx(jsdisp_t *obj, DWORD idx, jsval_t *r) dispex_prop_t *prop; HRESULT hres;
+ if(obj->builtin_info->class >= FIRST_TYPEDARRAY_JSCLASS && obj->builtin_info->class <= LAST_TYPEDARRAY_JSCLASS) + return obj->builtin_info->idx_get(obj, idx, r); + swprintf(name, ARRAY_SIZE(name), L"%d", idx);
hres = find_prop_name_prot(obj, string_hash(name), name, FALSE, &prop); @@ -2819,6 +2869,9 @@ HRESULT jsdisp_delete_idx(jsdisp_t *obj, DWORD idx) BOOL b; HRESULT hres;
+ if(obj->builtin_info->class >= FIRST_TYPEDARRAY_JSCLASS && obj->builtin_info->class <= LAST_TYPEDARRAY_JSCLASS) + return S_OK; + swprintf(buf, ARRAY_SIZE(buf), L"%d", idx);
hres = find_prop_name(obj, string_hash(buf), buf, FALSE, &prop); @@ -2910,6 +2963,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) { @@ -2917,12 +2971,17 @@ 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 = delete_prop(prop, ret); - }else { - *ret = TRUE; + if(override_idx(jsdisp, ptr, &idx)) { + *ret = FALSE; hres = S_OK; + }else { + hres = find_prop_name(jsdisp, string_hash(ptr), ptr, FALSE, &prop); + if(prop) { + hres = delete_prop(prop, ret); + }else { + *ret = TRUE; + hres = S_OK; + } }
jsdisp_release(jsdisp); @@ -2962,8 +3021,25 @@ 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;
+ if(override_idx(obj, name, &idx)) { + if(idx >= obj->builtin_info->idx_length(obj)) + return DISP_E_UNKNOWNNAME; + + 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 | PROPF_WRITABLE; + desc->mask = PROPF_ENUMERABLE | PROPF_WRITABLE | PROPF_CONFIGURABLE; + desc->explicit_value = TRUE; + return S_OK; + } + hres = find_prop_name(obj, string_hash(name), name, FALSE, &prop); if(FAILED(hres)) return hres; @@ -3006,8 +3082,19 @@ 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;
+ if(override_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 obj->builtin_info->idx_put(obj, idx, desc->value); + if(desc->explicit_getter || desc->explicit_setter) + return throw_error(obj->ctx, JS_E_NONCONFIGURABLE_REDEFINED, name); + return obj->builtin_info->idx_put(obj, idx, jsval_undefined()); + } + hres = find_prop_name(obj, string_hash(name), name, FALSE, &prop); if(FAILED(hres)) return hres; 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 353d0b0b5c0..3d0cdfb72c5 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; @@ -1688,7 +1691,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 ], @@ -2031,6 +2034,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 8d152db801a..494b9e651f6 100644 --- a/dlls/jscript/arraybuf.c +++ b/dlls/jscript/arraybuf.c @@ -966,8 +966,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 3d0cdfb72c5..da3fd12b227 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -2068,6 +2068,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"); @@ -2075,6 +2076,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"); @@ -2089,6 +2111,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); @@ -2184,6 +2226,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 494b9e651f6..a3c845c045b 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 da3fd12b227..155199f28ba 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -1767,6 +1767,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", @@ -1887,6 +1894,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); @@ -2021,6 +2031,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); @@ -2145,6 +2157,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 a3c845c045b..8cb972091b0 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) { @@ -1006,8 +1041,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 155199f28ba..bf0fef5118d 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -2123,6 +2123,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; @@ -2240,6 +2247,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); @@ -2254,7 +2278,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 8cb972091b0..f6468e75534 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 bf0fef5118d..830f8569fc3 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; @@ -2284,6 +2285,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 f6468e75534..0729817c571 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 830f8569fc3..3d199136e8e 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; @@ -2339,6 +2340,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); @@ -2368,8 +2450,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 0729817c571..019c44a60a8 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 3d199136e8e..1213eb48a4f 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -2481,6 +2481,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=143117
Your paranoid android.
=== w1064v1507 (32 bit report) ===
mshtml: script.c:1223: Test failed: L"index.html?xhr.js: async_xhr2.readyState = 2" script.c:1223: Test failed: L"index.html?xhr.js: async_xhr2.readyState = 2"
The special-casing of index access in generic dispatch functions could use a better interface, ideally be more general. Skipping the actual property allocation for indexed access seems desirable for other objects too. With that, accepting all possible indexes should be as simple as returning maximum possible length from `_get_length` implementation. Adding a function to `builtin_info_t` that would return a property descriptor for indexed properties (on per-class basis, possibly without relying on `dispex_prop_t`) should allow proper handling of defining/querying properties.
On Mon Feb 26 14:51:50 2024 +0000, Jacek Caban wrote:
The special-casing of index access in generic dispatch functions could use a better interface, ideally be more general. Skipping the actual property allocation for indexed access seems desirable for other objects too. With that, accepting all possible indexes should be as simple as returning maximum possible length from `_get_length` implementation. Adding a function to `builtin_info_t` that would return a property descriptor for indexed properties (on per-class basis, possibly without relying on `dispex_prop_t`) should allow proper handling of defining/querying properties.
Yeah, now that I think about it, I should make it more generic for sure. Might also be helpful later, we'll see.