From 37fe427d59043d70f92c70d315592232ad8cb358 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Tue, 5 Nov 2024 21:55:42 +0100 Subject: [PATCH] Add resizable ArrayBuffers (#646) This commit implements resizable ArrayBuffers - RABs for short - and extends typed arrays (TAs) to support fixed-length and length-tracking modes. SharedArrayBuffers (SABs) also support the maxByteLength option now but I cheated and allocate all memory upfront because atomically resizing memory allocations is hard and this commit is already big and complex. The lion's share is updating all the TA prototype methods to deal with RABs resizing underneath them. Method arguments can be arbitrary objects with arbitrary .valueOf methods and arbitrary side effects, like... resizing the RAB we're currently operating on. Fixes: https://github.com/quickjs-ng/quickjs/issues/477 --- gen/function_source.c | Bin 2896 -> 2896 bytes gen/hello.c | Bin 1429 -> 1429 bytes gen/hello_module.c | Bin 4027 -> 4027 bytes gen/repl.c | Bin 143326 -> 143326 bytes gen/test_fib.c | Bin 2844 -> 2844 bytes quickjs-atom.h | 1 + quickjs.c | 928 ++++++++++++++++++++++++++++++------------ test262.conf | 2 +- test262_errors.txt | 3 + tests/test_bjson.js | 36 ++ 10 files changed, 716 insertions(+), 254 deletions(-) diff --git a/gen/function_source.c b/gen/function_source.c index d4394f64559a0e106cb7840b10edf866cf0dbe38..f66527b1ec945d0ffcee99794541158784fe4e4b 100644 GIT binary patch delta 117 zcmca0c0p{y9!A5BdoM6DCQt5TYG6#A%+D;tlx#3rmst+T@nH^?G}2Kps4y@FlLlZi z1w;bX>|)jjDSgfC&zQW~n#GWj(P(lm>uN^R$&zdnnNkfVuVFh0=EzQ-%i_@% diff --git a/gen/hello.c b/gen/hello.c index 821267ca6bbb3431ba9ff3c4acd741bf7dd7a71f..2cff0d762b81805872a706bd9798be8651693424 100644 GIT binary patch delta 58 zcmbQrJ(YXH3P!_?E43LJlP4Q8iA{dZ*fDt$lQ@Tokq(!FK}G812aFQzCOROd;pB}> NHjK%e-!Uy`0ss~`5()qS delta 57 zcmV-90LK573zZ9yr~xpssVe~iVUsZe7L)4%hyi7j;Q|?gFO*{TdNU diff --git a/gen/hello_module.c b/gen/hello_module.c index c94fe07997c9b7d5064f27bd84d9f7af2f93f46b..b33c4d2b17e80e97bfa44cbd4cf1c14dd6bd976a 100644 GIT binary patch delta 183 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 SSzCe`z{4iH|JL3(^?v{<)e2(( delta 21167 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 MFeY!dWxvl10Bb@wNdN!< delta 214 zcmbOuHb-p2I!1$y>+dlMC+a8|R2Ue8Nkb5slstI>^F-sMR3r%lFgpcACL8E*A#@;1 z7)?%L@nAO6;hOxARdjM38#|-%DKVpW@bnnh%C4y(uH2v+gQr&&cN zZvoP^Z2Xg1*_0X0Cu_3FFdw|Lk!OHqr R*(d*Cb74%}Y|DP1830!iJk|gJ diff --git a/quickjs-atom.h b/quickjs-atom.h index 07c10d9..49c82a5 100644 --- a/quickjs-atom.h +++ b/quickjs-atom.h @@ -176,6 +176,7 @@ DEF(not_equal, "not-equal") DEF(timed_out, "timed-out") DEF(ok, "ok") DEF(toJSON, "toJSON") +DEF(maxByteLength, "maxByteLength") /* class names */ DEF(Object, "Object") DEF(Array, "Array") diff --git a/quickjs.c b/quickjs.c index e597a06..17c35d0 100644 --- a/quickjs.c +++ b/quickjs.c @@ -725,6 +725,7 @@ typedef struct JSProxyData { typedef struct JSArrayBuffer { int byte_length; /* 0 if detached */ + int max_byte_length; /* -1 if not resizable; >= byte_length otherwise */ uint8_t detached; uint8_t shared; /* if shared, the array buffer cannot be detached */ uint8_t *data; /* NULL if detached */ @@ -737,8 +738,9 @@ typedef struct JSTypedArray { struct list_head link; /* link to arraybuffer */ JSObject *obj; /* back pointer to the TypedArray/DataView object */ JSObject *buffer; /* based array buffer */ - uint32_t offset; /* offset in the array buffer */ - uint32_t length; /* length in the array buffer */ + uint32_t offset; /* byte offset in the array buffer */ + uint32_t length; /* byte length in the array buffer */ + BOOL track_rab; /* auto-track length of backing array buffer */ } JSTypedArray; typedef struct JSAsyncFunctionState { @@ -1196,11 +1198,14 @@ static BOOL is_valid_weakref_target(JSValue val); static void insert_weakref_record(JSValue target, struct JSWeakRefRecord *wr); static JSValue js_array_buffer_constructor3(JSContext *ctx, JSValue new_target, - uint64_t len, JSClassID class_id, + uint64_t len, uint64_t *max_len, + JSClassID class_id, uint8_t *buf, JSFreeArrayBufferDataFunc *free_func, void *opaque, BOOL alloc_flag); +static void js_array_buffer_free(JSRuntime *rt, void *opaque, void *ptr); static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValue obj); +static BOOL array_buffer_is_resizable(const JSArrayBuffer *abuf); static JSValue js_typed_array_constructor(JSContext *ctx, JSValue this_val, int argc, JSValue *argv, @@ -1208,10 +1213,12 @@ static JSValue js_typed_array_constructor(JSContext *ctx, static JSValue js_typed_array_constructor_ta(JSContext *ctx, JSValue new_target, JSValue src_obj, - int classid); -static BOOL typed_array_is_detached(JSContext *ctx, JSObject *p); + int classid, uint32_t len); +static BOOL typed_array_is_oob(JSObject *p); +static BOOL typed_array_is_resizable(JSObject *p); static uint32_t typed_array_get_length(JSContext *ctx, JSObject *p); static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx); +static JSValue JS_ThrowTypeErrorArrayBufferOOB(JSContext *ctx); static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx, BOOL is_arg); static JSValue js_generator_function_call(JSContext *ctx, JSValue func_obj, @@ -9022,9 +9029,14 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValue this_obj, } if (unlikely(idx >= (uint32_t)p->u.array.count)) { ta_out_of_bound: - if (typed_array_is_detached(ctx, p)) + if (typed_array_is_oob(p)) if (!(flags & JS_PROP_DEFINE_PROPERTY)) return TRUE; // per spec: no OOB exception + // XXX(bnoordhuis) questionable but generic methods like + // Array.prototype.fill invoked on RABs can end up here + // and should, per spec, not error + if (typed_array_is_resizable(p)) + return TRUE; return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound numeric index"); } p->u.array.u.double_ptr[idx] = d; @@ -33512,7 +33524,7 @@ typedef enum BCTagEnum { BC_TAG_SYMBOL, } BCTagEnum; -#define BC_VERSION 16 +#define BC_VERSION 17 typedef struct BCWriterState { JSContext *ctx; @@ -34116,6 +34128,7 @@ static int JS_WriteArrayBuffer(BCWriterState *s, JSValue obj) } bc_put_u8(s, BC_TAG_ARRAY_BUFFER); bc_put_leb128(s, abuf->byte_length); + bc_put_leb128(s, abuf->max_byte_length); dbuf_put(&s->dbuf, abuf->data, abuf->byte_length); return 0; } @@ -34127,6 +34140,7 @@ static int JS_WriteSharedArrayBuffer(BCWriterState *s, JSValue obj) assert(!abuf->detached); /* SharedArrayBuffer are never detached */ bc_put_u8(s, BC_TAG_SHARED_ARRAY_BUFFER); bc_put_leb128(s, abuf->byte_length); + bc_put_leb128(s, abuf->max_byte_length); bc_put_u64(s, (uintptr_t)abuf->data); if (js_resize_array(s->ctx, (void **)&s->sab_tab, sizeof(s->sab_tab[0]), &s->sab_tab_size, s->sab_tab_len + 1)) @@ -35302,16 +35316,31 @@ static JSValue JS_ReadTypedArray(BCReaderState *s) static JSValue JS_ReadArrayBuffer(BCReaderState *s) { JSContext *ctx = s->ctx; - uint32_t byte_length; + uint32_t byte_length, max_byte_length; + uint64_t max_byte_length_u64, *pmax_byte_length = NULL; JSValue obj; if (bc_get_leb128(s, &byte_length)) return JS_EXCEPTION; + if (bc_get_leb128(s, &max_byte_length)) + return JS_EXCEPTION; + if (max_byte_length < byte_length) + return JS_ThrowTypeError(ctx, "invalid array buffer"); + if (max_byte_length != UINT32_MAX) { + max_byte_length_u64 = max_byte_length; + pmax_byte_length = &max_byte_length_u64; + } if (unlikely(s->buf_end - s->ptr < byte_length)) { bc_read_error_end(s); return JS_EXCEPTION; } - obj = JS_NewArrayBufferCopy(ctx, s->ptr, byte_length); + // makes a copy of the input + obj = js_array_buffer_constructor3(ctx, JS_UNDEFINED, + byte_length, pmax_byte_length, + JS_CLASS_ARRAY_BUFFER, + (uint8_t*)s->ptr, + js_array_buffer_free, NULL, + /*alloc_flag*/TRUE); if (JS_IsException(obj)) goto fail; if (BC_add_object_ref(s, obj)) @@ -35326,13 +35355,22 @@ static JSValue JS_ReadArrayBuffer(BCReaderState *s) static JSValue JS_ReadSharedArrayBuffer(BCReaderState *s) { JSContext *ctx = s->ctx; - uint32_t byte_length; + uint32_t byte_length, max_byte_length; + uint64_t max_byte_length_u64, *pmax_byte_length = NULL; uint8_t *data_ptr; JSValue obj; uint64_t u64; if (bc_get_leb128(s, &byte_length)) return JS_EXCEPTION; + if (bc_get_leb128(s, &max_byte_length)) + return JS_EXCEPTION; + if (max_byte_length < byte_length) + return JS_ThrowTypeError(ctx, "invalid array buffer"); + if (max_byte_length != UINT32_MAX) { + max_byte_length_u64 = max_byte_length; + pmax_byte_length = &max_byte_length_u64; + } if (bc_get_u64(s, &u64)) return JS_EXCEPTION; data_ptr = (uint8_t *)(uintptr_t)u64; @@ -35342,7 +35380,8 @@ static JSValue JS_ReadSharedArrayBuffer(BCReaderState *s) /* keep the SAB pointer so that the user can clone it or free it */ s->sab_tab[s->sab_tab_len++] = data_ptr; /* the SharedArrayBuffer is cloned */ - obj = js_array_buffer_constructor3(ctx, JS_UNDEFINED, byte_length, + obj = js_array_buffer_constructor3(ctx, JS_UNDEFINED, + byte_length, pmax_byte_length, JS_CLASS_SHARED_ARRAY_BUFFER, data_ptr, NULL, NULL, FALSE); @@ -38335,7 +38374,41 @@ exception: #define special_filter 4 #define special_TA 8 -static int js_typed_array_get_length_internal(JSContext *ctx, JSValue obj); +static JSObject *get_typed_array(JSContext *ctx, JSValue this_val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) + goto fail; + p = JS_VALUE_GET_OBJ(this_val); + if (!(p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY)) { + fail: + JS_ThrowTypeError(ctx, "not a TypedArray"); + return NULL; + } + return p; +} + +// Be *very* careful if you touch the typed array's memory directly: +// the length is only valid until the next call into JS land because +// JS code can detach or resize the backing array buffer. Functions +// like JS_GetProperty and JS_ToIndex call JS code. +// +// Exclusively reading or writing elements with JS_GetProperty, +// JS_GetPropertyInt64, JS_SetProperty, etc. is safe because they +// perform bounds checks, as does js_get_fast_array_element. +static int js_typed_array_get_length_unsafe(JSContext *ctx, JSValue obj) +{ + JSObject *p; + p = get_typed_array(ctx, obj); + if (!p) + return -1; + if (typed_array_is_oob(p)) { + JS_ThrowTypeErrorArrayBufferOOB(ctx); + return -1; + } + return p->u.array.count; +} static JSValue js_typed_array___speciesCreate(JSContext *ctx, JSValue this_val, @@ -38354,7 +38427,7 @@ static JSValue js_array_every(JSContext *ctx, JSValue this_val, val = JS_UNDEFINED; if (special & special_TA) { obj = js_dup(this_val); - len = js_typed_array_get_length_internal(ctx, obj); + len = js_typed_array_get_length_unsafe(ctx, obj); if (len < 0) goto exception; } else { @@ -38509,7 +38582,7 @@ static JSValue js_array_reduce(JSContext *ctx, JSValue this_val, val = JS_UNDEFINED; if (special & special_TA) { obj = js_dup(this_val); - len = js_typed_array_get_length_internal(ctx, obj); + len = js_typed_array_get_length_unsafe(ctx, obj); if (len < 0) goto exception; } else { @@ -39817,8 +39890,8 @@ static JSValue js_array_iterator_next(JSContext *ctx, JSValue this_val, p = JS_VALUE_GET_OBJ(it->obj); if (p->class_id >= JS_CLASS_UINT8C_ARRAY && p->class_id <= JS_CLASS_FLOAT64_ARRAY) { - if (typed_array_is_detached(ctx, p)) { - JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + if (typed_array_is_oob(p)) { + JS_ThrowTypeErrorArrayBufferOOB(ctx); goto fail1; } len = p->u.array.count; @@ -51065,7 +51138,8 @@ static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT] = { static JSValue js_array_buffer_constructor3(JSContext *ctx, JSValue new_target, - uint64_t len, JSClassID class_id, + uint64_t len, uint64_t *max_len, + JSClassID class_id, uint8_t *buf, JSFreeArrayBufferDataFunc *free_func, void *opaque, BOOL alloc_flag) @@ -51073,7 +51147,15 @@ static JSValue js_array_buffer_constructor3(JSContext *ctx, JSRuntime *rt = ctx->rt; JSValue obj; JSArrayBuffer *abuf = NULL; + uint64_t sab_alloc_len; + if (!alloc_flag && buf && max_len && free_func != js_array_buffer_free) { + // not observable from JS land, only through C API misuse; + // JS code cannot create externally managed buffers directly + return JS_ThrowInternalError(ctx, + "resizable ArrayBuffers not supported " + "for externally managed buffers"); + } obj = js_create_from_ctor(ctx, new_target, class_id); if (JS_IsException(obj)) return obj; @@ -51082,18 +51164,26 @@ static JSValue js_array_buffer_constructor3(JSContext *ctx, JS_ThrowRangeError(ctx, "invalid array buffer length"); goto fail; } + if (max_len && *max_len > INT32_MAX) { + JS_ThrowRangeError(ctx, "invalid max array buffer length"); + goto fail; + } abuf = js_malloc(ctx, sizeof(*abuf)); if (!abuf) goto fail; abuf->byte_length = len; + abuf->max_byte_length = max_len ? *max_len : -1; if (alloc_flag) { if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER && rt->sab_funcs.sab_alloc) { + // TOOD(bnoordhuis) resizing backing memory for SABs atomically + // is hard so we cheat and allocate |maxByteLength| bytes upfront + sab_alloc_len = max_len ? *max_len : len; abuf->data = rt->sab_funcs.sab_alloc(rt->sab_funcs.sab_opaque, - max_int(len, 1)); + max_int(sab_alloc_len, 1)); if (!abuf->data) goto fail; - memset(abuf->data, 0, len); + memset(abuf->data, 0, sab_alloc_len); } else { /* the allocation must be done after the object creation */ abuf->data = js_mallocz(ctx, max_int(len, 1)); @@ -51129,18 +51219,19 @@ static void js_array_buffer_free(JSRuntime *rt, void *opaque, void *ptr) static JSValue js_array_buffer_constructor2(JSContext *ctx, JSValue new_target, - uint64_t len, JSClassID class_id) + uint64_t len, uint64_t *max_len, + JSClassID class_id) { - return js_array_buffer_constructor3(ctx, new_target, len, class_id, - NULL, js_array_buffer_free, NULL, - TRUE); + return js_array_buffer_constructor3(ctx, new_target, len, max_len, + class_id, NULL, js_array_buffer_free, + NULL, TRUE); } static JSValue js_array_buffer_constructor1(JSContext *ctx, JSValue new_target, - uint64_t len) + uint64_t len, uint64_t *max_len) { - return js_array_buffer_constructor2(ctx, new_target, len, + return js_array_buffer_constructor2(ctx, new_target, len, max_len, JS_CLASS_ARRAY_BUFFER); } @@ -51148,8 +51239,9 @@ JSValue JS_NewArrayBuffer(JSContext *ctx, uint8_t *buf, size_t len, JSFreeArrayBufferDataFunc *free_func, void *opaque, BOOL is_shared) { - return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len, - is_shared ? JS_CLASS_SHARED_ARRAY_BUFFER : JS_CLASS_ARRAY_BUFFER, + JSClassID class_id = + is_shared ? JS_CLASS_SHARED_ARRAY_BUFFER : JS_CLASS_ARRAY_BUFFER; + return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len, NULL, class_id, buf, free_func, opaque, FALSE); } @@ -51160,31 +51252,60 @@ JS_BOOL JS_IsArrayBuffer(JSValue obj) { /* create a new ArrayBuffer of length 'len' and copy 'buf' to it */ JSValue JS_NewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len) { - return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len, + return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len, NULL, JS_CLASS_ARRAY_BUFFER, (uint8_t *)buf, js_array_buffer_free, NULL, TRUE); } -static JSValue js_array_buffer_constructor(JSContext *ctx, - JSValue new_target, - int argc, JSValue *argv) +static JSValue js_array_buffer_constructor0(JSContext *ctx, JSValue new_target, + int argc, JSValue *argv, + JSClassID class_id) { - uint64_t len; + uint64_t len, max_len, *pmax_len = NULL; + JSValue obj, val; + int64_t i; + if (JS_ToIndex(ctx, &len, argv[0])) return JS_EXCEPTION; - return js_array_buffer_constructor1(ctx, new_target, len); + if (argc < 2) + goto next; + if (!JS_IsObject(argv[1])) + goto next; + obj = JS_ToObject(ctx, argv[1]); + if (JS_IsException(obj)) + return JS_EXCEPTION; + val = JS_GetProperty(ctx, obj, JS_ATOM_maxByteLength); + JS_FreeValue(ctx, obj); + if (JS_IsException(val)) + return JS_EXCEPTION; + if (JS_IsUndefined(val)) + goto next; + if (JS_ToInt64Free(ctx, &i, val)) + return JS_EXCEPTION; + // don't have to check i < 0 because len >= 0 + if (len > i || i > MAX_SAFE_INTEGER) + return JS_ThrowRangeError(ctx, "invalid array buffer max length"); + max_len = i; + pmax_len = &max_len; +next: + return js_array_buffer_constructor2(ctx, new_target, len, pmax_len, + class_id); +} + +static JSValue js_array_buffer_constructor(JSContext *ctx, JSValue new_target, + int argc, JSValue *argv) +{ + return js_array_buffer_constructor0(ctx, new_target, argc, argv, + JS_CLASS_ARRAY_BUFFER); } static JSValue js_shared_array_buffer_constructor(JSContext *ctx, JSValue new_target, int argc, JSValue *argv) { - uint64_t len; - if (JS_ToIndex(ctx, &len, argv[0])) - return JS_EXCEPTION; - return js_array_buffer_constructor2(ctx, new_target, len, + return js_array_buffer_constructor0(ctx, new_target, argc, argv, JS_CLASS_SHARED_ARRAY_BUFFER); } @@ -51250,6 +51371,11 @@ static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx) return JS_ThrowTypeError(ctx, "ArrayBuffer is detached"); } +static JSValue JS_ThrowTypeErrorArrayBufferOOB(JSContext *ctx) +{ + return JS_ThrowTypeError(ctx, "ArrayBuffer is detached or resized"); +} + // #sec-get-arraybuffer.prototype.detached static JSValue js_array_buffer_get_detached(JSContext *ctx, JSValue this_val) @@ -51273,6 +51399,27 @@ static JSValue js_array_buffer_get_byteLength(JSContext *ctx, return js_uint32(abuf->byte_length); } +static JSValue js_array_buffer_get_maxByteLength(JSContext *ctx, + JSValue this_val, + int class_id) +{ + JSArrayBuffer *abuf = JS_GetOpaque2(ctx, this_val, class_id); + if (!abuf) + return JS_EXCEPTION; + if (array_buffer_is_resizable(abuf)) + return js_uint32(abuf->max_byte_length); + return js_uint32(abuf->byte_length); +} + +static JSValue js_array_buffer_get_resizable(JSContext *ctx, JSValue this_val, + int class_id) +{ + JSArrayBuffer *abuf = JS_GetOpaque2(ctx, this_val, class_id); + if (!abuf) + return JS_EXCEPTION; + return js_bool(array_buffer_is_resizable(abuf)); +} + void JS_DetachArrayBuffer(JSContext *ctx, JSValue obj) { JSArrayBuffer *abuf = JS_GetOpaque(obj, JS_CLASS_ARRAY_BUFFER); @@ -51334,13 +51481,18 @@ uint8_t *JS_GetArrayBuffer(JSContext *ctx, size_t *psize, JSValue obj) return NULL; } -// ES #sec-arraybuffer.prototype.transfer -static JSValue js_array_buffer_transfer(JSContext *ctx, - JSValue this_val, - int argc, JSValue *argv) +static BOOL array_buffer_is_resizable(const JSArrayBuffer *abuf) { + return abuf->max_byte_length >= 0; +} + +// ES #sec-arraybuffer.prototype.transfer +static JSValue js_array_buffer_transfer(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic) +{ + BOOL transfer_to_fixed_length = magic & 1; JSArrayBuffer *abuf; - uint64_t new_len, old_len; + uint64_t new_len, old_len, max_len, *pmax_len; uint8_t *bs, *new_bs; abuf = JS_GetOpaque2(ctx, this_val, JS_CLASS_ARRAY_BUFFER); @@ -51354,10 +51506,22 @@ static JSValue js_array_buffer_transfer(JSContext *ctx, return JS_EXCEPTION; if (abuf->detached) return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + pmax_len = NULL; + if (!transfer_to_fixed_length) { + if (array_buffer_is_resizable(abuf)) { // carry over maxByteLength + max_len = abuf->max_byte_length; + if (new_len > max_len) + return JS_ThrowTypeError(ctx, "invalid array buffer length"); + // TODO(bnoordhuis) support externally managed RABs + if (abuf->free_func == js_array_buffer_free) + pmax_len = &max_len; + } + } /* create an empty AB */ if (new_len == 0) { JS_DetachArrayBuffer(ctx, this_val); - return js_array_buffer_constructor2(ctx, JS_UNDEFINED, 0, JS_CLASS_ARRAY_BUFFER); + return js_array_buffer_constructor2(ctx, JS_UNDEFINED, 0, pmax_len, + JS_CLASS_ARRAY_BUFFER); } bs = abuf->data; old_len = abuf->byte_length; @@ -51374,12 +51538,88 @@ static JSValue js_array_buffer_transfer(JSContext *ctx, abuf->data = NULL; abuf->byte_length = 0; abuf->detached = TRUE; - return js_array_buffer_constructor3(ctx, JS_UNDEFINED, new_len, + return js_array_buffer_constructor3(ctx, JS_UNDEFINED, new_len, pmax_len, JS_CLASS_ARRAY_BUFFER, bs, abuf->free_func, NULL, FALSE); } +static JSValue js_array_buffer_resize(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int class_id) +{ + uint32_t size_log2, size_elem; + struct list_head *el; + JSArrayBuffer *abuf; + JSTypedArray *ta; + JSObject *p; + uint8_t *data; + int64_t len; + + abuf = JS_GetOpaque2(ctx, this_val, class_id); + if (!abuf) + return JS_EXCEPTION; + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + if (!array_buffer_is_resizable(abuf)) + return JS_ThrowTypeError(ctx, "array buffer is not resizable"); + // TODO(bnoordhuis) support externally managed RABs + if (abuf->free_func != js_array_buffer_free) + return JS_ThrowTypeError(ctx, "external array buffer is not resizable"); + if (JS_ToInt64(ctx, &len, argv[0])) + return JS_EXCEPTION; + if (len < 0 || len > abuf->max_byte_length) { + bad_length: + return JS_ThrowRangeError(ctx, "invalid array buffer length"); + } + // SABs can only grow and we don't need to realloc because + // js_array_buffer_constructor3 commits all memory upfront; + // regular RABs are resizable both ways and realloc + if (abuf->shared) { + if (len < abuf->byte_length) + goto bad_length; + // Note this is off-spec; there's supposed to be a single atomic + // |byteLength| property that's shared across SABs but we store + // it per SAB instead. That means when thread A calls sab.grow(2) + // at time t0, and thread B calls sab.grow(1) at time t1, we don't + // throw a TypeError in thread B as the spec says we should, + // instead both threads get their own view of the backing memory, + // 2 bytes big in A, and 1 byte big in B + abuf->byte_length = len; + } else { + data = js_realloc(ctx, abuf->data, max_int(len, 1)); + if (!data) + return JS_EXCEPTION; + if (len > abuf->byte_length) + memset(&data[abuf->byte_length], 0, len - abuf->byte_length); + abuf->byte_length = len; + abuf->data = data; + } + data = abuf->data; + // update lengths of all typed arrays backed by this array buffer + list_for_each(el, &abuf->array_list) { + ta = list_entry(el, JSTypedArray, link); + p = ta->obj; + if (p->class_id == JS_CLASS_DATAVIEW) + continue; + p->u.array.count = 0; + p->u.array.u.ptr = NULL; + size_log2 = typed_array_size_log2(p->class_id); + size_elem = 1 << size_log2; + if (ta->track_rab) { + if (len >= (int64_t)ta->offset + size_elem) { + p->u.array.count = (len - ta->offset) >> size_log2; + p->u.array.u.ptr = &data[ta->offset]; + } + } else { + if (len >= (int64_t)ta->offset + ta->length) { + p->u.array.count = ta->length >> size_log2; + p->u.array.u.ptr = &data[ta->offset]; + } + } + } + return JS_UNDEFINED; +} + static JSValue js_array_buffer_slice(JSContext *ctx, JSValue this_val, int argc, JSValue *argv, int class_id) @@ -51409,7 +51649,7 @@ static JSValue js_array_buffer_slice(JSContext *ctx, return ctor; if (JS_IsUndefined(ctor)) { new_obj = js_array_buffer_constructor2(ctx, JS_UNDEFINED, new_len, - class_id); + NULL, class_id); } else { JSValue args[1]; args[0] = JS_NewInt64(ctx, new_len); @@ -51448,10 +51688,13 @@ static JSValue js_array_buffer_slice(JSContext *ctx, static const JSCFunctionListEntry js_array_buffer_proto_funcs[] = { JS_CGETSET_MAGIC_DEF("byteLength", js_array_buffer_get_byteLength, NULL, JS_CLASS_ARRAY_BUFFER ), + JS_CGETSET_MAGIC_DEF("maxByteLength", js_array_buffer_get_maxByteLength, NULL, JS_CLASS_ARRAY_BUFFER ), + JS_CGETSET_MAGIC_DEF("resizable", js_array_buffer_get_resizable, NULL, JS_CLASS_ARRAY_BUFFER ), JS_CGETSET_DEF("detached", js_array_buffer_get_detached, NULL ), + JS_CFUNC_MAGIC_DEF("resize", 1, js_array_buffer_resize, JS_CLASS_ARRAY_BUFFER ), JS_CFUNC_MAGIC_DEF("slice", 2, js_array_buffer_slice, JS_CLASS_ARRAY_BUFFER ), - JS_CFUNC_DEF("transfer", 0, js_array_buffer_transfer ), - JS_CFUNC_DEF("transferToFixedLength", 0, js_array_buffer_transfer ), + JS_CFUNC_MAGIC_DEF("transfer", 0, js_array_buffer_transfer, 0 ), + JS_CFUNC_MAGIC_DEF("transferToFixedLength", 0, js_array_buffer_transfer, 1 ), JS_PROP_STRING_DEF("[Symbol.toStringTag]", "ArrayBuffer", JS_PROP_CONFIGURABLE ), }; @@ -51463,40 +51706,53 @@ static const JSCFunctionListEntry js_shared_array_buffer_funcs[] = { static const JSCFunctionListEntry js_shared_array_buffer_proto_funcs[] = { JS_CGETSET_MAGIC_DEF("byteLength", js_array_buffer_get_byteLength, NULL, JS_CLASS_SHARED_ARRAY_BUFFER ), + JS_CGETSET_MAGIC_DEF("maxByteLength", js_array_buffer_get_maxByteLength, NULL, JS_CLASS_SHARED_ARRAY_BUFFER ), + JS_CGETSET_MAGIC_DEF("growable", js_array_buffer_get_resizable, NULL, JS_CLASS_SHARED_ARRAY_BUFFER ), + JS_CFUNC_MAGIC_DEF("grow", 1, js_array_buffer_resize, JS_CLASS_SHARED_ARRAY_BUFFER ), JS_CFUNC_MAGIC_DEF("slice", 2, js_array_buffer_slice, JS_CLASS_SHARED_ARRAY_BUFFER ), JS_PROP_STRING_DEF("[Symbol.toStringTag]", "SharedArrayBuffer", JS_PROP_CONFIGURABLE ), }; -static JSObject *get_typed_array(JSContext *ctx, - JSValue this_val, - int is_dataview) +// is the typed array detached or out of bounds relative to its RAB? +// |p| must be a typed array, *not* a DataView +static BOOL typed_array_is_oob(JSObject *p) { - JSObject *p; - if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) - goto fail; - p = JS_VALUE_GET_OBJ(this_val); - if (is_dataview) { - if (p->class_id != JS_CLASS_DATAVIEW) - goto fail; - } else { - if (!(p->class_id >= JS_CLASS_UINT8C_ARRAY && - p->class_id <= JS_CLASS_FLOAT64_ARRAY)) { - fail: - JS_ThrowTypeError(ctx, "not a %s", is_dataview ? "DataView" : "TypedArray"); - return NULL; - } - } - return p; + JSArrayBuffer *abuf; + JSTypedArray *ta; + int len, size_elem; + int64_t end; + + assert(p->class_id >= JS_CLASS_UINT8C_ARRAY); + assert(p->class_id <= JS_CLASS_FLOAT64_ARRAY); + + ta = p->u.typed_array; + abuf = ta->buffer->u.array_buffer; + if (abuf->detached) + return TRUE; + len = abuf->byte_length; + if (ta->offset > len) + return TRUE; + if (ta->track_rab) + return FALSE; + if (len < (int64_t)ta->offset + ta->length) + return TRUE; + size_elem = 1 << typed_array_size_log2(p->class_id); + end = (int64_t)ta->offset + (int64_t)p->u.array.count * size_elem; + return end > len; } -/* WARNING: 'p' must be a typed array */ -static BOOL typed_array_is_detached(JSContext *ctx, JSObject *p) +// |p| must be a typed array, *not* a DataView +static BOOL typed_array_is_resizable(JSObject *p) { - JSTypedArray *ta = p->u.typed_array; - JSArrayBuffer *abuf = ta->buffer->u.array_buffer; - /* XXX: could simplify test by ensuring that - p->u.array.u.ptr is NULL iff it is detached */ - return abuf->detached; + JSArrayBuffer *abuf; + JSTypedArray *ta; + + assert(p->class_id >= JS_CLASS_UINT8C_ARRAY); + assert(p->class_id <= JS_CLASS_FLOAT64_ARRAY); + + ta = p->u.typed_array; + abuf = ta->buffer->u.array_buffer; + return array_buffer_is_resizable(abuf); } /* WARNING: 'p' must be a typed array. Works even if the array buffer @@ -51511,76 +51767,65 @@ static uint32_t typed_array_get_length(JSContext *ctx, JSObject *p) static int validate_typed_array(JSContext *ctx, JSValue this_val) { JSObject *p; - p = get_typed_array(ctx, this_val, 0); + p = get_typed_array(ctx, this_val); if (!p) return -1; - if (typed_array_is_detached(ctx, p)) { - JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + if (typed_array_is_oob(p)) { + JS_ThrowTypeErrorArrayBufferOOB(ctx); return -1; } return 0; } -static JSValue js_typed_array_get_length(JSContext *ctx, - JSValue this_val) +static JSValue js_typed_array_get_length(JSContext *ctx, JSValue this_val) { JSObject *p; - p = get_typed_array(ctx, this_val, 0); + p = get_typed_array(ctx, this_val); if (!p) return JS_EXCEPTION; return js_int32(p->u.array.count); } -static JSValue js_typed_array_get_buffer(JSContext *ctx, - JSValue this_val, int is_dataview) +static JSValue js_typed_array_get_buffer(JSContext *ctx, JSValue this_val) { JSObject *p; JSTypedArray *ta; - p = get_typed_array(ctx, this_val, is_dataview); + p = get_typed_array(ctx, this_val); if (!p) return JS_EXCEPTION; ta = p->u.typed_array; return js_dup(JS_MKPTR(JS_TAG_OBJECT, ta->buffer)); } -static JSValue js_typed_array_get_byteLength(JSContext *ctx, - JSValue this_val, - int is_dataview) +static JSValue js_typed_array_get_byteLength(JSContext *ctx, JSValue this_val) { - JSObject *p; + uint32_t size_log2; JSTypedArray *ta; - p = get_typed_array(ctx, this_val, is_dataview); + JSObject *p; + + p = get_typed_array(ctx, this_val); if (!p) return JS_EXCEPTION; - if (typed_array_is_detached(ctx, p)) { - if (is_dataview) { - return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - } else { - return js_int32(0); - } - } + if (typed_array_is_oob(p)) + return js_int32(0); ta = p->u.typed_array; - return js_int32(ta->length); + if (!ta->track_rab) + return js_uint32(ta->length); + size_log2 = typed_array_size_log2(p->class_id); + return js_int64((int64_t)p->u.array.count << size_log2); } -static JSValue js_typed_array_get_byteOffset(JSContext *ctx, - JSValue this_val, - int is_dataview) +static JSValue js_typed_array_get_byteOffset(JSContext *ctx, JSValue this_val) { JSObject *p; JSTypedArray *ta; - p = get_typed_array(ctx, this_val, is_dataview); + p = get_typed_array(ctx, this_val); if (!p) return JS_EXCEPTION; - if (typed_array_is_detached(ctx, p)) { - if (is_dataview) { - return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - } else { - return js_int32(0); - } - } + if (typed_array_is_oob(p)) + return js_int32(0); ta = p->u.typed_array; - return js_int32(ta->offset); + return js_uint32(ta->offset); } /* Return the buffer associated to the typed array or an exception if @@ -51593,11 +51838,11 @@ JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValue obj, { JSObject *p; JSTypedArray *ta; - p = get_typed_array(ctx, obj, FALSE); + p = get_typed_array(ctx, obj); if (!p) return JS_EXCEPTION; - if (typed_array_is_detached(ctx, p)) - return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + if (typed_array_is_oob(p)) + return JS_ThrowTypeErrorArrayBufferOOB(ctx); ta = p->u.typed_array; if (pbyte_offset) *pbyte_offset = ta->offset; @@ -51616,11 +51861,11 @@ uint8_t *JS_GetUint8Array(JSContext *ctx, size_t *psize, JSValue obj) JSObject *p; JSTypedArray *ta; JSArrayBuffer *abuf; - p = get_typed_array(ctx, obj, FALSE); + p = get_typed_array(ctx, obj); if (!p) goto fail; - if (typed_array_is_detached(ctx, p)) { - JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + if (typed_array_is_oob(p)) { + JS_ThrowTypeErrorArrayBufferOOB(ctx); goto fail; } if (p->class_id != JS_CLASS_UINT8_ARRAY && p->class_id != JS_CLASS_UINT8C_ARRAY) { @@ -51658,21 +51903,22 @@ static JSValue js_typed_array_set_internal(JSContext *ctx, JSObject *p; JSObject *src_p; uint32_t i; - int64_t src_len, offset; + int64_t dst_len, src_len, offset; JSValue val, src_obj = JS_UNDEFINED; - p = get_typed_array(ctx, dst, 0); + p = get_typed_array(ctx, dst); if (!p) goto fail; if (JS_ToInt64Sat(ctx, &offset, off)) goto fail; if (offset < 0) goto range_error; - if (typed_array_is_detached(ctx, p)) { + if (typed_array_is_oob(p)) { detached: - JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + JS_ThrowTypeErrorArrayBufferOOB(ctx); goto fail; } + dst_len = p->u.array.count; src_obj = JS_ToObject(ctx, src); if (JS_IsException(src_obj)) goto fail; @@ -51685,11 +51931,11 @@ static JSValue js_typed_array_set_internal(JSContext *ctx, JSArrayBuffer *src_abuf = src_ta->buffer->u.array_buffer; int shift = typed_array_size_log2(p->class_id); - if (src_abuf->detached) + if (typed_array_is_oob(src_p)) goto detached; src_len = src_p->u.array.count; - if (offset > (int64_t)(p->u.array.count - src_len)) + if (offset > dst_len - src_len) goto range_error; /* copying between typed objects */ @@ -51705,9 +51951,11 @@ static JSValue js_typed_array_set_internal(JSContext *ctx, } /* otherwise, default behavior is slow but correct */ } else { + // can change |dst| as a side effect; per spec, + // perform the range check against its old length if (js_get_length64(ctx, &src_len, src_obj)) goto fail; - if (offset > (int64_t)(p->u.array.count - src_len)) { + if (offset > dst_len - src_len) { range_error: JS_ThrowRangeError(ctx, "invalid array length"); goto fail; @@ -51720,7 +51968,7 @@ static JSValue js_typed_array_set_internal(JSContext *ctx, // Per spec: detaching the TA mid-iteration is allowed and should // not throw an exception. Because iteration over the source array is // observable, we cannot bail out early when the TA is first detached. - if (typed_array_is_detached(ctx, p)) { + if (typed_array_is_oob(p)) { JS_FreeValue(ctx, val); } else if (JS_SetPropertyUint32(ctx, dst, offset + i, val) < 0) { goto fail; @@ -51740,23 +51988,21 @@ static JSValue js_typed_array_at(JSContext *ctx, JSValue this_val, JSObject *p; int64_t idx, len; - p = get_typed_array(ctx, this_val, /*is_dataview*/0); + p = get_typed_array(ctx, this_val); if (!p) return JS_EXCEPTION; + if (typed_array_is_oob(p)) + return JS_ThrowTypeErrorArrayBufferOOB(ctx); + len = p->u.array.count; - if (typed_array_is_detached(ctx, p)) { - JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - return JS_EXCEPTION; - } - + // note: can change p->u.array.count if (JS_ToInt64Sat(ctx, &idx, argv[0])) return JS_EXCEPTION; - len = p->u.array.count; if (idx < 0) idx = len + idx; - if (idx < 0 || idx >= len) + if (idx < 0 || idx >= p->u.array.count) return JS_UNDEFINED; switch (p->class_id) { @@ -51794,32 +52040,37 @@ static JSValue js_typed_array_with(JSContext *ctx, JSValue this_val, { JSValue arr, val; JSObject *p; - int64_t idx, len; + int64_t idx; + uint32_t len, oldlen, newlen; - p = get_typed_array(ctx, this_val, /*is_dataview*/0); + p = get_typed_array(ctx, this_val); if (!p) return JS_EXCEPTION; + oldlen = p->u.array.count; if (JS_ToInt64Sat(ctx, &idx, argv[0])) return JS_EXCEPTION; - len = p->u.array.count; - if (idx < 0) - idx = len + idx; - if (idx < 0 || idx >= len) - return JS_ThrowRangeError(ctx, "invalid array index"); - val = JS_ToPrimitive(ctx, argv[1], HINT_NUMBER); if (JS_IsException(val)) return JS_EXCEPTION; + newlen = p->u.array.count; + if (idx < 0) + idx = newlen + idx; + if (idx < 0 || idx >= newlen) { + JS_FreeValue(ctx, val); + return JS_ThrowRangeError(ctx, "invalid array index"); + } + + len = min_uint32(oldlen, newlen); arr = js_typed_array_constructor_ta(ctx, JS_UNDEFINED, this_val, - p->class_id); + p->class_id, len); if (JS_IsException(arr)) { JS_FreeValue(ctx, val); return JS_EXCEPTION; } - if (JS_SetPropertyInt64(ctx, arr, idx, val) < 0) { + if (idx < len && JS_SetPropertyInt64(ctx, arr, idx, val) < 0) { JS_FreeValue(ctx, arr); return JS_EXCEPTION; } @@ -51845,21 +52096,6 @@ static JSValue js_create_typed_array_iterator(JSContext *ctx, JSValue this_val, return js_create_array_iterator(ctx, this_val, argc, argv, magic); } -/* return < 0 if exception */ -static int js_typed_array_get_length_internal(JSContext *ctx, - JSValue obj) -{ - JSObject *p; - p = get_typed_array(ctx, obj, 0); - if (!p) - return -1; - if (typed_array_is_detached(ctx, p)) { - JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - return -1; - } - return p->u.array.count; -} - static JSValue js_typed_array_create(JSContext *ctx, JSValue ctor, int argc, JSValue *argv) { @@ -51871,7 +52107,7 @@ static JSValue js_typed_array_create(JSContext *ctx, JSValue ctor, if (JS_IsException(ret)) return ret; /* validate the typed array */ - new_len = js_typed_array_get_length_internal(ctx, ret); + new_len = js_typed_array_get_length_unsafe(ctx, ret); if (new_len < 0) goto fail; if (argc == 1) { @@ -51898,7 +52134,7 @@ static JSValue js_typed_array___speciesCreate(JSContext *ctx, int argc1; obj = argv[0]; - p = get_typed_array(ctx, obj, 0); + p = get_typed_array(ctx, obj); if (!p) return JS_EXCEPTION; ctor = JS_SpeciesConstructor(ctx, obj, JS_UNDEFINED); @@ -52033,11 +52269,14 @@ static JSValue js_typed_array_copyWithin(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { JSObject *p; - int len, to, from, final, count, shift; + int len, to, from, final, count, shift, space; - len = js_typed_array_get_length_internal(ctx, this_val); - if (len < 0) + p = get_typed_array(ctx, this_val); + if (!p) return JS_EXCEPTION; + if (typed_array_is_oob(p)) + return JS_ThrowTypeErrorArrayBufferOOB(ctx); + len = p->u.array.count; if (JS_ToInt32Clamp(ctx, &to, argv[0], 0, len, len)) return JS_EXCEPTION; @@ -52051,11 +52290,14 @@ static JSValue js_typed_array_copyWithin(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } + if (typed_array_is_oob(p)) + return JS_ThrowTypeErrorArrayBufferOOB(ctx); + + // RAB may have been resized by evil .valueOf method + space = p->u.array.count - max_int(to, from); count = min_int(final - from, len - to); + count = min_int(count, space); if (count > 0) { - p = JS_VALUE_GET_OBJ(this_val); - if (typed_array_is_detached(ctx, p)) - return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); shift = typed_array_size_log2(p->class_id); memmove(p->u.array.u.uint8_ptr + (to << shift), p->u.array.u.uint8_ptr + (from << shift), @@ -52071,10 +52313,12 @@ static JSValue js_typed_array_fill(JSContext *ctx, JSValue this_val, int len, k, final, shift; uint64_t v64; - len = js_typed_array_get_length_internal(ctx, this_val); - if (len < 0) + p = get_typed_array(ctx, this_val); + if (!p) return JS_EXCEPTION; - p = JS_VALUE_GET_OBJ(this_val); + if (typed_array_is_oob(p)) + return JS_ThrowTypeErrorArrayBufferOOB(ctx); + len = p->u.array.count; if (p->class_id == JS_CLASS_UINT8C_ARRAY) { int32_t v; @@ -52122,9 +52366,11 @@ static JSValue js_typed_array_fill(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; } - if (typed_array_is_detached(ctx, p)) - return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + if (typed_array_is_oob(p)) + return JS_ThrowTypeErrorArrayBufferOOB(ctx); + // RAB may have been resized by evil .valueOf method + final = min_int(final, p->u.array.count); shift = typed_array_size_log2(p->class_id); switch(shift) { case 0: @@ -52163,7 +52409,7 @@ static JSValue js_typed_array_find(JSContext *ctx, JSValue this_val, int dir; val = JS_UNDEFINED; - len = js_typed_array_get_length_internal(ctx, this_val); + len = js_typed_array_get_length_unsafe(ctx, this_val); if (len < 0) goto exception; @@ -52228,13 +52474,19 @@ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValue this_val, double d; float f; uint16_t hf; + BOOL oob; + + p = get_typed_array(ctx, this_val); + if (!p) + return JS_EXCEPTION; + if (typed_array_is_oob(p)) + return JS_ThrowTypeErrorArrayBufferOOB(ctx); + len = p->u.array.count; - len = js_typed_array_get_length_internal(ctx, this_val); - if (len < 0) - goto exception; if (len == 0) goto done; + oob = FALSE; if (special == special_lastIndexOf) { k = len - 1; if (argc > 1) { @@ -52260,23 +52512,37 @@ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValue this_val, } else { k = 0; if (argc > 1) { - if (JS_ToInt32Clamp(ctx, &k, argv[1], 0, len, len)) + if (JS_ToInt32Sat(ctx, &k, argv[1])) goto exception; + if (k < 0) { + k += len; + if (k < 0) + k = 0; + } else if (k > len) { + k = len; + oob = TRUE; + } } stop = len; inc = 1; } - p = JS_VALUE_GET_OBJ(this_val); /* if the array was detached, no need to go further (but no exception is raised) */ - if (typed_array_is_detached(ctx, p)) { + if (typed_array_is_oob(p) || len > p->u.array.count) { /* "includes" scans all the properties, so "undefined" can match */ if (special == special_includes && JS_IsUndefined(argv[0]) && len > 0) - res = 0; + res = oob ? -1 : 0; goto done; } + // RAB may have been resized by evil .valueOf method + len = min_int(len, p->u.array.count); + if (len == 0) + goto done; + k = min_int(k, len); + stop = min_int(stop, len); + is_bigint = 0; is_int = 0; /* avoid warning */ v64 = 0; /* avoid warning */ @@ -52325,7 +52591,9 @@ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValue this_val, pv = p->u.array.u.uint8_ptr; v = v64; if (inc > 0) { - pp = memchr(pv + k, v, len - k); + pp = NULL; + if (pv) + pp = memchr(pv + k, v, len - k); if (pp) res = pp - pv; } else { @@ -52494,35 +52762,42 @@ static JSValue js_typed_array_join(JSContext *ctx, JSValue this_val, { JSValue sep = JS_UNDEFINED, el; StringBuffer b_s, *b = &b_s; - JSString *p = NULL; - int i, n; + JSString *s = NULL; + JSObject *p; + int i, len, oldlen, newlen; int c; - n = js_typed_array_get_length_internal(ctx, this_val); - if (n < 0) - goto exception; + p = get_typed_array(ctx, this_val); + if (!p) + return JS_EXCEPTION; + if (typed_array_is_oob(p)) + return JS_ThrowTypeErrorArrayBufferOOB(ctx); + len = oldlen = newlen = p->u.array.count; c = ','; /* default separator */ if (!toLocaleString && argc > 0 && !JS_IsUndefined(argv[0])) { sep = JS_ToString(ctx, argv[0]); if (JS_IsException(sep)) goto exception; - p = JS_VALUE_GET_STRING(sep); - if (p->len == 1 && !p->is_wide_char) - c = p->u.str8[0]; + s = JS_VALUE_GET_STRING(sep); + if (s->len == 1 && !s->is_wide_char) + c = s->u.str8[0]; else c = -1; + // ToString(sep) can detach or resize the arraybuffer as a side effect + newlen = p->u.array.count; + len = min_int(len, newlen); } string_buffer_init(ctx, b, 0); /* XXX: optimize with direct access */ - for(i = 0; i < n; i++) { + for(i = 0; i < len; i++) { if (i > 0) { if (c >= 0) { if (string_buffer_putc8(b, c)) goto fail; } else { - if (string_buffer_concat(b, p, 0, p->len)) + if (string_buffer_concat(b, s, 0, s->len)) goto fail; } } @@ -52538,6 +52813,19 @@ static JSValue js_typed_array_join(JSContext *ctx, JSValue this_val, goto fail; } } + + // add extra separators in case RAB was resized by evil .valueOf method + i = max_int(1, newlen); + for(/*empty*/; i < oldlen; i++) { + if (c >= 0) { + if (string_buffer_putc8(b, c)) + goto fail; + } else { + if (string_buffer_concat(b, s, 0, s->len)) + goto fail; + } + } + JS_FreeValue(ctx, sep); return string_buffer_end(b); @@ -52554,7 +52842,7 @@ static JSValue js_typed_array_reverse(JSContext *ctx, JSValue this_val, JSObject *p; int len; - len = js_typed_array_get_length_internal(ctx, this_val); + len = js_typed_array_get_length_unsafe(ctx, this_val); if (len < 0) return JS_EXCEPTION; if (len > 0) { @@ -52617,11 +52905,11 @@ static JSValue js_typed_array_toReversed(JSContext *ctx, JSValue this_val, JSValue arr, ret; JSObject *p; - p = get_typed_array(ctx, this_val, /*is_dataview*/0); + p = get_typed_array(ctx, this_val); if (!p) return JS_EXCEPTION; arr = js_typed_array_constructor_ta(ctx, JS_UNDEFINED, this_val, - p->class_id); + p->class_id, p->u.array.count); if (JS_IsException(arr)) return JS_EXCEPTION; ret = js_typed_array_reverse(ctx, arr, argc, argv); @@ -52635,12 +52923,15 @@ static JSValue js_typed_array_slice(JSContext *ctx, JSValue this_val, JSValue args[2]; JSValue arr, val; JSObject *p, *p1; - int n, len, start, final, count, shift; + int n, len, start, final, count, shift, space; arr = JS_UNDEFINED; - len = js_typed_array_get_length_internal(ctx, this_val); - if (len < 0) - goto exception; + p = get_typed_array(ctx, this_val); + if (!p) + return JS_EXCEPTION; + if (typed_array_is_oob(p)) + return JS_ThrowTypeErrorArrayBufferOOB(ctx); + len = p->u.array.count; if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) goto exception; @@ -52651,11 +52942,6 @@ static JSValue js_typed_array_slice(JSContext *ctx, JSValue this_val, } count = max_int(final - start, 0); - p = get_typed_array(ctx, this_val, 0); - if (p == NULL) - goto exception; - shift = typed_array_size_log2(p->class_id); - args[0] = this_val; args[1] = js_int32(count); arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args); @@ -52667,14 +52953,21 @@ static JSValue js_typed_array_slice(JSContext *ctx, JSValue this_val, || validate_typed_array(ctx, arr)) goto exception; - p1 = get_typed_array(ctx, arr, 0); + if (len != p->u.array.count) + goto slow_path; + + p1 = get_typed_array(ctx, arr); if (p1 != NULL && p->class_id == p1->class_id && typed_array_get_length(ctx, p1) >= count && typed_array_get_length(ctx, p) >= start + count) { + shift = typed_array_size_log2(p->class_id); memmove(p1->u.array.u.uint8_ptr, p->u.array.u.uint8_ptr + (start << shift), count << shift); } else { + slow_path: + space = max_int(0, p->u.array.count - start); + count = min_int(count, space); for (n = 0; n < count; n++) { val = JS_GetPropertyValue(ctx, this_val, js_int32(start + n)); if (JS_IsException(val)) @@ -52695,41 +52988,53 @@ static JSValue js_typed_array_slice(JSContext *ctx, JSValue this_val, static JSValue js_typed_array_subarray(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + JSArrayBuffer *abuf; + JSTypedArray *ta; JSValue args[4]; JSValue arr, byteOffset, ta_buffer; JSObject *p; int len, start, final, count, shift, offset; - p = get_typed_array(ctx, this_val, 0); + p = get_typed_array(ctx, this_val); if (!p) goto exception; len = p->u.array.count; if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) goto exception; - final = len; if (!JS_IsUndefined(argv[1])) { if (JS_ToInt32Clamp(ctx, &final, argv[1], 0, len, len)) goto exception; } count = max_int(final - start, 0); - byteOffset = js_typed_array_get_byteOffset(ctx, this_val, 0); + byteOffset = js_typed_array_get_byteOffset(ctx, this_val); if (JS_IsException(byteOffset)) goto exception; + ta = p->u.typed_array; + abuf = ta->buffer->u.array_buffer; + if (ta->offset > abuf->byte_length) + goto range_error; + if (ta->offset == abuf->byte_length && count > 0) { + range_error: + JS_ThrowRangeError(ctx, "invalid offset"); + goto exception; + } shift = typed_array_size_log2(p->class_id); offset = JS_VALUE_GET_INT(byteOffset) + (start << shift); JS_FreeValue(ctx, byteOffset); - ta_buffer = js_typed_array_get_buffer(ctx, this_val, 0); + ta_buffer = js_typed_array_get_buffer(ctx, this_val); if (JS_IsException(ta_buffer)) goto exception; args[0] = this_val; args[1] = ta_buffer; args[2] = js_int32(offset); args[3] = js_int32(count); + // result is length-tracking if source TA is and no explicit count is given + if (ta->track_rab && JS_IsUndefined(argv[1])) + args[3] = JS_UNDEFINED; arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 4, args); JS_FreeValue(ctx, ta_buffer); return arr; - exception: return JS_EXCEPTION; } @@ -52850,7 +53155,6 @@ struct TA_sort_context { JSValue arr; JSValue cmp; JSValue (*getfun)(JSContext *ctx, const void *a); - uint8_t *array_ptr; /* cannot change unless the array is detached */ int elt_size; }; @@ -52864,16 +53168,18 @@ static int js_TA_cmp_generic(const void *a, const void *b, void *opaque) { int cmp; p = JS_VALUE_GET_OBJ(psc->arr); - if (typed_array_is_detached(ctx, p)) + if (typed_array_is_oob(p)) return 0; cmp = 0; if (!psc->exception) { a_idx = *(uint32_t *)a; b_idx = *(uint32_t *)b; - argv[0] = psc->getfun(ctx, psc->array_ptr + + if (a_idx >= p->u.array.count || b_idx >= p->u.array.count) + return 0; + argv[0] = psc->getfun(ctx, (char *)p->u.array.u.ptr + a_idx * (size_t)psc->elt_size); - argv[1] = psc->getfun(ctx, psc->array_ptr + + argv[1] = psc->getfun(ctx, (char *)p->u.array.u.ptr + b_idx * (size_t)(psc->elt_size)); res = JS_Call(ctx, psc->cmp, JS_UNDEFINED, 2, argv); if (JS_IsException(res)) { @@ -52910,22 +53216,24 @@ static JSValue js_typed_array_sort(JSContext *ctx, JSValue this_val, int len; size_t elt_size; struct TA_sort_context tsc; - void *array_ptr; int (*cmpfun)(const void *a, const void *b, void *opaque); + p = get_typed_array(ctx, this_val); + if (!p) + return JS_EXCEPTION; + if (typed_array_is_oob(p)) + return JS_ThrowTypeErrorArrayBufferOOB(ctx); + tsc.ctx = ctx; tsc.exception = 0; tsc.arr = this_val; tsc.cmp = argv[0]; - len = js_typed_array_get_length_internal(ctx, this_val); - if (len < 0) - return JS_EXCEPTION; if (!JS_IsUndefined(tsc.cmp) && check_function(ctx, tsc.cmp)) return JS_EXCEPTION; + len = p->u.array.count; if (len > 1) { - p = JS_VALUE_GET_OBJ(this_val); switch (p->class_id) { case JS_CLASS_INT8_ARRAY: tsc.getfun = js_TA_get_int8; @@ -52975,7 +53283,6 @@ static JSValue js_typed_array_sort(JSContext *ctx, JSValue this_val, default: abort(); } - array_ptr = p->u.array.u.ptr; elt_size = 1 << typed_array_size_log2(p->class_id); if (!JS_IsUndefined(tsc.cmp)) { uint32_t *array_idx; @@ -52988,14 +53295,16 @@ static JSValue js_typed_array_sort(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; for(i = 0; i < len; i++) array_idx[i] = i; - tsc.array_ptr = array_ptr; tsc.elt_size = elt_size; rqsort(array_idx, len, sizeof(array_idx[0]), js_TA_cmp_generic, &tsc); if (tsc.exception) goto fail; // per spec: typed array can be detached mid-iteration - if (typed_array_is_detached(ctx, p)) + if (typed_array_is_oob(p)) + goto done; + len = min_int(len, p->u.array.count); + if (len == 0) goto done; array_tmp = js_malloc(ctx, len * elt_size); if (!array_tmp) { @@ -53003,30 +53312,30 @@ static JSValue js_typed_array_sort(JSContext *ctx, JSValue this_val, js_free(ctx, array_idx); return JS_EXCEPTION; } - memcpy(array_tmp, array_ptr, len * elt_size); + memcpy(array_tmp, p->u.array.u.ptr, len * elt_size); switch(elt_size) { case 1: for(i = 0; i < len; i++) { j = array_idx[i]; - ((uint8_t *)array_ptr)[i] = ((uint8_t *)array_tmp)[j]; + p->u.array.u.uint8_ptr[i] = ((uint8_t *)array_tmp)[j]; } break; case 2: for(i = 0; i < len; i++) { j = array_idx[i]; - ((uint16_t *)array_ptr)[i] = ((uint16_t *)array_tmp)[j]; + p->u.array.u.uint16_ptr[i] = ((uint16_t *)array_tmp)[j]; } break; case 4: for(i = 0; i < len; i++) { j = array_idx[i]; - ((uint32_t *)array_ptr)[i] = ((uint32_t *)array_tmp)[j]; + p->u.array.u.uint32_ptr[i] = ((uint32_t *)array_tmp)[j]; } break; case 8: for(i = 0; i < len; i++) { j = array_idx[i]; - ((uint64_t *)array_ptr)[i] = ((uint64_t *)array_tmp)[j]; + p->u.array.u.uint64_ptr[i] = ((uint64_t *)array_tmp)[j]; } break; default: @@ -53036,7 +53345,7 @@ static JSValue js_typed_array_sort(JSContext *ctx, JSValue this_val, done: js_free(ctx, array_idx); } else { - rqsort(array_ptr, len, elt_size, cmpfun, &tsc); + rqsort(p->u.array.u.ptr, len, elt_size, cmpfun, &tsc); if (tsc.exception) return JS_EXCEPTION; } @@ -53050,11 +53359,11 @@ static JSValue js_typed_array_toSorted(JSContext *ctx, JSValue this_val, JSValue arr, ret; JSObject *p; - p = get_typed_array(ctx, this_val, /*is_dataview*/0); + p = get_typed_array(ctx, this_val); if (!p) return JS_EXCEPTION; arr = js_typed_array_constructor_ta(ctx, JS_UNDEFINED, this_val, - p->class_id); + p->class_id, p->u.array.count); if (JS_IsException(arr)) return JS_EXCEPTION; ret = js_typed_array_sort(ctx, arr, argc, argv); @@ -53072,9 +53381,9 @@ static const JSCFunctionListEntry js_typed_array_base_proto_funcs[] = { JS_CGETSET_DEF("length", js_typed_array_get_length, NULL ), JS_CFUNC_DEF("at", 1, js_typed_array_at ), JS_CFUNC_DEF("with", 2, js_typed_array_with ), - JS_CGETSET_MAGIC_DEF("buffer", js_typed_array_get_buffer, NULL, 0 ), - JS_CGETSET_MAGIC_DEF("byteLength", js_typed_array_get_byteLength, NULL, 0 ), - JS_CGETSET_MAGIC_DEF("byteOffset", js_typed_array_get_byteOffset, NULL, 0 ), + JS_CGETSET_DEF("buffer", js_typed_array_get_buffer, NULL ), + JS_CGETSET_DEF("byteLength", js_typed_array_get_byteLength, NULL ), + JS_CGETSET_DEF("byteOffset", js_typed_array_get_byteOffset, NULL ), JS_CFUNC_DEF("set", 1, js_typed_array_set ), JS_CFUNC_MAGIC_DEF("values", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_VALUE ), JS_ALIAS_DEF("[Symbol.iterator]", "values" ), @@ -53116,8 +53425,8 @@ static JSValue js_typed_array_base_constructor(JSContext *ctx, } /* 'obj' must be an allocated typed array object */ -static int typed_array_init(JSContext *ctx, JSValue obj, - JSValue buffer, uint64_t offset, uint64_t len) +static int typed_array_init(JSContext *ctx, JSValue obj, JSValue buffer, + uint64_t offset, uint64_t len, BOOL track_rab) { JSTypedArray *ta; JSObject *p, *pbuffer; @@ -53137,6 +53446,7 @@ static int typed_array_init(JSContext *ctx, JSValue obj, ta->buffer = pbuffer; ta->offset = offset; ta->length = len << size_log2; + ta->track_rab = track_rab; list_add_tail(&ta->link, &abuf->array_list); p->u.typed_array = ta; p->u.array.count = len; @@ -53218,10 +53528,11 @@ static JSValue js_typed_array_constructor_obj(JSContext *ctx, } buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED, - len << size_log2); + len << size_log2, + NULL); if (JS_IsException(buffer)) goto fail; - if (typed_array_init(ctx, ret, buffer, 0, len)) + if (typed_array_init(ctx, ret, buffer, 0, len, /*track_rab*/FALSE)) goto fail; for(i = 0; i < len; i++) { @@ -53242,12 +53553,12 @@ static JSValue js_typed_array_constructor_obj(JSContext *ctx, static JSValue js_typed_array_constructor_ta(JSContext *ctx, JSValue new_target, JSValue src_obj, - int classid) + int classid, uint32_t len) { JSObject *p, *src_buffer; JSTypedArray *ta; JSValue obj, buffer; - uint32_t len, i; + uint32_t i; int size_log2; JSArrayBuffer *src_abuf, *abuf; @@ -53255,27 +53566,27 @@ static JSValue js_typed_array_constructor_ta(JSContext *ctx, if (JS_IsException(obj)) return obj; p = JS_VALUE_GET_OBJ(src_obj); - if (typed_array_is_detached(ctx, p)) { - JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + if (typed_array_is_oob(p)) { + JS_ThrowTypeErrorArrayBufferOOB(ctx); goto fail; } ta = p->u.typed_array; - len = p->u.array.count; src_buffer = ta->buffer; src_abuf = src_buffer->u.array_buffer; size_log2 = typed_array_size_log2(classid); buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED, - (uint64_t)len << size_log2); + (uint64_t)len << size_log2, + NULL); if (JS_IsException(buffer)) goto fail; /* necessary because it could have been detached */ - if (typed_array_is_detached(ctx, p)) { + if (typed_array_is_oob(p)) { JS_FreeValue(ctx, buffer); - JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + JS_ThrowTypeErrorArrayBufferOOB(ctx); goto fail; } abuf = JS_GetOpaque(buffer, JS_CLASS_ARRAY_BUFFER); - if (typed_array_init(ctx, obj, buffer, 0, len)) + if (typed_array_init(ctx, obj, buffer, 0, len, /*track_rab*/FALSE)) goto fail; if (p->class_id == classid) { /* same type: copy the content */ @@ -53301,6 +53612,7 @@ static JSValue js_typed_array_constructor(JSContext *ctx, int argc, JSValue *argv, int classid) { + BOOL track_rab = FALSE; JSValue buffer, obj; JSArrayBuffer *abuf; int size_log2; @@ -53311,7 +53623,8 @@ static JSValue js_typed_array_constructor(JSContext *ctx, if (JS_ToIndex(ctx, &len, argv[0])) return JS_EXCEPTION; buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED, - len << size_log2); + len << size_log2, + NULL); if (JS_IsException(buffer)) return JS_EXCEPTION; offset = 0; @@ -53328,8 +53641,10 @@ static JSValue js_typed_array_constructor(JSContext *ctx, offset > abuf->byte_length) return JS_ThrowRangeError(ctx, "invalid offset"); if (JS_IsUndefined(argv[2])) { - if ((abuf->byte_length & ((1 << size_log2) - 1)) != 0) - goto invalid_length; + track_rab = array_buffer_is_resizable(abuf); + if (!track_rab) + if ((abuf->byte_length & ((1 << size_log2) - 1)) != 0) + goto invalid_length; len = (abuf->byte_length - offset) >> size_log2; } else { if (JS_ToIndex(ctx, &len, argv[2])) @@ -53345,7 +53660,8 @@ static JSValue js_typed_array_constructor(JSContext *ctx, } else { if (p->class_id >= JS_CLASS_UINT8C_ARRAY && p->class_id <= JS_CLASS_FLOAT64_ARRAY) { - return js_typed_array_constructor_ta(ctx, new_target, argv[0], classid); + return js_typed_array_constructor_ta(ctx, new_target, argv[0], + classid, p->u.array.count); } else { return js_typed_array_constructor_obj(ctx, new_target, argv[0], classid); } @@ -53357,7 +53673,7 @@ static JSValue js_typed_array_constructor(JSContext *ctx, JS_FreeValue(ctx, buffer); return JS_EXCEPTION; } - if (typed_array_init(ctx, obj, buffer, offset, len)) { + if (typed_array_init(ctx, obj, buffer, offset, len, track_rab)) { JS_FreeValue(ctx, obj); return JS_EXCEPTION; } @@ -53393,6 +53709,8 @@ static JSValue js_dataview_constructor(JSContext *ctx, JSValue new_target, int argc, JSValue *argv) { + BOOL recompute_len = FALSE; + BOOL track_rab = FALSE; JSArrayBuffer *abuf; uint64_t offset; uint32_t len; @@ -53422,6 +53740,9 @@ static JSValue js_dataview_constructor(JSContext *ctx, if (l > len) return JS_ThrowRangeError(ctx, "invalid byteLength"); len = l; + } else { + recompute_len = TRUE; + track_rab = array_buffer_is_resizable(abuf); } obj = js_create_from_ctor(ctx, new_target, JS_CLASS_DATAVIEW); @@ -53432,6 +53753,16 @@ static JSValue js_dataview_constructor(JSContext *ctx, JS_ThrowTypeErrorDetachedArrayBuffer(ctx); goto fail; } + // RAB could have been resized in js_create_from_ctor() + if (offset > abuf->byte_length) { + goto out_of_bound; + } else if (recompute_len) { + len = abuf->byte_length - offset; + } else if (offset + len > abuf->byte_length) { + out_of_bound: + JS_ThrowRangeError(ctx, "invalid byteOffset or byteLength"); + goto fail; + } ta = js_malloc(ctx, sizeof(*ta)); if (!ta) { fail: @@ -53443,11 +53774,88 @@ static JSValue js_dataview_constructor(JSContext *ctx, ta->buffer = JS_VALUE_GET_OBJ(js_dup(buffer)); ta->offset = offset; ta->length = len; + ta->track_rab = track_rab; list_add_tail(&ta->link, &abuf->array_list); p->u.typed_array = ta; return obj; } +// is the DataView out of bounds relative to its parent arraybuffer? +static BOOL dataview_is_oob(JSObject *p) +{ + JSArrayBuffer *abuf; + JSTypedArray *ta; + + assert(p->class_id == JS_CLASS_DATAVIEW); + ta = p->u.typed_array; + abuf = ta->buffer->u.array_buffer; + if (abuf->detached) + return TRUE; + if (ta->offset > abuf->byte_length) + return TRUE; + if (ta->track_rab) + return FALSE; + return (int64_t)ta->offset + ta->length > abuf->byte_length; +} + +static JSObject *get_dataview(JSContext *ctx, JSValue this_val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) + goto fail; + p = JS_VALUE_GET_OBJ(this_val); + if (p->class_id != JS_CLASS_DATAVIEW) { + fail: + JS_ThrowTypeError(ctx, "not a DataView"); + return NULL; + } + return p; +} + +static JSValue js_dataview_get_buffer(JSContext *ctx, JSValue this_val) +{ + JSObject *p; + JSTypedArray *ta; + p = get_dataview(ctx, this_val); + if (!p) + return JS_EXCEPTION; + ta = p->u.typed_array; + return js_dup(JS_MKPTR(JS_TAG_OBJECT, ta->buffer)); +} + +static JSValue js_dataview_get_byteLength(JSContext *ctx, JSValue this_val) +{ + JSArrayBuffer *abuf; + JSTypedArray *ta; + JSObject *p; + + p = get_dataview(ctx, this_val); + if (!p) + return JS_EXCEPTION; + if (dataview_is_oob(p)) + return JS_ThrowTypeErrorArrayBufferOOB(ctx); + ta = p->u.typed_array; + if (ta->track_rab) { + abuf = ta->buffer->u.array_buffer; + return js_uint32(abuf->byte_length - ta->offset); + } + return js_uint32(ta->length); +} + +static JSValue js_dataview_get_byteOffset(JSContext *ctx, JSValue this_val) +{ + JSTypedArray *ta; + JSObject *p; + + p = get_dataview(ctx, this_val); + if (!p) + return JS_EXCEPTION; + if (dataview_is_oob(p)) + return JS_ThrowTypeErrorArrayBufferOOB(ctx); + ta = p->u.typed_array; + return js_uint32(ta->offset); +} + static JSValue js_dataview_getValue(JSContext *ctx, JSValue this_obj, int argc, JSValue *argv, int class_id) @@ -53471,8 +53879,14 @@ static JSValue js_dataview_getValue(JSContext *ctx, abuf = ta->buffer->u.array_buffer; if (abuf->detached) return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + // order matters: this check should come before the next one if ((pos + size) > ta->length) return JS_ThrowRangeError(ctx, "out of bound"); + // test262 expects a TypeError for this and V8, in its infinite wisdom, + // throws a "detached array buffer" exception, but IMO that doesn't make + // sense because the buffer is not in fact detached, it's still there + if ((int64_t)ta->offset + ta->length > abuf->byte_length) + return JS_ThrowTypeError(ctx, "out of bound"); ptr = abuf->data + ta->offset + pos; switch(class_id) { @@ -53609,8 +54023,14 @@ static JSValue js_dataview_setValue(JSContext *ctx, abuf = ta->buffer->u.array_buffer; if (abuf->detached) return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + // order matters: this check should come before the next one if ((pos + size) > ta->length) return JS_ThrowRangeError(ctx, "out of bound"); + // test262 expects a TypeError for this and V8, in its infinite wisdom, + // throws a "detached array buffer" exception, but IMO that doesn't make + // sense because the buffer is not in fact detached, it's still there + if ((int64_t)ta->offset + ta->length > abuf->byte_length) + return JS_ThrowTypeError(ctx, "out of bound"); ptr = abuf->data + ta->offset + pos; switch(class_id) { @@ -53646,9 +54066,9 @@ static JSValue js_dataview_setValue(JSContext *ctx, } static const JSCFunctionListEntry js_dataview_proto_funcs[] = { - JS_CGETSET_MAGIC_DEF("buffer", js_typed_array_get_buffer, NULL, 1 ), - JS_CGETSET_MAGIC_DEF("byteLength", js_typed_array_get_byteLength, NULL, 1 ), - JS_CGETSET_MAGIC_DEF("byteOffset", js_typed_array_get_byteOffset, NULL, 1 ), + JS_CGETSET_DEF("buffer", js_dataview_get_buffer, NULL ), + JS_CGETSET_DEF("byteLength", js_dataview_get_byteLength, NULL ), + JS_CGETSET_DEF("byteOffset", js_dataview_get_byteOffset, NULL ), JS_CFUNC_MAGIC_DEF("getInt8", 1, js_dataview_getValue, JS_CLASS_INT8_ARRAY ), JS_CFUNC_MAGIC_DEF("getUint8", 1, js_dataview_getValue, JS_CLASS_UINT8_ARRAY ), JS_CFUNC_MAGIC_DEF("getInt16", 1, js_dataview_getValue, JS_CLASS_INT16_ARRAY ), @@ -53685,7 +54105,7 @@ static JSValue js_new_uint8array(JSContext *ctx, JSValue buffer) } JSArrayBuffer *abuf = js_get_array_buffer(ctx, buffer); assert(abuf != NULL); - if (typed_array_init(ctx, obj, buffer, 0, abuf->byte_length)) { + if (typed_array_init(ctx, obj, buffer, 0, abuf->byte_length, /*track_rab*/FALSE)) { // 'buffer' is freed on error above. JS_FreeValue(ctx, obj); return JS_EXCEPTION; @@ -53697,15 +54117,17 @@ JSValue JS_NewUint8Array(JSContext *ctx, uint8_t *buf, size_t len, JSFreeArrayBufferDataFunc *free_func, void *opaque, JS_BOOL is_shared) { - JSValue buffer = js_array_buffer_constructor3(ctx, JS_UNDEFINED, len, - is_shared ? JS_CLASS_SHARED_ARRAY_BUFFER : JS_CLASS_ARRAY_BUFFER, - buf, free_func, opaque, FALSE); + JSClassID class_id = + is_shared ? JS_CLASS_SHARED_ARRAY_BUFFER : JS_CLASS_ARRAY_BUFFER; + JSValue buffer = js_array_buffer_constructor3(ctx, JS_UNDEFINED, len, NULL, + class_id, buf, free_func, + opaque, FALSE); return js_new_uint8array(ctx, buffer); } JSValue JS_NewUint8ArrayCopy(JSContext *ctx, const uint8_t *buf, size_t len) { - JSValue buffer = js_array_buffer_constructor3(ctx, JS_UNDEFINED, len, + JSValue buffer = js_array_buffer_constructor3(ctx, JS_UNDEFINED, len, NULL, JS_CLASS_ARRAY_BUFFER, (uint8_t *)buf, js_array_buffer_free, NULL, diff --git a/test262.conf b/test262.conf index 5384a0b..dfec6f7 100644 --- a/test262.conf +++ b/test262.conf @@ -173,7 +173,7 @@ regexp-named-groups regexp-unicode-property-escapes regexp-v-flag RegExp.escape=skip -resizable-arraybuffer=skip +resizable-arraybuffer rest-parameters Set set-methods diff --git a/test262_errors.txt b/test262_errors.txt index dc71ba7..bfcf918 100644 --- a/test262_errors.txt +++ b/test262_errors.txt @@ -389,4 +389,7 @@ test262/test/language/expressions/assignment/target-super-computed-reference.js: test262/test/language/expressions/in/private-field-invalid-assignment-target.js:23: unexpected error type: Test262: This statement should not be evaluated. test262/test/language/expressions/in/private-field-invalid-assignment-target.js:23: strict mode: unexpected error type: Test262: This statement should not be evaluated. test262/test/language/module-code/top-level-await/async-module-does-not-block-sibling-modules.js:13: SyntaxError: Could not find export 'check' in module 'test262/test/language/module-code/top-level-await/async-module-sync_FIXTURE.js' +test262/test/staging/ArrayBuffer/resizable/object-define-property-define-properties.js:55: strict mode: unexpected error: Test262Error: Expected a TypeError to be thrown but no exception was thrown at all +test262/test/staging/ArrayBuffer/resizable/object-define-property-parameter-conversion-grows.js:67: strict mode: unexpected error: TypeError: out-of-bound index in typed array +test262/test/staging/ArrayBuffer/resizable/object-define-property-parameter-conversion-shrinks.js:59: strict mode: unexpected error: Test262Error: Expected a TypeError to be thrown but no exception was thrown at all test262/test/staging/top-level-await/tla-hang-entry.js:10: TypeError: $DONE() not called diff --git a/tests/test_bjson.js b/tests/test_bjson.js index 4976015..db4a962 100644 --- a/tests/test_bjson.js +++ b/tests/test_bjson.js @@ -139,6 +139,41 @@ function bjson_test(a) } } +function bjson_test_arraybuffer() +{ + var buf, array_buffer; + + array_buffer = new ArrayBuffer(4); + assert(array_buffer.byteLength, 4); + assert(array_buffer.maxByteLength, 4); + assert(array_buffer.resizable, false); + buf = bjson.write(array_buffer); + array_buffer = bjson.read(buf, 0, buf.byteLength); + assert(array_buffer.byteLength, 4); + assert(array_buffer.maxByteLength, 4); + assert(array_buffer.resizable, false); + + array_buffer = new ArrayBuffer(4, {maxByteLength: 4}); + assert(array_buffer.byteLength, 4); + assert(array_buffer.maxByteLength, 4); + assert(array_buffer.resizable, true); + buf = bjson.write(array_buffer); + array_buffer = bjson.read(buf, 0, buf.byteLength); + assert(array_buffer.byteLength, 4); + assert(array_buffer.maxByteLength, 4); + assert(array_buffer.resizable, true); + + array_buffer = new ArrayBuffer(4, {maxByteLength: 8}); + assert(array_buffer.byteLength, 4); + assert(array_buffer.maxByteLength, 8); + assert(array_buffer.resizable, true); + buf = bjson.write(array_buffer); + array_buffer = bjson.read(buf, 0, buf.byteLength); + assert(array_buffer.byteLength, 4); + assert(array_buffer.maxByteLength, 8); + assert(array_buffer.resizable, true); +} + /* test multiple references to an object including circular references */ function bjson_test_reference() @@ -292,6 +327,7 @@ function bjson_test_all() assert(e instanceof TypeError); } + bjson_test_arraybuffer(); bjson_test_reference(); bjson_test_regexp(); bjson_test_map();