@@ -841,6 +841,7 @@ typedef struct JSArrayBuffer {
841841 int byte_length; /* 0 if detached */
842842 int max_byte_length; /* -1 if not resizable; >= byte_length otherwise */
843843 uint8_t detached;
844+ uint8_t immutable;
844845 uint8_t shared; /* if shared, the array buffer cannot be detached */
845846 uint8_t *data; /* NULL if detached */
846847 struct list_head array_list;
@@ -1342,9 +1343,11 @@ static JSValue js_typed_array_constructor_ta(JSContext *ctx,
13421343 JSValueConst src_obj,
13431344 int classid, uint32_t len);
13441345static bool is_typed_array(JSClassID class_id);
1346+ static bool typed_array_is_immutable(JSObject *p);
13451347static bool typed_array_is_oob(JSObject *p);
13461348static uint32_t typed_array_length(JSObject *p);
13471349static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx);
1350+ static JSValue JS_ThrowTypeErrorImmutableArrayBuffer(JSContext *ctx);
13481351static JSValue JS_ThrowTypeErrorArrayBufferOOB(JSContext *ctx);
13491352static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx,
13501353 bool is_arg);
@@ -9182,6 +9185,10 @@ static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc,
91829185 if (desc) {
91839186 desc->flags = JS_PROP_WRITABLE | JS_PROP_ENUMERABLE |
91849187 JS_PROP_CONFIGURABLE;
9188+ if (is_typed_array(p->class_id) && typed_array_is_immutable(p)) {
9189+ desc->flags &= ~JS_PROP_WRITABLE;
9190+ desc->flags &= ~JS_PROP_CONFIGURABLE;
9191+ }
91859192 desc->getter = JS_UNDEFINED;
91869193 desc->setter = JS_UNDEFINED;
91879194 desc->value = JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx);
@@ -10180,6 +10187,8 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj,
1018010187 case JS_CLASS_UINT8C_ARRAY:
1018110188 if (JS_ToUint8ClampFree(ctx, &v, val))
1018210189 goto ta_cvt_fail;
10190+ if (typed_array_is_immutable(p))
10191+ goto ta_immutable;
1018310192 /* Note: the conversion can detach the typed array, so the
1018410193 array bound check must be done after */
1018510194 if (unlikely(idx >= (uint32_t)p->u.array.count))
@@ -10190,6 +10199,8 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj,
1019010199 case JS_CLASS_UINT8_ARRAY:
1019110200 if (JS_ToInt32Free(ctx, &v, val))
1019210201 goto ta_cvt_fail;
10202+ if (typed_array_is_immutable(p))
10203+ goto ta_immutable;
1019310204 if (unlikely(idx >= (uint32_t)p->u.array.count))
1019410205 goto ta_out_of_bound;
1019510206 p->u.array.u.uint8_ptr[idx] = v;
@@ -10198,6 +10209,8 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj,
1019810209 case JS_CLASS_UINT16_ARRAY:
1019910210 if (JS_ToInt32Free(ctx, &v, val))
1020010211 goto ta_cvt_fail;
10212+ if (typed_array_is_immutable(p))
10213+ goto ta_immutable;
1020110214 if (unlikely(idx >= (uint32_t)p->u.array.count))
1020210215 goto ta_out_of_bound;
1020310216 p->u.array.u.uint16_ptr[idx] = v;
@@ -10206,6 +10219,8 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj,
1020610219 case JS_CLASS_UINT32_ARRAY:
1020710220 if (JS_ToInt32Free(ctx, &v, val))
1020810221 goto ta_cvt_fail;
10222+ if (typed_array_is_immutable(p))
10223+ goto ta_immutable;
1020910224 if (unlikely(idx >= (uint32_t)p->u.array.count))
1021010225 goto ta_out_of_bound;
1021110226 p->u.array.u.uint32_ptr[idx] = v;
@@ -10217,6 +10232,8 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj,
1021710232 int64_t v;
1021810233 if (JS_ToBigInt64Free(ctx, &v, val))
1021910234 goto ta_cvt_fail;
10235+ if (typed_array_is_immutable(p))
10236+ goto ta_immutable;
1022010237 if (unlikely(idx >= (uint32_t)p->u.array.count))
1022110238 goto ta_out_of_bound;
1022210239 p->u.array.u.uint64_ptr[idx] = v;
@@ -10225,13 +10242,17 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj,
1022510242 case JS_CLASS_FLOAT16_ARRAY:
1022610243 if (JS_ToFloat64Free(ctx, &d, val))
1022710244 goto ta_cvt_fail;
10245+ if (typed_array_is_immutable(p))
10246+ goto ta_immutable;
1022810247 if (unlikely(idx >= (uint32_t)p->u.array.count))
1022910248 goto ta_out_of_bound;
1023010249 p->u.array.u.fp16_ptr[idx] = tofp16(d);
1023110250 break;
1023210251 case JS_CLASS_FLOAT32_ARRAY:
1023310252 if (JS_ToFloat64Free(ctx, &d, val))
1023410253 goto ta_cvt_fail;
10254+ if (typed_array_is_immutable(p))
10255+ goto ta_immutable;
1023510256 if (unlikely(idx >= (uint32_t)p->u.array.count))
1023610257 goto ta_out_of_bound;
1023710258 p->u.array.u.float_ptr[idx] = d;
@@ -10245,6 +10266,10 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj,
1024510266 }
1024610267 return -1;
1024710268 }
10269+ if (typed_array_is_immutable(p)) {
10270+ ta_immutable:
10271+ return false;
10272+ }
1024810273 if (unlikely(idx >= (uint32_t)p->u.array.count)) {
1024910274 ta_out_of_bound:
1025010275 if (typed_array_is_oob(p))
@@ -55442,6 +55467,7 @@ static JSValue js_array_buffer_constructor3(JSContext *ctx,
5544255467 }
5544355468 init_list_head(&abuf->array_list);
5544455469 abuf->detached = false;
55470+ abuf->immutable = false;
5544555471 abuf->shared = (class_id == JS_CLASS_SHARED_ARRAY_BUFFER);
5544655472 abuf->opaque = opaque;
5544755473 abuf->free_func = free_func;
@@ -55611,6 +55637,11 @@ static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx)
5561155637 return JS_ThrowTypeError(ctx, "ArrayBuffer is detached");
5561255638}
5561355639
55640+ static JSValue JS_ThrowTypeErrorImmutableArrayBuffer(JSContext *ctx)
55641+ {
55642+ return JS_ThrowTypeError(ctx, "ArrayBuffer is immutable");
55643+ }
55644+
5561455645static JSValue JS_ThrowTypeErrorArrayBufferOOB(JSContext *ctx)
5561555646{
5561655647 return JS_ThrowTypeError(ctx, "ArrayBuffer is detached or resized");
@@ -55628,6 +55659,15 @@ static JSValue js_array_buffer_get_detached(JSContext *ctx,
5562855659 return js_bool(abuf->detached);
5562955660}
5563055661
55662+ static JSValue js_array_buffer_get_immutable(JSContext *ctx,
55663+ JSValueConst this_val)
55664+ {
55665+ JSArrayBuffer *abuf = JS_GetOpaque2(ctx, this_val, JS_CLASS_ARRAY_BUFFER);
55666+ if (!abuf)
55667+ return JS_EXCEPTION;
55668+ return js_bool(abuf->immutable);
55669+ }
55670+
5563155671static JSValue js_array_buffer_get_byteLength(JSContext *ctx,
5563255672 JSValueConst this_val,
5563355673 int class_id)
@@ -55764,28 +55804,38 @@ static void js_array_buffer_update_typed_arrays(JSArrayBuffer *abuf)
5576455804 }
5576555805}
5576655806
55807+ enum {
55808+ JS_ARRAY_BUFFER_TRANSFER,
55809+ JS_ARRAY_BUFFER_TRANSFER_TO_IMMUTABLE,
55810+ JS_ARRAY_BUFFER_TRANSFER_TO_FIXED_LENGTH,
55811+ };
55812+
5576755813// ES #sec-arraybuffer.prototype.transfer
5576855814static JSValue js_array_buffer_transfer(JSContext *ctx, JSValueConst this_val,
5576955815 int argc, JSValueConst *argv, int magic)
5577055816{
55771- bool transfer_to_fixed_length = magic & 1;
5577255817 JSArrayBuffer *abuf;
5577355818 uint64_t new_len, old_len, max_len, *pmax_len;
5577455819 uint8_t *bs, *new_bs;
55820+ JSValue ret;
5577555821
5577655822 abuf = JS_GetOpaque2(ctx, this_val, JS_CLASS_ARRAY_BUFFER);
5577755823 if (!abuf)
5577855824 return JS_EXCEPTION;
5577955825 if (abuf->shared)
5578055826 return JS_ThrowTypeError(ctx, "cannot transfer a SharedArrayBuffer");
55827+ if (magic == JS_ARRAY_BUFFER_TRANSFER_TO_IMMUTABLE && abuf->immutable)
55828+ return JS_ThrowTypeErrorImmutableArrayBuffer(ctx);
5578155829 if (argc < 1 || JS_IsUndefined(argv[0]))
5578255830 new_len = abuf->byte_length;
5578355831 else if (JS_ToIndex(ctx, &new_len, argv[0]))
5578455832 return JS_EXCEPTION;
55833+ if (magic != JS_ARRAY_BUFFER_TRANSFER_TO_IMMUTABLE && abuf->immutable)
55834+ return JS_ThrowTypeErrorImmutableArrayBuffer(ctx);
5578555835 if (abuf->detached)
5578655836 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
5578755837 pmax_len = NULL;
55788- if (!transfer_to_fixed_length ) {
55838+ if (magic == JS_ARRAY_BUFFER_TRANSFER ) {
5578955839 if (array_buffer_is_resizable(abuf)) { // carry over maxByteLength
5579055840 max_len = abuf->max_byte_length;
5579155841 if (new_len > max_len)
@@ -55798,8 +55848,9 @@ static JSValue js_array_buffer_transfer(JSContext *ctx, JSValueConst this_val,
5579855848 /* create an empty AB */
5579955849 if (new_len == 0) {
5580055850 JS_DetachArrayBuffer(ctx, this_val);
55801- return js_array_buffer_constructor2(ctx, JS_UNDEFINED, 0, pmax_len,
55802- JS_CLASS_ARRAY_BUFFER);
55851+ ret = js_array_buffer_constructor2(ctx, JS_UNDEFINED, 0, pmax_len,
55852+ JS_CLASS_ARRAY_BUFFER);
55853+ goto fini;
5580355854 }
5580455855 bs = abuf->data;
5580555856 old_len = abuf->byte_length;
@@ -55817,10 +55868,20 @@ static JSValue js_array_buffer_transfer(JSContext *ctx, JSValueConst this_val,
5581755868 abuf->byte_length = 0;
5581855869 abuf->detached = true;
5581955870 js_array_buffer_update_typed_arrays(abuf);
55820- return js_array_buffer_constructor3(ctx, JS_UNDEFINED, new_len, pmax_len,
55821- JS_CLASS_ARRAY_BUFFER,
55822- bs, abuf->free_func,
55823- NULL, false);
55871+ ret = js_array_buffer_constructor3(ctx, JS_UNDEFINED, new_len, pmax_len,
55872+ JS_CLASS_ARRAY_BUFFER, bs,
55873+ abuf->free_func, NULL,
55874+ /*alloc_flag*/false);
55875+ fini:
55876+ if (magic == JS_ARRAY_BUFFER_TRANSFER_TO_IMMUTABLE) {
55877+ if (JS_IsException(ret))
55878+ return JS_EXCEPTION;
55879+ abuf = JS_GetOpaque2(ctx, ret, JS_CLASS_ARRAY_BUFFER);
55880+ if (!abuf)
55881+ return JS_EXCEPTION;
55882+ abuf->immutable = true;
55883+ }
55884+ return ret;
5582455885}
5582555886
5582655887static JSValue js_array_buffer_resize(JSContext *ctx, JSValueConst this_val,
@@ -55833,6 +55894,8 @@ static JSValue js_array_buffer_resize(JSContext *ctx, JSValueConst this_val,
5583355894 abuf = JS_GetOpaque2(ctx, this_val, class_id);
5583455895 if (!abuf)
5583555896 return JS_EXCEPTION;
55897+ if (abuf->immutable)
55898+ return JS_ThrowTypeErrorImmutableArrayBuffer(ctx);
5583655899 if (JS_ToInt64(ctx, &len, argv[0]))
5583755900 return JS_EXCEPTION;
5583855901 if (abuf->detached)
@@ -55875,17 +55938,23 @@ static JSValue js_array_buffer_resize(JSContext *ctx, JSValueConst this_val,
5587555938
5587655939static JSValue js_array_buffer_slice(JSContext *ctx,
5587755940 JSValueConst this_val,
55878- int argc, JSValueConst *argv, int class_id )
55941+ int argc, JSValueConst *argv, int magic )
5587955942{
5588055943 JSArrayBuffer *abuf, *new_abuf;
5588155944 int64_t len, start, end, new_len;
5588255945 JSValue ctor, new_obj;
55946+ bool immutable;
55947+ int class_id;
5588355948
55949+ immutable = magic & 1;
55950+ class_id = magic >> 1;
5588455951 abuf = JS_GetOpaque2(ctx, this_val, class_id);
5588555952 if (!abuf)
5588655953 return JS_EXCEPTION;
5588755954 if (abuf->detached)
5588855955 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
55956+ if (abuf->immutable)
55957+ return JS_ThrowTypeErrorImmutableArrayBuffer(ctx);
5588955958 len = abuf->byte_length;
5589055959
5589155960 if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len))
@@ -55897,9 +55966,15 @@ static JSValue js_array_buffer_slice(JSContext *ctx,
5589755966 return JS_EXCEPTION;
5589855967 }
5589955968 new_len = max_int64(end - start, 0);
55900- ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED);
55901- if (JS_IsException(ctor))
55902- return ctor;
55969+ // note: difference between slice and sliceToImmutable is that the
55970+ // latter does not have this step:
55971+ // 1. Let _ctor_ be ? SpeciesConstructor(_O_, %ArrayBuffer%)
55972+ ctor = JS_UNDEFINED;
55973+ if (!immutable) {
55974+ ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED);
55975+ if (JS_IsException(ctor))
55976+ return ctor;
55977+ }
5590355978 if (JS_IsUndefined(ctor)) {
5590455979 new_obj = js_array_buffer_constructor2(ctx, JS_UNDEFINED, new_len,
5590555980 NULL, class_id);
@@ -55915,6 +55990,10 @@ static JSValue js_array_buffer_slice(JSContext *ctx,
5591555990 new_abuf = JS_GetOpaque2(ctx, new_obj, class_id);
5591655991 if (!new_abuf)
5591755992 goto fail;
55993+ if (new_abuf->immutable) {
55994+ JS_ThrowTypeErrorImmutableArrayBuffer(ctx);
55995+ goto fail;
55996+ }
5591855997 if (js_same_value(ctx, new_obj, this_val)) {
5591955998 JS_ThrowTypeError(ctx, "cannot use identical ArrayBuffer");
5592055999 goto fail;
@@ -55933,6 +56012,7 @@ static JSValue js_array_buffer_slice(JSContext *ctx,
5593356012 goto fail;
5593456013 }
5593556014 memcpy(new_abuf->data, abuf->data + start, new_len);
56015+ new_abuf->immutable = immutable;
5593656016 return new_obj;
5593756017 fail:
5593856018 JS_FreeValue(ctx, new_obj);
@@ -55944,10 +56024,13 @@ static const JSCFunctionListEntry js_array_buffer_proto_funcs[] = {
5594456024 JS_CGETSET_MAGIC_DEF("maxByteLength", js_array_buffer_get_maxByteLength, NULL, JS_CLASS_ARRAY_BUFFER ),
5594556025 JS_CGETSET_MAGIC_DEF("resizable", js_array_buffer_get_resizable, NULL, JS_CLASS_ARRAY_BUFFER ),
5594656026 JS_CGETSET_DEF("detached", js_array_buffer_get_detached, NULL ),
56027+ JS_CGETSET_DEF("immutable", js_array_buffer_get_immutable, NULL ),
5594756028 JS_CFUNC_MAGIC_DEF("resize", 1, js_array_buffer_resize, JS_CLASS_ARRAY_BUFFER ),
55948- JS_CFUNC_MAGIC_DEF("slice", 2, js_array_buffer_slice, JS_CLASS_ARRAY_BUFFER ),
55949- JS_CFUNC_MAGIC_DEF("transfer", 0, js_array_buffer_transfer, 0 ),
55950- JS_CFUNC_MAGIC_DEF("transferToFixedLength", 0, js_array_buffer_transfer, 1 ),
56029+ JS_CFUNC_MAGIC_DEF("slice", 2, js_array_buffer_slice, JS_CLASS_ARRAY_BUFFER*2 + /*immutable*/0 ),
56030+ JS_CFUNC_MAGIC_DEF("sliceToImmutable", 2, js_array_buffer_slice, JS_CLASS_ARRAY_BUFFER*2 + /*immutable*/1 ),
56031+ JS_CFUNC_MAGIC_DEF("transfer", 0, js_array_buffer_transfer, JS_ARRAY_BUFFER_TRANSFER ),
56032+ JS_CFUNC_MAGIC_DEF("transferToImmutable", 0, js_array_buffer_transfer, JS_ARRAY_BUFFER_TRANSFER_TO_IMMUTABLE ),
56033+ JS_CFUNC_MAGIC_DEF("transferToFixedLength", 0, js_array_buffer_transfer, JS_ARRAY_BUFFER_TRANSFER_TO_FIXED_LENGTH ),
5595156034 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "ArrayBuffer", JS_PROP_CONFIGURABLE ),
5595256035};
5595356036
@@ -55962,7 +56045,7 @@ static const JSCFunctionListEntry js_shared_array_buffer_proto_funcs[] = {
5596256045 JS_CGETSET_MAGIC_DEF("maxByteLength", js_array_buffer_get_maxByteLength, NULL, JS_CLASS_SHARED_ARRAY_BUFFER ),
5596356046 JS_CGETSET_MAGIC_DEF("growable", js_array_buffer_get_resizable, NULL, JS_CLASS_SHARED_ARRAY_BUFFER ),
5596456047 JS_CFUNC_MAGIC_DEF("grow", 1, js_array_buffer_resize, JS_CLASS_SHARED_ARRAY_BUFFER ),
55965- JS_CFUNC_MAGIC_DEF("slice", 2, js_array_buffer_slice, JS_CLASS_SHARED_ARRAY_BUFFER ),
56048+ JS_CFUNC_MAGIC_DEF("slice", 2, js_array_buffer_slice, JS_CLASS_SHARED_ARRAY_BUFFER*2 + /*immutable*/0 ),
5596656049 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "SharedArrayBuffer", JS_PROP_CONFIGURABLE ),
5596756050};
5596856051
@@ -55971,6 +56054,18 @@ static bool is_typed_array(JSClassID class_id)
5597156054 return class_id >= JS_CLASS_UINT8C_ARRAY && class_id <= JS_CLASS_FLOAT64_ARRAY;
5597256055}
5597356056
56057+ // |p| must be a typed array, *not* a DataView
56058+ static bool typed_array_is_immutable(JSObject *p)
56059+ {
56060+ JSArrayBuffer *abuf;
56061+ JSTypedArray *ta;
56062+
56063+ assert(is_typed_array(p->class_id));
56064+ ta = p->u.typed_array;
56065+ abuf = ta->buffer->u.array_buffer;
56066+ return abuf->immutable;
56067+ }
56068+
5597456069// is the typed array detached or out of bounds relative to its RAB?
5597556070// |p| must be a typed array, *not* a DataView
5597656071static bool typed_array_is_oob(JSObject *p)
@@ -56161,6 +56256,10 @@ static JSValue js_typed_array_set_internal(JSContext *ctx,
5616156256 p = get_typed_array(ctx, dst);
5616256257 if (!p)
5616356258 goto fail;
56259+ if (typed_array_is_immutable(p)) {
56260+ JS_ThrowTypeErrorImmutableArrayBuffer(ctx);
56261+ goto fail;
56262+ }
5616456263 if (JS_ToInt64Sat(ctx, &offset, off))
5616556264 goto fail;
5616656265 if (offset < 0)
@@ -56523,6 +56622,8 @@ static JSValue js_typed_array_copyWithin(JSContext *ctx, JSValueConst this_val,
5652356622 p = get_typed_array(ctx, this_val);
5652456623 if (!p)
5652556624 return JS_EXCEPTION;
56625+ if (typed_array_is_immutable(p))
56626+ return JS_ThrowTypeErrorImmutableArrayBuffer(ctx);
5652656627 if (typed_array_is_oob(p))
5652756628 return JS_ThrowTypeErrorArrayBufferOOB(ctx);
5652856629 len = p->u.array.count;
@@ -56565,6 +56666,8 @@ static JSValue js_typed_array_fill(JSContext *ctx, JSValueConst this_val,
5656556666 p = get_typed_array(ctx, this_val);
5656656667 if (!p)
5656756668 return JS_EXCEPTION;
56669+ if (typed_array_is_immutable(p))
56670+ return JS_ThrowTypeErrorImmutableArrayBuffer(ctx);
5656856671 if (typed_array_is_oob(p))
5656956672 return JS_ThrowTypeErrorArrayBufferOOB(ctx);
5657056673 len = p->u.array.count;
@@ -57472,6 +57575,8 @@ static JSValue js_typed_array_sort(JSContext *ctx, JSValueConst this_val,
5747257575 p = get_typed_array(ctx, this_val);
5747357576 if (!p)
5747457577 return JS_EXCEPTION;
57578+ if (typed_array_is_immutable(p))
57579+ return JS_ThrowTypeErrorImmutableArrayBuffer(ctx);
5747557580 if (typed_array_is_oob(p))
5747657581 return JS_ThrowTypeErrorArrayBufferOOB(ctx);
5747757582
@@ -58263,6 +58368,9 @@ static JSValue js_dataview_setValue(JSContext *ctx,
5826358368 ta = JS_GetOpaque2(ctx, this_obj, JS_CLASS_DATAVIEW);
5826458369 if (!ta)
5826558370 return JS_EXCEPTION;
58371+ abuf = ta->buffer->u.array_buffer;
58372+ if (abuf->immutable)
58373+ return JS_ThrowTypeErrorImmutableArrayBuffer(ctx);
5826658374 size = 1 << typed_array_size_log2(class_id);
5826758375 if (JS_ToIndex(ctx, &pos, argv[0]))
5826858376 return JS_EXCEPTION;
@@ -58296,7 +58404,6 @@ static JSValue js_dataview_setValue(JSContext *ctx,
5829658404 }
5829758405 littleEndian = argc > 2 && JS_ToBool(ctx, argv[2]);
5829858406 is_swap = littleEndian ^ !is_be();
58299- abuf = ta->buffer->u.array_buffer;
5830058407 if (abuf->detached)
5830158408 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
5830258409 // order matters: this check should come before the next one
0 commit comments