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() {