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.

Headers and Blob

+1502 -82
+10 -10
examples/results.txt
··· 464 464 compat-table/es6/Map.constructor-invokes-set.js: failed 465 465 compat-table/es6/Map.constructor-requires-new.js: OK 466 466 compat-table/es6/Map.iterator-closing.js: failed 467 - compat-table/es6/Map.iterator-prototype-chain.js: TypeError: Cannot read properties of null (reading 'hasOwnProperty') 467 + compat-table/es6/Map.iterator-prototype-chain.js: OK 468 468 compat-table/es6/Map.js: OK 469 469 compat-table/es6/Map.prototype-not-instance.js: failed 470 470 compat-table/es6/Map.prototype.Symbol.iterator.js: OK ··· 581 581 compat-table/es6/Set.constructor-invokes-add.js: failed 582 582 compat-table/es6/Set.constructor-requires-new.js: OK 583 583 compat-table/es6/Set.iterator-closing.js: failed 584 - compat-table/es6/Set.iterator-prototype-chain.js: TypeError: Cannot read properties of null (reading 'hasOwnProperty') 584 + compat-table/es6/Set.iterator-prototype-chain.js: OK 585 585 compat-table/es6/Set.js: OK 586 586 compat-table/es6/Set.prototype-not-instance.js: failed 587 587 compat-table/es6/Set.prototype.Symbol.iterator.js: OK ··· 1343 1343 compat-table/es2025/Iterator.instanceof.js: ReferenceError: 'Iterator' is not defined 1344 1344 compat-table/es2025/Iterator.prototype.Symbol.toStringTag.js: ReferenceError: 'Iterator' is not defined 1345 1345 compat-table/es2025/Iterator.prototype.drop.js: TypeError: undefined is not a function 1346 - compat-table/es2025/Iterator.prototype.every.js: OK 1347 - compat-table/es2025/Iterator.prototype.filter.js: OK 1348 - compat-table/es2025/Iterator.prototype.find.js: OK 1349 - compat-table/es2025/Iterator.prototype.flatMap.js: OK 1350 - compat-table/es2025/Iterator.prototype.forEach.js: OK 1351 - compat-table/es2025/Iterator.prototype.map.js: OK 1352 - compat-table/es2025/Iterator.prototype.reduce.js: OK 1353 - compat-table/es2025/Iterator.prototype.some.js: OK 1346 + compat-table/es2025/Iterator.prototype.every.js: TypeError: undefined is not a function 1347 + compat-table/es2025/Iterator.prototype.filter.js: TypeError: undefined is not a function 1348 + compat-table/es2025/Iterator.prototype.find.js: TypeError: undefined is not a function 1349 + compat-table/es2025/Iterator.prototype.flatMap.js: TypeError: undefined is not a function 1350 + compat-table/es2025/Iterator.prototype.forEach.js: TypeError: undefined is not a function 1351 + compat-table/es2025/Iterator.prototype.map.js: TypeError: undefined is not a function 1352 + compat-table/es2025/Iterator.prototype.reduce.js: TypeError: undefined is not a function 1353 + compat-table/es2025/Iterator.prototype.some.js: TypeError: undefined is not a function 1354 1354 compat-table/es2025/Iterator.prototype.take.js: TypeError: undefined is not a function 1355 1355 compat-table/es2025/Iterator.prototype.toArray.js: TypeError: undefined is not a function 1356 1356 compat-table/es2025/Promise.try.js: OK
+113
examples/spec/blob.js
··· 1 + import { test, testThrows, summary } from './helpers.js'; 2 + 3 + console.log('Blob constructor\n'); 4 + 5 + const empty = new Blob(); 6 + test('empty Blob size', empty.size, 0); 7 + test('empty Blob type', empty.type, ''); 8 + 9 + const b1 = new Blob(['hello']); 10 + test('string part size', b1.size, 5); 11 + 12 + const b2 = new Blob(['hello', ' world']); 13 + test('two string parts size', b2.size, 11); 14 + 15 + const b3 = new Blob(['hello'], { type: 'text/plain' }); 16 + test('type set from options', b3.type, 'text/plain'); 17 + 18 + const b4 = new Blob(['hello'], { type: 'TEXT/PLAIN' }); 19 + test('type lowercased', b4.type, 'text/plain'); 20 + 21 + const b5 = new Blob(['x'], { type: 'te\x01xt' }); 22 + test('invalid type byte โ†’ empty string', b5.type, ''); 23 + 24 + testThrows('requires new', () => Blob(['x'])); 25 + 26 + console.log('\nBlob from typed parts\n'); 27 + 28 + const ab = new Uint8Array([1, 2, 3]).buffer; 29 + const b6 = new Blob([ab]); 30 + test('ArrayBuffer part size', b6.size, 3); 31 + 32 + const ta = new Uint8Array([4, 5, 6]); 33 + const b7 = new Blob([ta]); 34 + test('Uint8Array part size', b7.size, 3); 35 + 36 + const b8 = new Blob([b1, ' there']); 37 + test('Blob part size', b8.size, 11); 38 + 39 + console.log('\nBlob.text()\n'); 40 + 41 + const bt = await new Blob(['hello world']).text(); 42 + test('text() content', bt, 'hello world'); 43 + 44 + const be = await new Blob().text(); 45 + test('empty blob text()', be, ''); 46 + 47 + console.log('\nBlob.arrayBuffer()\n'); 48 + 49 + const bab = await new Blob([new Uint8Array([10, 20, 30])]).arrayBuffer(); 50 + const view = new Uint8Array(bab); 51 + test('arrayBuffer() byte 0', view[0], 10); 52 + test('arrayBuffer() byte 1', view[1], 20); 53 + test('arrayBuffer() byte 2', view[2], 30); 54 + 55 + console.log('\nBlob.bytes()\n'); 56 + 57 + const bytes = await new Blob([new Uint8Array([7, 8, 9])]).bytes(); 58 + test('bytes() is Uint8Array', bytes instanceof Uint8Array, true); 59 + test('bytes() byte 0', bytes[0], 7); 60 + test('bytes() byte 2', bytes[2], 9); 61 + 62 + console.log('\nBlob.slice()\n'); 63 + 64 + const src = new Blob(['hello world']); 65 + const s1 = src.slice(0, 5); 66 + test('slice(0,5) size', s1.size, 5); 67 + test('slice content', await s1.text(), 'hello'); 68 + 69 + const s2 = src.slice(6); 70 + test('slice(6) size', s2.size, 5); 71 + test('slice(6) content', await s2.text(), 'world'); 72 + 73 + const s3 = src.slice(-5); 74 + test('slice(-5) size', s3.size, 5); 75 + test('slice(-5) content', await s3.text(), 'world'); 76 + 77 + const s4 = src.slice(0, 5, 'text/plain'); 78 + test('slice with type', s4.type, 'text/plain'); 79 + 80 + const s5 = src.slice(5, 3); 81 + test('slice end < start โ†’ empty', s5.size, 0); 82 + 83 + console.log('\nFile constructor\n'); 84 + 85 + const f1 = new File(['content'], 'test.txt'); 86 + test('File name', f1.name, 'test.txt'); 87 + test('File size', f1.size, 7); 88 + test('File type default', f1.type, ''); 89 + test('File lastModified is number', typeof f1.lastModified, 'number'); 90 + test('File lastModified > 0', f1.lastModified > 0, true); 91 + 92 + const f2 = new File(['data'], 'file.txt', { type: 'text/plain', lastModified: 1000 }); 93 + test('File type from options', f2.type, 'text/plain'); 94 + test('File lastModified from options', f2.lastModified, 1000); 95 + 96 + testThrows('File requires new', () => File(['x'], 'f.txt')); 97 + testThrows('File requires 2 args', () => new File(['x'])); 98 + 99 + console.log('\nFile instanceof Blob\n'); 100 + 101 + test('File instanceof Blob', f1 instanceof Blob, true); 102 + test('File instanceof File', f1 instanceof File, true); 103 + test('Blob not instanceof File', b1 instanceof File, false); 104 + 105 + const ft = await f1.text(); 106 + test('File.text() works', ft, 'content'); 107 + 108 + console.log('\nSymbol.toStringTag\n'); 109 + 110 + test('Blob toStringTag', Object.prototype.toString.call(b1), '[object Blob]'); 111 + test('File toStringTag', Object.prototype.toString.call(f1), '[object File]'); 112 + 113 + summary();
+114
examples/spec/headers.js
··· 1 + import { test, testThrows, testDeep, summary } from './helpers.js'; 2 + 3 + console.log('Headers constructor\n'); 4 + 5 + const h0 = new Headers(); 6 + test('empty constructor', h0.has('x-foo'), false); 7 + 8 + const h1 = new Headers({ 'Content-Type': 'text/html', 'x-custom': 'val' }); 9 + test('init from record', h1.get('content-type'), 'text/html'); 10 + test('record key case-folded', h1.get('Content-Type'), 'text/html'); 11 + 12 + const h2 = new Headers([['x-a', '1'], ['x-b', '2']]); 13 + test('init from sequence', h2.get('x-a'), '1'); 14 + test('init from sequence second', h2.get('x-b'), '2'); 15 + 16 + const h3 = new Headers(new Headers({ 'x-copy': 'yes' })); 17 + test('init from another Headers', h3.get('x-copy'), 'yes'); 18 + 19 + testThrows('requires new', () => Headers()); 20 + testThrows('null init throws', () => new Headers(null)); 21 + testThrows('string init throws', () => new Headers('bad')); 22 + testThrows('number init throws', () => new Headers(42)); 23 + 24 + console.log('\nappend / get / has / delete / set\n'); 25 + 26 + const h = new Headers(); 27 + 28 + h.append('x-foo', 'one'); 29 + test('append then get', h.get('x-foo'), 'one'); 30 + 31 + h.append('x-foo', 'two'); 32 + test('append combines values', h.get('x-foo'), 'one, two'); 33 + 34 + test('has returns true', h.has('x-foo'), true); 35 + test('has case-insensitive', h.has('X-FOO'), true); 36 + test('has missing returns false', h.has('x-bar'), false); 37 + 38 + h.set('x-foo', 'replaced'); 39 + test('set replaces combined value', h.get('x-foo'), 'replaced'); 40 + 41 + h.delete('x-foo'); 42 + test('delete removes header', h.has('x-foo'), false); 43 + test('get deleted returns null', h.get('x-foo'), null); 44 + 45 + console.log('\nset-cookie special case\n'); 46 + 47 + const hc = new Headers(); 48 + hc.append('set-cookie', 'a=1'); 49 + hc.append('set-cookie', 'b=2'); 50 + test('set-cookie not combined', hc.get('set-cookie'), 'a=1'); 51 + 52 + const cookies = hc.getSetCookie ? hc.getSetCookie() : null; 53 + if (cookies) { 54 + test('getSetCookie length', cookies.length, 2); 55 + test('getSetCookie first', cookies[0], 'a=1'); 56 + test('getSetCookie second', cookies[1], 'b=2'); 57 + } 58 + 59 + console.log('\nvalue normalization\n'); 60 + 61 + const hn = new Headers(); 62 + hn.set('x-trim', ' hello '); 63 + test('leading/trailing whitespace stripped', hn.get('x-trim'), 'hello'); 64 + 65 + hn.set('x-tab', '\thello\t'); 66 + test('leading/trailing tab stripped', hn.get('x-tab'), 'hello'); 67 + 68 + console.log('\ninvalid name / value\n'); 69 + 70 + testThrows('empty name throws', () => h.set('', 'val')); 71 + testThrows('name with space throws', () => h.set('x bad', 'val')); 72 + testThrows('name with colon throws', () => h.set('x:bad', 'val')); 73 + 74 + console.log('\nforEach\n'); 75 + 76 + const hf = new Headers({ 'b-key': 'bval', 'a-key': 'aval' }); 77 + const seen = []; 78 + hf.forEach((val, name) => seen.push(`${name}:${val}`)); 79 + test('forEach visits all entries', seen.length, 2); 80 + test('forEach sorted order', seen[0], 'a-key:aval'); 81 + test('forEach sorted order 2', seen[1], 'b-key:bval'); 82 + 83 + console.log('\nentries / keys / values iteration\n'); 84 + 85 + const hi = new Headers({ 'b-hdr': '2', 'a-hdr': '1' }); 86 + 87 + const entries = [...hi.entries()]; 88 + test('entries sorted', entries[0][0], 'a-hdr'); 89 + test('entries value', entries[0][1], '1'); 90 + test('entries length', entries.length, 2); 91 + 92 + const keys = [...hi.keys()]; 93 + test('keys sorted', keys[0], 'a-hdr'); 94 + test('keys length', keys.length, 2); 95 + 96 + const vals = [...hi.values()]; 97 + test('values order matches keys', vals[0], '1'); 98 + 99 + console.log('\nlive iteration\n'); 100 + 101 + const hl = new Headers({ 'x-a': '1' }); 102 + const iter = hl.entries(); 103 + hl.set('x-b', '2'); 104 + const all = []; 105 + for (const e of { [Symbol.iterator]: () => iter }) all.push(e[0]); 106 + test('live iterator sees added key', all.includes('x-b'), true); 107 + 108 + console.log('\niterator prototype chain\n'); 109 + 110 + const it = new Headers().entries(); 111 + const iterProto = Object.getPrototypeOf(Object.getPrototypeOf(it)); 112 + test('iterator proto chain has Symbol.iterator', typeof iterProto[Symbol.iterator], 'function'); 113 + 114 + summary();
+4 -1
include/ant.h
··· 160 160 void js_setup_import_meta(ant_t *js, const char *filename); 161 161 162 162 typedef ant_value_t (*js_getter_fn)(ant_t *js, ant_value_t obj, const char *key, size_t key_len); 163 + typedef ant_value_t (*js_keys_fn)(ant_t *js, ant_value_t obj); 164 + 163 165 typedef bool (*js_setter_fn)(ant_t *js, ant_value_t obj, const char *key, size_t key_len, ant_value_t value); 164 166 typedef bool (*js_deleter_fn)(ant_t *js, ant_value_t obj, const char *key, size_t key_len); 165 - typedef ant_value_t (*js_keys_fn)(ant_t *js, ant_value_t obj); 167 + typedef void (*js_finalizer_fn)(ant_t *js, ant_object_t *obj); 166 168 167 169 void js_set_getter(ant_value_t obj, js_getter_fn getter); 168 170 void js_set_setter(ant_value_t obj, js_setter_fn setter); 169 171 void js_set_deleter(ant_value_t obj, js_deleter_fn deleter); 170 172 void js_set_keys(ant_value_t obj, js_keys_fn keys); 173 + void js_set_finalizer(ant_value_t obj, js_finalizer_fn fn); 171 174 172 175 ant_value_t js_get_slot(ant_value_t obj, internal_slot_t slot); 173 176 void js_set_slot(ant_value_t obj, internal_slot_t slot, ant_value_t value);
+24
include/modules/blob.h
··· 1 + #ifndef BLOB_H 2 + #define BLOB_H 3 + 4 + #include <stdint.h> 5 + #include <stddef.h> 6 + #include "types.h" 7 + 8 + typedef struct { 9 + uint8_t *data; 10 + size_t size; 11 + char *type; 12 + char *name; 13 + int64_t last_modified; 14 + } blob_data_t; 15 + 16 + void init_blob_module(void); 17 + 18 + blob_data_t *blob_get_data(ant_value_t obj); 19 + ant_value_t blob_create(ant_t *js, const uint8_t *data, size_t size, const char *type); 20 + 21 + extern ant_value_t g_blob_proto; 22 + extern ant_value_t g_file_proto; 23 + 24 + #endif
+3
include/modules/collections.h
··· 52 52 map_entry_t **get_map_from_obj(ant_t *js, ant_value_t obj); 53 53 set_entry_t **get_set_from_obj(ant_t *js, ant_value_t obj); 54 54 55 + extern ant_value_t g_map_iter_proto; 56 + extern ant_value_t g_set_iter_proto; 57 + 55 58 #endif
+6
include/modules/headers.h
··· 1 + #ifndef HEADERS_H 2 + #define HEADERS_H 3 + 4 + void init_headers_module(void); 5 + 6 + #endif
+1
include/object.h
··· 47 47 ant_value_t *extra_slots; 48 48 49 49 struct ant_object *gc_pending_next; 50 + void (*finalizer)(ant_t *, struct ant_object *); 50 51 ant_value_t inobj[ANT_INOBJ_MAX_SLOTS]; 51 52 52 53 union {
+128 -51
src/ant.c
··· 57 57 #include "modules/ffi.h" 58 58 #include "modules/date.h" 59 59 #include "modules/buffer.h" 60 + #include "modules/blob.h" 60 61 #include "modules/collections.h" 61 62 #include "modules/lmdb.h" 62 63 #include "modules/regex.h" ··· 1341 1342 return n; 1342 1343 } 1343 1344 1345 + if (tag_str) { 1346 + bool is_blob = (tlen == 4 && memcmp(tag_str, "Blob", 4) == 0); 1347 + bool is_file = (tlen == 4 && memcmp(tag_str, "File", 4) == 0); 1348 + if (is_blob || is_file) { 1349 + blob_data_t *bd = blob_get_data(obj); 1350 + n += cpy(buf + n, REMAIN(n, len), is_file ? "File" : "Blob", 4); 1351 + n += cpy(buf + n, REMAIN(n, len), " { size: ", 9); 1352 + n += (size_t) snprintf(buf + n, REMAIN(n, len), "%zu", bd ? bd->size : 0); 1353 + n += cpy(buf + n, REMAIN(n, len), ", type: '", 9); 1354 + if (bd && bd->type) n += cpy(buf + n, REMAIN(n, len), bd->type, strlen(bd->type)); 1355 + n += cpy(buf + n, REMAIN(n, len), "'", 1); 1356 + if (is_file) { 1357 + n += cpy(buf + n, REMAIN(n, len), ", name: '", 9); 1358 + if (bd && bd->name) n += cpy(buf + n, REMAIN(n, len), bd->name, strlen(bd->name)); 1359 + n += cpy(buf + n, REMAIN(n, len), "'", 1); 1360 + n += cpy(buf + n, REMAIN(n, len), ", lastModified: ", 16); 1361 + n += (size_t) snprintf(buf + n, REMAIN(n, len), "%" PRId64, bd ? bd->last_modified : 0); 1362 + } 1363 + n += cpy(buf + n, REMAIN(n, len), " }", 2); 1364 + pop_stringify(); 1365 + return n; 1366 + }} 1367 + 1344 1368 print_tagged_object: 1345 1369 n += cpy(buf + n, REMAIN(n, len), "Object [", 8); 1346 1370 n += cpy(buf + n, REMAIN(n, len), (const char *)(uintptr_t)(toff), tlen); ··· 7771 7795 return mkval(T_ARR, vdata(result)); 7772 7796 } 7773 7797 7774 - static ant_value_t builtin_array_keys(ant_t *js, ant_value_t *args, int nargs) { 7775 - (void) args; 7776 - (void) nargs; 7777 - ant_value_t arr = js->this_val; 7778 - if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) { 7779 - return js_mkerr(js, "keys called on non-array"); 7798 + static ant_value_t arr_values_iter_next(ant_t *js, ant_value_t *args, int nargs) { 7799 + ant_value_t iter = js->this_val; 7800 + ant_value_t array = js_get_slot(iter, SLOT_DATA); 7801 + ant_value_t idx_v = js_get_slot(iter, SLOT_ITER_STATE); 7802 + ant_offset_t idx = (vtype(idx_v) == T_NUM) ? (ant_offset_t)js_getnum(idx_v) : 0; 7803 + ant_value_t result = js_mkobj(js); 7804 + ant_offset_t len = js_arr_len(js, array); 7805 + 7806 + if (idx >= len) { 7807 + js_set(js, result, "done", js_true); 7808 + js_set(js, result, "value", js_mkundef()); 7809 + return result; 7780 7810 } 7781 7811 7782 - ant_offset_t len = get_array_length(js, arr); 7812 + js_set_slot(iter, SLOT_ITER_STATE, js_mknum((double)(idx + 1))); 7813 + js_set(js, result, "done", js_false); 7814 + js_set(js, result, "value", js_arr_get(js, array, idx)); 7783 7815 7784 - ant_value_t result = mkarr(js); 7785 - if (is_err(result)) return result; 7816 + return result; 7817 + } 7818 + 7819 + static ant_value_t arr_keys_iter_next(ant_t *js, ant_value_t *args, int nargs) { 7820 + ant_value_t iter = js->this_val; 7821 + ant_value_t array = js_get_slot(iter, SLOT_DATA); 7822 + ant_value_t idx_v = js_get_slot(iter, SLOT_ITER_STATE); 7823 + ant_offset_t idx = (vtype(idx_v) == T_NUM) ? (ant_offset_t)js_getnum(idx_v) : 0; 7824 + ant_value_t result = js_mkobj(js); 7825 + ant_offset_t len = js_arr_len(js, array); 7786 7826 7787 - for (ant_offset_t i = 0; i < len; i++) { 7788 - arr_set(js, result, i, tov((double) i)); 7827 + if (idx >= len) { 7828 + js_set(js, result, "done", js_true); 7829 + js_set(js, result, "value", js_mkundef()); 7830 + return result; 7789 7831 } 7790 7832 7791 - return mkval(T_ARR, vdata(result)); 7833 + js_set_slot(iter, SLOT_ITER_STATE, js_mknum((double)(idx + 1))); 7834 + js_set(js, result, "done", js_false); 7835 + js_set(js, result, "value", js_mknum((double)idx)); 7836 + 7837 + return result; 7792 7838 } 7793 7839 7794 - static ant_value_t builtin_array_values(ant_t *js, ant_value_t *args, int nargs) { 7795 - (void) args; 7796 - (void) nargs; 7797 - ant_value_t arr = js->this_val; 7798 - if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) { 7799 - return js_mkerr(js, "values called on non-array"); 7840 + static ant_value_t arr_entries_iter_next(ant_t *js, ant_value_t *args, int nargs) { 7841 + ant_value_t iter = js->this_val; 7842 + ant_value_t array = js_get_slot(iter, SLOT_DATA); 7843 + ant_value_t idx_v = js_get_slot(iter, SLOT_ITER_STATE); 7844 + ant_offset_t idx = (vtype(idx_v) == T_NUM) ? (ant_offset_t)js_getnum(idx_v) : 0; 7845 + ant_value_t result = js_mkobj(js); 7846 + ant_offset_t len = js_arr_len(js, array); 7847 + 7848 + if (idx >= len) { 7849 + js_set(js, result, "done", js_true); 7850 + js_set(js, result, "value", js_mkundef()); 7851 + return result; 7800 7852 } 7801 7853 7802 - ant_offset_t len = get_array_length(js, arr); 7854 + ant_value_t pair = js_mkarr(js); 7855 + js_arr_push(js, pair, js_mknum((double)idx)); 7856 + js_arr_push(js, pair, js_arr_get(js, array, idx)); 7857 + js_set_slot(iter, SLOT_ITER_STATE, js_mknum((double)(idx + 1))); 7858 + js_set(js, result, "done", js_false); 7859 + js_set(js, result, "value", pair); 7803 7860 7804 - ant_value_t result = mkarr(js); 7805 - if (is_err(result)) return result; 7806 - 7807 - for (ant_offset_t i = 0; i < len; i++) { 7808 - arr_set(js, result, i, arr_get(js, arr, i)); 7809 - } 7810 - 7811 - return mkval(T_ARR, vdata(result)); 7861 + return result; 7862 + } 7863 + 7864 + static ant_value_t g_arr_values_iter_proto = 0; 7865 + static ant_value_t g_arr_keys_iter_proto = 0; 7866 + static ant_value_t g_arr_entries_iter_proto = 0; 7867 + 7868 + static void ensure_array_iter_protos(ant_t *js) { 7869 + if (g_arr_values_iter_proto) return; 7870 + 7871 + g_arr_values_iter_proto = js_mkobj(js); 7872 + js_set_proto_init(g_arr_values_iter_proto, js->sym.iterator_proto); 7873 + js_set(js, g_arr_values_iter_proto, "next", js_mkfun(arr_values_iter_next)); 7874 + 7875 + g_arr_keys_iter_proto = js_mkobj(js); 7876 + js_set_proto_init(g_arr_keys_iter_proto, js->sym.iterator_proto); 7877 + js_set(js, g_arr_keys_iter_proto, "next", js_mkfun(arr_keys_iter_next)); 7878 + 7879 + g_arr_entries_iter_proto = js_mkobj(js); 7880 + js_set_proto_init(g_arr_entries_iter_proto, js->sym.iterator_proto); 7881 + js_set(js, g_arr_entries_iter_proto, "next", js_mkfun(arr_entries_iter_next)); 7882 + } 7883 + 7884 + static ant_value_t make_array_iterator(ant_t *js, ant_value_t iter_proto) { 7885 + ensure_array_iter_protos(js); 7886 + ant_value_t iter = js_mkobj(js); 7887 + js_set_slot_wb(js, iter, SLOT_DATA, js->this_val); 7888 + js_set_slot(iter, SLOT_ITER_STATE, js_mknum(0)); 7889 + js_set_proto_init(iter, iter_proto); 7890 + return iter; 7891 + } 7892 + 7893 + static ant_value_t builtin_array_keys(ant_t *js, ant_value_t *args, int nargs) { 7894 + if (vtype(js->this_val) != T_ARR && vtype(js->this_val) != T_OBJ) 7895 + return js_mkerr(js, "keys called on non-array"); 7896 + return make_array_iterator(js, g_arr_keys_iter_proto); 7897 + } 7898 + 7899 + static ant_value_t builtin_array_values(ant_t *js, ant_value_t *args, int nargs) { 7900 + if (vtype(js->this_val) != T_ARR && vtype(js->this_val) != T_OBJ) 7901 + return js_mkerr(js, "values called on non-array"); 7902 + return make_array_iterator(js, g_arr_values_iter_proto); 7812 7903 } 7813 7904 7814 7905 static ant_value_t builtin_array_entries(ant_t *js, ant_value_t *args, int nargs) { 7815 - (void) args; 7816 - (void) nargs; 7817 - ant_value_t arr = js->this_val; 7818 - if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) { 7906 + if (vtype(js->this_val) != T_ARR && vtype(js->this_val) != T_OBJ) 7819 7907 return js_mkerr(js, "entries called on non-array"); 7820 - } 7821 - 7822 - ant_offset_t len = get_array_length(js, arr); 7823 - 7824 - ant_value_t result = mkarr(js); 7825 - if (is_err(result)) return result; 7826 - 7827 - for (ant_offset_t i = 0; i < len; i++) { 7828 - ant_value_t entry = mkarr(js); 7829 - if (is_err(entry)) return entry; 7830 - 7831 - ant_value_t elem = arr_get(js, arr, i); 7832 - 7833 - arr_set(js, entry, 0, tov((double) i)); 7834 - arr_set(js, entry, 1, elem); 7835 - 7836 - arr_set(js, result, i, mkval(T_ARR, vdata(entry))); 7837 - } 7838 - 7839 - return mkval(T_ARR, vdata(result)); 7908 + return make_array_iterator(js, g_arr_entries_iter_proto); 7840 7909 } 7841 7910 7842 7911 static ant_value_t builtin_array_toString(ant_t *js, ant_value_t *args, int nargs) { ··· 12622 12691 ant_exotic_ops_t *ops = obj_ensure_exotic_ops(ptr); 12623 12692 if (!ops) return; 12624 12693 ops->deleter = deleter; 12694 + } 12695 + 12696 + void js_set_finalizer(ant_value_t obj, js_finalizer_fn fn) { 12697 + if (!is_object_type(obj)) return; 12698 + if (vtype(obj) != T_OBJ) obj = js_as_obj(obj); 12699 + ant_object_t *ptr = js_obj_ptr(obj); 12700 + if (!ptr) return; 12701 + ptr->finalizer = fn; 12625 12702 } 12626 12703 12627 12704 void js_set_keys(ant_value_t obj, js_keys_fn keys) {
+2
src/gc/objects.c
··· 473 473 474 474 void gc_object_free(ant_t *js, ant_object_t *obj) { 475 475 if (!obj) return; 476 + if (obj->finalizer) obj->finalizer(js, obj); 477 + 476 478 obj->mark_epoch = ANT_GC_DEAD; 477 479 478 480 if (obj->shape) {
+4
src/main.c
··· 74 74 #include "modules/globals.h" 75 75 #include "modules/v8.h" 76 76 #include "modules/worker_threads.h" 77 + #include "modules/headers.h" 78 + #include "modules/blob.h" 77 79 78 80 int js_result = EXIT_SUCCESS; 79 81 typedef int (*cmd_fn)(int argc, char **argv); ··· 580 582 init_domexception_module(); 581 583 init_globals_module(); 582 584 init_abort_module(); 585 + init_headers_module(); 586 + init_blob_module(); 583 587 init_math_module(); 584 588 init_bigint_module(); 585 589 init_date_module();
+464
src/modules/blob.c
··· 1 + #include <stdlib.h> 2 + #include <string.h> 3 + #include <stdint.h> 4 + #include <inttypes.h> 5 + #include <time.h> 6 + #include <ctype.h> 7 + #include <stdio.h> 8 + 9 + #include "ant.h" 10 + #include "errors.h" 11 + #include "runtime.h" 12 + #include "internal.h" 13 + #include "descriptors.h" 14 + 15 + #include "modules/blob.h" 16 + #include "modules/buffer.h" 17 + #include "modules/symbol.h" 18 + 19 + ant_value_t g_blob_proto = 0; 20 + ant_value_t g_file_proto = 0; 21 + 22 + blob_data_t *blob_get_data(ant_value_t obj) { 23 + ant_value_t slot = js_get_slot(obj, SLOT_DATA); 24 + if (vtype(slot) != T_NUM) return NULL; 25 + return (blob_data_t *)(uintptr_t)(size_t)js_getnum(slot); 26 + } 27 + 28 + static blob_data_t *blob_data_new(const uint8_t *data, size_t size, const char *type) { 29 + blob_data_t *bd = calloc(1, sizeof(blob_data_t)); 30 + 31 + if (!bd) return NULL; 32 + if (size > 0 && data) { 33 + bd->data = malloc(size); 34 + if (!bd->data) { free(bd); return NULL; } 35 + memcpy(bd->data, data, size); 36 + } 37 + 38 + bd->size = size; 39 + bd->type = type ? strdup(type) : strdup(""); 40 + 41 + return bd; 42 + } 43 + 44 + static char *normalize_mime_type(const char *s) { 45 + if (!s) return strdup(""); 46 + for (const unsigned char *p = (const unsigned char *)s; *p; p++) { 47 + if (*p < 0x20 || *p > 0x7E) return strdup(""); 48 + } 49 + 50 + size_t len = strlen(s); 51 + char *out = malloc(len + 1); 52 + 53 + if (!out) return strdup(""); 54 + for (size_t i = 0; i <= len; i++) out[i] = (char)tolower((unsigned char)s[i]); 55 + 56 + return out; 57 + } 58 + 59 + typedef struct { 60 + uint8_t *buf; 61 + size_t size; 62 + size_t cap; 63 + } byte_buf_t; 64 + 65 + static bool byte_buf_grow(byte_buf_t *b, size_t extra) { 66 + size_t needed = b->size + extra; 67 + if (needed <= b->cap) return true; 68 + size_t new_cap = b->cap ? b->cap * 2 : 64; 69 + while (new_cap < needed) new_cap *= 2; 70 + uint8_t *p = realloc(b->buf, new_cap); 71 + if (!p) return false; 72 + b->buf = p; 73 + b->cap = new_cap; 74 + return true; 75 + } 76 + 77 + static bool byte_buf_append(byte_buf_t *b, const uint8_t *data, size_t len) { 78 + if (!byte_buf_grow(b, len)) return false; 79 + memcpy(b->buf + b->size, data, len); 80 + b->size += len; 81 + return true; 82 + } 83 + 84 + static ant_value_t process_blob_part(ant_t *js, byte_buf_t *buf, ant_value_t part) { 85 + uint8_t t = vtype(part); 86 + 87 + if (t == T_TYPEDARRAY) { 88 + TypedArrayData *ta = (TypedArrayData *)js_gettypedarray(part); 89 + if (!ta || !ta->buffer) return js_mkundef(); 90 + if (!byte_buf_append(buf, ta->buffer->data + ta->byte_offset, ta->byte_length)) 91 + return js_mkerr(js, "out of memory"); 92 + return js_mkundef(); 93 + } 94 + 95 + if (t == T_OBJ) { 96 + ant_value_t buf_slot = js_get_slot(part, SLOT_BUFFER); 97 + if (vtype(buf_slot) == T_TYPEDARRAY) { 98 + TypedArrayData *ta = (TypedArrayData *)js_gettypedarray(buf_slot); 99 + if (ta && ta->buffer && !ta->buffer->is_detached) { 100 + if (!byte_buf_append(buf, ta->buffer->data + ta->byte_offset, ta->byte_length)) 101 + return js_mkerr(js, "out of memory"); 102 + } 103 + return js_mkundef(); 104 + } 105 + if (vtype(buf_slot) == T_NUM) { 106 + ArrayBufferData *abd = (ArrayBufferData *)(uintptr_t)(size_t)js_getnum(buf_slot); 107 + if (abd && !abd->is_detached) { 108 + if (!byte_buf_append(buf, abd->data, abd->length)) 109 + return js_mkerr(js, "out of memory"); 110 + return js_mkundef(); 111 + } 112 + } 113 + blob_data_t *bd = blob_get_data(part); 114 + if (bd && bd->size > 0) { 115 + if (!byte_buf_append(buf, bd->data, bd->size)) 116 + return js_mkerr(js, "out of memory"); 117 + return js_mkundef(); 118 + } 119 + } 120 + 121 + ant_value_t str = (t == T_STR) ? part : js_tostring_val(js, part); 122 + if (is_err(str)) return str; 123 + 124 + size_t len; 125 + char *s = js_getstr(js, str, &len); 126 + if (s && len > 0) { 127 + if (!byte_buf_append(buf, (const uint8_t *)s, len)) 128 + return js_mkerr(js, "out of memory"); 129 + } 130 + return js_mkundef(); 131 + } 132 + 133 + static ant_value_t process_blob_parts(ant_t *js, byte_buf_t *buf, ant_value_t parts) { 134 + uint8_t t = vtype(parts); 135 + if (t == T_UNDEF || t == T_NULL) return js_mkundef(); 136 + 137 + if (t != T_OBJ && t != T_ARR && t != T_FUNC) 138 + return js_mkerr_typed(js, JS_ERR_TYPE, 139 + "Failed to construct 'Blob': The provided value cannot be converted to a sequence."); 140 + 141 + ant_value_t iter_fn = js_get_sym(js, parts, get_iterator_sym()); 142 + if (vtype(iter_fn) != T_FUNC && vtype(iter_fn) != T_CFUNC) 143 + return js_mkerr_typed(js, JS_ERR_TYPE, 144 + "Failed to construct 'Blob': The provided value is not of type 'BlobPart'"); 145 + 146 + ant_value_t iter = sv_call_native(js, iter_fn, parts, NULL, 0); 147 + if (is_err(iter)) return iter; 148 + 149 + ant_value_t next_fn = js_getprop_fallback(js, iter, "next"); 150 + 151 + for (;;) { 152 + ant_value_t result = sv_call_native(js, next_fn, iter, NULL, 0); 153 + if (is_err(result)) return result; 154 + if (js_truthy(js, js_get(js, result, "done"))) break; 155 + 156 + ant_value_t r = process_blob_part(js, buf, js_get(js, result, "value")); 157 + if (is_err(r)) return r; 158 + } 159 + return js_mkundef(); 160 + } 161 + 162 + static void blob_finalize(ant_t *js, ant_object_t *obj) { 163 + (void)js; 164 + if (!obj->extra_slots) return; 165 + ant_extra_slot_t *entries = (ant_extra_slot_t *)obj->extra_slots; 166 + for (uint8_t i = 0; i < obj->extra_count; i++) { 167 + if (entries[i].slot == SLOT_DATA && vtype(entries[i].value) == T_NUM) { 168 + blob_data_t *bd = (blob_data_t *)(uintptr_t)(size_t)js_getnum(entries[i].value); 169 + if (bd) { free(bd->data); free(bd->type); free(bd->name); free(bd); } 170 + return; 171 + } 172 + } 173 + } 174 + 175 + ant_value_t blob_create(ant_t *js, const uint8_t *data, size_t size, const char *type) { 176 + blob_data_t *bd = blob_data_new(data, size, type); 177 + if (!bd) return js_mkerr(js, "out of memory"); 178 + ant_value_t obj = js_mkobj(js); 179 + js_set_proto_init(obj, g_blob_proto); 180 + js_set_slot(obj, SLOT_DATA, ANT_PTR(bd)); 181 + js_set_finalizer(obj, blob_finalize); 182 + return obj; 183 + } 184 + 185 + static ant_value_t blob_get_size(ant_t *js, ant_value_t *args, int nargs) { 186 + (void)args; (void)nargs; 187 + blob_data_t *bd = blob_get_data(js->this_val); 188 + return js_mknum(bd ? (double)bd->size : 0); 189 + } 190 + 191 + static ant_value_t blob_get_type(ant_t *js, ant_value_t *args, int nargs) { 192 + (void)args; (void)nargs; 193 + blob_data_t *bd = blob_get_data(js->this_val); 194 + if (!bd || !bd->type) return js_mkstr(js, "", 0); 195 + return js_mkstr(js, bd->type, strlen(bd->type)); 196 + } 197 + 198 + static ant_value_t file_get_name(ant_t *js, ant_value_t *args, int nargs) { 199 + (void)args; (void)nargs; 200 + blob_data_t *bd = blob_get_data(js->this_val); 201 + if (!bd || !bd->name) return js_mkstr(js, "", 0); 202 + return js_mkstr(js, bd->name, strlen(bd->name)); 203 + } 204 + 205 + static ant_value_t file_get_last_modified(ant_t *js, ant_value_t *args, int nargs) { 206 + (void)args; (void)nargs; 207 + blob_data_t *bd = blob_get_data(js->this_val); 208 + return js_mknum(bd ? (double)bd->last_modified : 0); 209 + } 210 + 211 + static ant_value_t js_blob_text(ant_t *js, ant_value_t *args, int nargs) { 212 + (void)args; (void)nargs; 213 + blob_data_t *bd = blob_get_data(js->this_val); 214 + ant_value_t promise = js_mkpromise(js); 215 + ant_value_t str = (!bd || bd->size == 0) 216 + ? js_mkstr(js, "", 0) 217 + : js_mkstr(js, (const char *)bd->data, bd->size); 218 + js_resolve_promise(js, promise, str); 219 + return promise; 220 + } 221 + 222 + static ant_value_t js_blob_array_buffer(ant_t *js, ant_value_t *args, int nargs) { 223 + (void)args; (void)nargs; 224 + blob_data_t *bd = blob_get_data(js->this_val); 225 + ant_value_t promise = js_mkpromise(js); 226 + 227 + size_t sz = (bd && bd->data) ? bd->size : 0; 228 + ArrayBufferData *abd = create_array_buffer_data(sz); 229 + if (!abd) { js_reject_promise(js, promise, js_mkerr(js, "out of memory")); return promise; } 230 + if (sz > 0 && bd) memcpy(abd->data, bd->data, sz); 231 + 232 + js_resolve_promise(js, promise, create_arraybuffer_obj(js, abd)); 233 + return promise; 234 + } 235 + 236 + static ant_value_t js_blob_bytes(ant_t *js, ant_value_t *args, int nargs) { 237 + (void)args; (void)nargs; 238 + blob_data_t *bd = blob_get_data(js->this_val); 239 + ant_value_t promise = js_mkpromise(js); 240 + 241 + size_t sz = (bd && bd->data) ? bd->size : 0; 242 + ArrayBufferData *abd = create_array_buffer_data(sz); 243 + if (!abd) { js_reject_promise(js, promise, js_mkerr(js, "out of memory")); return promise; } 244 + if (sz > 0 && bd) memcpy(abd->data, bd->data, sz); 245 + 246 + js_resolve_promise(js, promise, 247 + create_typed_array(js, TYPED_ARRAY_UINT8, abd, 0, sz, "Uint8Array")); 248 + return promise; 249 + } 250 + 251 + static ant_value_t js_blob_slice(ant_t *js, ant_value_t *args, int nargs) { 252 + blob_data_t *bd = blob_get_data(js->this_val); 253 + size_t blob_size = bd ? bd->size : 0; 254 + 255 + ssize_t start = 0; 256 + if (nargs >= 1 && vtype(args[0]) != T_UNDEF) { 257 + double d = js_to_number(js, args[0]); 258 + start = (ssize_t)d; 259 + if (start < 0) start = (ssize_t)blob_size + start; 260 + if (start < 0) start = 0; 261 + if ((size_t)start > blob_size) start = (ssize_t)blob_size; 262 + } 263 + 264 + ssize_t end = (ssize_t)blob_size; 265 + if (nargs >= 2 && vtype(args[1]) != T_UNDEF) { 266 + double d = js_to_number(js, args[1]); 267 + end = (ssize_t)d; 268 + if (end < 0) end = (ssize_t)blob_size + end; 269 + if (end < 0) end = 0; 270 + if ((size_t)end > blob_size) end = (ssize_t)blob_size; 271 + } 272 + 273 + if (end < start) end = start; 274 + 275 + size_t new_size = (size_t)(end - start); 276 + const uint8_t *src = (bd && bd->data && new_size > 0) ? (bd->data + start) : NULL; 277 + 278 + const char *new_type = (bd && bd->type) ? bd->type : ""; 279 + char *type_owned = NULL; 280 + if (nargs >= 3 && vtype(args[2]) != T_UNDEF) { 281 + ant_value_t tv = args[2]; 282 + if (vtype(tv) != T_STR) { tv = js_tostring_val(js, tv); if (is_err(tv)) return tv; } 283 + type_owned = normalize_mime_type(js_getstr(js, tv, NULL)); 284 + new_type = type_owned; 285 + } 286 + 287 + ant_value_t result = blob_create(js, src, new_size, new_type); 288 + free(type_owned); 289 + return result; 290 + } 291 + 292 + static ant_value_t js_blob_stream(ant_t *js, ant_value_t *args, int nargs) { 293 + (void)args; (void)nargs; 294 + return js_mkerr_typed(js, JS_ERR_TYPE, "Blob.stream() is not yet implemented"); 295 + } 296 + 297 + static ant_value_t js_blob_ctor(ant_t *js, ant_value_t *args, int nargs) { 298 + if (vtype(js->new_target) == T_UNDEF) 299 + return js_mkerr_typed(js, JS_ERR_TYPE, "Blob constructor requires 'new'"); 300 + 301 + byte_buf_t buf = {NULL, 0, 0}; 302 + 303 + if (nargs >= 1 && vtype(args[0]) != T_UNDEF) { 304 + uint8_t pt = vtype(args[0]); 305 + if (pt != T_OBJ && pt != T_ARR && pt != T_FUNC) 306 + return js_mkerr_typed(js, JS_ERR_TYPE, 307 + "Failed to construct 'Blob': The provided value cannot be converted to a sequence."); 308 + ant_value_t r = process_blob_parts(js, &buf, args[0]); 309 + if (is_err(r)) { free(buf.buf); return r; } 310 + } 311 + 312 + const char *type_str = ""; 313 + char *type_owned = NULL; 314 + 315 + if (nargs >= 2 && vtype(args[1]) != T_UNDEF && vtype(args[1]) != T_NULL) { 316 + uint8_t ot = vtype(args[1]); 317 + if (ot != T_OBJ && ot != T_ARR && ot != T_FUNC && ot != T_CFUNC) { 318 + free(buf.buf); 319 + return js_mkerr_typed(js, JS_ERR_TYPE, 320 + "Failed to construct 'Blob': The 'options' argument is not an object."); 321 + } 322 + // access "endings" before "type" per lexicographic order (WPT requirement) 323 + (void)js_get(js, args[1], "endings"); 324 + ant_value_t type_v = js_get(js, args[1], "type"); 325 + if (vtype(type_v) != T_UNDEF) { 326 + if (vtype(type_v) != T_STR) { 327 + type_v = js_tostring_val(js, type_v); 328 + if (is_err(type_v)) { free(buf.buf); return type_v; } 329 + } 330 + type_owned = normalize_mime_type(js_getstr(js, type_v, NULL)); 331 + type_str = type_owned; 332 + } 333 + } 334 + 335 + blob_data_t *bd = blob_data_new(buf.buf, buf.size, type_str); 336 + free(buf.buf); free(type_owned); 337 + if (!bd) return js_mkerr(js, "out of memory"); 338 + 339 + ant_value_t obj = js_mkobj(js); 340 + ant_value_t proto = js_instance_proto_from_new_target(js, g_blob_proto); 341 + if (is_object_type(proto)) js_set_proto_init(obj, proto); 342 + 343 + js_set_slot(obj, SLOT_DATA, ANT_PTR(bd)); 344 + js_set_finalizer(obj, blob_finalize); 345 + return obj; 346 + } 347 + 348 + static ant_value_t js_file_ctor(ant_t *js, ant_value_t *args, int nargs) { 349 + if (vtype(js->new_target) == T_UNDEF) 350 + return js_mkerr_typed(js, JS_ERR_TYPE, "File constructor requires 'new'"); 351 + if (nargs < 2) 352 + return js_mkerr_typed(js, JS_ERR_TYPE, "File constructor requires at least 2 arguments"); 353 + 354 + byte_buf_t buf = {NULL, 0, 0}; 355 + 356 + if (vtype(args[0]) != T_UNDEF) { 357 + uint8_t pt = vtype(args[0]); 358 + if (pt != T_OBJ && pt != T_ARR && pt != T_FUNC) { 359 + return js_mkerr_typed(js, JS_ERR_TYPE, 360 + "Failed to construct 'File': The provided value cannot be converted to a sequence."); 361 + } 362 + ant_value_t r = process_blob_parts(js, &buf, args[0]); 363 + if (is_err(r)) { free(buf.buf); return r; } 364 + } 365 + 366 + ant_value_t name_v = args[1]; 367 + if (vtype(name_v) != T_STR) { 368 + name_v = js_tostring_val(js, name_v); 369 + if (is_err(name_v)) { free(buf.buf); return name_v; } 370 + } 371 + const char *name_str = js_getstr(js, name_v, NULL); 372 + 373 + const char *type_str = ""; 374 + char *type_owned = NULL; 375 + 376 + struct timespec ts; 377 + clock_gettime(CLOCK_REALTIME, &ts); 378 + int64_t last_modified = (int64_t)ts.tv_sec * 1000LL + (int64_t)(ts.tv_nsec / 1000000); 379 + 380 + if (nargs >= 3 && vtype(args[2]) != T_UNDEF && vtype(args[2]) != T_NULL) { 381 + ant_value_t opts = args[2]; 382 + uint8_t ot = vtype(opts); 383 + if (ot == T_OBJ || ot == T_ARR) { 384 + ant_value_t type_v = js_get(js, opts, "type"); 385 + if (vtype(type_v) != T_UNDEF) { 386 + if (vtype(type_v) != T_STR) { 387 + type_v = js_tostring_val(js, type_v); 388 + if (is_err(type_v)) { free(buf.buf); return type_v; } 389 + } 390 + type_owned = normalize_mime_type(js_getstr(js, type_v, NULL)); 391 + type_str = type_owned; 392 + } 393 + ant_value_t lm_v = js_get(js, opts, "lastModified"); 394 + if (vtype(lm_v) != T_UNDEF) { 395 + double d = js_to_number(js, lm_v); 396 + if (d == d) last_modified = (int64_t)d; 397 + } 398 + } 399 + } 400 + 401 + blob_data_t *bd = blob_data_new(buf.buf, buf.size, type_str); 402 + free(buf.buf); free(type_owned); 403 + if (!bd) return js_mkerr(js, "out of memory"); 404 + 405 + bd->name = strdup(name_str ? name_str : ""); 406 + bd->last_modified = last_modified; 407 + 408 + ant_value_t obj = js_mkobj(js); 409 + ant_value_t proto = js_instance_proto_from_new_target(js, g_file_proto); 410 + if (is_object_type(proto)) js_set_proto_init(obj, proto); 411 + 412 + js_set_slot(obj, SLOT_DATA, ANT_PTR(bd)); 413 + js_set_finalizer(obj, blob_finalize); 414 + 415 + return obj; 416 + } 417 + 418 + static ant_value_t make_ctor(ant_t *js, ant_cfunc_t fn, ant_value_t proto, const char *name, size_t nlen) { 419 + ant_value_t obj = js_mkobj(js); 420 + js_set_slot(obj, SLOT_CFUNC, js_mkfun(fn)); 421 + js_mkprop_fast(js, obj, "prototype", 9, proto); 422 + js_mkprop_fast(js, obj, "name", 4, js_mkstr(js, name, nlen)); 423 + js_set_descriptor(js, obj, "name", 4, 0); 424 + 425 + ant_value_t fn_val = js_obj_to_func(obj); 426 + js_set(js, proto, "constructor", fn_val); 427 + js_set_descriptor(js, proto, "constructor", 11, JS_DESC_W | JS_DESC_C); 428 + 429 + return fn_val; 430 + } 431 + 432 + void init_blob_module(void) { 433 + ant_t *js = rt->js; 434 + ant_value_t g = js_glob(js); 435 + 436 + g_blob_proto = js_mkobj(js); 437 + 438 + js_set_getter_desc(js, g_blob_proto, "size", 4, js_mkfun(blob_get_size), JS_DESC_C); 439 + js_set_getter_desc(js, g_blob_proto, "type", 4, js_mkfun(blob_get_type), JS_DESC_C); 440 + 441 + js_set(js, g_blob_proto, "text", js_mkfun(js_blob_text)); 442 + js_set(js, g_blob_proto, "arrayBuffer", js_mkfun(js_blob_array_buffer)); 443 + js_set(js, g_blob_proto, "bytes", js_mkfun(js_blob_bytes)); 444 + js_set(js, g_blob_proto, "slice", js_mkfun(js_blob_slice)); 445 + js_set(js, g_blob_proto, "stream", js_mkfun(js_blob_stream)); 446 + 447 + js_set_sym(js, g_blob_proto, get_toStringTag_sym(), js_mkstr(js, "Blob", 4)); 448 + ant_value_t blob_ctor = make_ctor(js, js_blob_ctor, g_blob_proto, "Blob", 4); 449 + 450 + js_set(js, g, "Blob", blob_ctor); 451 + js_set_descriptor(js, g, "Blob", 4, JS_DESC_W | JS_DESC_C); 452 + 453 + g_file_proto = js_mkobj(js); 454 + js_set_proto_init(g_file_proto, g_blob_proto); 455 + 456 + js_set_getter_desc(js, g_file_proto, "name", 4, js_mkfun(file_get_name), JS_DESC_C); 457 + js_set_getter_desc(js, g_file_proto, "lastModified", 12, js_mkfun(file_get_last_modified), JS_DESC_C); 458 + 459 + js_set_sym(js, g_file_proto, get_toStringTag_sym(), js_mkstr(js, "File", 4)); 460 + ant_value_t file_ctor = make_ctor(js, js_file_ctor, g_file_proto, "File", 4); 461 + 462 + js_set(js, g, "File", file_ctor); 463 + js_set_descriptor(js, g, "File", 4, JS_DESC_W | JS_DESC_C); 464 + }
+13 -6
src/modules/collections.c
··· 12 12 #include "modules/collections.h" 13 13 #include "modules/symbol.h" 14 14 15 + ant_value_t g_map_iter_proto = 0; 16 + ant_value_t g_set_iter_proto = 0; 17 + 15 18 static const char *ant_value_to_key(ant_t *js, ant_value_t val) { 16 19 if (vtype(val) == T_STR) { 17 20 ant_offset_t len; ··· 232 235 state->type = type; 233 236 234 237 ant_value_t iter = js_mkobj(js); 238 + js_set_proto_init(iter, g_map_iter_proto); 235 239 js_set_slot(iter, SLOT_ITER_STATE, ANT_PTR(state)); 236 - js_set(js, iter, "next", js_mkfun(map_iter_next)); 237 - 238 - js_set_sym(js, iter, get_iterator_sym(), js_mkfun(sym_this_cb)); 239 240 240 241 return iter; 241 242 } ··· 305 306 state->type = type; 306 307 307 308 ant_value_t iter = js_mkobj(js); 309 + js_set_proto_init(iter, g_set_iter_proto); 308 310 js_set_slot(iter, SLOT_ITER_STATE, ANT_PTR(state)); 309 - js_set(js, iter, "next", js_mkfun(set_iter_next)); 310 - 311 - js_set_sym(js, iter, get_iterator_sym(), js_mkfun(sym_this_cb)); 312 311 313 312 return iter; 314 313 } ··· 942 941 943 942 ant_value_t iter_sym = get_iterator_sym(); 944 943 ant_value_t tag_sym = get_toStringTag_sym(); 944 + 945 + g_map_iter_proto = js_mkobj(js); 946 + js_set_proto_init(g_map_iter_proto, js->sym.iterator_proto); 947 + js_set(js, g_map_iter_proto, "next", js_mkfun(map_iter_next)); 948 + 949 + g_set_iter_proto = js_mkobj(js); 950 + js_set_proto_init(g_set_iter_proto, js->sym.iterator_proto); 951 + js_set(js, g_set_iter_proto, "next", js_mkfun(set_iter_next)); 945 952 946 953 ant_value_t map_proto = js_mkobj(js); 947 954 js_set_proto_init(map_proto, object_proto);
+611
src/modules/headers.c
··· 1 + #include <stdlib.h> 2 + #include <string.h> 3 + #include <ctype.h> 4 + #include <stdio.h> 5 + 6 + #include "ant.h" 7 + #include "errors.h" 8 + #include "runtime.h" 9 + #include "internal.h" 10 + #include "descriptors.h" 11 + #include "silver/engine.h" 12 + 13 + #include "modules/headers.h" 14 + #include "modules/symbol.h" 15 + 16 + typedef struct hdr_entry { 17 + char *name; 18 + char *value; 19 + struct hdr_entry *next; 20 + } hdr_entry_t; 21 + 22 + typedef struct { 23 + hdr_entry_t *head; 24 + hdr_entry_t **tail; 25 + size_t count; 26 + } hdr_list_t; 27 + 28 + typedef struct { 29 + char *name; 30 + char *value; 31 + } sorted_pair_t; 32 + 33 + typedef struct { 34 + hdr_list_t *list; 35 + size_t index; 36 + int kind; 37 + } hdr_iter_t; 38 + 39 + enum { 40 + ITER_ENTRIES = 0, 41 + ITER_KEYS = 1, 42 + ITER_VALUES = 2 43 + }; 44 + 45 + static ant_value_t g_headers_proto = 0; 46 + static ant_value_t g_headers_iter_proto = 0; 47 + 48 + static hdr_list_t *list_new(void) { 49 + hdr_list_t *l = ant_calloc(sizeof(hdr_list_t)); 50 + if (!l) return NULL; 51 + l->head = NULL; 52 + l->tail = &l->head; 53 + return l; 54 + } 55 + 56 + static void list_free(hdr_list_t *l) { 57 + if (!l) return; 58 + for (hdr_entry_t *e = l->head; e; ) { 59 + hdr_entry_t *n = e->next; 60 + free(e->name); free(e->value); free(e); 61 + e = n; 62 + } 63 + free(l); 64 + } 65 + 66 + static hdr_list_t *get_list(ant_value_t obj) { 67 + ant_value_t slot = js_get_slot(obj, SLOT_DATA); 68 + if (vtype(slot) != T_NUM) return NULL; 69 + return (hdr_list_t *)(uintptr_t)(size_t)js_getnum(slot); 70 + } 71 + 72 + static bool is_token_char(unsigned char c) { 73 + if (c > 127) return false; 74 + static const char ok[] = 75 + "!#$%&'*+-.^_`|~" 76 + "0123456789" 77 + "abcdefghijklmnopqrstuvwxyz" 78 + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 79 + return strchr(ok, (char)c) != NULL; 80 + } 81 + 82 + static bool is_valid_name(const char *s) { 83 + if (!s || !*s) return false; 84 + for (const unsigned char *p = (const unsigned char *)s; *p; p++) 85 + if (!is_token_char(*p)) return false; 86 + return true; 87 + } 88 + 89 + static bool is_valid_value(const char *s) { 90 + if (!s) return false; 91 + for (const unsigned char *p = (const unsigned char *)s; *p; p++) { 92 + unsigned char c = *p; 93 + if (c == 0 || c == '\r' || c == '\n' || c > 127) return false; 94 + } 95 + return true; 96 + } 97 + 98 + static char *normalize_value(const char *s) { 99 + if (!s) return strdup(""); 100 + while (*s == ' ' || *s == '\t') s++; 101 + 102 + size_t len = strlen(s); 103 + while (len > 0 && (s[len - 1] == ' ' || s[len - 1] == '\t')) len--; 104 + 105 + char *out = malloc(len + 1); 106 + if (!out) return NULL; 107 + 108 + memcpy(out, s, len); 109 + out[len] = '\0'; 110 + 111 + return out; 112 + } 113 + 114 + static char *lowercase_dup(const char *s) { 115 + if (!s) return strdup(""); 116 + size_t len = strlen(s); 117 + char *out = malloc(len + 1); 118 + if (!out) return NULL; 119 + for (size_t i = 0; i <= len; i++) 120 + out[i] = (char)tolower((unsigned char)s[i]); 121 + return out; 122 + } 123 + 124 + static void list_append_raw(hdr_list_t *l, const char *lower_name, const char *value) { 125 + hdr_entry_t *e = ant_calloc(sizeof(hdr_entry_t)); 126 + if (!e) return; 127 + e->name = strdup(lower_name); 128 + e->value = strdup(value); 129 + *l->tail = e; 130 + l->tail = &e->next; 131 + l->count++; 132 + } 133 + 134 + static void list_delete_name(hdr_list_t *l, const char *lower_name) { 135 + hdr_entry_t **pp = &l->head; 136 + l->tail = &l->head; 137 + while (*pp) { 138 + if (strcmp((*pp)->name, lower_name) == 0) { 139 + hdr_entry_t *dead = *pp; 140 + *pp = dead->next; 141 + free(dead->name); free(dead->value); free(dead); 142 + l->count--; 143 + } else { 144 + l->tail = &(*pp)->next; 145 + pp = &(*pp)->next; 146 + }} 147 + } 148 + 149 + static int cmp_pairs(const void *a, const void *b) { 150 + return strcmp(((const sorted_pair_t *)a)->name, ((const sorted_pair_t *)b)->name); 151 + } 152 + 153 + static sorted_pair_t *build_sorted_view(hdr_list_t *l, size_t *out) { 154 + *out = 0; 155 + if (!l || l->count == 0) return NULL; 156 + 157 + sorted_pair_t *raw = malloc(l->count * sizeof(sorted_pair_t)); 158 + if (!raw) return NULL; 159 + 160 + size_t n = 0; 161 + for (hdr_entry_t *e = l->head; e; e = e->next) { 162 + raw[n].name = e->name; 163 + raw[n].value = e->value; 164 + n++; 165 + } 166 + 167 + qsort(raw, n, sizeof(sorted_pair_t), cmp_pairs); 168 + sorted_pair_t *res = malloc(n * sizeof(sorted_pair_t)); 169 + if (!res) { free(raw); return NULL; } 170 + 171 + size_t ri = 0; 172 + for (size_t i = 0; i < n; ) { 173 + if (strcmp(raw[i].name, "set-cookie") == 0) { 174 + res[ri].name = strdup(raw[i].name); 175 + res[ri].value = strdup(raw[i].value); 176 + ri++; i++; 177 + } else { 178 + size_t j = i + 1; 179 + size_t total = strlen(raw[i].value); 180 + while (j < n && strcmp(raw[j].name, raw[i].name) == 0) { 181 + total += 2 + strlen(raw[j].value); 182 + j++; 183 + } 184 + char *combined = malloc(total + 1); 185 + if (!combined) combined = strdup(""); 186 + size_t pos = 0; 187 + for (size_t k = i; k < j; k++) { 188 + if (k > i) { combined[pos++] = ','; combined[pos++] = ' '; } 189 + size_t vl = strlen(raw[k].value); 190 + memcpy(combined + pos, raw[k].value, vl); 191 + pos += vl; 192 + } 193 + combined[pos] = '\0'; 194 + res[ri].name = strdup(raw[i].name); 195 + res[ri].value = combined; 196 + ri++; i = j; 197 + }} 198 + 199 + free(raw); 200 + *out = ri; 201 + 202 + return res; 203 + } 204 + 205 + static void free_sorted_view(sorted_pair_t *v, size_t n) { 206 + if (!v) return; 207 + for (size_t i = 0; i < n; i++) { free(v[i].name); free(v[i].value); } 208 + free(v); 209 + } 210 + 211 + static ant_value_t headers_append_pair(ant_t *js, hdr_list_t *l, ant_value_t name_v, ant_value_t value_v) { 212 + if (vtype(name_v) != T_STR) { 213 + name_v = js_tostring_val(js, name_v); 214 + if (is_err(name_v)) return name_v; 215 + } 216 + 217 + if (vtype(value_v) != T_STR) { 218 + value_v = js_tostring_val(js, value_v); 219 + if (is_err(value_v)) return value_v; 220 + } 221 + 222 + const char *name = js_getstr(js, name_v, NULL); 223 + const char *value = js_getstr(js, value_v, NULL); 224 + 225 + if (!is_valid_name(name)) 226 + return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid header name: %s", name ? name : ""); 227 + 228 + char *norm = normalize_value(value); 229 + if (!norm) return js_mkerr(js, "out of memory"); 230 + if (!is_valid_value(norm)) { 231 + free(norm); 232 + return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid header value"); 233 + } 234 + 235 + char *lower = lowercase_dup(name); 236 + if (!lower) { free(norm); return js_mkerr(js, "out of memory"); } 237 + 238 + list_append_raw(l, lower, norm); 239 + free(lower); free(norm); 240 + return js_mkundef(); 241 + } 242 + 243 + static ant_value_t init_from_sequence(ant_t *js, hdr_list_t *l, ant_value_t seq) { 244 + ant_value_t iter_fn = js_get_sym(js, seq, get_iterator_sym()); 245 + if (vtype(iter_fn) != T_FUNC && vtype(iter_fn) != T_CFUNC) 246 + return js_mkerr_typed(js, JS_ERR_TYPE, "Headers init is not iterable"); 247 + 248 + ant_value_t iter = sv_call_native(js, iter_fn, seq, NULL, 0); 249 + if (is_err(iter)) return iter; 250 + 251 + ant_value_t next_fn = js_getprop_fallback(js, iter, "next"); 252 + 253 + for (;;) { 254 + ant_value_t result = sv_call_native(js, next_fn, iter, NULL, 0); 255 + if (is_err(result)) return result; 256 + if (js_truthy(js, js_get(js, result, "done"))) break; 257 + 258 + ant_value_t pair = js_get(js, result, "value"); 259 + uint8_t pt = vtype(pair); 260 + if (pt != T_ARR && pt != T_OBJ) 261 + return js_mkerr_typed(js, JS_ERR_TYPE, "Each header init pair must be a sequence"); 262 + 263 + if (js_arr_len(js, pair) != 2) 264 + return js_mkerr_typed(js, JS_ERR_TYPE, 265 + "Each header init pair must have exactly 2 elements"); 266 + 267 + ant_value_t r = headers_append_pair(js, l, js_arr_get(js, pair, 0), 268 + js_arr_get(js, pair, 1)); 269 + if (is_err(r)) return r; 270 + } 271 + return js_mkundef(); 272 + } 273 + 274 + // iterate own enumerable properties as a record 275 + static ant_value_t init_from_record(ant_t *js, hdr_list_t *l, ant_value_t obj) { 276 + ant_iter_t it = js_prop_iter_begin(js, obj); 277 + const char *key; 278 + size_t key_len; 279 + ant_value_t val; 280 + 281 + while (js_prop_iter_next(&it, &key, &key_len, &val)) { 282 + ant_value_t r = headers_append_pair(js, l, js_mkstr(js, key, key_len), val); 283 + if (is_err(r)) { js_prop_iter_end(&it); return r; } 284 + } 285 + js_prop_iter_end(&it); 286 + return js_mkundef(); 287 + } 288 + 289 + static ant_value_t headers_iter_next(ant_t *js, ant_value_t *args, int nargs) { 290 + ant_value_t state_val = js_get_slot(js->this_val, SLOT_ITER_STATE); 291 + if (vtype(state_val) == T_UNDEF) return js_mkerr(js, "Invalid iterator"); 292 + hdr_iter_t *st = (hdr_iter_t *)(uintptr_t)(size_t)js_getnum(state_val); 293 + 294 + size_t count = 0; 295 + sorted_pair_t *view = build_sorted_view(st->list, &count); 296 + 297 + ant_value_t result = js_mkobj(js); 298 + 299 + if (st->index >= count) { 300 + free_sorted_view(view, count); 301 + js_set(js, result, "done", js_true); 302 + js_set(js, result, "value", js_mkundef()); 303 + return result; 304 + } 305 + 306 + sorted_pair_t *e = &view[st->index]; 307 + ant_value_t value; 308 + 309 + switch (st->kind) { 310 + case ITER_KEYS: 311 + value = js_mkstr(js, e->name, strlen(e->name)); 312 + break; 313 + case ITER_VALUES: 314 + value = js_mkstr(js, e->value, strlen(e->value)); 315 + break; 316 + default: { 317 + value = js_mkarr(js); 318 + js_arr_push(js, value, js_mkstr(js, e->name, strlen(e->name))); 319 + js_arr_push(js, value, js_mkstr(js, e->value, strlen(e->value))); 320 + break; 321 + }} 322 + 323 + free_sorted_view(view, count); 324 + st->index++; 325 + 326 + js_set(js, result, "done", js_false); 327 + js_set(js, result, "value", value); 328 + return result; 329 + } 330 + 331 + static ant_value_t make_headers_iter(ant_t *js, ant_value_t headers_obj, int kind) { 332 + hdr_list_t *l = get_list(headers_obj); 333 + 334 + hdr_iter_t *st = ant_calloc(sizeof(hdr_iter_t)); 335 + if (!st) return js_mkerr(js, "out of memory"); 336 + st->list = l ? l : list_new(); 337 + st->kind = kind; 338 + 339 + ant_value_t iter = js_mkobj(js); 340 + js_set_proto_init(iter, g_headers_iter_proto); 341 + js_set_slot(iter, SLOT_ITER_STATE, ANT_PTR(st)); 342 + return iter; 343 + } 344 + 345 + static ant_value_t js_headers_append(ant_t *js, ant_value_t *args, int nargs) { 346 + if (nargs < 2) return js_mkerr_typed(js, JS_ERR_TYPE, "Headers.append requires 2 arguments"); 347 + hdr_list_t *l = get_list(js->this_val); 348 + if (!l) return js_mkerr(js, "Invalid Headers object"); 349 + return headers_append_pair(js, l, args[0], args[1]); 350 + } 351 + 352 + static ant_value_t js_headers_set(ant_t *js, ant_value_t *args, int nargs) { 353 + if (nargs < 2) return js_mkerr_typed(js, JS_ERR_TYPE, "Headers.set requires 2 arguments"); 354 + hdr_list_t *l = get_list(js->this_val); 355 + if (!l) return js_mkerr(js, "Invalid Headers object"); 356 + 357 + ant_value_t name_v = args[0]; 358 + ant_value_t value_v = args[1]; 359 + if (vtype(name_v) != T_STR) { name_v = js_tostring_val(js, name_v); if (is_err(name_v)) return name_v; } 360 + if (vtype(value_v) != T_STR) { value_v = js_tostring_val(js, value_v); if (is_err(value_v)) return value_v; } 361 + 362 + const char *name = js_getstr(js, name_v, NULL); 363 + const char *value = js_getstr(js, value_v, NULL); 364 + 365 + if (!is_valid_name(name)) 366 + return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid header name: %s", name ? name : ""); 367 + 368 + char *norm = normalize_value(value); 369 + if (!norm) return js_mkerr(js, "out of memory"); 370 + if (!is_valid_value(norm)) { free(norm); return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid header value"); } 371 + 372 + char *lower = lowercase_dup(name); 373 + if (!lower) { free(norm); return js_mkerr(js, "out of memory"); } 374 + 375 + list_delete_name(l, lower); 376 + list_append_raw(l, lower, norm); 377 + free(lower); free(norm); 378 + return js_mkundef(); 379 + } 380 + 381 + static ant_value_t js_headers_get(ant_t *js, ant_value_t *args, int nargs) { 382 + if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "Headers.get requires 1 argument"); 383 + hdr_list_t *l = get_list(js->this_val); 384 + if (!l) return js_mknull(); 385 + 386 + ant_value_t name_v = args[0]; 387 + if (vtype(name_v) != T_STR) { name_v = js_tostring_val(js, name_v); if (is_err(name_v)) return name_v; } 388 + const char *name = js_getstr(js, name_v, NULL); 389 + if (!is_valid_name(name)) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid header name"); 390 + 391 + char *lower = lowercase_dup(name); 392 + if (!lower) return js_mkerr(js, "out of memory"); 393 + 394 + // set-cookie is never combined per Fetch spec 395 + if (strcmp(lower, "set-cookie") == 0) { 396 + for (hdr_entry_t *e = l->head; e; e = e->next) { 397 + if (strcmp(e->name, lower) == 0) { 398 + ant_value_t ret = js_mkstr(js, e->value, strlen(e->value)); 399 + free(lower); 400 + return ret; 401 + } 402 + } 403 + free(lower); 404 + return js_mknull(); 405 + } 406 + 407 + size_t total = 0; 408 + int count = 0; 409 + for (hdr_entry_t *e = l->head; e; e = e->next) { 410 + if (strcmp(e->name, lower) == 0) { 411 + if (count > 0) total += 2; 412 + total += strlen(e->value); 413 + count++; 414 + } 415 + } 416 + 417 + if (count == 0) { free(lower); return js_mknull(); } 418 + 419 + char *combined = malloc(total + 1); 420 + if (!combined) { free(lower); return js_mkerr(js, "out of memory"); } 421 + 422 + size_t pos = 0; 423 + int seen = 0; 424 + for (hdr_entry_t *e = l->head; e; e = e->next) { 425 + if (strcmp(e->name, lower) == 0) { 426 + if (seen > 0) { combined[pos++] = ','; combined[pos++] = ' '; } 427 + size_t vl = strlen(e->value); 428 + memcpy(combined + pos, e->value, vl); 429 + pos += vl; 430 + seen++; 431 + } 432 + } 433 + combined[pos] = '\0'; 434 + free(lower); 435 + 436 + ant_value_t ret = js_mkstr(js, combined, pos); 437 + free(combined); 438 + return ret; 439 + } 440 + 441 + static ant_value_t js_headers_has(ant_t *js, ant_value_t *args, int nargs) { 442 + if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "Headers.has requires 1 argument"); 443 + hdr_list_t *l = get_list(js->this_val); 444 + if (!l) return js_false; 445 + 446 + ant_value_t name_v = args[0]; 447 + if (vtype(name_v) != T_STR) { name_v = js_tostring_val(js, name_v); if (is_err(name_v)) return name_v; } 448 + const char *name = js_getstr(js, name_v, NULL); 449 + if (!is_valid_name(name)) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid header name"); 450 + 451 + char *lower = lowercase_dup(name); 452 + if (!lower) return js_mkerr(js, "out of memory"); 453 + 454 + bool found = false; 455 + for (hdr_entry_t *e = l->head; e; e = e->next) { 456 + if (strcmp(e->name, lower) == 0) { found = true; break; } 457 + } 458 + free(lower); 459 + return js_bool(found); 460 + } 461 + 462 + static ant_value_t js_headers_delete(ant_t *js, ant_value_t *args, int nargs) { 463 + if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "Headers.delete requires 1 argument"); 464 + hdr_list_t *l = get_list(js->this_val); 465 + if (!l) return js_mkundef(); 466 + 467 + ant_value_t name_v = args[0]; 468 + if (vtype(name_v) != T_STR) { name_v = js_tostring_val(js, name_v); if (is_err(name_v)) return name_v; } 469 + const char *name = js_getstr(js, name_v, NULL); 470 + if (!is_valid_name(name)) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid header name"); 471 + 472 + char *lower = lowercase_dup(name); 473 + if (!lower) return js_mkerr(js, "out of memory"); 474 + list_delete_name(l, lower); 475 + free(lower); 476 + return js_mkundef(); 477 + } 478 + 479 + static ant_value_t js_headers_get_set_cookie(ant_t *js, ant_value_t *args, int nargs) { 480 + (void)args; (void)nargs; 481 + hdr_list_t *l = get_list(js->this_val); 482 + ant_value_t arr = js_mkarr(js); 483 + if (!l) return arr; 484 + for (hdr_entry_t *e = l->head; e; e = e->next) { 485 + if (strcmp(e->name, "set-cookie") == 0) 486 + js_arr_push(js, arr, js_mkstr(js, e->value, strlen(e->value))); 487 + } 488 + return arr; 489 + } 490 + 491 + static ant_value_t js_headers_for_each(ant_t *js, ant_value_t *args, int nargs) { 492 + if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "Headers.forEach requires 1 argument"); 493 + 494 + ant_value_t cb = args[0]; 495 + uint8_t cbt = vtype(cb); 496 + if (cbt != T_FUNC && cbt != T_CFUNC) 497 + return js_mkerr_typed(js, JS_ERR_TYPE, "Headers.forEach callback must be callable"); 498 + 499 + ant_value_t this_obj = js->this_val; 500 + hdr_list_t *l = get_list(this_obj); 501 + if (!l) return js_mkundef(); 502 + 503 + ant_value_t this_arg = (nargs >= 2) ? args[1] : js_mkundef(); 504 + 505 + size_t count = 0; 506 + sorted_pair_t *view = build_sorted_view(l, &count); 507 + 508 + for (size_t i = 0; i < count; i++) { 509 + ant_value_t call_args[3] = { 510 + js_mkstr(js, view[i].value, strlen(view[i].value)), 511 + js_mkstr(js, view[i].name, strlen(view[i].name)), 512 + this_obj 513 + }; 514 + ant_value_t r = sv_vm_call(js->vm, js, cb, this_arg, call_args, 3, NULL, false); 515 + if (is_err(r)) { free_sorted_view(view, count); return r; } 516 + } 517 + 518 + free_sorted_view(view, count); 519 + return js_mkundef(); 520 + } 521 + 522 + static ant_value_t js_headers_keys(ant_t *js, ant_value_t *args, int nargs) { 523 + return make_headers_iter(js, js->this_val, ITER_KEYS); 524 + } 525 + 526 + static ant_value_t js_headers_values(ant_t *js, ant_value_t *args, int nargs) { 527 + return make_headers_iter(js, js->this_val, ITER_VALUES); 528 + } 529 + 530 + static ant_value_t js_headers_entries(ant_t *js, ant_value_t *args, int nargs) { 531 + return make_headers_iter(js, js->this_val, ITER_ENTRIES); 532 + } 533 + 534 + static ant_value_t js_headers_symbol_iterator(ant_t *js, ant_value_t *args, int nargs) { 535 + return make_headers_iter(js, js->this_val, ITER_ENTRIES); 536 + } 537 + 538 + static ant_value_t js_headers_ctor(ant_t *js, ant_value_t *args, int nargs) { 539 + if (vtype(js->new_target) == T_UNDEF) 540 + return js_mkerr_typed(js, JS_ERR_TYPE, "Headers constructor requires 'new'"); 541 + 542 + hdr_list_t *l = list_new(); 543 + if (!l) return js_mkerr(js, "out of memory"); 544 + 545 + ant_value_t init = (nargs >= 1) ? args[0] : js_mkundef(); 546 + 547 + if (vtype(init) != T_UNDEF) { 548 + uint8_t t = vtype(init); 549 + 550 + if (t == T_NULL || (t != T_OBJ && t != T_ARR && t != T_FUNC && t != T_CFUNC)) { 551 + list_free(l); 552 + return js_mkerr_typed(js, JS_ERR_TYPE, 553 + "Failed to construct 'Headers': The provided value is not of type 'HeadersInit'"); 554 + } 555 + 556 + ant_value_t iter_fn = js_get_sym(js, init, get_iterator_sym()); 557 + bool has_iter = (vtype(iter_fn) == T_FUNC || vtype(iter_fn) == T_CFUNC); 558 + 559 + ant_value_t r; 560 + if (t == T_ARR || has_iter) r = init_from_sequence(js, l, init); 561 + else r = init_from_record(js, l, init); 562 + if (is_err(r)) { list_free(l); return r; } 563 + } 564 + 565 + ant_value_t obj = js_mkobj(js); 566 + ant_value_t proto = js_instance_proto_from_new_target(js, g_headers_proto); 567 + if (is_object_type(proto)) js_set_proto_init(obj, proto); 568 + 569 + js_set_slot(obj, SLOT_DATA, ANT_PTR(l)); 570 + return obj; 571 + } 572 + 573 + void init_headers_module(void) { 574 + ant_t *js = rt->js; 575 + ant_value_t g = js_glob(js); 576 + 577 + g_headers_iter_proto = js_mkobj(js); 578 + js_set_proto_init(g_headers_iter_proto, js->sym.iterator_proto); 579 + js_set(js, g_headers_iter_proto, "next", js_mkfun(headers_iter_next)); 580 + js_set_descriptor(js, g_headers_iter_proto, "next", 4, JS_DESC_W | JS_DESC_E | JS_DESC_C); 581 + js_set_sym(js, g_headers_iter_proto, get_iterator_sym(), js_mkfun(sym_this_cb)); 582 + 583 + g_headers_proto = js_mkobj(js); 584 + 585 + js_set(js, g_headers_proto, "append", js_mkfun(js_headers_append)); 586 + js_set(js, g_headers_proto, "set", js_mkfun(js_headers_set)); 587 + js_set(js, g_headers_proto, "get", js_mkfun(js_headers_get)); 588 + js_set(js, g_headers_proto, "has", js_mkfun(js_headers_has)); 589 + js_set(js, g_headers_proto, "delete", js_mkfun(js_headers_delete)); 590 + js_set(js, g_headers_proto, "forEach", js_mkfun(js_headers_for_each)); 591 + js_set(js, g_headers_proto, "keys", js_mkfun(js_headers_keys)); 592 + js_set(js, g_headers_proto, "values", js_mkfun(js_headers_values)); 593 + js_set(js, g_headers_proto, "entries", js_mkfun(js_headers_entries)); 594 + js_set(js, g_headers_proto, "getSetCookie", js_mkfun(js_headers_get_set_cookie)); 595 + 596 + js_set_sym(js, g_headers_proto, get_iterator_sym(), js_mkfun(js_headers_symbol_iterator)); 597 + js_set_sym_getter_desc(js, g_headers_proto, get_toStringTag_sym(), js_mkfun(sym_this_cb), 0); 598 + 599 + ant_value_t ctor_obj = js_mkobj(js); 600 + js_set_slot(ctor_obj, SLOT_CFUNC, js_mkfun(js_headers_ctor)); 601 + js_mkprop_fast(js, ctor_obj, "prototype", 9, g_headers_proto); 602 + js_mkprop_fast(js, ctor_obj, "name", 4, js_mkstr(js, "Headers", 7)); 603 + js_set_descriptor(js, ctor_obj, "name", 4, 0); 604 + 605 + ant_value_t ctor = js_obj_to_func(ctor_obj); 606 + js_set(js, g_headers_proto, "constructor", ctor); 607 + js_set_descriptor(js, g_headers_proto, "constructor", 11, JS_DESC_W | JS_DESC_C); 608 + 609 + js_set(js, g, "Headers", ctor); 610 + js_set_descriptor(js, g, "Headers", 7, JS_DESC_W | JS_DESC_C); 611 + }
+1 -10
src/modules/observable.c
··· 447 447 } 448 448 449 449 ant_value_t iteratorMethod = js_get_sym(js, x, get_iterator_sym()); 450 - 451 - if (!is_callable(iteratorMethod) && vtype(x) == T_ARR) { 452 - ant_value_t array_ctor = js_get(js, js_glob(js), "Array"); 453 - ant_value_t array_proto = js_get(js, array_ctor, "prototype"); 454 - iteratorMethod = js_get_sym(js, array_proto, get_iterator_sym()); 455 - } 456 - 457 - if (!is_callable(iteratorMethod)) { 458 - return js_mkerr_typed(js, JS_ERR_TYPE, "Object is not observable or iterable"); 459 - } 450 + if (!is_callable(iteratorMethod)) return js_mkerr_typed(js, JS_ERR_TYPE, "Object is not observable or iterable"); 460 451 461 452 ant_value_t data = js_mkobj(js); 462 453 js_set(js, data, "iterable", x);
+4 -4
src/silver/ops/iteration.h
··· 28 28 iter_type_t *out_type 29 29 ) { 30 30 if (vtype(obj) != T_OBJ) return false; 31 + if (!g_map_iter_proto || js_get_proto(js, obj) != g_map_iter_proto) return false; 32 + 31 33 ant_value_t state_val = js_get_slot(obj, SLOT_ITER_STATE); 32 34 if (vtype(state_val) == T_UNDEF) return false; 33 35 34 36 map_iterator_state_t *st = (map_iterator_state_t *)(uintptr_t)js_getnum(state_val); 35 37 if (!st) return false; 36 - if (st->type > ITER_TYPE_MAP_ENTRIES) return false; 37 38 38 39 *out_state = st; 39 40 *out_type = st->type; ··· 47 48 iter_type_t *out_type 48 49 ) { 49 50 if (vtype(obj) != T_OBJ) return false; 51 + if (!g_set_iter_proto || js_get_proto(js, obj) != g_set_iter_proto) return false; 52 + 50 53 ant_value_t state_val = js_get_slot(obj, SLOT_ITER_STATE); 51 54 if (vtype(state_val) == T_UNDEF) return false; 52 55 53 56 set_iterator_state_t *st = (set_iterator_state_t *)(uintptr_t)js_getnum(state_val); 54 57 if (!st) return false; 55 58 56 - if (st->type != ITER_TYPE_SET_VALUES && st->type != ITER_TYPE_SET_ENTRIES) 57 - return false; 58 - 59 59 *out_state = st; 60 60 *out_type = st->type; 61 61