For TypedArrays read byteOffset before getting start and final

Previously, in issue #418 and pr #417 I misread the spec that in `subarray` we
should be able to access the `byteOffset` from a detached buffer. Thinking more
about it, something didn't seem right and I started a discussion in the TC39
group [1].

It turns out we shouldn't be able to read the ``byteOffset`` from detched
buffers. Instead, the spec says we should just read the `byteOffset` value
before we access `start` and `finish`. In the test262 test [2] the buffer is
detached when accessing the `end` inside the `valueOf()` conversion, and the
test expects to see the `byteOffset` before it was detached.

So to fix it, ensure we access and save the `byteOffset` value first, then get
the `start` and `finish`.

[1]
https://es.discourse.group/t/typedarray-subarray-byteoffset-with-detached-buffers/2381

[2] https://github.com/tc39/test262/blob/main/test/built-ins/TypedArray/prototype/subarray/byteoffset-with-detached-buffer.js
This commit is contained in:
Nick Vatamaniuc 2025-06-29 13:50:55 -04:00
parent 458c34d29d
commit 1ab4972246
No known key found for this signature in database
GPG Key ID: FC04DFBC9657A78E

View File

@ -54097,14 +54097,24 @@ static JSValue js_typed_array_subarray(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv) int argc, JSValueConst *argv)
{ {
JSValueConst args[4]; JSValueConst args[4];
JSValue arr, ta_buffer; JSValue arr, byteOffset, ta_buffer;
JSTypedArray *ta;
JSObject *p; JSObject *p;
int len, start, final, count, shift, offset; int len, start, final, count, shift, byte_offset, offset;
p = get_typed_array(ctx, this_val, 0); p = get_typed_array(ctx, this_val, 0);
if (!p) if (!p)
goto exception; goto exception;
/* Fetch byteOffset first, then access start and final. It's technically
possible for the buffer to be detached when accessing start of final, by
way of `valueOf()` for instance, and the spec indicates we should preserve
the byteOffset value from before it might be detached */
byteOffset = js_typed_array_get_byteOffset(ctx, this_val, 0);
if (JS_IsException(byteOffset))
goto exception;
byte_offset = JS_VALUE_GET_INT(byteOffset);
JS_FreeValue(ctx, byteOffset);
len = p->u.array.count; len = p->u.array.count;
if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len))
goto exception; goto exception;
@ -54116,9 +54126,7 @@ static JSValue js_typed_array_subarray(JSContext *ctx, JSValueConst this_val,
} }
count = max_int(final - start, 0); count = max_int(final - start, 0);
shift = typed_array_size_log2(p->class_id); shift = typed_array_size_log2(p->class_id);
ta = p->u.typed_array; offset = byte_offset + (start << shift);
/* Read byteOffset (ta->offset) even if detached */
offset = ta->offset + (start << shift);
ta_buffer = js_typed_array_get_buffer(ctx, this_val, 0); ta_buffer = js_typed_array_get_buffer(ctx, this_val, 0);
if (JS_IsException(ta_buffer)) if (JS_IsException(ta_buffer))
goto exception; goto exception;