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 | 190 +++++++++++++++++++++++++++++++++++++++ dlls/jscript/global.c | 4 + dlls/jscript/jscript.h | 5 +- dlls/jscript/object.c | 1 + dlls/mshtml/tests/es5.js | 48 ++++++++++ 6 files changed, 248 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..291af484138 --- /dev/null +++ b/dlls/jscript/arraybuf.c @@ -0,0 +1,190 @@ +/* + * 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, jsdisp_t **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->dispex; + 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) +{ + DWORD size = 0; + jsdisp_t *obj; + 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, &obj); + if(FAILED(hres)) + return hres; + *r = jsval_obj(obj); + } + 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 = [