-- v2: jscript: Implement ArrayBuffer.prototype.slice. jscript: Implement DataView getters. jscript: Implement DataView setters. jscript: Add initial implementation of DataView. jscript: Add initial implementation of ArrayBuffer.
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/Makefile.in | 1 + dlls/jscript/arraybuf.c | 189 +++++++++++++++++++++++++++++++++++++++ dlls/jscript/global.c | 4 + dlls/jscript/jscript.h | 5 +- dlls/jscript/object.c | 1 + dlls/mshtml/tests/es5.js | 48 ++++++++++ 6 files changed, 247 insertions(+), 1 deletion(-) create mode 100644 dlls/jscript/arraybuf.c
diff --git a/dlls/jscript/Makefile.in b/dlls/jscript/Makefile.in index c37b0dd69f3..86d9035800e 100644 --- a/dlls/jscript/Makefile.in +++ b/dlls/jscript/Makefile.in @@ -4,6 +4,7 @@ IMPORTS = oleaut32 ole32 user32 advapi32 SOURCES = \ activex.c \ array.c \ + arraybuf.c \ bool.c \ cc_parser.y \ compile.c \ diff --git a/dlls/jscript/arraybuf.c b/dlls/jscript/arraybuf.c new file mode 100644 index 00000000000..3afce4c46fe --- /dev/null +++ b/dlls/jscript/arraybuf.c @@ -0,0 +1,189 @@ +/* + * Copyright 2024 Gabriel Ivăncescu for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + + +#include <limits.h> + +#include "jscript.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(jscript); + +typedef struct { + jsdisp_t dispex; + DWORD size; + DECLSPEC_ALIGN(sizeof(double)) BYTE buf[]; +} ArrayBufferInstance; + +static inline ArrayBufferInstance *arraybuf_from_jsdisp(jsdisp_t *jsdisp) +{ + return CONTAINING_RECORD(jsdisp, ArrayBufferInstance, dispex); +} + +static HRESULT ArrayBuffer_get_byteLength(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) +{ + TRACE("%p\n", jsthis); + + *r = jsval_number(arraybuf_from_jsdisp(jsthis)->size); + return S_OK; +} + +static HRESULT ArrayBuffer_slice(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, + jsval_t *r) +{ + FIXME("not implemented\n"); + + return E_NOTIMPL; +} + +static const builtin_prop_t ArrayBuffer_props[] = { + {L"byteLength", NULL, 0, ArrayBuffer_get_byteLength}, + {L"slice", ArrayBuffer_slice, PROPF_METHOD|2}, +}; + +static const builtin_info_t ArrayBuffer_info = { + JSCLASS_ARRAYBUFFER, + NULL, + ARRAY_SIZE(ArrayBuffer_props), + ArrayBuffer_props, + NULL, + NULL +}; + +static const builtin_prop_t ArrayBufferInst_props[] = { + {L"byteLength", NULL, 0, ArrayBuffer_get_byteLength}, +}; + +static const builtin_info_t ArrayBufferInst_info = { + JSCLASS_ARRAYBUFFER, + NULL, + ARRAY_SIZE(ArrayBufferInst_props), + ArrayBufferInst_props, + NULL, + NULL +}; + +static HRESULT create_arraybuf(script_ctx_t *ctx, DWORD size, ArrayBufferInstance **ret) +{ + ArrayBufferInstance *arraybuf; + HRESULT hres; + + if(!(arraybuf = calloc(1, FIELD_OFFSET(ArrayBufferInstance, buf[size])))) + return E_OUTOFMEMORY; + + hres = init_dispex_from_constr(&arraybuf->dispex, ctx, &ArrayBufferInst_info, ctx->arraybuf_constr); + if(FAILED(hres)) { + free(arraybuf); + return hres; + } + arraybuf->size = size; + + *ret = arraybuf; + return S_OK; +} + +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"); + + return E_NOTIMPL; +} + +static HRESULT ArrayBufferConstr_value(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, + jsval_t *r) +{ + ArrayBufferInstance *arraybuf; + DWORD size = 0; + HRESULT hres; + + TRACE("\n"); + + switch(flags) { + case DISPATCH_METHOD: + case DISPATCH_CONSTRUCT: { + if(argc) { + double n; + hres = to_integer(ctx, argv[0], &n); + if(FAILED(hres)) + return hres; + if(n < 0.0) + return JS_E_INVALID_LENGTH; + if(n > (UINT_MAX - FIELD_OFFSET(ArrayBufferInstance, buf[0]))) + return E_OUTOFMEMORY; + size = n; + } + + if(r) { + hres = create_arraybuf(ctx, size, &arraybuf); + if(FAILED(hres)) + return hres; + *r = jsval_obj(&arraybuf->dispex); + } + break; + } + default: + FIXME("unimplemented flags: %x\n", flags); + return E_NOTIMPL; + } + + return S_OK; +} + +static const builtin_prop_t ArrayBufferConstr_props[] = { + {L"isView", ArrayBufferConstr_isView, PROPF_METHOD|1}, +}; + +static const builtin_info_t ArrayBufferConstr_info = { + JSCLASS_FUNCTION, + Function_value, + ARRAY_SIZE(ArrayBufferConstr_props), + ArrayBufferConstr_props, + NULL, + NULL +}; + +HRESULT init_arraybuf_constructors(script_ctx_t *ctx) +{ + ArrayBufferInstance *arraybuf; + HRESULT hres; + + if(ctx->version < SCRIPTLANGUAGEVERSION_ES5) + return S_OK; + + if(!(arraybuf = calloc(1, FIELD_OFFSET(ArrayBufferInstance, buf[0])))) + return E_OUTOFMEMORY; + + hres = init_dispex(&arraybuf->dispex, ctx, &ArrayBuffer_info, ctx->object_prototype); + if(FAILED(hres)) { + free(arraybuf); + return hres; + } + + hres = create_builtin_constructor(ctx, ArrayBufferConstr_value, L"ArrayBuffer", &ArrayBufferConstr_info, + PROPF_CONSTR|1, &arraybuf->dispex, &ctx->arraybuf_constr); + jsdisp_release(&arraybuf->dispex); + if(FAILED(hres)) + return hres; + + hres = jsdisp_define_data_property(ctx->global, L"ArrayBuffer", PROPF_CONFIGURABLE | PROPF_WRITABLE, + jsval_obj(ctx->arraybuf_constr)); + + return hres; +} diff --git a/dlls/jscript/global.c b/dlls/jscript/global.c index 997b2542a9e..cc108b84e4b 100644 --- a/dlls/jscript/global.c +++ b/dlls/jscript/global.c @@ -1148,5 +1148,9 @@ HRESULT init_global(script_ctx_t *ctx) if(FAILED(hres)) return hres;
+ hres = init_arraybuf_constructors(ctx); + if(FAILED(hres)) + return hres; + return init_set_constructor(ctx); } diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index b62d577a45b..dd7aa4dddcc 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -116,6 +116,7 @@ typedef enum { JSCLASS_ARGUMENTS, JSCLASS_VBARRAY, JSCLASS_JSON, + JSCLASS_ARRAYBUFFER, JSCLASS_MAP, JSCLASS_SET, JSCLASS_WEAKMAP, @@ -435,11 +436,12 @@ struct _script_ctx_t { jsdisp_t *regexp_constr; jsdisp_t *string_constr; jsdisp_t *vbarray_constr; + jsdisp_t *arraybuf_constr; jsdisp_t *map_prototype; jsdisp_t *set_prototype; jsdisp_t *weakmap_prototype; }; - jsdisp_t *global_objects[23]; + jsdisp_t *global_objects[24]; }; }; C_ASSERT(RTL_SIZEOF_THROUGH_FIELD(script_ctx_t, weakmap_prototype) == RTL_SIZEOF_THROUGH_FIELD(script_ctx_t, global_objects)); @@ -464,6 +466,7 @@ HRESULT init_global(script_ctx_t*); HRESULT init_function_constr(script_ctx_t*,jsdisp_t*); HRESULT create_object_prototype(script_ctx_t*,jsdisp_t**); HRESULT init_set_constructor(script_ctx_t*); +HRESULT init_arraybuf_constructors(script_ctx_t*);
HRESULT create_activex_constr(script_ctx_t*,jsdisp_t**); HRESULT create_array_constr(script_ctx_t*,jsdisp_t*,jsdisp_t**); diff --git a/dlls/jscript/object.c b/dlls/jscript/object.c index 65b25cab239..cba35927378 100644 --- a/dlls/jscript/object.c +++ b/dlls/jscript/object.c @@ -50,6 +50,7 @@ static HRESULT Object_toString(script_ctx_t *ctx, jsval_t vthis, WORD flags, uns L"[object Object]", L"[object Object]", L"[object Object]", + L"[object ArrayBuffer]", L"[object Object]", L"[object Object]", L"[object Object]" diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index b6ce7be3c54..b9d9b0d03d6 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -28,6 +28,7 @@ var JS_E_VBARRAY_EXPECTED = 0x800a1395; var JS_E_ENUMERATOR_EXPECTED = 0x800a1397; var JS_E_REGEXP_EXPECTED = 0x800a1398; var JS_E_UNEXPECTED_QUANTIFIER = 0x800a139a; +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; @@ -1681,6 +1682,53 @@ sync_test("RegExp", function() { } });
+sync_test("ArrayBuffers & Views", function() { + var r, buf; + + function test_own_props(obj_name, props) { + var obj = eval(obj_name); + for(var i = 0; i < props.length; i++) + ok(Object.prototype.hasOwnProperty.call(obj, props[i]), props[i] + " not a property of " + obj_name); + } + + function test_readonly(obj, prop, val) { + var name = Object.getPrototypeOf(obj).constructor.toString(); + name = name.substring(9, name.indexOf("(", 9)) + ".prototype." + prop; + obj[prop] = val + 42; + ok(obj[prop] === val, name + " not read-only"); + } + + test_own_props("ArrayBuffer", [ "isView" ]); + test_own_props("ArrayBuffer.prototype", [ "byteLength", "slice" ]); + test_own_data_prop_desc(ArrayBuffer.prototype, "byteLength", false, false, false); + + r = Object.prototype.toString.call(new ArrayBuffer()); + ok(r === "[object ArrayBuffer]", "Object toString(new ArrayBuffer()) = " + r); + r = ArrayBuffer.length; + ok(r === 1, "ArrayBuffer.length = " + r); + r = ArrayBuffer.isView.length; + ok(r === 1, "ArrayBuffer.isView.length = " + r); + r = ArrayBuffer.prototype.slice.length; + ok(r === 2, "ArrayBuffer.prototype.slice.length = " + r); + + try { + new ArrayBuffer(-1); + ok(false, "new ArrayBuffer(-1) did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_INVALID_LENGTH, "new ArrayBuffer(-1) threw " + n); + } + + buf = new ArrayBuffer(); + ok(buf.byteLength === 0, "ArrayBuffer().byteLength = " + buf.byteLength); + buf = new ArrayBuffer(13.1); + ok(buf.byteLength === 13, "ArrayBuffer(13).byteLength = " + buf.byteLength); + buf = ArrayBuffer("10"); + ok(buf.byteLength === 10, "ArrayBuffer(10).byteLength = " + buf.byteLength); + test_readonly(buf, "byteLength", 10); + test_own_data_prop_desc(buf, "byteLength", false, false, false); +}); + sync_test("builtin_context", function() { var nullDisp = external.nullDisp; var tests = [
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/arraybuf.c | 235 +++++++++++++++++++++++++++++++++++++++ dlls/jscript/error.c | 3 + dlls/jscript/jscript.h | 7 +- dlls/jscript/jscript.rc | 3 + dlls/jscript/object.c | 1 + dlls/jscript/resource.h | 3 + dlls/mshtml/tests/es5.js | 127 ++++++++++++++++++++- 7 files changed, 377 insertions(+), 2 deletions(-)
diff --git a/dlls/jscript/arraybuf.c b/dlls/jscript/arraybuf.c index 3afce4c46fe..b8604f1acf6 100644 --- a/dlls/jscript/arraybuf.c +++ b/dlls/jscript/arraybuf.c @@ -31,11 +31,30 @@ typedef struct { DECLSPEC_ALIGN(sizeof(double)) BYTE buf[]; } ArrayBufferInstance;
+typedef struct { + jsdisp_t dispex; + + ArrayBufferInstance *buffer; + DWORD offset; + DWORD size; +} DataViewInstance; + static inline ArrayBufferInstance *arraybuf_from_jsdisp(jsdisp_t *jsdisp) { return CONTAINING_RECORD(jsdisp, ArrayBufferInstance, dispex); }
+static inline DataViewInstance *dataview_from_jsdisp(jsdisp_t *jsdisp) +{ + return CONTAINING_RECORD(jsdisp, DataViewInstance, dispex); +} + +static inline ArrayBufferInstance *arraybuf_this(jsval_t vthis) +{ + jsdisp_t *jsdisp = is_object_instance(vthis) ? to_jsdisp(get_object(vthis)) : NULL; + return (jsdisp && is_class(jsdisp, JSCLASS_ARRAYBUFFER)) ? arraybuf_from_jsdisp(jsdisp) : NULL; +} + static HRESULT ArrayBuffer_get_byteLength(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) { TRACE("%p\n", jsthis); @@ -159,10 +178,180 @@ static const builtin_info_t ArrayBufferConstr_info = { NULL };
+static inline DataViewInstance *dataview_this(jsval_t vthis) +{ + jsdisp_t *jsdisp = is_object_instance(vthis) ? to_jsdisp(get_object(vthis)) : NULL; + return (jsdisp && is_class(jsdisp, JSCLASS_DATAVIEW)) ? dataview_from_jsdisp(jsdisp) : NULL; +} + +static HRESULT DataView_get_buffer(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, + jsval_t *r) +{ + DataViewInstance *view; + + TRACE("\n"); + + if(!(view = dataview_this(vthis))) + return JS_E_NOT_DATAVIEW; + if(r) *r = jsval_obj(jsdisp_addref(&view->buffer->dispex)); + return S_OK; +} + +static HRESULT DataView_get_byteLength(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, + jsval_t *r) +{ + DataViewInstance *view; + + TRACE("\n"); + + if(!(view = dataview_this(vthis))) + return JS_E_NOT_DATAVIEW; + if(r) *r = jsval_number(view->size); + return S_OK; +} + +static HRESULT DataView_get_byteOffset(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, + jsval_t *r) +{ + DataViewInstance *view; + + TRACE("\n"); + + if(!(view = dataview_this(vthis))) + return JS_E_NOT_DATAVIEW; + if(r) *r = jsval_number(view->offset); + return S_OK; +} + +static void DataView_destructor(jsdisp_t *dispex) +{ + DataViewInstance *view = dataview_from_jsdisp(dispex); + if(view->buffer) + jsdisp_release(&view->buffer->dispex); + free(view); +} + +static HRESULT DataView_gc_traverse(struct gc_ctx *gc_ctx, enum gc_traverse_op op, jsdisp_t *dispex) +{ + DataViewInstance *view = dataview_from_jsdisp(dispex); + return gc_process_linked_obj(gc_ctx, op, dispex, &view->buffer->dispex, (void**)&view->buffer); +} + +static const builtin_info_t DataView_info = { + JSCLASS_DATAVIEW, + NULL, + 0, + NULL, + DataView_destructor, + NULL, + NULL, + NULL, + NULL, + DataView_gc_traverse +}; + +static const builtin_info_t DataViewInst_info = { + JSCLASS_DATAVIEW, + NULL, + 0, + NULL, + DataView_destructor, + NULL, + NULL, + NULL, + NULL, + DataView_gc_traverse +}; + +static HRESULT DataViewConstr_value(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, + jsval_t *r) +{ + ArrayBufferInstance *arraybuf; + DataViewInstance *view; + DWORD offset = 0, size; + HRESULT hres; + + TRACE("\n"); + + switch(flags) { + case DISPATCH_METHOD: + case DISPATCH_CONSTRUCT: { + if(!argc || !(arraybuf = arraybuf_this(argv[0]))) + return JS_E_DATAVIEW_NO_ARGUMENT; + size = arraybuf->size; + + if(argc > 1) { + double offs, len, maxsize = size; + hres = to_integer(ctx, argv[1], &offs); + if(FAILED(hres)) + return hres; + if(offs < 0.0 || offs > maxsize) + return JS_E_DATAVIEW_INVALID_OFFSET; + offset = offs; + + if(argc > 2 && !is_undefined(argv[2])) { + hres = to_integer(ctx, argv[2], &len); + if(FAILED(hres)) + return hres; + if(len < 0.0 || offs+len > maxsize) + return JS_E_DATAVIEW_INVALID_OFFSET; + size = len; + }else + size -= offset; + } + + if(!r) + return S_OK; + + if(!(view = calloc(1, sizeof(DataViewInstance)))) + return E_OUTOFMEMORY; + + hres = init_dispex_from_constr(&view->dispex, ctx, &DataViewInst_info, ctx->dataview_constr); + if(FAILED(hres)) { + free(view); + return hres; + } + + jsdisp_addref(&arraybuf->dispex); + view->buffer = arraybuf; + view->offset = offset; + view->size = size; + + *r = jsval_obj(&view->dispex); + break; + } + default: + FIXME("unimplemented flags: %x\n", flags); + return E_NOTIMPL; + } + + return S_OK; +} + +static const builtin_info_t DataViewConstr_info = { + JSCLASS_FUNCTION, + Function_value, + 0, + NULL, + NULL, + NULL +}; + HRESULT init_arraybuf_constructors(script_ctx_t *ctx) { + static const struct { + const WCHAR *name; + builtin_invoke_t get; + } DataView_getters[] = { + { L"buffer", DataView_get_buffer }, + { L"byteLength", DataView_get_byteLength }, + { L"byteOffset", DataView_get_byteOffset }, + }; ArrayBufferInstance *arraybuf; + DataViewInstance *view; + property_desc_t desc; HRESULT hres; + unsigned i;
if(ctx->version < SCRIPTLANGUAGEVERSION_ES5) return S_OK; @@ -184,6 +373,52 @@ HRESULT init_arraybuf_constructors(script_ctx_t *ctx)
hres = jsdisp_define_data_property(ctx->global, L"ArrayBuffer", PROPF_CONFIGURABLE | PROPF_WRITABLE, jsval_obj(ctx->arraybuf_constr)); + if(FAILED(hres)) + return hres; + + if(!(view = calloc(1, sizeof(DataViewInstance)))) + return E_OUTOFMEMORY; + + hres = create_arraybuf(ctx, 0, &view->buffer); + if(FAILED(hres)) { + free(view); + return hres; + } + + hres = init_dispex(&view->dispex, ctx, &DataView_info, ctx->object_prototype); + if(FAILED(hres)) { + jsdisp_release(&view->buffer->dispex); + free(view); + return hres; + } + + desc.flags = PROPF_CONFIGURABLE; + desc.mask = PROPF_CONFIGURABLE | PROPF_ENUMERABLE; + desc.explicit_getter = desc.explicit_setter = TRUE; + desc.explicit_value = FALSE; + desc.setter = NULL; + + /* FIXME: If we find we need builtin accessors in other places, we should consider a more generic solution */ + for(i = 0; i < ARRAY_SIZE(DataView_getters); i++) { + hres = create_builtin_function(ctx, DataView_getters[i].get, NULL, NULL, PROPF_METHOD, NULL, &desc.getter); + if(SUCCEEDED(hres)) { + hres = jsdisp_define_property(&view->dispex, DataView_getters[i].name, &desc); + jsdisp_release(desc.getter); + } + if(FAILED(hres)) { + jsdisp_release(&view->dispex); + return hres; + } + } + + hres = create_builtin_constructor(ctx, DataViewConstr_value, L"DataView", &DataViewConstr_info, + PROPF_CONSTR|1, &view->dispex, &ctx->dataview_constr); + jsdisp_release(&view->dispex); + if(FAILED(hres)) + return hres; + + hres = jsdisp_define_data_property(ctx->global, L"DataView", PROPF_CONFIGURABLE | PROPF_WRITABLE, + jsval_obj(ctx->dataview_constr));
return hres; } diff --git a/dlls/jscript/error.c b/dlls/jscript/error.c index f6eda62b88d..1c84ff70f87 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_NOT_DATAVIEW: + case JS_E_DATAVIEW_NO_ARGUMENT: case JS_E_WRONG_THIS: case JS_E_KEY_NOT_OBJECT: case JS_E_PROP_DESC_MISMATCH: @@ -494,6 +496,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_DATAVIEW_INVALID_OFFSET: constr = ctx->range_error_constr; break;
diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index dd7aa4dddcc..d792b34b3d1 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -117,6 +117,7 @@ typedef enum { JSCLASS_VBARRAY, JSCLASS_JSON, JSCLASS_ARRAYBUFFER, + JSCLASS_DATAVIEW, JSCLASS_MAP, JSCLASS_SET, JSCLASS_WEAKMAP, @@ -437,11 +438,12 @@ struct _script_ctx_t { jsdisp_t *string_constr; jsdisp_t *vbarray_constr; jsdisp_t *arraybuf_constr; + jsdisp_t *dataview_constr; jsdisp_t *map_prototype; jsdisp_t *set_prototype; jsdisp_t *weakmap_prototype; }; - jsdisp_t *global_objects[24]; + jsdisp_t *global_objects[25]; }; }; C_ASSERT(RTL_SIZEOF_THROUGH_FIELD(script_ctx_t, weakmap_prototype) == RTL_SIZEOF_THROUGH_FIELD(script_ctx_t, global_objects)); @@ -579,6 +581,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_NOT_DATAVIEW MAKE_JSERROR(IDS_NOT_DATAVIEW) +#define JS_E_DATAVIEW_NO_ARGUMENT MAKE_JSERROR(IDS_DATAVIEW_NO_ARGUMENT) +#define JS_E_DATAVIEW_INVALID_OFFSET MAKE_JSERROR(IDS_DATAVIEW_INVALID_OFFSET) #define JS_E_WRONG_THIS MAKE_JSERROR(IDS_WRONG_THIS) #define JS_E_KEY_NOT_OBJECT MAKE_JSERROR(IDS_KEY_NOT_OBJECT) #define JS_E_PROP_DESC_MISMATCH MAKE_JSERROR(IDS_PROP_DESC_MISMATCH) diff --git a/dlls/jscript/jscript.rc b/dlls/jscript/jscript.rc index e5289a550a3..31e2d18cf57 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_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_OFFSET "DataView constructor argument offset is invalid" IDS_WRONG_THIS "'this' is not a | object" IDS_KEY_NOT_OBJECT "'key' is not an object" IDS_PROP_DESC_MISMATCH "Property cannot have both accessors and a value" diff --git a/dlls/jscript/object.c b/dlls/jscript/object.c index cba35927378..16ecc3dea3c 100644 --- a/dlls/jscript/object.c +++ b/dlls/jscript/object.c @@ -53,6 +53,7 @@ static HRESULT Object_toString(script_ctx_t *ctx, jsval_t vthis, WORD flags, uns L"[object ArrayBuffer]", L"[object Object]", L"[object Object]", + L"[object Object]", L"[object Object]" };
diff --git a/dlls/jscript/resource.h b/dlls/jscript/resource.h index ad771b67475..6d92e05a837 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_NOT_DATAVIEW 0x13DF +#define IDS_DATAVIEW_NO_ARGUMENT 0x13E0 +#define IDS_DATAVIEW_INVALID_OFFSET 0x13E2 #define IDS_WRONG_THIS 0x13FC #define IDS_KEY_NOT_OBJECT 0x13FD /* FIXME: This is not compatible with native, but we would diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index b9d9b0d03d6..dc15ed3af8a 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_NOT_DATAVIEW = 0x800a13df; +var JS_E_DATAVIEW_NO_ARGUMENT = 0x800a13e0; +var JS_E_DATAVIEW_INVALID_OFFSET = 0x800a13e2; var JS_E_WRONG_THIS = 0x800a13fc;
var tests = []; @@ -1683,7 +1686,7 @@ sync_test("RegExp", function() { });
sync_test("ArrayBuffers & Views", function() { - var r, buf; + var i, r, buf, view, view2, arr;
function test_own_props(obj_name, props) { var obj = eval(obj_name); @@ -1691,6 +1694,12 @@ sync_test("ArrayBuffers & Views", function() { ok(Object.prototype.hasOwnProperty.call(obj, props[i]), props[i] + " not a property of " + obj_name); }
+ function test_not_own_props(obj_name, props) { + var obj = eval(obj_name); + for(var i = 0; i < props.length; i++) + ok(!Object.prototype.hasOwnProperty.call(obj, props[i]), props[i] + " is a property of " + obj_name); + } + function test_readonly(obj, prop, val) { var name = Object.getPrototypeOf(obj).constructor.toString(); name = name.substring(9, name.indexOf("(", 9)) + ".prototype." + prop; @@ -1727,6 +1736,122 @@ sync_test("ArrayBuffers & Views", function() { ok(buf.byteLength === 10, "ArrayBuffer(10).byteLength = " + buf.byteLength); test_readonly(buf, "byteLength", 10); test_own_data_prop_desc(buf, "byteLength", false, false, false); + + test_own_props("DataView.prototype", [ + "buffer", "byteLength", "byteOffset" + ]); + + r = Object.prototype.toString.call(new DataView(buf)); + ok(r === "[object Object]", "Object toString(new DataView(buf)) = " + r); + r = DataView.length; + ok(r === 1, "DataView.length = " + r); + + /* DataView.prototype has actual accessors, but others don't */ + arr = [ "buffer", "byteLength", "byteOffset" ]; + for(i = 0; i < arr.length; i++) { + var prop = arr[i], desc = Object.getOwnPropertyDescriptor(DataView.prototype, prop); + ok(!("value" in desc), "DataView: value is in desc"); + ok(!("writable" in desc), "DataView: writable is in desc"); + ok(desc.enumerable === false, "DataView: desc.enumerable = " + desc.enumerable); + ok(desc.configurable === true, "DataView: desc.configurable = " + desc.configurable); + ok(Object.getPrototypeOf(desc.get) === Function.prototype, "DataView: desc.get not a function: " + desc.get); + ok("set" in desc, "DataView: set is not in desc"); + ok(desc.set === undefined, "DataView: desc.set not undefined: " + desc.set); + try { + desc.get.call(null); + ok(false, "DataView: calling " + prop + " getter with null did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_NOT_DATAVIEW, "DataView: calling " + prop + " getter with null threw " + n); + } + try { + desc.get.call({}); + ok(false, "DataView: calling " + prop + " getter with an object did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_NOT_DATAVIEW, "DataView: calling " + prop + " getter with an object threw " + n); + } + try { + desc.get.call(DataView); + ok(false, "DataView: calling " + prop + " getter with DataView constructor did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_NOT_DATAVIEW, "DataView: calling " + prop + " getter with DataView constructor threw " + n); + } + try { + desc.get.call(new ArrayBuffer()); + ok(false, "DataView: calling " + prop + " getter with ArrayBuffer did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_NOT_DATAVIEW, "DataView: calling " + prop + " getter with ArrayBuffer threw " + n); + } + r = desc.get.call(DataView.prototype); + if(prop === "buffer") + ok(Object.getPrototypeOf(r) === ArrayBuffer.prototype, "DataView: calling " + prop + " getter with DataView.prototype returned " + r); + else + ok(r === 0, "DataView: calling " + prop + " getter with DataView.prototype returned " + r); + } + + try { + new DataView(); + ok(false, "new DataView() did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_DATAVIEW_NO_ARGUMENT, "new DataView() threw " + n); + } + try { + new DataView(ArrayBuffer); + ok(false, "new DataView(ArrayBuffer) did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_DATAVIEW_NO_ARGUMENT, "new DataView(ArrayBuffer) threw " + n); + } + try { + new DataView(buf, -1); + ok(false, "new DataView(buf, -1) did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_DATAVIEW_INVALID_OFFSET, "new DataView(buf, -1) threw " + n); + } + try { + new DataView(buf, 11); + ok(false, "new DataView(buf, 11) did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_DATAVIEW_INVALID_OFFSET, "new DataView(buf, 11) threw " + n); + } + try { + new DataView(buf, 9, 2); + ok(false, "new DataView(buf, 9, 2) did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_DATAVIEW_INVALID_OFFSET, "new DataView(buf, 9, 2) threw " + n); + } + + view = new DataView(buf, 9, 1); + ok(view.buffer === buf, "DataView(buf, 9, 1).buffer = " + view.buffer); + ok(view.byteLength === 1, "DataView(buf, 9, 1).byteLength = " + view.byteLength); + ok(view.byteOffset === 9, "DataView(buf, 9, 1).byteOffset = " + view.byteOffset); + test_readonly(view, "byteLength", 1); + test_readonly(view, "byteOffset", 9); + test_not_own_props("view", [ "buffer", "byteLength", "byteOffset" ]); + + view = new DataView(buf, 10); + ok(view.buffer === buf, "DataView(buf, 10).buffer = " + view.buffer); + ok(view.byteLength === 0, "DataView(buf, 10).byteLength = " + view.byteLength); + ok(view.byteOffset === 10, "DataView(buf, 10).byteOffset = " + view.byteOffset); + view = new DataView(buf, 1, 7); + ok(view.buffer === buf, "DataView(buf, 1, 7).buffer = " + view.buffer); + ok(view.byteLength === 7, "DataView(buf, 1, 7).byteLength = " + view.byteLength); + ok(view.byteOffset === 1, "DataView(buf, 1, 7).byteOffset = " + view.byteOffset); + view2 = new DataView(buf, 6); + ok(view2.buffer === buf, "DataView(buf, 6).buffer = " + view2.buffer); + ok(view2.byteLength === 4, "DataView(buf, 6).byteLength = " + view2.byteLength); + ok(view2.byteOffset === 6, "DataView(buf, 6).byteOffset = " + view2.byteOffset); + view = DataView(buf); + ok(view.buffer === buf, "DataView(buf).buffer = " + view.buffer); + ok(view.byteLength === 10, "DataView(buf).byteLength = " + view.byteLength); + ok(view.byteOffset === 0, "DataView(buf).byteOffset = " + view.byteOffset); });
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 | 173 ++++++++++++++++++++++++++++++++++++++- 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 | 82 ++++++++++++++++++- 6 files changed, 256 insertions(+), 3 deletions(-)
diff --git a/dlls/jscript/arraybuf.c b/dlls/jscript/arraybuf.c index b8604f1acf6..66d41509ff5 100644 --- a/dlls/jscript/arraybuf.c +++ b/dlls/jscript/arraybuf.c @@ -223,6 +223,175 @@ static HRESULT DataView_get_byteOffset(script_ctx_t *ctx, jsval_t vthis, WORD fl return S_OK; }
+static inline void copy_type_data(void *dst, const void *src, unsigned type_size, BOOL little_endian) +{ + const BYTE *in = src; + BYTE *out = dst; + unsigned i; + + if(little_endian) + memcpy(out, in, type_size); + else + for(i = 0; i < type_size; i++) + out[i] = in[type_size - i - 1]; +} + +static HRESULT set_data(script_ctx_t *ctx, jsval_t vthis, unsigned argc, jsval_t *argv, unsigned type_size, const void *val) +{ + BOOL little_endian = FALSE; + DataViewInstance *view; + HRESULT hres; + DWORD offset; + BYTE *data; + double n; + + if(!(view = dataview_this(vthis))) + return JS_E_NOT_DATAVIEW; + if(is_undefined(argv[0]) || is_undefined(argv[1])) + return JS_E_DATAVIEW_NO_ARGUMENT; + + hres = to_integer(ctx, argv[0], &n); + if(FAILED(hres)) + return hres; + + if(n < 0.0 || n + type_size > view->size) + return JS_E_DATAVIEW_INVALID_ACCESS; + + offset = n; + data = &view->buffer->buf[view->offset + offset]; + + if(type_size == 1) { + data[0] = *(const BYTE*)val; + return S_OK; + } + + if(argc > 2) { + hres = to_boolean(argv[2], &little_endian); + if(FAILED(hres)) + return hres; + } + + copy_type_data(data, val, type_size, little_endian); + return S_OK; +} + +static HRESULT DataView_setFloat32(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) +{ + HRESULT hres; + double n; + float v; + + TRACE("\n"); + + if(argc < 2) + return JS_E_DATAVIEW_NO_ARGUMENT; + hres = to_number(ctx, argv[1], &n); + if(FAILED(hres)) + return hres; + v = n; /* FIXME: don't assume rounding mode is round-to-nearest ties-to-even */ + + hres = set_data(ctx, vthis, argc, argv, sizeof(v), &v); + if(FAILED(hres)) + return hres; + if(r) *r = jsval_undefined(); + return S_OK; +} + +static HRESULT DataView_setFloat64(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) +{ + HRESULT hres; + double v; + + TRACE("\n"); + + if(argc < 2) + return JS_E_DATAVIEW_NO_ARGUMENT; + hres = to_number(ctx, argv[1], &v); + if(FAILED(hres)) + return hres; + + hres = set_data(ctx, vthis, argc, argv, sizeof(v), &v); + if(FAILED(hres)) + return hres; + if(r) *r = jsval_undefined(); + return S_OK; +} + +static HRESULT DataView_setInt8(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) +{ + HRESULT hres; + INT32 n; + INT8 v; + + TRACE("\n"); + + if(argc < 2) + return JS_E_DATAVIEW_NO_ARGUMENT; + hres = to_int32(ctx, argv[1], &n); + if(FAILED(hres)) + return hres; + v = n; + + hres = set_data(ctx, vthis, argc, argv, sizeof(v), &v); + if(FAILED(hres)) + return hres; + if(r) *r = jsval_undefined(); + return S_OK; +} + +static HRESULT DataView_setInt16(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) +{ + HRESULT hres; + INT32 n; + INT16 v; + + TRACE("\n"); + + if(argc < 2) + return JS_E_DATAVIEW_NO_ARGUMENT; + hres = to_int32(ctx, argv[1], &n); + if(FAILED(hres)) + return hres; + v = n; + + hres = set_data(ctx, vthis, argc, argv, sizeof(v), &v); + if(FAILED(hres)) + return hres; + if(r) *r = jsval_undefined(); + return S_OK; +} + +static HRESULT DataView_setInt32(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) +{ + HRESULT hres; + INT32 v; + + TRACE("\n"); + + if(argc < 2) + return JS_E_DATAVIEW_NO_ARGUMENT; + hres = to_int32(ctx, argv[1], &v); + if(FAILED(hres)) + return hres; + + hres = set_data(ctx, vthis, argc, argv, sizeof(v), &v); + if(FAILED(hres)) + return hres; + if(r) *r = jsval_undefined(); + return S_OK; +} + +static const builtin_prop_t DataView_props[] = { + {L"setFloat32", DataView_setFloat32, PROPF_METHOD|1}, + {L"setFloat64", DataView_setFloat64, PROPF_METHOD|1}, + {L"setInt16", DataView_setInt16, PROPF_METHOD|1}, + {L"setInt32", DataView_setInt32, PROPF_METHOD|1}, + {L"setInt8", DataView_setInt8, PROPF_METHOD|1}, + {L"setUint16", DataView_setInt16, PROPF_METHOD|1}, + {L"setUint32", DataView_setInt32, PROPF_METHOD|1}, + {L"setUint8", DataView_setInt8, PROPF_METHOD|1}, +}; + static void DataView_destructor(jsdisp_t *dispex) { DataViewInstance *view = dataview_from_jsdisp(dispex); @@ -240,8 +409,8 @@ static HRESULT DataView_gc_traverse(struct gc_ctx *gc_ctx, enum gc_traverse_op o static const builtin_info_t DataView_info = { JSCLASS_DATAVIEW, NULL, - 0, - NULL, + ARRAY_SIZE(DataView_props), + DataView_props, DataView_destructor, NULL, NULL, diff --git a/dlls/jscript/error.c b/dlls/jscript/error.c index 1c84ff70f87..60d090ec76e 100644 --- a/dlls/jscript/error.c +++ b/dlls/jscript/error.c @@ -496,6 +496,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_DATAVIEW_INVALID_ACCESS: case JS_E_DATAVIEW_INVALID_OFFSET: constr = ctx->range_error_constr; break; diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index d792b34b3d1..07e0c4693be 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -583,6 +583,7 @@ static inline HRESULT disp_call_value(script_ctx_t *ctx, IDispatch *disp, jsval_ #define JS_E_NONWRITABLE_MODIFIED MAKE_JSERROR(IDS_NONWRITABLE_MODIFIED) #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) #define JS_E_DATAVIEW_INVALID_OFFSET MAKE_JSERROR(IDS_DATAVIEW_INVALID_OFFSET) #define JS_E_WRONG_THIS MAKE_JSERROR(IDS_WRONG_THIS) #define JS_E_KEY_NOT_OBJECT MAKE_JSERROR(IDS_KEY_NOT_OBJECT) diff --git a/dlls/jscript/jscript.rc b/dlls/jscript/jscript.rc index 31e2d18cf57..1cfc9040f51 100644 --- a/dlls/jscript/jscript.rc +++ b/dlls/jscript/jscript.rc @@ -78,6 +78,7 @@ STRINGTABLE IDS_NONWRITABLE_MODIFIED "Cannot modify non-writable property '|'" 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" IDS_DATAVIEW_INVALID_OFFSET "DataView constructor argument offset is invalid" IDS_WRONG_THIS "'this' is not a | object" IDS_KEY_NOT_OBJECT "'key' is not an object" diff --git a/dlls/jscript/resource.h b/dlls/jscript/resource.h index 6d92e05a837..e2b60b98bd5 100644 --- a/dlls/jscript/resource.h +++ b/dlls/jscript/resource.h @@ -76,6 +76,7 @@ #define IDS_NONWRITABLE_MODIFIED 0x13D7 #define IDS_NOT_DATAVIEW 0x13DF #define IDS_DATAVIEW_NO_ARGUMENT 0x13E0 +#define IDS_DATAVIEW_INVALID_ACCESS 0x13E1 #define IDS_DATAVIEW_INVALID_OFFSET 0x13E2 #define IDS_WRONG_THIS 0x13FC #define IDS_KEY_NOT_OBJECT 0x13FD diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index dc15ed3af8a..1801df9663c 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -34,6 +34,7 @@ var JS_E_NONCONFIGURABLE_REDEFINED = 0x800a13d6; var JS_E_NONWRITABLE_MODIFIED = 0x800a13d7; var JS_E_NOT_DATAVIEW = 0x800a13df; var JS_E_DATAVIEW_NO_ARGUMENT = 0x800a13e0; +var JS_E_DATAVIEW_INVALID_ACCESS = 0x800a13e1; var JS_E_DATAVIEW_INVALID_OFFSET = 0x800a13e2; var JS_E_WRONG_THIS = 0x800a13fc;
@@ -1688,6 +1689,17 @@ sync_test("RegExp", function() { sync_test("ArrayBuffers & Views", function() { var i, r, buf, view, view2, arr;
+ var types = [ + [ "Int8", 1 ], + [ "Uint8", 1 ], + [ "Int16", 2 ], + [ "Uint16", 2 ], + [ "Int32", 4 ], + [ "Uint32", 4 ], + [ "Float32", 4 ], + [ "Float64", 8 ] + ]; + function test_own_props(obj_name, props) { var obj = eval(obj_name); for(var i = 0; i < props.length; i++) @@ -1738,7 +1750,11 @@ sync_test("ArrayBuffers & Views", function() { test_own_data_prop_desc(buf, "byteLength", false, false, false);
test_own_props("DataView.prototype", [ - "buffer", "byteLength", "byteOffset" + "buffer", "byteLength", "byteOffset", + "setInt8", "setUint8", + "setInt16", "setUint16", + "setInt32", "setUint32", + "setFloat32", "setFloat64" ]);
r = Object.prototype.toString.call(new DataView(buf)); @@ -1852,6 +1868,70 @@ sync_test("ArrayBuffers & Views", function() { ok(view.buffer === buf, "DataView(buf).buffer = " + view.buffer); ok(view.byteLength === 10, "DataView(buf).byteLength = " + view.byteLength); ok(view.byteOffset === 0, "DataView(buf).byteOffset = " + view.byteOffset); + + for(i = 0; i < types.length; i++) { + var method = "set" + types[i][0], offs = 11 - types[i][1]; + r = DataView.prototype[method].length; + ok(r === 1, "DataView.prototype." + method + ".length = " + r); + try { + view[method](); + ok(false, "view." + method + "() did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_DATAVIEW_NO_ARGUMENT, "view." + method + "() threw " + n); + } + try { + view[method](0); + ok(false, "view." + method + "(0) did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_DATAVIEW_NO_ARGUMENT, "view." + method + "(0) threw " + n); + } + try { + view[method](-1, 0); + ok(false, "view." + method + "(-1, 0) did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_DATAVIEW_INVALID_ACCESS, "view." + method + "(-1, 0) threw " + n); + } + try { + view[method](offs, 0); + ok(false, "view." + method + "(" + offs + ", 0) did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_DATAVIEW_INVALID_ACCESS, "view." + method + "(" + offs + ", 0) threw " + n); + } + try { + view[method].call(null, 0, 0); + ok(false, "view." + method + "(0, 0) with null context did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_NOT_DATAVIEW, "view." + method + "(0, 0) with null context threw " + n); + } + try { + view[method].call({}, 0, 0); + ok(false, "view." + method + "(0, 0) with an object context did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_NOT_DATAVIEW, "view." + method + "(0, 0) with an object context threw " + n); + } + } + + r = view.setInt8(1, -257); + ok(r === undefined, "view.setInt8(1, -1) returned " + r); + r = view.setUint32(2, "12345678", true); + ok(r === undefined, "view.setUint32(2, '12345678', true) returned " + r); + r = view.setInt16(3, 65535, true); + ok(r === undefined, "view.setInt16(3, 65535) returned " + r); + r = view.setUint32(0, -2, true); + ok(r === undefined, "view.setUint32(0, -2) returned " + r); + r = view.setFloat32(6, 1234.5, true); + ok(r === undefined, "view.setFloat32(6, 1234.5) returned " + r); + + /* setters differing only in signedness have identical behavior, but they're not the same methods */ + ok(view.setInt8 !== view.setUint8, "setInt8 and setUint8 are the same method"); + ok(view.setInt16 !== view.setUint16, "setInt16 and setUint16 are the same method"); + ok(view.setInt32 !== view.setUint32, "setInt32 and setUint32 are the same method"); });
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 | 159 +++++++++++++++++++++++++++++++++++++++ dlls/mshtml/tests/es5.js | 65 ++++++++++++++-- 2 files changed, 219 insertions(+), 5 deletions(-)
diff --git a/dlls/jscript/arraybuf.c b/dlls/jscript/arraybuf.c index 66d41509ff5..56bda2a73fd 100644 --- a/dlls/jscript/arraybuf.c +++ b/dlls/jscript/arraybuf.c @@ -236,6 +236,45 @@ static inline void copy_type_data(void *dst, const void *src, unsigned type_size out[i] = in[type_size - i - 1]; }
+static HRESULT get_data(script_ctx_t *ctx, jsval_t vthis, unsigned argc, jsval_t *argv, unsigned type_size, void *ret) +{ + BOOL little_endian = FALSE; + DataViewInstance *view; + HRESULT hres; + DWORD offset; + BYTE *data; + double n; + + if(!(view = dataview_this(vthis))) + return JS_E_NOT_DATAVIEW; + if(!argc || is_undefined(argv[0])) + return JS_E_DATAVIEW_NO_ARGUMENT; + + hres = to_integer(ctx, argv[0], &n); + if(FAILED(hres)) + return hres; + + if(n < 0.0 || n + type_size > view->size) + return JS_E_DATAVIEW_INVALID_ACCESS; + + offset = n; + data = &view->buffer->buf[view->offset + offset]; + + if(type_size == 1) { + *(BYTE*)ret = data[0]; + return S_OK; + } + + if(argc > 1) { + hres = to_boolean(argv[1], &little_endian); + if(FAILED(hres)) + return hres; + } + + copy_type_data(ret, data, type_size, little_endian); + return S_OK; +} + static HRESULT set_data(script_ctx_t *ctx, jsval_t vthis, unsigned argc, jsval_t *argv, unsigned type_size, const void *val) { BOOL little_endian = FALSE; @@ -275,6 +314,118 @@ static HRESULT set_data(script_ctx_t *ctx, jsval_t vthis, unsigned argc, jsval_t return S_OK; }
+static HRESULT DataView_getFloat32(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) +{ + HRESULT hres; + float v; + + TRACE("\n"); + + hres = get_data(ctx, vthis, argc, argv, sizeof(v), &v); + if(FAILED(hres)) + return hres; + if(r) *r = jsval_number(v); + return S_OK; +} + +static HRESULT DataView_getFloat64(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) +{ + HRESULT hres; + double v; + + TRACE("\n"); + + hres = get_data(ctx, vthis, argc, argv, sizeof(v), &v); + if(FAILED(hres)) + return hres; + if(r) *r = jsval_number(v); + return S_OK; +} + +static HRESULT DataView_getInt8(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) +{ + HRESULT hres; + INT8 v; + + TRACE("\n"); + + hres = get_data(ctx, vthis, argc, argv, sizeof(v), &v); + if(FAILED(hres)) + return hres; + if(r) *r = jsval_number(v); + return S_OK; +} + +static HRESULT DataView_getInt16(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) +{ + HRESULT hres; + INT16 v; + + TRACE("\n"); + + hres = get_data(ctx, vthis, argc, argv, sizeof(v), &v); + if(FAILED(hres)) + return hres; + if(r) *r = jsval_number(v); + return S_OK; +} + +static HRESULT DataView_getInt32(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) +{ + HRESULT hres; + INT32 v; + + TRACE("\n"); + + hres = get_data(ctx, vthis, argc, argv, sizeof(v), &v); + if(FAILED(hres)) + return hres; + if(r) *r = jsval_number(v); + return S_OK; +} + +static HRESULT DataView_getUint8(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) +{ + HRESULT hres; + UINT8 v; + + TRACE("\n"); + + hres = get_data(ctx, vthis, argc, argv, sizeof(v), &v); + if(FAILED(hres)) + return hres; + if(r) *r = jsval_number(v); + return S_OK; +} + +static HRESULT DataView_getUint16(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) +{ + HRESULT hres; + UINT16 v; + + TRACE("\n"); + + hres = get_data(ctx, vthis, argc, argv, sizeof(v), &v); + if(FAILED(hres)) + return hres; + if(r) *r = jsval_number(v); + return S_OK; +} + +static HRESULT DataView_getUint32(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) +{ + HRESULT hres; + UINT32 v; + + TRACE("\n"); + + hres = get_data(ctx, vthis, argc, argv, sizeof(v), &v); + if(FAILED(hres)) + return hres; + if(r) *r = jsval_number(v); + return S_OK; +} + static HRESULT DataView_setFloat32(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { HRESULT hres; @@ -382,6 +533,14 @@ static HRESULT DataView_setInt32(script_ctx_t *ctx, jsval_t vthis, WORD flags, u }
static const builtin_prop_t DataView_props[] = { + {L"getFloat32", DataView_getFloat32, PROPF_METHOD|1}, + {L"getFloat64", DataView_getFloat64, PROPF_METHOD|1}, + {L"getInt16", DataView_getInt16, PROPF_METHOD|1}, + {L"getInt32", DataView_getInt32, PROPF_METHOD|1}, + {L"getInt8", DataView_getInt8, PROPF_METHOD|1}, + {L"getUint16", DataView_getUint16, PROPF_METHOD|1}, + {L"getUint32", DataView_getUint32, PROPF_METHOD|1}, + {L"getUint8", DataView_getUint8, PROPF_METHOD|1}, {L"setFloat32", DataView_setFloat32, PROPF_METHOD|1}, {L"setFloat64", DataView_setFloat64, PROPF_METHOD|1}, {L"setInt16", DataView_setInt16, PROPF_METHOD|1}, diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index 1801df9663c..a18fea6476a 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -1751,10 +1751,10 @@ sync_test("ArrayBuffers & Views", function() {
test_own_props("DataView.prototype", [ "buffer", "byteLength", "byteOffset", - "setInt8", "setUint8", - "setInt16", "setUint16", - "setInt32", "setUint32", - "setFloat32", "setFloat64" + "getInt8", "setInt8", "getUint8", "setUint8", + "getInt16", "setInt16", "getUint16", "setUint16", + "getInt32", "setInt32", "getUint32", "setUint32", + "getFloat32", "setFloat32", "getFloat64", "setFloat64" ]);
r = Object.prototype.toString.call(new DataView(buf)); @@ -1869,8 +1869,51 @@ sync_test("ArrayBuffers & Views", function() { ok(view.byteLength === 10, "DataView(buf).byteLength = " + view.byteLength); ok(view.byteOffset === 0, "DataView(buf).byteOffset = " + view.byteOffset);
+ for(i = 0; i < 10; i++) { + r = view.getInt8(i); + ok(r === 0, "view byte " + i + " = " + r); + } + for(i = 0; i < types.length; i++) { - var method = "set" + types[i][0], offs = 11 - types[i][1]; + var method = "get" + types[i][0], offs = 11 - types[i][1]; + r = DataView.prototype[method].length; + ok(r === 1, "DataView.prototype." + method + ".length = " + r); + try { + view[method](); + ok(false, "view." + method + "() did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_DATAVIEW_NO_ARGUMENT, "view." + method + "() threw " + n); + } + try { + view[method](-1); + ok(false, "view." + method + "(-1) did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_DATAVIEW_INVALID_ACCESS, "view." + method + "(-1) threw " + n); + } + try { + view[method](offs); + ok(false, "view." + method + "(" + offs + ") did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_DATAVIEW_INVALID_ACCESS, "view." + method + "(" + offs + ") threw " + n); + } + try { + view[method].call(null, 0); + ok(false, "view." + method + "(0) with null context did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_NOT_DATAVIEW, "view." + method + "(0) with null context threw " + n); + } + try { + view[method].call({}, 0); + ok(false, "view." + method + "(0) with an object context did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_NOT_DATAVIEW, "view." + method + "(0) with an object context threw " + n); + } + method = "set" + types[i][0]; r = DataView.prototype[method].length; ok(r === 1, "DataView.prototype." + method + ".length = " + r); try { @@ -1919,14 +1962,26 @@ sync_test("ArrayBuffers & Views", function() {
r = view.setInt8(1, -257); ok(r === undefined, "view.setInt8(1, -1) returned " + r); + r = view.getUint16(0); + ok(r === 255, "view.getUint16(0) returned " + r); + r = view.getUint16(0, true); + ok(r === 65280, "view.getUint16(0, true) returned " + r); r = view.setUint32(2, "12345678", true); ok(r === undefined, "view.setUint32(2, '12345678', true) returned " + r); + r = view.getInt32(1); + ok(r === -11640388, "view.getInt32(1) returned " + r); r = view.setInt16(3, 65535, true); ok(r === undefined, "view.setInt16(3, 65535) returned " + r); + r = view.getUint16(3); + ok(r === 65535, "view.getUint16(3) returned " + r); r = view.setUint32(0, -2, true); ok(r === undefined, "view.setUint32(0, -2) returned " + r); + r = view.getInt32(0, true); + ok(r === -2, "view.getInt32(0) returned " + r); r = view.setFloat32(6, 1234.5, true); ok(r === undefined, "view.setFloat32(6, 1234.5) returned " + r); + r = view2.getFloat32(0, true); + ok(r === 1234.5, "view2.getFloat32(0) returned " + r);
/* setters differing only in signedness have identical behavior, but they're not the same methods */ ok(view.setInt8 !== view.setUint8, "setInt8 and setUint8 are the same method");
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/arraybuf.c | 48 ++++++++++++++++++++++++++++++++++++++-- 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 | 46 +++++++++++++++++++++++++++++++++++++- 6 files changed, 95 insertions(+), 3 deletions(-)
diff --git a/dlls/jscript/arraybuf.c b/dlls/jscript/arraybuf.c index 56bda2a73fd..62dfe614d65 100644 --- a/dlls/jscript/arraybuf.c +++ b/dlls/jscript/arraybuf.c @@ -55,6 +55,8 @@ static inline ArrayBufferInstance *arraybuf_this(jsval_t vthis) return (jsdisp && is_class(jsdisp, JSCLASS_ARRAYBUFFER)) ? arraybuf_from_jsdisp(jsdisp) : NULL; }
+static HRESULT create_arraybuf(script_ctx_t*,DWORD,ArrayBufferInstance**); + static HRESULT ArrayBuffer_get_byteLength(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) { TRACE("%p\n", jsthis); @@ -66,9 +68,51 @@ static HRESULT ArrayBuffer_get_byteLength(script_ctx_t *ctx, jsdisp_t *jsthis, j static HRESULT ArrayBuffer_slice(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { - FIXME("not implemented\n"); + ArrayBufferInstance *arraybuf, *ret; + DWORD begin = 0, end, size; + HRESULT hres; + double n;
- return E_NOTIMPL; + TRACE("\n"); + + if(!(arraybuf = arraybuf_this(vthis))) + return JS_E_ARRAYBUFFER_EXPECTED; + end = arraybuf->size; + if(!r) + return S_OK; + + if(argc) { + hres = to_integer(ctx, argv[0], &n); + if(FAILED(hres)) + return hres; + if(n < 0.0) + n += arraybuf->size; + if(n >= 0.0 && n < arraybuf->size) { + begin = n; + if(argc > 1 && !is_undefined(argv[1])) { + hres = to_integer(ctx, argv[1], &n); + if(FAILED(hres)) + return hres; + if(n < 0.0) + n += arraybuf->size; + if(n >= 0.0) { + end = n < arraybuf->size ? n : arraybuf->size; + end = end < begin ? begin : end; + }else + end = begin; + } + }else + end = 0; + } + + size = end - begin; + hres = create_arraybuf(ctx, size, &ret); + if(FAILED(hres)) + return hres; + memcpy(ret->buf, arraybuf->buf + begin, size); + + *r = jsval_obj(&ret->dispex); + return S_OK; }
static const builtin_prop_t ArrayBuffer_props[] = { diff --git a/dlls/jscript/error.c b/dlls/jscript/error.c index 60d090ec76e..81e3a3a9739 100644 --- a/dlls/jscript/error.c +++ b/dlls/jscript/error.c @@ -487,6 +487,7 @@ jsdisp_t *create_builtin_error(script_ctx_t *ctx) case JS_E_DATAVIEW_NO_ARGUMENT: case JS_E_WRONG_THIS: case JS_E_KEY_NOT_OBJECT: + case JS_E_ARRAYBUFFER_EXPECTED: case JS_E_PROP_DESC_MISMATCH: case JS_E_INVALID_WRITABLE_PROP_DESC: constr = ctx->type_error_constr; diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index 07e0c4693be..2453cba4939 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -587,6 +587,7 @@ static inline HRESULT disp_call_value(script_ctx_t *ctx, IDispatch *disp, jsval_ #define JS_E_DATAVIEW_INVALID_OFFSET MAKE_JSERROR(IDS_DATAVIEW_INVALID_OFFSET) #define JS_E_WRONG_THIS MAKE_JSERROR(IDS_WRONG_THIS) #define JS_E_KEY_NOT_OBJECT MAKE_JSERROR(IDS_KEY_NOT_OBJECT) +#define JS_E_ARRAYBUFFER_EXPECTED MAKE_JSERROR(IDS_ARRAYBUFFER_EXPECTED) #define JS_E_PROP_DESC_MISMATCH MAKE_JSERROR(IDS_PROP_DESC_MISMATCH) #define JS_E_INVALID_WRITABLE_PROP_DESC MAKE_JSERROR(IDS_INVALID_WRITABLE_PROP_DESC)
diff --git a/dlls/jscript/jscript.rc b/dlls/jscript/jscript.rc index 1cfc9040f51..cbdfde507b7 100644 --- a/dlls/jscript/jscript.rc +++ b/dlls/jscript/jscript.rc @@ -82,6 +82,7 @@ STRINGTABLE IDS_DATAVIEW_INVALID_OFFSET "DataView constructor argument offset is invalid" IDS_WRONG_THIS "'this' is not a | object" IDS_KEY_NOT_OBJECT "'key' is not an object" + IDS_ARRAYBUFFER_EXPECTED "ArrayBuffer object expected" IDS_PROP_DESC_MISMATCH "Property cannot have both accessors and a value"
IDS_COMPILATION_ERROR "Microsoft JScript compilation error" diff --git a/dlls/jscript/resource.h b/dlls/jscript/resource.h index e2b60b98bd5..801e940b95e 100644 --- a/dlls/jscript/resource.h +++ b/dlls/jscript/resource.h @@ -80,6 +80,7 @@ #define IDS_DATAVIEW_INVALID_OFFSET 0x13E2 #define IDS_WRONG_THIS 0x13FC #define IDS_KEY_NOT_OBJECT 0x13FD +#define IDS_ARRAYBUFFER_EXPECTED 0x15E4 /* FIXME: This is not compatible with native, but we would * conflict with IDS_UNSUPPORTED_ACTION otherwise */ #define IDS_PROP_DESC_MISMATCH 0x1F00 diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index a18fea6476a..353d0b0b5c0 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -37,6 +37,7 @@ var JS_E_DATAVIEW_NO_ARGUMENT = 0x800a13e0; var JS_E_DATAVIEW_INVALID_ACCESS = 0x800a13e1; var JS_E_DATAVIEW_INVALID_OFFSET = 0x800a13e2; var JS_E_WRONG_THIS = 0x800a13fc; +var JS_E_ARRAYBUFFER_EXPECTED = 0x800a15e4;
var tests = [];
@@ -1687,7 +1688,7 @@ sync_test("RegExp", function() { });
sync_test("ArrayBuffers & Views", function() { - var i, r, buf, view, view2, arr; + var i, r, buf, buf2, view, view2, arr;
var types = [ [ "Int8", 1 ], @@ -1732,6 +1733,20 @@ sync_test("ArrayBuffers & Views", function() { r = ArrayBuffer.prototype.slice.length; ok(r === 2, "ArrayBuffer.prototype.slice.length = " + r);
+ try { + ArrayBuffer.prototype.slice.call(null); + ok(false, "ArrayBuffer: calling slice with null context did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_ARRAYBUFFER_EXPECTED, "ArrayBuffer: calling slice with null context threw " + n); + } + try { + ArrayBuffer.prototype.slice.call({}); + ok(false, "ArrayBuffer: calling slice with an object context did not throw exception"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === JS_E_ARRAYBUFFER_EXPECTED, "ArrayBuffer: calling slice with an object context threw " + n); + } try { new ArrayBuffer(-1); ok(false, "new ArrayBuffer(-1) did not throw exception"); @@ -1983,10 +1998,39 @@ sync_test("ArrayBuffers & Views", function() { r = view2.getFloat32(0, true); ok(r === 1234.5, "view2.getFloat32(0) returned " + r);
+ r = buf.slice(-9, 1); + ok(r instanceof ArrayBuffer, "buf.slice did not return an ArrayBuffer"); + ok(r.byteLength === 0, "buf.slice(-9, 1).byteLength = " + r.byteLength); + r = buf.slice(); + ok(r.byteLength === 10, "buf.slice().byteLength = " + r.byteLength); + r = buf.slice(9, 16); + ok(r.byteLength === 1, "buf.slice(9, 16).byteLength = " + r.byteLength); + r = buf.slice(-9, -1); + ok(r.byteLength === 8, "buf.slice(-9, -1).byteLength = " + r.byteLength); + /* setters differing only in signedness have identical behavior, but they're not the same methods */ ok(view.setInt8 !== view.setUint8, "setInt8 and setUint8 are the same method"); ok(view.setInt16 !== view.setUint16, "setInt16 and setUint16 are the same method"); ok(view.setInt32 !== view.setUint32, "setInt32 and setUint32 are the same method"); + + /* slice makes a copy */ + buf2 = buf.slice(-9); + 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); + + r = view2.getUint32(0); + ok(r === 4294967040, "buf.slice(-9) view(1).getUint32(0) returned " + r); + view2.setInt16(0, -5); + r = view2.getUint16(1); + ok(r === 64511, "buf.slice(-9) view(1).getUint16(1) returned " + r); + r = view.getInt32(1); + ok(r === -1, "view.getInt32(1) after slice changed returned " + r); + + r = view2.setFloat64(0, 11.875); + 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); });
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=143043
Your paranoid android.
=== w7pro64 (64 bit report) ===
mshtml: script.c:1223: Test failed: L"/index.html?dom.js:document_lastModified: lastModified too far from navigationStart: 1012"
This merge request was approved by Jacek Caban.