MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

improve buffer roots

+216 -7
+58
examples/spec/buffer.js
··· 247 247 test('Buffer binary from hex byte 2', binaryFromHex[2], 0x02); 248 248 test('Buffer binary from hex byte 3', binaryFromHex[3], 0xff); 249 249 250 + console.log('\nArrayBuffer Transfer Tests\n'); 251 + 252 + const transferBuf = new ArrayBuffer(16); 253 + const transferView = new Uint8Array(transferBuf); 254 + transferView[0] = 42; 255 + transferView[1] = 123; 256 + test('ArrayBuffer detached initial', transferBuf.detached, false); 257 + 258 + const newBuf = transferBuf.transfer(32); 259 + test('ArrayBuffer transfer - old detached', transferBuf.detached, true); 260 + test('ArrayBuffer transfer - old byteLength', transferBuf.byteLength, 0); 261 + test('ArrayBuffer transfer - new detached', newBuf.detached, false); 262 + test('ArrayBuffer transfer - new byteLength', newBuf.byteLength, 32); 263 + 264 + const newView = new Uint8Array(newBuf); 265 + test('ArrayBuffer transfer - data preserved [0]', newView[0], 42); 266 + test('ArrayBuffer transfer - data preserved [1]', newView[1], 123); 267 + 268 + test('TypedArray on detached buffer read', transferView[0], undefined); 269 + transferView[0] = 99; 270 + test('TypedArray on detached buffer write ignored', transferView[0], undefined); 271 + 272 + const shrinkBuf = new ArrayBuffer(100); 273 + const shrunkBuf = shrinkBuf.transfer(50); 274 + test('ArrayBuffer transfer shrink - old detached', shrinkBuf.detached, true); 275 + test('ArrayBuffer transfer shrink - new byteLength', shrunkBuf.byteLength, 50); 276 + 277 + const sameSizeBuf = new ArrayBuffer(64); 278 + const sameSizeView = new Uint8Array(sameSizeBuf); 279 + sameSizeView[0] = 255; 280 + const transferredSame = sameSizeBuf.transfer(); 281 + test('ArrayBuffer transfer no arg - uses same size', transferredSame.byteLength, 64); 282 + test('ArrayBuffer transfer no arg - data preserved', new Uint8Array(transferredSame)[0], 255); 283 + 284 + const fixedBuf = new ArrayBuffer(32); 285 + const fixedTransferred = fixedBuf.transferToFixedLength(16); 286 + test('ArrayBuffer transferToFixedLength - old detached', fixedBuf.detached, true); 287 + test('ArrayBuffer transferToFixedLength - new byteLength', fixedTransferred.byteLength, 16); 288 + 289 + let transferError = null; 290 + try { 291 + transferBuf.transfer(); 292 + } catch (e) { 293 + transferError = e.message; 294 + } 295 + test('ArrayBuffer transfer detached throws', transferError, 'Cannot transfer a detached ArrayBuffer'); 296 + 297 + let sliceError = null; 298 + try { 299 + transferBuf.slice(0, 10); 300 + } catch (e) { 301 + sliceError = e.message; 302 + } 303 + test('ArrayBuffer slice detached throws', sliceError, 'Cannot slice a detached ArrayBuffer'); 304 + 305 + const sharedBuf = new SharedArrayBuffer(16); 306 + test('SharedArrayBuffer detached', sharedBuf.detached, undefined); 307 + 250 308 summary();
+2
include/modules/buffer.h
··· 5 5 #include <stddef.h> 6 6 7 7 void init_buffer_module(void); 8 + void cleanup_buffer_module(void); 8 9 9 10 typedef struct { 10 11 uint8_t *data; ··· 12 13 size_t capacity; 13 14 int ref_count; 14 15 int is_shared; 16 + int is_detached; 15 17 } ArrayBufferData; 16 18 17 19 typedef enum {
+26 -5
src/ant.c
··· 14237 14237 14238 14238 jsoff_t existing_off = lkp(js, as_obj, prop_str, prop_len); 14239 14239 14240 + if (existing_off == 0) { 14241 + if (js_truthy(js, get_slot(js, as_obj, SLOT_FROZEN))) 14242 + return js_mkerr(js, "Cannot define property %.*s, object is not extensible", (int)prop_len, prop_str); 14243 + if (js_truthy(js, get_slot(js, as_obj, SLOT_SEALED))) 14244 + return js_mkerr(js, "Cannot define property %.*s, object is not extensible", (int)prop_len, prop_str); 14245 + if (get_slot(js, as_obj, SLOT_EXTENSIBLE) == js_mkfalse()) 14246 + return js_mkerr(js, "Cannot define property %.*s, object is not extensible", (int)prop_len, prop_str); 14247 + } 14248 + 14240 14249 if (has_get || has_set) { 14241 14250 int desc_flags = 14242 14251 (enumerable ? JS_DESC_E : 0) | ··· 14262 14271 js_set_descriptor(js, as_obj, prop_str, prop_len, desc_flags); 14263 14272 14264 14273 if (existing_off > 0) { 14265 - if (is_nonconfig_prop(js, existing_off)) { 14266 - return js_mkerr(js, "Cannot redefine non-configurable property"); 14267 - } 14274 + bool is_frozen = js_truthy(js, get_slot(js, as_obj, SLOT_FROZEN)); 14275 + bool is_nonconfig = is_nonconfig_prop(js, existing_off) || is_frozen; 14276 + bool is_readonly = is_const_prop(js, existing_off) || is_frozen; 14268 14277 14269 - if (has_value) { 14270 - saveval(js, existing_off + sizeof(jsoff_t) * 2, value); 14278 + if (is_nonconfig) { 14279 + if (configurable) return js_mkerr(js, 14280 + "Cannot redefine property %.*s: cannot change configurable from false to true", 14281 + (int)prop_len, prop_str 14282 + ); 14283 + 14284 + if (is_readonly && has_writable && writable) return js_mkerr(js, 14285 + "Cannot redefine property %.*s: cannot change writable from false to true", 14286 + (int)prop_len, prop_str 14287 + ); 14271 14288 } 14289 + 14290 + if (is_readonly && has_value) return js_mkerr(js, "Cannot assign to read-only property '%.*s'", (int)prop_len, prop_str); 14291 + if (has_value) saveval(js, existing_off + sizeof(jsoff_t) * 2, value); 14272 14292 14273 14293 if (!writable || !configurable) { 14274 14294 jsoff_t head = (jsoff_t) vdata(as_obj); ··· 22035 22055 22036 22056 esm_cleanup_module_cache(); 22037 22057 code_arena_reset(); 22058 + cleanup_buffer_module(); 22038 22059 22039 22060 if (js->errmsg) { 22040 22061 free(js->errmsg);
+130 -2
src/modules/buffer.c
··· 19 19 #include "modules/symbol.h" 20 20 21 21 #define TA_ARENA_SIZE (16 * 1024 * 1024) 22 + #define BUFFER_REGISTRY_INITIAL_CAP 64 23 + 22 24 static uint8_t *ta_arena = NULL; 23 25 static size_t ta_arena_offset = 0; 24 26 27 + static ArrayBufferData **buffer_registry = NULL; 28 + static size_t buffer_registry_count = 0; 29 + static size_t buffer_registry_cap = 0; 30 + 31 + static void register_buffer(ArrayBufferData *data) { 32 + if (!data) return; 33 + 34 + if (!buffer_registry) { 35 + buffer_registry = calloc(BUFFER_REGISTRY_INITIAL_CAP, sizeof(ArrayBufferData *)); 36 + if (!buffer_registry) return; 37 + buffer_registry_cap = BUFFER_REGISTRY_INITIAL_CAP; 38 + } 39 + 40 + if (buffer_registry_count >= buffer_registry_cap) { 41 + size_t new_cap = buffer_registry_cap * 2; 42 + ArrayBufferData **new_reg = realloc(buffer_registry, new_cap * sizeof(ArrayBufferData *)); 43 + if (!new_reg) return; 44 + buffer_registry = new_reg; 45 + buffer_registry_cap = new_cap; 46 + } 47 + 48 + buffer_registry[buffer_registry_count++] = data; 49 + } 50 + 51 + static void unregister_buffer(ArrayBufferData *data) { 52 + if (!data || !buffer_registry) return; 53 + 54 + for (size_t i = 0; i < buffer_registry_count; i++) { 55 + if (buffer_registry[i] == data) { 56 + buffer_registry[i] = buffer_registry[--buffer_registry_count]; 57 + return; 58 + } 59 + } 60 + } 61 + 25 62 static inline ssize_t normalize_index(ssize_t idx, ssize_t len) { 26 63 if (idx < 0) idx += len; 27 64 if (idx < 0) return 0; ··· 153 190 data->capacity = length; 154 191 data->ref_count = 1; 155 192 data->is_shared = 0; 193 + data->is_detached = 0; 194 + 195 + register_buffer(data); 156 196 return data; 157 197 } 158 198 ··· 165 205 static void free_array_buffer_data(ArrayBufferData *data) { 166 206 if (!data) return; 167 207 data->ref_count--; 168 - if (data->ref_count <= 0) free(data); 208 + if (data->ref_count <= 0) { 209 + unregister_buffer(data); 210 + free(data); 211 + } 169 212 } 170 213 171 214 static size_t get_element_size(TypedArrayType type) { ··· 216 259 217 260 ArrayBufferData *data = (ArrayBufferData *)(uintptr_t)js_getnum(data_val); 218 261 if (!data) return js_mkerr(js, "Invalid ArrayBuffer"); 262 + if (data->is_detached) return js_mkerr(js, "Cannot slice a detached ArrayBuffer"); 219 263 220 264 ssize_t len = (ssize_t)data->length; 221 265 ssize_t begin = 0, end = len; ··· 242 286 return new_obj; 243 287 } 244 288 289 + // ArrayBuffer.prototype.transfer(newLength) 290 + static jsval_t js_arraybuffer_transfer(struct js *js, jsval_t *args, int nargs) { 291 + jsval_t this_val = js_getthis(js); 292 + jsval_t data_val = js_get_slot(js, this_val, SLOT_BUFFER); 293 + 294 + if (vtype(data_val) != T_NUM) { 295 + return js_mkerr(js, "Not an ArrayBuffer"); 296 + } 297 + 298 + ArrayBufferData *data = (ArrayBufferData *)(uintptr_t)js_getnum(data_val); 299 + if (!data) return js_mkerr(js, "Invalid ArrayBuffer"); 300 + 301 + if (data->is_detached) { 302 + return js_mkerr(js, "Cannot transfer a detached ArrayBuffer"); 303 + } 304 + 305 + if (data->is_shared) { 306 + return js_mkerr(js, "Cannot transfer a SharedArrayBuffer"); 307 + } 308 + 309 + size_t new_length = data->length; 310 + if (nargs > 0 && vtype(args[0]) == T_NUM) { 311 + new_length = (size_t)js_getnum(args[0]); 312 + } 313 + 314 + ArrayBufferData *new_data = create_array_buffer_data(new_length); 315 + if (!new_data) return js_mkerr(js, "Failed to allocate new ArrayBuffer"); 316 + 317 + size_t copy_length = data->length < new_length ? data->length : new_length; 318 + memcpy(new_data->data, data->data, copy_length); 319 + 320 + data->is_detached = 1; 321 + data->length = 0; 322 + js_set(js, this_val, "byteLength", js_mknum(0)); 323 + 324 + jsval_t new_obj = js_mkobj(js); 325 + jsval_t proto = js_get_ctor_proto(js, "ArrayBuffer", 11); 326 + 327 + if (is_special_object(proto)) js_set_proto(js, new_obj, proto); 328 + js_set_slot(js, new_obj, SLOT_BUFFER, ANT_PTR(new_data)); 329 + js_set(js, new_obj, "byteLength", js_mknum((double)new_length)); 330 + 331 + return new_obj; 332 + } 333 + 334 + // ArrayBuffer.prototype.transferToFixedLength(newLength) 335 + static jsval_t js_arraybuffer_transferToFixedLength(struct js *js, jsval_t *args, int nargs) { 336 + return js_arraybuffer_transfer(js, args, nargs); 337 + } 338 + 339 + // ArrayBuffer.prototype.detached getter 340 + static jsval_t js_arraybuffer_detached_getter(struct js *js, jsval_t *args, int nargs) { 341 + jsval_t this_val = js_getthis(js); 342 + jsval_t data_val = js_get_slot(js, this_val, SLOT_BUFFER); 343 + 344 + if (vtype(data_val) != T_NUM) { 345 + return js_mkfalse(); 346 + } 347 + 348 + ArrayBufferData *data = (ArrayBufferData *)(uintptr_t)js_getnum(data_val); 349 + if (!data) return js_mktrue(); 350 + 351 + return data->is_detached ? js_mktrue() : js_mkfalse(); 352 + } 353 + 245 354 static jsval_t typedarray_index_getter(struct js *js, jsval_t obj, const char *key, size_t key_len) { 246 355 if (key_len == 0 || key_len > 10) return js_mkundef(); 247 356 ··· 255 364 jsval_t ta_val = js_get_slot(js, obj, SLOT_BUFFER); 256 365 TypedArrayData *ta_data = (TypedArrayData *)js_gettypedarray(ta_val); 257 366 if (!ta_data || index >= ta_data->length) return js_mkundef(); 367 + if (!ta_data->buffer || ta_data->buffer->is_detached) return js_mkundef(); 258 368 259 369 uint8_t *data = ta_data->buffer->data + ta_data->byte_offset; 260 370 double value; ··· 291 401 292 402 jsval_t ta_val = js_get_slot(js, obj, SLOT_BUFFER); 293 403 TypedArrayData *ta_data = (TypedArrayData *)js_gettypedarray(ta_val); 294 - if (!ta_data || index >= ta_data->length) return false; 404 + if (!ta_data || index >= ta_data->length) return true; 405 + if (!ta_data->buffer || ta_data->buffer->is_detached) return true; 295 406 296 407 double num_val = vtype(value) == T_NUM ? js_getnum(value) : 0; 297 408 uint8_t *data = ta_data->buffer->data + ta_data->byte_offset; ··· 1558 1669 jsval_t arraybuffer_proto = js_mkobj(js); 1559 1670 1560 1671 js_set(js, arraybuffer_proto, "slice", js_mkfun(js_arraybuffer_slice)); 1672 + js_set(js, arraybuffer_proto, "transfer", js_mkfun(js_arraybuffer_transfer)); 1673 + js_set(js, arraybuffer_proto, "transferToFixedLength", js_mkfun(js_arraybuffer_transferToFixedLength)); 1674 + js_set_getter_desc(js, arraybuffer_proto, "detached", 8, js_mkfun(js_arraybuffer_detached_getter), JS_DESC_E); 1561 1675 js_set(js, arraybuffer_proto, get_toStringTag_sym_key(), js_mkstr(js, "ArrayBuffer", 11)); 1562 1676 1563 1677 js_set_slot(js, arraybuffer_ctor_obj, SLOT_CFUNC, js_mkfun(js_arraybuffer_constructor)); ··· 1653 1767 js_set_descriptor(js, buffer_ctor_obj, "name", 4, 0); 1654 1768 js_set(js, glob, "Buffer", js_obj_to_func(buffer_ctor_obj)); 1655 1769 } 1770 + 1771 + void cleanup_buffer_module(void) { 1772 + if (buffer_registry) { 1773 + for (size_t i = 0; i < buffer_registry_count; i++) { 1774 + if (buffer_registry[i]) free(buffer_registry[i]); 1775 + } 1776 + free(buffer_registry); 1777 + buffer_registry = NULL; 1778 + buffer_registry_count = 0; 1779 + buffer_registry_cap = 0; 1780 + } 1781 + 1782 + ta_arena_offset = 0; 1783 + }