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.

Atomic, SharedArrayBuffer, 0x, 0o, 0b

+1364 -10
+12 -7
README.txt
··· 15 15 with Radix3 routing, parameter handling, and various response types. 16 16 17 17 MODULES: 18 - - Ant.serve() - HTTP server 19 - - import() - ESM module loading 20 - - Timers - setTimeout, setInterval, queueMicrotask 21 - - fetch() - HTTP client 22 - - crypto.* - Cryptography 23 - - JSON.* - JSON parsing 24 - - console.* - Logging 18 + - Ant.serve() - HTTP server 19 + - import() - ESM module loading 20 + - Timers - setTimeout, setInterval, queueMicrotask 21 + - fetch() - HTTP client 22 + - crypto.* - Cryptography 23 + - JSON.* - JSON parsing 24 + - console.* - Logging 25 + - Atomics.* - Atomic operations (add, sub, and, or, xor, etc.) 26 + - SharedArrayBuffer - Shared memory buffers 27 + - TypedArrays - Int8Array, Uint8Array, Int32Array, etc. 25 28 26 29 FEATURES: 27 30 - Async/await and Promises 28 31 - ES6+ syntax (arrow functions, classes, template literals) 32 + - Number literals (binary 0b1010, octal 0o755, hex 0xFF) 33 + - Atomic operations for concurrent programming 29 34 - Signal handlers (SIGINT, SIGTERM, etc.) 30 35 - Embedded garbage collector
+88
examples/atomics.js
··· 1 + console.log('1. Creating SharedArrayBuffer'); 2 + const sharedBuffer = new SharedArrayBuffer(256); 3 + console.log(' SharedArrayBuffer created with size:', sharedBuffer.byteLength, 'bytes\n'); 4 + 5 + console.log('2. Creating TypedArrays on SharedArrayBuffer'); 6 + const int32View = new Int32Array(sharedBuffer); 7 + const uint8View = new Uint8Array(sharedBuffer); 8 + console.log(' Int32Array length:', int32View.length); 9 + console.log(' Uint8Array length:', uint8View.length, '\n'); 10 + 11 + console.log('3. Basic atomic store and load'); 12 + Atomics.store(int32View, 0, 42); 13 + const value = Atomics.load(int32View, 0); 14 + console.log(' Stored 42, loaded:', value, '\n'); 15 + 16 + console.log('4. Atomic add operation'); 17 + Atomics.store(int32View, 1, 10); 18 + const oldValue = Atomics.add(int32View, 1, 5); 19 + const newValue = Atomics.load(int32View, 1); 20 + console.log(' Old value:', oldValue); 21 + console.log(' Added 5, new value:', newValue, '\n'); 22 + 23 + console.log('5. Compare and exchange'); 24 + Atomics.store(int32View, 2, 100); 25 + console.log(' Initial value:', Atomics.load(int32View, 2)); 26 + 27 + const result1 = Atomics.compareExchange(int32View, 2, 50, 200); 28 + console.log(' Expected 50, got:', result1, '(no change)'); 29 + console.log(' Current value:', Atomics.load(int32View, 2)); 30 + 31 + const result2 = Atomics.compareExchange(int32View, 2, 100, 200); 32 + console.log(' Expected 100, got:', result2, '(changed!)'); 33 + console.log(' Current value:', Atomics.load(int32View, 2), '\n'); 34 + 35 + console.log('6. Bitwise operations'); 36 + Atomics.store(uint8View, 0, 0b11110000); 37 + console.log(' Initial: 0b' + Atomics.load(uint8View, 0).toString(2).padStart(8, '0')); 38 + 39 + Atomics.and(uint8View, 0, 0b00111100); 40 + console.log(' After AND with 0b00111100: 0b' + Atomics.load(uint8View, 0).toString(2).padStart(8, '0')); 41 + 42 + Atomics.or(uint8View, 0, 0b00000011); 43 + console.log(' After OR with 0b00000011: 0b' + Atomics.load(uint8View, 0).toString(2).padStart(8, '0')); 44 + 45 + Atomics.xor(uint8View, 0, 0b11111111); 46 + console.log(' After XOR with 0b11111111: 0b' + Atomics.load(uint8View, 0).toString(2).padStart(8, '0'), '\n'); 47 + 48 + console.log('7. Lock-free operations check'); 49 + console.log(' 1 byte:', Atomics.isLockFree(1) ? 'Lock-free โœ“' : 'Uses locks'); 50 + console.log(' 2 bytes:', Atomics.isLockFree(2) ? 'Lock-free โœ“' : 'Uses locks'); 51 + console.log(' 4 bytes:', Atomics.isLockFree(4) ? 'Lock-free โœ“' : 'Uses locks'); 52 + console.log(' 8 bytes:', Atomics.isLockFree(8) ? 'Lock-free โœ“' : 'Uses locks', '\n'); 53 + 54 + console.log('8. Atomic counter example'); 55 + Atomics.store(int32View, 10, 0); 56 + console.log(' Counter initialized to:', Atomics.load(int32View, 10)); 57 + 58 + for (let i = 0; i < 10; i++) { 59 + Atomics.add(int32View, 10, 1); 60 + } 61 + console.log(' After 10 atomic increments:', Atomics.load(int32View, 10), '\n'); 62 + 63 + console.log('9. Spin-lock pattern example'); 64 + const LOCK_INDEX = 20; 65 + const UNLOCKED = 0; 66 + const LOCKED = 1; 67 + 68 + Atomics.store(int32View, LOCK_INDEX, UNLOCKED); 69 + console.log(' Lock initialized'); 70 + 71 + function tryAcquireLock() { 72 + return Atomics.compareExchange(int32View, LOCK_INDEX, UNLOCKED, LOCKED) === UNLOCKED; 73 + } 74 + 75 + function releaseLock() { 76 + Atomics.store(int32View, LOCK_INDEX, UNLOCKED); 77 + } 78 + 79 + if (tryAcquireLock()) { 80 + console.log(' Lock acquired โœ“'); 81 + console.log(' Performing critical operation...'); 82 + releaseLock(); 83 + console.log(' Lock released โœ“'); 84 + } else { 85 + console.log(' Failed to acquire lock'); 86 + } 87 + 88 + console.log('\n=== Example completed successfully ===');
+30
include/modules/atomics.h
··· 1 + #ifndef ATOMICS_H 2 + #define ATOMICS_H 3 + 4 + #include <stdint.h> 5 + #include <stddef.h> 6 + #include <pthread.h> 7 + 8 + void init_atomics_module(void); 9 + 10 + typedef struct WaitQueueEntry { 11 + pthread_cond_t cond; 12 + pthread_mutex_t mutex; 13 + int32_t *address; 14 + int notified; 15 + struct WaitQueueEntry *next; 16 + } WaitQueueEntry; 17 + 18 + typedef struct { 19 + WaitQueueEntry *head; 20 + pthread_mutex_t lock; 21 + } WaitQueue; 22 + 23 + void wait_queue_init(WaitQueue *queue); 24 + void wait_queue_cleanup(WaitQueue *queue); 25 + 26 + void wait_queue_add(WaitQueue *queue, WaitQueueEntry *entry); 27 + void wait_queue_remove(WaitQueue *queue, WaitQueueEntry *entry); 28 + int wait_queue_notify(WaitQueue *queue, int32_t *address, int count); 29 + 30 + #endif
+1
include/modules/buffer.h
··· 11 11 size_t length; 12 12 size_t capacity; 13 13 int ref_count; 14 + int is_shared; 14 15 } ArrayBufferData; 15 16 16 17 typedef enum {
+1 -1
meson.build
··· 74 74 build_date = run_command('date', '+%Y-%m-%d', check: true).stdout().strip() 75 75 76 76 version_conf = configuration_data() 77 - version_conf.set('ANT_VERSION', '0.0.8.3') 77 + version_conf.set('ANT_VERSION', '0.0.8.4') 78 78 version_conf.set('ANT_GIT_HASH', git_hash) 79 79 version_conf.set('ANT_BUILD_DATE', build_date) 80 80
+31 -2
src/ant.c
··· 2245 2245 js->tlen = 1; 2246 2246 break; 2247 2247 } 2248 + 2248 2249 char *end; 2249 - js->tval = tov(strtod(buf, &end)); 2250 - jsoff_t numlen = (jsoff_t) (end - buf); 2250 + double value = 0; 2251 + jsoff_t numlen = 0; 2252 + 2253 + if (buf[0] == '0' && js->toff + 2 < js->clen) { 2254 + if (buf[1] == 'b' || buf[1] == 'B') { 2255 + numlen = 2; 2256 + while (js->toff + numlen < js->clen && (buf[numlen] == '0' || buf[numlen] == '1')) { 2257 + value = value * 2 + (buf[numlen] - '0'); 2258 + numlen++; 2259 + } 2260 + js->tval = tov(value); 2261 + } else if (buf[1] == 'o' || buf[1] == 'O') { 2262 + numlen = 2; 2263 + while (js->toff + numlen < js->clen && buf[numlen] >= '0' && buf[numlen] <= '7') { 2264 + value = value * 8 + (buf[numlen] - '0'); 2265 + numlen++; 2266 + } 2267 + js->tval = tov(value); 2268 + } else if (buf[1] == 'x' || buf[1] == 'X') { 2269 + js->tval = tov(strtod(buf, &end)); 2270 + numlen = (jsoff_t) (end - buf); 2271 + } else { 2272 + js->tval = tov(strtod(buf, &end)); 2273 + numlen = (jsoff_t) (end - buf); 2274 + } 2275 + } else { 2276 + js->tval = tov(strtod(buf, &end)); 2277 + numlen = (jsoff_t) (end - buf); 2278 + } 2279 + 2251 2280 if (js->toff + numlen < js->clen && buf[numlen] == 'n') { 2252 2281 js->tok = TOK_BIGINT; 2253 2282 js->tlen = numlen + 1;
+2
src/main.c
··· 12 12 13 13 #include "modules/builtin.h" 14 14 #include "modules/buffer.h" 15 + #include "modules/atomics.h" 15 16 #include "modules/io.h" 16 17 #include "modules/fs.h" 17 18 #include "modules/crypto.h" ··· 156 157 157 158 init_builtin_module(); 158 159 init_buffer_module(); 160 + init_atomics_module(); 159 161 init_crypto_module(); 160 162 init_fetch_module(); 161 163 init_console_module();
+841
src/modules/atomics.c
··· 1 + #include <stdlib.h> 2 + #include <stdio.h> 3 + #include <string.h> 4 + #include <stdatomic.h> 5 + #include <pthread.h> 6 + #include <time.h> 7 + #include <errno.h> 8 + 9 + #include "ant.h" 10 + #include "runtime.h" 11 + #include "modules/buffer.h" 12 + #include "modules/atomics.h" 13 + 14 + static WaitQueue global_wait_queue; 15 + static pthread_once_t wait_queue_init_once = PTHREAD_ONCE_INIT; 16 + 17 + static void init_wait_queue(void) { 18 + wait_queue_init(&global_wait_queue); 19 + } 20 + 21 + void wait_queue_init(WaitQueue *queue) { 22 + queue->head = NULL; 23 + pthread_mutex_init(&queue->lock, NULL); 24 + } 25 + 26 + void wait_queue_cleanup(WaitQueue *queue) { 27 + pthread_mutex_lock(&queue->lock); 28 + WaitQueueEntry *current = queue->head; 29 + while (current) { 30 + WaitQueueEntry *next = current->next; 31 + pthread_cond_destroy(&current->cond); 32 + pthread_mutex_destroy(&current->mutex); 33 + free(current); 34 + current = next; 35 + } 36 + queue->head = NULL; 37 + pthread_mutex_unlock(&queue->lock); 38 + pthread_mutex_destroy(&queue->lock); 39 + } 40 + 41 + void wait_queue_add(WaitQueue *queue, WaitQueueEntry *entry) { 42 + pthread_mutex_lock(&queue->lock); 43 + entry->next = queue->head; 44 + queue->head = entry; 45 + pthread_mutex_unlock(&queue->lock); 46 + } 47 + 48 + void wait_queue_remove(WaitQueue *queue, WaitQueueEntry *entry) { 49 + pthread_mutex_lock(&queue->lock); 50 + WaitQueueEntry **current = &queue->head; 51 + while (*current) { 52 + if (*current == entry) { 53 + *current = entry->next; 54 + break; 55 + } 56 + current = &(*current)->next; 57 + } 58 + pthread_mutex_unlock(&queue->lock); 59 + } 60 + 61 + int wait_queue_notify(WaitQueue *queue, int32_t *address, int count) { 62 + pthread_mutex_lock(&queue->lock); 63 + int notified = 0; 64 + WaitQueueEntry *current = queue->head; 65 + 66 + while (current && (count == -1 || notified < count)) { 67 + if (current->address == address) { 68 + pthread_mutex_lock(&current->mutex); 69 + current->notified = 1; 70 + pthread_cond_signal(&current->cond); 71 + pthread_mutex_unlock(&current->mutex); 72 + notified++; 73 + } 74 + current = current->next; 75 + } 76 + 77 + pthread_mutex_unlock(&queue->lock); 78 + return notified; 79 + } 80 + 81 + static bool get_atomic_array_data(struct js *js, jsval_t this_val, TypedArrayData **out_data, uint8_t **out_ptr) { 82 + jsval_t ta_data_val = js_get(js, this_val, "_typedarray_data"); 83 + if (js_type(ta_data_val) != JS_NUM) { 84 + return false; 85 + } 86 + 87 + TypedArrayData *ta_data = (TypedArrayData *)(uintptr_t)js_getnum(ta_data_val); 88 + if (!ta_data || !ta_data->buffer) { 89 + return false; 90 + } 91 + 92 + *out_data = ta_data; 93 + *out_ptr = ta_data->buffer->data + ta_data->byte_offset; 94 + return true; 95 + } 96 + 97 + // Atomics.add(typedArray, index, value) 98 + static jsval_t js_atomics_add(struct js *js, jsval_t *args, int nargs) { 99 + if (nargs < 3) { 100 + return js_mkerr(js, "Atomics.add requires 3 arguments"); 101 + } 102 + 103 + TypedArrayData *ta_data; 104 + uint8_t *ptr; 105 + if (!get_atomic_array_data(js, args[0], &ta_data, &ptr)) { 106 + return js_mkerr(js, "First argument must be a TypedArray"); 107 + } 108 + 109 + size_t index = (size_t)js_getnum(args[1]); 110 + if (index >= ta_data->length) { 111 + return js_mkerr(js, "Index out of bounds"); 112 + } 113 + 114 + int32_t value = (int32_t)js_getnum(args[2]); 115 + int32_t old_value; 116 + 117 + switch (ta_data->type) { 118 + case TYPED_ARRAY_INT8: { 119 + _Atomic int8_t *atomic_ptr = (_Atomic int8_t *)(ptr + index); 120 + old_value = atomic_fetch_add(atomic_ptr, (int8_t)value); 121 + break; 122 + } 123 + case TYPED_ARRAY_UINT8: { 124 + _Atomic uint8_t *atomic_ptr = (_Atomic uint8_t *)(ptr + index); 125 + old_value = atomic_fetch_add(atomic_ptr, (uint8_t)value); 126 + break; 127 + } 128 + case TYPED_ARRAY_INT16: { 129 + _Atomic int16_t *atomic_ptr = (_Atomic int16_t *)(ptr + index * 2); 130 + old_value = atomic_fetch_add(atomic_ptr, (int16_t)value); 131 + break; 132 + } 133 + case TYPED_ARRAY_UINT16: { 134 + _Atomic uint16_t *atomic_ptr = (_Atomic uint16_t *)(ptr + index * 2); 135 + old_value = atomic_fetch_add(atomic_ptr, (uint16_t)value); 136 + break; 137 + } 138 + case TYPED_ARRAY_INT32: { 139 + _Atomic int32_t *atomic_ptr = (_Atomic int32_t *)(ptr + index * 4); 140 + old_value = atomic_fetch_add(atomic_ptr, value); 141 + break; 142 + } 143 + case TYPED_ARRAY_UINT32: { 144 + _Atomic uint32_t *atomic_ptr = (_Atomic uint32_t *)(ptr + index * 4); 145 + old_value = atomic_fetch_add(atomic_ptr, (uint32_t)value); 146 + break; 147 + } 148 + default: 149 + return js_mkerr(js, "TypedArray type not supported for atomic operations"); 150 + } 151 + 152 + return js_mknum((double)old_value); 153 + } 154 + 155 + // Atomics.and(typedArray, index, value) 156 + static jsval_t js_atomics_and(struct js *js, jsval_t *args, int nargs) { 157 + if (nargs < 3) { 158 + return js_mkerr(js, "Atomics.and requires 3 arguments"); 159 + } 160 + 161 + TypedArrayData *ta_data; 162 + uint8_t *ptr; 163 + if (!get_atomic_array_data(js, args[0], &ta_data, &ptr)) { 164 + return js_mkerr(js, "First argument must be a TypedArray"); 165 + } 166 + 167 + size_t index = (size_t)js_getnum(args[1]); 168 + if (index >= ta_data->length) { 169 + return js_mkerr(js, "Index out of bounds"); 170 + } 171 + 172 + int32_t value = (int32_t)js_getnum(args[2]); 173 + int32_t old_value; 174 + 175 + switch (ta_data->type) { 176 + case TYPED_ARRAY_INT8: { 177 + _Atomic int8_t *atomic_ptr = (_Atomic int8_t *)(ptr + index); 178 + old_value = atomic_fetch_and(atomic_ptr, (int8_t)value); 179 + break; 180 + } 181 + case TYPED_ARRAY_UINT8: { 182 + _Atomic uint8_t *atomic_ptr = (_Atomic uint8_t *)(ptr + index); 183 + old_value = atomic_fetch_and(atomic_ptr, (uint8_t)value); 184 + break; 185 + } 186 + case TYPED_ARRAY_INT16: { 187 + _Atomic int16_t *atomic_ptr = (_Atomic int16_t *)(ptr + index * 2); 188 + old_value = atomic_fetch_and(atomic_ptr, (int16_t)value); 189 + break; 190 + } 191 + case TYPED_ARRAY_UINT16: { 192 + _Atomic uint16_t *atomic_ptr = (_Atomic uint16_t *)(ptr + index * 2); 193 + old_value = atomic_fetch_and(atomic_ptr, (uint16_t)value); 194 + break; 195 + } 196 + case TYPED_ARRAY_INT32: { 197 + _Atomic int32_t *atomic_ptr = (_Atomic int32_t *)(ptr + index * 4); 198 + old_value = atomic_fetch_and(atomic_ptr, value); 199 + break; 200 + } 201 + case TYPED_ARRAY_UINT32: { 202 + _Atomic uint32_t *atomic_ptr = (_Atomic uint32_t *)(ptr + index * 4); 203 + old_value = atomic_fetch_and(atomic_ptr, (uint32_t)value); 204 + break; 205 + } 206 + default: 207 + return js_mkerr(js, "TypedArray type not supported for atomic bitwise operations"); 208 + } 209 + 210 + return js_mknum((double)old_value); 211 + } 212 + 213 + // Atomics.compareExchange(typedArray, index, expectedValue, replacementValue) 214 + static jsval_t js_atomics_compareExchange(struct js *js, jsval_t *args, int nargs) { 215 + if (nargs < 4) { 216 + return js_mkerr(js, "Atomics.compareExchange requires 4 arguments"); 217 + } 218 + 219 + TypedArrayData *ta_data; 220 + uint8_t *ptr; 221 + if (!get_atomic_array_data(js, args[0], &ta_data, &ptr)) { 222 + return js_mkerr(js, "First argument must be a TypedArray"); 223 + } 224 + 225 + size_t index = (size_t)js_getnum(args[1]); 226 + if (index >= ta_data->length) { 227 + return js_mkerr(js, "Index out of bounds"); 228 + } 229 + 230 + int32_t expected = (int32_t)js_getnum(args[2]); 231 + int32_t replacement = (int32_t)js_getnum(args[3]); 232 + 233 + switch (ta_data->type) { 234 + case TYPED_ARRAY_INT8: { 235 + int8_t exp_i8 = (int8_t)expected; 236 + _Atomic int8_t *atomic_ptr = (_Atomic int8_t *)(ptr + index); 237 + atomic_compare_exchange_strong(atomic_ptr, &exp_i8, (int8_t)replacement); 238 + expected = (int32_t)exp_i8; 239 + break; 240 + } 241 + case TYPED_ARRAY_UINT8: { 242 + uint8_t exp_u8 = (uint8_t)expected; 243 + _Atomic uint8_t *atomic_ptr = (_Atomic uint8_t *)(ptr + index); 244 + atomic_compare_exchange_strong(atomic_ptr, &exp_u8, (uint8_t)replacement); 245 + expected = (int32_t)exp_u8; 246 + break; 247 + } 248 + case TYPED_ARRAY_INT16: { 249 + int16_t exp_i16 = (int16_t)expected; 250 + _Atomic int16_t *atomic_ptr = (_Atomic int16_t *)(ptr + index * 2); 251 + atomic_compare_exchange_strong(atomic_ptr, &exp_i16, (int16_t)replacement); 252 + expected = (int32_t)exp_i16; 253 + break; 254 + } 255 + case TYPED_ARRAY_UINT16: { 256 + uint16_t exp_u16 = (uint16_t)expected; 257 + _Atomic uint16_t *atomic_ptr = (_Atomic uint16_t *)(ptr + index * 2); 258 + atomic_compare_exchange_strong(atomic_ptr, &exp_u16, (uint16_t)replacement); 259 + expected = (int32_t)exp_u16; 260 + break; 261 + } 262 + case TYPED_ARRAY_INT32: { 263 + _Atomic int32_t *atomic_ptr = (_Atomic int32_t *)(ptr + index * 4); 264 + atomic_compare_exchange_strong(atomic_ptr, &expected, replacement); 265 + break; 266 + } 267 + case TYPED_ARRAY_UINT32: { 268 + uint32_t exp_u32 = (uint32_t)expected; 269 + _Atomic uint32_t *atomic_ptr = (_Atomic uint32_t *)(ptr + index * 4); 270 + atomic_compare_exchange_strong(atomic_ptr, &exp_u32, (uint32_t)replacement); 271 + expected = (int32_t)exp_u32; 272 + break; 273 + } 274 + default: 275 + return js_mkerr(js, "TypedArray type not supported for atomic operations"); 276 + } 277 + 278 + return js_mknum((double)expected); 279 + } 280 + 281 + // Atomics.exchange(typedArray, index, value) 282 + static jsval_t js_atomics_exchange(struct js *js, jsval_t *args, int nargs) { 283 + if (nargs < 3) { 284 + return js_mkerr(js, "Atomics.exchange requires 3 arguments"); 285 + } 286 + 287 + TypedArrayData *ta_data; 288 + uint8_t *ptr; 289 + if (!get_atomic_array_data(js, args[0], &ta_data, &ptr)) { 290 + return js_mkerr(js, "First argument must be a TypedArray"); 291 + } 292 + 293 + size_t index = (size_t)js_getnum(args[1]); 294 + if (index >= ta_data->length) { 295 + return js_mkerr(js, "Index out of bounds"); 296 + } 297 + 298 + int32_t value = (int32_t)js_getnum(args[2]); 299 + int32_t old_value; 300 + 301 + switch (ta_data->type) { 302 + case TYPED_ARRAY_INT8: { 303 + _Atomic int8_t *atomic_ptr = (_Atomic int8_t *)(ptr + index); 304 + old_value = atomic_exchange(atomic_ptr, (int8_t)value); 305 + break; 306 + } 307 + case TYPED_ARRAY_UINT8: { 308 + _Atomic uint8_t *atomic_ptr = (_Atomic uint8_t *)(ptr + index); 309 + old_value = atomic_exchange(atomic_ptr, (uint8_t)value); 310 + break; 311 + } 312 + case TYPED_ARRAY_INT16: { 313 + _Atomic int16_t *atomic_ptr = (_Atomic int16_t *)(ptr + index * 2); 314 + old_value = atomic_exchange(atomic_ptr, (int16_t)value); 315 + break; 316 + } 317 + case TYPED_ARRAY_UINT16: { 318 + _Atomic uint16_t *atomic_ptr = (_Atomic uint16_t *)(ptr + index * 2); 319 + old_value = atomic_exchange(atomic_ptr, (uint16_t)value); 320 + break; 321 + } 322 + case TYPED_ARRAY_INT32: { 323 + _Atomic int32_t *atomic_ptr = (_Atomic int32_t *)(ptr + index * 4); 324 + old_value = atomic_exchange(atomic_ptr, value); 325 + break; 326 + } 327 + case TYPED_ARRAY_UINT32: { 328 + _Atomic uint32_t *atomic_ptr = (_Atomic uint32_t *)(ptr + index * 4); 329 + old_value = atomic_exchange(atomic_ptr, (uint32_t)value); 330 + break; 331 + } 332 + default: 333 + return js_mkerr(js, "TypedArray type not supported for atomic operations"); 334 + } 335 + 336 + return js_mknum((double)old_value); 337 + } 338 + 339 + // Atomics.isLockFree(size) 340 + static jsval_t js_atomics_isLockFree(struct js *js, jsval_t *args, int nargs) { 341 + if (nargs < 1) { 342 + return js_mkerr(js, "Atomics.isLockFree requires 1 argument"); 343 + } 344 + 345 + int size = (int)js_getnum(args[0]); 346 + bool is_lock_free = false; 347 + 348 + switch (size) { 349 + case 1: 350 + is_lock_free = ATOMIC_CHAR_LOCK_FREE == 2; 351 + break; 352 + case 2: 353 + is_lock_free = ATOMIC_SHORT_LOCK_FREE == 2; 354 + break; 355 + case 4: 356 + is_lock_free = ATOMIC_INT_LOCK_FREE == 2; 357 + break; 358 + case 8: 359 + is_lock_free = ATOMIC_LLONG_LOCK_FREE == 2; 360 + break; 361 + default: 362 + is_lock_free = false; 363 + } 364 + 365 + return is_lock_free ? js_mktrue() : js_mkfalse(); 366 + } 367 + 368 + // Atomics.load(typedArray, index) 369 + static jsval_t js_atomics_load(struct js *js, jsval_t *args, int nargs) { 370 + if (nargs < 2) { 371 + return js_mkerr(js, "Atomics.load requires 2 arguments"); 372 + } 373 + 374 + TypedArrayData *ta_data; 375 + uint8_t *ptr; 376 + if (!get_atomic_array_data(js, args[0], &ta_data, &ptr)) { 377 + return js_mkerr(js, "First argument must be a TypedArray"); 378 + } 379 + 380 + size_t index = (size_t)js_getnum(args[1]); 381 + if (index >= ta_data->length) { 382 + return js_mkerr(js, "Index out of bounds"); 383 + } 384 + 385 + int32_t value; 386 + 387 + switch (ta_data->type) { 388 + case TYPED_ARRAY_INT8: { 389 + _Atomic int8_t *atomic_ptr = (_Atomic int8_t *)(ptr + index); 390 + value = atomic_load(atomic_ptr); 391 + break; 392 + } 393 + case TYPED_ARRAY_UINT8: { 394 + _Atomic uint8_t *atomic_ptr = (_Atomic uint8_t *)(ptr + index); 395 + value = atomic_load(atomic_ptr); 396 + break; 397 + } 398 + case TYPED_ARRAY_INT16: { 399 + _Atomic int16_t *atomic_ptr = (_Atomic int16_t *)(ptr + index * 2); 400 + value = atomic_load(atomic_ptr); 401 + break; 402 + } 403 + case TYPED_ARRAY_UINT16: { 404 + _Atomic uint16_t *atomic_ptr = (_Atomic uint16_t *)(ptr + index * 2); 405 + value = atomic_load(atomic_ptr); 406 + break; 407 + } 408 + case TYPED_ARRAY_INT32: { 409 + _Atomic int32_t *atomic_ptr = (_Atomic int32_t *)(ptr + index * 4); 410 + value = atomic_load(atomic_ptr); 411 + break; 412 + } 413 + case TYPED_ARRAY_UINT32: { 414 + _Atomic uint32_t *atomic_ptr = (_Atomic uint32_t *)(ptr + index * 4); 415 + value = atomic_load(atomic_ptr); 416 + break; 417 + } 418 + default: 419 + return js_mkerr(js, "TypedArray type not supported for atomic operations"); 420 + } 421 + 422 + return js_mknum((double)value); 423 + } 424 + 425 + // Atomics.or(typedArray, index, value) 426 + static jsval_t js_atomics_or(struct js *js, jsval_t *args, int nargs) { 427 + if (nargs < 3) { 428 + return js_mkerr(js, "Atomics.or requires 3 arguments"); 429 + } 430 + 431 + TypedArrayData *ta_data; 432 + uint8_t *ptr; 433 + if (!get_atomic_array_data(js, args[0], &ta_data, &ptr)) { 434 + return js_mkerr(js, "First argument must be a TypedArray"); 435 + } 436 + 437 + size_t index = (size_t)js_getnum(args[1]); 438 + if (index >= ta_data->length) { 439 + return js_mkerr(js, "Index out of bounds"); 440 + } 441 + 442 + int32_t value = (int32_t)js_getnum(args[2]); 443 + int32_t old_value; 444 + 445 + switch (ta_data->type) { 446 + case TYPED_ARRAY_INT8: { 447 + _Atomic int8_t *atomic_ptr = (_Atomic int8_t *)(ptr + index); 448 + old_value = atomic_fetch_or(atomic_ptr, (int8_t)value); 449 + break; 450 + } 451 + case TYPED_ARRAY_UINT8: { 452 + _Atomic uint8_t *atomic_ptr = (_Atomic uint8_t *)(ptr + index); 453 + old_value = atomic_fetch_or(atomic_ptr, (uint8_t)value); 454 + break; 455 + } 456 + case TYPED_ARRAY_INT16: { 457 + _Atomic int16_t *atomic_ptr = (_Atomic int16_t *)(ptr + index * 2); 458 + old_value = atomic_fetch_or(atomic_ptr, (int16_t)value); 459 + break; 460 + } 461 + case TYPED_ARRAY_UINT16: { 462 + _Atomic uint16_t *atomic_ptr = (_Atomic uint16_t *)(ptr + index * 2); 463 + old_value = atomic_fetch_or(atomic_ptr, (uint16_t)value); 464 + break; 465 + } 466 + case TYPED_ARRAY_INT32: { 467 + _Atomic int32_t *atomic_ptr = (_Atomic int32_t *)(ptr + index * 4); 468 + old_value = atomic_fetch_or(atomic_ptr, value); 469 + break; 470 + } 471 + case TYPED_ARRAY_UINT32: { 472 + _Atomic uint32_t *atomic_ptr = (_Atomic uint32_t *)(ptr + index * 4); 473 + old_value = atomic_fetch_or(atomic_ptr, (uint32_t)value); 474 + break; 475 + } 476 + default: 477 + return js_mkerr(js, "TypedArray type not supported for atomic bitwise operations"); 478 + } 479 + 480 + return js_mknum((double)old_value); 481 + } 482 + 483 + // Atomics.store(typedArray, index, value) 484 + static jsval_t js_atomics_store(struct js *js, jsval_t *args, int nargs) { 485 + if (nargs < 3) { 486 + return js_mkerr(js, "Atomics.store requires 3 arguments"); 487 + } 488 + 489 + TypedArrayData *ta_data; 490 + uint8_t *ptr; 491 + if (!get_atomic_array_data(js, args[0], &ta_data, &ptr)) { 492 + return js_mkerr(js, "First argument must be a TypedArray"); 493 + } 494 + 495 + size_t index = (size_t)js_getnum(args[1]); 496 + if (index >= ta_data->length) { 497 + return js_mkerr(js, "Index out of bounds"); 498 + } 499 + 500 + int32_t value = (int32_t)js_getnum(args[2]); 501 + 502 + switch (ta_data->type) { 503 + case TYPED_ARRAY_INT8: { 504 + _Atomic int8_t *atomic_ptr = (_Atomic int8_t *)(ptr + index); 505 + atomic_store(atomic_ptr, (int8_t)value); 506 + break; 507 + } 508 + case TYPED_ARRAY_UINT8: { 509 + _Atomic uint8_t *atomic_ptr = (_Atomic uint8_t *)(ptr + index); 510 + atomic_store(atomic_ptr, (uint8_t)value); 511 + break; 512 + } 513 + case TYPED_ARRAY_INT16: { 514 + _Atomic int16_t *atomic_ptr = (_Atomic int16_t *)(ptr + index * 2); 515 + atomic_store(atomic_ptr, (int16_t)value); 516 + break; 517 + } 518 + case TYPED_ARRAY_UINT16: { 519 + _Atomic uint16_t *atomic_ptr = (_Atomic uint16_t *)(ptr + index * 2); 520 + atomic_store(atomic_ptr, (uint16_t)value); 521 + break; 522 + } 523 + case TYPED_ARRAY_INT32: { 524 + _Atomic int32_t *atomic_ptr = (_Atomic int32_t *)(ptr + index * 4); 525 + atomic_store(atomic_ptr, value); 526 + break; 527 + } 528 + case TYPED_ARRAY_UINT32: { 529 + _Atomic uint32_t *atomic_ptr = (_Atomic uint32_t *)(ptr + index * 4); 530 + atomic_store(atomic_ptr, (uint32_t)value); 531 + break; 532 + } 533 + default: 534 + return js_mkerr(js, "TypedArray type not supported for atomic operations"); 535 + } 536 + 537 + return js_mknum((double)value); 538 + } 539 + 540 + // Atomics.sub(typedArray, index, value) 541 + static jsval_t js_atomics_sub(struct js *js, jsval_t *args, int nargs) { 542 + if (nargs < 3) { 543 + return js_mkerr(js, "Atomics.sub requires 3 arguments"); 544 + } 545 + 546 + TypedArrayData *ta_data; 547 + uint8_t *ptr; 548 + if (!get_atomic_array_data(js, args[0], &ta_data, &ptr)) { 549 + return js_mkerr(js, "First argument must be a TypedArray"); 550 + } 551 + 552 + size_t index = (size_t)js_getnum(args[1]); 553 + if (index >= ta_data->length) { 554 + return js_mkerr(js, "Index out of bounds"); 555 + } 556 + 557 + int32_t value = (int32_t)js_getnum(args[2]); 558 + int32_t old_value; 559 + 560 + switch (ta_data->type) { 561 + case TYPED_ARRAY_INT8: { 562 + _Atomic int8_t *atomic_ptr = (_Atomic int8_t *)(ptr + index); 563 + old_value = atomic_fetch_sub(atomic_ptr, (int8_t)value); 564 + break; 565 + } 566 + case TYPED_ARRAY_UINT8: { 567 + _Atomic uint8_t *atomic_ptr = (_Atomic uint8_t *)(ptr + index); 568 + old_value = atomic_fetch_sub(atomic_ptr, (uint8_t)value); 569 + break; 570 + } 571 + case TYPED_ARRAY_INT16: { 572 + _Atomic int16_t *atomic_ptr = (_Atomic int16_t *)(ptr + index * 2); 573 + old_value = atomic_fetch_sub(atomic_ptr, (int16_t)value); 574 + break; 575 + } 576 + case TYPED_ARRAY_UINT16: { 577 + _Atomic uint16_t *atomic_ptr = (_Atomic uint16_t *)(ptr + index * 2); 578 + old_value = atomic_fetch_sub(atomic_ptr, (uint16_t)value); 579 + break; 580 + } 581 + case TYPED_ARRAY_INT32: { 582 + _Atomic int32_t *atomic_ptr = (_Atomic int32_t *)(ptr + index * 4); 583 + old_value = atomic_fetch_sub(atomic_ptr, value); 584 + break; 585 + } 586 + case TYPED_ARRAY_UINT32: { 587 + _Atomic uint32_t *atomic_ptr = (_Atomic uint32_t *)(ptr + index * 4); 588 + old_value = atomic_fetch_sub(atomic_ptr, (uint32_t)value); 589 + break; 590 + } 591 + default: 592 + return js_mkerr(js, "TypedArray type not supported for atomic operations"); 593 + } 594 + 595 + return js_mknum((double)old_value); 596 + } 597 + 598 + // Atomics.xor(typedArray, index, value) 599 + static jsval_t js_atomics_xor(struct js *js, jsval_t *args, int nargs) { 600 + if (nargs < 3) { 601 + return js_mkerr(js, "Atomics.xor requires 3 arguments"); 602 + } 603 + 604 + TypedArrayData *ta_data; 605 + uint8_t *ptr; 606 + if (!get_atomic_array_data(js, args[0], &ta_data, &ptr)) { 607 + return js_mkerr(js, "First argument must be a TypedArray"); 608 + } 609 + 610 + size_t index = (size_t)js_getnum(args[1]); 611 + if (index >= ta_data->length) { 612 + return js_mkerr(js, "Index out of bounds"); 613 + } 614 + 615 + int32_t value = (int32_t)js_getnum(args[2]); 616 + int32_t old_value; 617 + 618 + switch (ta_data->type) { 619 + case TYPED_ARRAY_INT8: { 620 + _Atomic int8_t *atomic_ptr = (_Atomic int8_t *)(ptr + index); 621 + old_value = atomic_fetch_xor(atomic_ptr, (int8_t)value); 622 + break; 623 + } 624 + case TYPED_ARRAY_UINT8: { 625 + _Atomic uint8_t *atomic_ptr = (_Atomic uint8_t *)(ptr + index); 626 + old_value = atomic_fetch_xor(atomic_ptr, (uint8_t)value); 627 + break; 628 + } 629 + case TYPED_ARRAY_INT16: { 630 + _Atomic int16_t *atomic_ptr = (_Atomic int16_t *)(ptr + index * 2); 631 + old_value = atomic_fetch_xor(atomic_ptr, (int16_t)value); 632 + break; 633 + } 634 + case TYPED_ARRAY_UINT16: { 635 + _Atomic uint16_t *atomic_ptr = (_Atomic uint16_t *)(ptr + index * 2); 636 + old_value = atomic_fetch_xor(atomic_ptr, (uint16_t)value); 637 + break; 638 + } 639 + case TYPED_ARRAY_INT32: { 640 + _Atomic int32_t *atomic_ptr = (_Atomic int32_t *)(ptr + index * 4); 641 + old_value = atomic_fetch_xor(atomic_ptr, value); 642 + break; 643 + } 644 + case TYPED_ARRAY_UINT32: { 645 + _Atomic uint32_t *atomic_ptr = (_Atomic uint32_t *)(ptr + index * 4); 646 + old_value = atomic_fetch_xor(atomic_ptr, (uint32_t)value); 647 + break; 648 + } 649 + default: 650 + return js_mkerr(js, "TypedArray type not supported for atomic bitwise operations"); 651 + } 652 + 653 + return js_mknum((double)old_value); 654 + } 655 + 656 + // Atomics.wait(typedArray, index, value, timeout) 657 + static jsval_t js_atomics_wait(struct js *js, jsval_t *args, int nargs) { 658 + if (nargs < 3) { 659 + return js_mkerr(js, "Atomics.wait requires at least 3 arguments"); 660 + } 661 + 662 + pthread_once(&wait_queue_init_once, init_wait_queue); 663 + 664 + TypedArrayData *ta_data; 665 + uint8_t *ptr; 666 + if (!get_atomic_array_data(js, args[0], &ta_data, &ptr)) { 667 + return js_mkerr(js, "First argument must be a TypedArray"); 668 + } 669 + 670 + if (ta_data->type != TYPED_ARRAY_INT32) { 671 + return js_mkerr(js, "Atomics.wait only works with Int32Array"); 672 + } 673 + 674 + size_t index = (size_t)js_getnum(args[1]); 675 + if (index >= ta_data->length) { 676 + return js_mkerr(js, "Index out of bounds"); 677 + } 678 + 679 + int32_t expected_value = (int32_t)js_getnum(args[2]); 680 + int64_t timeout_ms = -1; 681 + 682 + if (nargs > 3 && js_type(args[3]) == JS_NUM) { 683 + timeout_ms = (int64_t)js_getnum(args[3]); 684 + } 685 + 686 + _Atomic int32_t *atomic_ptr = (_Atomic int32_t *)(ptr + index * 4); 687 + int32_t current_value = atomic_load(atomic_ptr); 688 + 689 + if (current_value != expected_value) { 690 + return js_mkstr(js, "not-equal", 9); 691 + } 692 + 693 + WaitQueueEntry entry; 694 + pthread_cond_init(&entry.cond, NULL); 695 + pthread_mutex_init(&entry.mutex, NULL); 696 + entry.address = (int32_t *)atomic_ptr; 697 + entry.notified = 0; 698 + entry.next = NULL; 699 + 700 + wait_queue_add(&global_wait_queue, &entry); 701 + pthread_mutex_lock(&entry.mutex); 702 + 703 + const char *result = "ok"; 704 + if (timeout_ms < 0) { 705 + while (!entry.notified) pthread_cond_wait(&entry.cond, &entry.mutex); 706 + } else { 707 + struct timespec ts; 708 + clock_gettime(CLOCK_REALTIME, &ts); 709 + ts.tv_sec += timeout_ms / 1000; 710 + ts.tv_nsec += (timeout_ms % 1000) * 1000000; 711 + if (ts.tv_nsec >= 1000000000) { 712 + ts.tv_sec++; 713 + ts.tv_nsec -= 1000000000; 714 + } 715 + 716 + int wait_result = pthread_cond_timedwait(&entry.cond, &entry.mutex, &ts); 717 + if (wait_result == ETIMEDOUT && !entry.notified) result = "timed-out"; 718 + } 719 + 720 + pthread_mutex_unlock(&entry.mutex); 721 + wait_queue_remove(&global_wait_queue, &entry); 722 + 723 + pthread_cond_destroy(&entry.cond); 724 + pthread_mutex_destroy(&entry.mutex); 725 + 726 + return js_mkstr(js, result, strlen(result)); 727 + } 728 + 729 + // Atomics.notify(typedArray, index, count) 730 + static jsval_t js_atomics_notify(struct js *js, jsval_t *args, int nargs) { 731 + if (nargs < 2) { 732 + return js_mkerr(js, "Atomics.notify requires at least 2 arguments"); 733 + } 734 + 735 + pthread_once(&wait_queue_init_once, init_wait_queue); 736 + 737 + TypedArrayData *ta_data; 738 + uint8_t *ptr; 739 + if (!get_atomic_array_data(js, args[0], &ta_data, &ptr)) { 740 + return js_mkerr(js, "First argument must be a TypedArray"); 741 + } 742 + 743 + if (ta_data->type != TYPED_ARRAY_INT32) { 744 + return js_mkerr(js, "Atomics.notify only works with Int32Array"); 745 + } 746 + 747 + size_t index = (size_t)js_getnum(args[1]); 748 + if (index >= ta_data->length) { 749 + return js_mkerr(js, "Index out of bounds"); 750 + } 751 + 752 + int count = -1; 753 + if (nargs > 2 && js_type(args[2]) == JS_NUM) { 754 + count = (int)js_getnum(args[2]); 755 + } 756 + 757 + int32_t *address = (int32_t *)(ptr + index * 4); 758 + int notified = wait_queue_notify(&global_wait_queue, address, count); 759 + 760 + return js_mknum((double)notified); 761 + } 762 + 763 + // Atomics.waitAsync(typedArray, index, value, timeout) 764 + static jsval_t js_atomics_waitAsync(struct js *js, jsval_t *args, int nargs) { 765 + if (nargs < 3) { 766 + return js_mkerr(js, "Atomics.waitAsync requires at least 3 arguments"); 767 + } 768 + 769 + TypedArrayData *ta_data; 770 + uint8_t *ptr; 771 + if (!get_atomic_array_data(js, args[0], &ta_data, &ptr)) { 772 + return js_mkerr(js, "First argument must be a TypedArray"); 773 + } 774 + 775 + if (ta_data->type != TYPED_ARRAY_INT32) { 776 + return js_mkerr(js, "Atomics.waitAsync only works with Int32Array"); 777 + } 778 + 779 + size_t index = (size_t)js_getnum(args[1]); 780 + if (index >= ta_data->length) { 781 + return js_mkerr(js, "Index out of bounds"); 782 + } 783 + 784 + int32_t expected_value = (int32_t)js_getnum(args[2]); 785 + _Atomic int32_t *atomic_ptr = (_Atomic int32_t *)(ptr + index * 4); 786 + int32_t current_value = atomic_load(atomic_ptr); 787 + 788 + jsval_t result_obj = js_mkobj(js); 789 + if (current_value != expected_value) { 790 + js_set(js, result_obj, "async", js_mkfalse()); 791 + js_set(js, result_obj, "value", js_mkstr(js, "not-equal", 9)); 792 + return result_obj; 793 + } 794 + 795 + jsval_t promise = js_mkpromise(js); 796 + js_set(js, result_obj, "async", js_mktrue()); 797 + js_set(js, result_obj, "value", promise); 798 + js_resolve_promise(js, promise, js_mkstr(js, "ok", 2)); 799 + 800 + return result_obj; 801 + } 802 + 803 + // Atomics.pause() 804 + static jsval_t js_atomics_pause(struct js *js, jsval_t *args, int nargs) { 805 + (void)js; 806 + (void)args; 807 + (void)nargs; 808 + 809 + #if defined(__x86_64__) || defined(__i386__) 810 + __builtin_ia32_pause(); 811 + #elif defined(__aarch64__) || defined(__arm__) 812 + __asm__ __volatile__("yield"); 813 + #endif 814 + 815 + return js_mkundef(); 816 + } 817 + 818 + void init_atomics_module(void) { 819 + struct js *js = rt->js; 820 + 821 + jsval_t glob = js_glob(js); 822 + jsval_t atomics = js_mkobj(js); 823 + 824 + js_set(js, atomics, "add", js_mkfun(js_atomics_add)); 825 + js_set(js, atomics, "and", js_mkfun(js_atomics_and)); 826 + js_set(js, atomics, "compareExchange", js_mkfun(js_atomics_compareExchange)); 827 + js_set(js, atomics, "exchange", js_mkfun(js_atomics_exchange)); 828 + js_set(js, atomics, "isLockFree", js_mkfun(js_atomics_isLockFree)); 829 + js_set(js, atomics, "load", js_mkfun(js_atomics_load)); 830 + js_set(js, atomics, "notify", js_mkfun(js_atomics_notify)); 831 + js_set(js, atomics, "or", js_mkfun(js_atomics_or)); 832 + js_set(js, atomics, "pause", js_mkfun(js_atomics_pause)); 833 + js_set(js, atomics, "store", js_mkfun(js_atomics_store)); 834 + js_set(js, atomics, "sub", js_mkfun(js_atomics_sub)); 835 + js_set(js, atomics, "wait", js_mkfun(js_atomics_wait)); 836 + js_set(js, atomics, "waitAsync", js_mkfun(js_atomics_waitAsync)); 837 + js_set(js, atomics, "xor", js_mkfun(js_atomics_xor)); 838 + 839 + js_set(js, atomics, "@@toStringTag", js_mkstr(js, "Atomics", 7)); 840 + js_set(js, glob, "Atomics", atomics); 841 + }
+33
src/modules/buffer.c
··· 95 95 data->length = length; 96 96 data->capacity = length; 97 97 data->ref_count = 1; 98 + data->is_shared = 0; 99 + return data; 100 + } 101 + 102 + static ArrayBufferData *create_shared_array_buffer_data(size_t length) { 103 + ArrayBufferData *data = create_array_buffer_data(length); 104 + if (data) data->is_shared = 1; 98 105 return data; 99 106 } 100 107 ··· 672 679 return js_mknum((double)to_write); 673 680 } 674 681 682 + static jsval_t js_sharedarraybuffer_constructor(struct js *js, jsval_t *args, int nargs) { 683 + size_t length = 0; 684 + if (nargs > 0 && js_type(args[0]) == JS_NUM) { 685 + length = (size_t)js_getnum(args[0]); 686 + } 687 + 688 + ArrayBufferData *data = create_shared_array_buffer_data(length); 689 + if (!data) { 690 + return js_mkerr(js, "Failed to allocate SharedArrayBuffer"); 691 + } 692 + 693 + jsval_t obj = js_mkobj(js); 694 + js_set(js, obj, "_arraybuffer_data", js_mknum((double)(uintptr_t)data)); 695 + js_set(js, obj, "_shared", js_mktrue()); 696 + js_set(js, obj, "byteLength", js_mknum((double)length)); 697 + js_set(js, obj, "slice", js_mkfun(js_arraybuffer_slice)); 698 + 699 + return obj; 700 + } 701 + 675 702 void init_buffer_module() { 676 703 struct js *js = rt->js; 677 704 jsval_t glob = js_glob(js); ··· 713 740 js_set(js, dataview_proto, "getFloat32", js_mkfun(js_dataview_getFloat32)); 714 741 js_set(js, dataview_constructor, "prototype", dataview_proto); 715 742 js_set(js, glob, "DataView", dataview_constructor); 743 + 744 + jsval_t sharedarraybuffer_constructor = js_mkfun(js_sharedarraybuffer_constructor); 745 + jsval_t sharedarraybuffer_proto = js_mkobj(js); 746 + js_set(js, sharedarraybuffer_proto, "slice", js_mkfun(js_arraybuffer_slice)); 747 + js_set(js, sharedarraybuffer_constructor, "prototype", sharedarraybuffer_proto); 748 + js_set(js, glob, "SharedArrayBuffer", sharedarraybuffer_constructor); 716 749 717 750 jsval_t buffer_obj = js_mkobj(js); 718 751 js_set(js, buffer_obj, "from", js_mkfun(js_buffer_from));
+147
tests/test_atomics.cjs
··· 1 + console.log('=== Atomics API Tests ===\n'); 2 + 3 + // Test 1: SharedArrayBuffer creation 4 + console.log('Test 1: SharedArrayBuffer creation'); 5 + const sab = new SharedArrayBuffer(1024); 6 + console.log('SharedArrayBuffer byteLength:', sab.byteLength); 7 + console.log('SharedArrayBuffer created:', sab.byteLength === 1024 ? 'PASS' : 'FAIL'); 8 + 9 + // Test 2: Int32Array on SharedArrayBuffer 10 + console.log('\nTest 2: Int32Array on SharedArrayBuffer'); 11 + const ta = new Int32Array(sab); 12 + console.log('Int32Array length:', ta.length); 13 + console.log('Int32Array byteLength:', ta.byteLength); 14 + 15 + // Test 3: Atomics.store and Atomics.load 16 + console.log('\nTest 3: Atomics.store and Atomics.load'); 17 + ta[0] = 0; 18 + console.log('Initial value:', ta[0]); 19 + Atomics.store(ta, 0, 12); 20 + const loaded = Atomics.load(ta, 0); 21 + console.log('After Atomics.store(ta, 0, 12):', loaded); 22 + console.log('Atomics.store/load test:', loaded === 12 ? 'PASS' : 'FAIL'); 23 + 24 + // Test 4: Atomics.add 25 + console.log('\nTest 4: Atomics.add'); 26 + Atomics.store(ta, 0, 5); 27 + const oldAdd = Atomics.add(ta, 0, 12); 28 + const newAdd = Atomics.load(ta, 0); 29 + console.log('Old value:', oldAdd); 30 + console.log('New value after add(12):', newAdd); 31 + console.log('Atomics.add test:', oldAdd === 5 && newAdd === 17 ? 'PASS' : 'FAIL'); 32 + 33 + // Test 5: Atomics.sub 34 + console.log('\nTest 5: Atomics.sub'); 35 + Atomics.store(ta, 0, 12); 36 + const oldSub = Atomics.sub(ta, 0, 2); 37 + const newSub = Atomics.load(ta, 0); 38 + console.log('Old value:', oldSub); 39 + console.log('New value after sub(2):', newSub); 40 + console.log('Atomics.sub test:', oldSub === 12 && newSub === 10 ? 'PASS' : 'FAIL'); 41 + 42 + // Test 6: Atomics.and 43 + console.log('\nTest 6: Atomics.and'); 44 + Atomics.store(ta, 0, 17); 45 + const oldAnd = Atomics.and(ta, 0, 1); 46 + const newAnd = Atomics.load(ta, 0); 47 + console.log('Old value (17):', oldAnd); 48 + console.log('New value after and(1):', newAnd); 49 + console.log('Atomics.and test:', oldAnd === 17 && newAnd === 1 ? 'PASS' : 'FAIL'); 50 + 51 + // Test 7: Atomics.or 52 + console.log('\nTest 7: Atomics.or'); 53 + Atomics.store(ta, 0, 12); 54 + const oldOr = Atomics.or(ta, 0, 1); 55 + const newOr = Atomics.load(ta, 0); 56 + console.log('Old value (12):', oldOr); 57 + console.log('New value after or(1):', newOr); 58 + console.log('Atomics.or test:', oldOr === 12 && newOr === 13 ? 'PASS' : 'FAIL'); 59 + 60 + // Test 8: Atomics.xor 61 + console.log('\nTest 8: Atomics.xor'); 62 + Atomics.store(ta, 0, 10); 63 + const oldXor = Atomics.xor(ta, 0, 1); 64 + const newXor = Atomics.load(ta, 0); 65 + console.log('Old value (10):', oldXor); 66 + console.log('New value after xor(1):', newXor); 67 + console.log('Atomics.xor test:', oldXor === 10 && newXor === 11 ? 'PASS' : 'FAIL'); 68 + 69 + // Test 9: Atomics.exchange 70 + console.log('\nTest 9: Atomics.exchange'); 71 + Atomics.store(ta, 0, 1); 72 + const oldExchange = Atomics.exchange(ta, 0, 12); 73 + const newExchange = Atomics.load(ta, 0); 74 + console.log('Old value:', oldExchange); 75 + console.log('New value after exchange(12):', newExchange); 76 + console.log('Atomics.exchange test:', oldExchange === 1 && newExchange === 12 ? 'PASS' : 'FAIL'); 77 + 78 + // Test 10: Atomics.compareExchange (match) 79 + console.log('\nTest 10: Atomics.compareExchange (match)'); 80 + Atomics.store(ta, 0, 5); 81 + const resultMatch = Atomics.compareExchange(ta, 0, 5, 12); 82 + const valueMatch = Atomics.load(ta, 0); 83 + console.log('Expected 5, got:', resultMatch); 84 + console.log('New value:', valueMatch); 85 + console.log('Atomics.compareExchange (match) test:', resultMatch === 5 && valueMatch === 12 ? 'PASS' : 'FAIL'); 86 + 87 + // Test 11: Atomics.compareExchange (no match) 88 + console.log('\nTest 11: Atomics.compareExchange (no match)'); 89 + Atomics.store(ta, 0, 1); 90 + const resultNoMatch = Atomics.compareExchange(ta, 0, 5, 12); 91 + const valueNoMatch = Atomics.load(ta, 0); 92 + console.log('Expected 5, got:', resultNoMatch); 93 + console.log('Value unchanged:', valueNoMatch); 94 + console.log('Atomics.compareExchange (no match) test:', resultNoMatch === 1 && valueNoMatch === 1 ? 'PASS' : 'FAIL'); 95 + 96 + // Test 12: Atomics.isLockFree 97 + console.log('\nTest 12: Atomics.isLockFree'); 98 + console.log('isLockFree(1):', Atomics.isLockFree(1)); 99 + console.log('isLockFree(2):', Atomics.isLockFree(2)); 100 + console.log('isLockFree(3):', Atomics.isLockFree(3)); 101 + console.log('isLockFree(4):', Atomics.isLockFree(4)); 102 + console.log('isLockFree(8):', Atomics.isLockFree(8)); 103 + console.log('Atomics.isLockFree test:', Atomics.isLockFree(4) === true ? 'PASS' : 'FAIL'); 104 + 105 + // Test 13: Atomics.notify (without waiters) 106 + console.log('\nTest 13: Atomics.notify (without waiters)'); 107 + const int32 = new Int32Array(sab); 108 + Atomics.store(int32, 0, 0); 109 + const notified = Atomics.notify(int32, 0, 1); 110 + console.log('Agents notified:', notified); 111 + console.log('Atomics.notify test:', notified === 0 ? 'PASS' : 'FAIL'); 112 + 113 + // Test 14: Comprehensive example from MDN 114 + console.log('\nTest 14: Comprehensive example'); 115 + const sab2 = new SharedArrayBuffer(1024); 116 + const ta2 = new Uint8Array(sab2); 117 + 118 + ta2[0] = 0; 119 + console.log('ta2[0]:', ta2[0]); // 0 120 + ta2[0] = 5; 121 + console.log('ta2[0] = 5:', ta2[0]); // 5 122 + 123 + Atomics.add(ta2, 0, 12); 124 + console.log('After Atomics.add(ta2, 0, 12):', Atomics.load(ta2, 0)); // 17 125 + 126 + Atomics.and(ta2, 0, 1); 127 + console.log('After Atomics.and(ta2, 0, 1):', Atomics.load(ta2, 0)); // 1 128 + 129 + Atomics.compareExchange(ta2, 0, 5, 12); 130 + console.log('After Atomics.compareExchange(ta2, 0, 5, 12):', Atomics.load(ta2, 0)); // 1 (no change) 131 + 132 + Atomics.exchange(ta2, 0, 12); 133 + console.log('After Atomics.exchange(ta2, 0, 12):', Atomics.load(ta2, 0)); // 12 134 + 135 + Atomics.or(ta2, 0, 1); 136 + console.log('After Atomics.or(ta2, 0, 1):', Atomics.load(ta2, 0)); // 13 137 + 138 + Atomics.store(ta2, 0, 12); 139 + console.log('After Atomics.store(ta2, 0, 12):', Atomics.load(ta2, 0)); // 12 140 + 141 + Atomics.sub(ta2, 0, 2); 142 + console.log('After Atomics.sub(ta2, 0, 2):', Atomics.load(ta2, 0)); // 10 143 + 144 + Atomics.xor(ta2, 0, 1); 145 + console.log('After Atomics.xor(ta2, 0, 1):', Atomics.load(ta2, 0)); // 11 146 + 147 + console.log('\n=== All Atomics tests completed ===');
+109
tests/test_atomics_with_literals.cjs
··· 1 + // Test Atomics API with Binary, Octal, and Hex literals 2 + console.log('=== Atomics with Number Literals Test ===\n'); 3 + 4 + const sab = new SharedArrayBuffer(1024); 5 + const ta = new Uint8Array(sab); 6 + 7 + console.log('Test 1: Initialize with binary literal'); 8 + Atomics.store(ta, 0, 0b00000000); 9 + console.log('Stored 0b00000000:', Atomics.load(ta, 0)); 10 + console.log('PASS:', Atomics.load(ta, 0) === 0); 11 + 12 + console.log('\nTest 2: Add using hex literal'); 13 + const old1 = Atomics.add(ta, 0, 0xFF); 14 + console.log('Added 0xFF to 0, old value:', old1); 15 + console.log('New value:', Atomics.load(ta, 0)); 16 + console.log('PASS:', Atomics.load(ta, 0) === 0xFF); 17 + 18 + console.log('\nTest 3: AND with binary mask'); 19 + Atomics.store(ta, 1, 0b11111111); 20 + const old2 = Atomics.and(ta, 1, 0b11110000); 21 + console.log('0b11111111 & 0b11110000 =', Atomics.load(ta, 1)); 22 + console.log('PASS:', Atomics.load(ta, 1) === 0b11110000); 23 + 24 + console.log('\nTest 4: OR with hex value'); 25 + const old3 = Atomics.or(ta, 1, 0x0F); 26 + console.log('0b11110000 | 0x0F =', Atomics.load(ta, 1)); 27 + console.log('PASS:', Atomics.load(ta, 1) === 0xFF); 28 + 29 + console.log('\nTest 5: XOR with octal value'); 30 + Atomics.store(ta, 2, 0o377); // 255 in octal 31 + const old4 = Atomics.xor(ta, 2, 0b10101010); 32 + console.log('0o377 ^ 0b10101010 =', Atomics.load(ta, 2)); 33 + console.log('PASS:', Atomics.load(ta, 2) === (0o377 ^ 0b10101010)); 34 + 35 + console.log('\nTest 6: CompareExchange with mixed literals'); 36 + Atomics.store(ta, 3, 0x10); 37 + const result = Atomics.compareExchange(ta, 3, 0b00010000, 0o40); 38 + console.log('Expected 0b00010000 (16), got:', result); 39 + console.log('New value (0o40 = 32):', Atomics.load(ta, 3)); 40 + console.log('PASS:', result === 0x10 && Atomics.load(ta, 3) === 0o40); 41 + 42 + console.log('\nTest 7: Bitwise flags using binary literals'); 43 + const FLAG_READ = 0b0001; 44 + const FLAG_WRITE = 0b0010; 45 + const FLAG_EXECUTE = 0b0100; 46 + const FLAG_DELETE = 0b1000; 47 + 48 + Atomics.store(ta, 4, 0); 49 + Atomics.or(ta, 4, FLAG_READ); 50 + Atomics.or(ta, 4, FLAG_WRITE); 51 + const flags = Atomics.load(ta, 4); 52 + console.log('Flags after OR READ|WRITE:', flags.toString(2).padStart(4, '0')); 53 + console.log('Has READ:', (flags & FLAG_READ) !== 0); 54 + console.log('Has WRITE:', (flags & FLAG_WRITE) !== 0); 55 + console.log('Has EXECUTE:', (flags & FLAG_EXECUTE) !== 0); 56 + console.log('PASS:', flags === 0b0011); 57 + 58 + console.log('\nTest 8: Color manipulation with hex'); 59 + const RED = 0xFF; 60 + const GREEN = 0xFF; 61 + const BLUE = 0xFF; 62 + 63 + Atomics.store(ta, 5, RED); 64 + Atomics.store(ta, 6, GREEN); 65 + Atomics.store(ta, 7, BLUE); 66 + 67 + console.log('RGB values:'); 68 + console.log(' R:', Atomics.load(ta, 5)); 69 + console.log(' G:', Atomics.load(ta, 6)); 70 + console.log(' B:', Atomics.load(ta, 7)); 71 + console.log('PASS:', Atomics.load(ta, 5) === 255); 72 + 73 + console.log('\nTest 9: Mask operations with all formats'); 74 + const MASK_BIN = 0b11110000; 75 + const MASK_OCT = 0o360; // Same as 0b11110000 76 + const MASK_HEX = 0xF0; // Same as 0b11110000 77 + 78 + Atomics.store(ta, 8, 0xFF); 79 + Atomics.and(ta, 8, MASK_BIN); 80 + const result1 = Atomics.load(ta, 8); 81 + 82 + Atomics.store(ta, 9, 0xFF); 83 + Atomics.and(ta, 9, MASK_OCT); 84 + const result2 = Atomics.load(ta, 9); 85 + 86 + Atomics.store(ta, 10, 0xFF); 87 + Atomics.and(ta, 10, MASK_HEX); 88 + const result3 = Atomics.load(ta, 10); 89 + 90 + console.log('All masks equivalent:', result1 === result2 && result2 === result3); 91 + console.log('PASS:', result1 === 0xF0); 92 + 93 + console.log('\nTest 10: Complex atomic operation chain'); 94 + Atomics.store(ta, 11, 0b00000000); 95 + console.log('Start:', Atomics.load(ta, 11).toString(2).padStart(8, '0')); 96 + 97 + Atomics.or(ta, 11, 0b00001111); 98 + console.log('After OR 0b00001111:', Atomics.load(ta, 11).toString(2).padStart(8, '0')); 99 + 100 + Atomics.and(ta, 11, 0b11110111); 101 + console.log('After AND 0b11110111:', Atomics.load(ta, 11).toString(2).padStart(8, '0')); 102 + 103 + Atomics.xor(ta, 11, 0xFF); 104 + console.log('After XOR 0xFF:', Atomics.load(ta, 11).toString(2).padStart(8, '0')); 105 + 106 + const final = Atomics.load(ta, 11); 107 + console.log('PASS:', final === 0b11111000); 108 + 109 + console.log('\n=== All tests completed successfully ===');
+69
tests/test_number_literals.cjs
··· 1 + console.log('=== Number Literal Tests ===\n'); 2 + 3 + // Test 1: Binary literals 4 + console.log('Test 1: Binary literals'); 5 + console.log('0b0:', 0b0); 6 + console.log('0b1:', 0b1); 7 + console.log('0b10:', 0b10); 8 + console.log('0b1010:', 0b1010); 9 + console.log('0b11111111:', 0b11111111); 10 + console.log('Binary 0b1010 === 10:', 0b1010 === 10 ? 'PASS' : 'FAIL'); 11 + console.log('Binary 0b11111111 === 255:', 0b11111111 === 255 ? 'PASS' : 'FAIL'); 12 + 13 + // Test 2: Octal literals 14 + console.log('\nTest 2: Octal literals'); 15 + console.log('0o0:', 0o0); 16 + console.log('0o7:', 0o7); 17 + console.log('0o10:', 0o10); 18 + console.log('0o755:', 0o755); 19 + console.log('0o777:', 0o777); 20 + console.log('Octal 0o10 === 8:', 0o10 === 8 ? 'PASS' : 'FAIL'); 21 + console.log('Octal 0o755 === 493:', 0o755 === 493 ? 'PASS' : 'FAIL'); 22 + 23 + // Test 3: Hexadecimal literals 24 + console.log('\nTest 3: Hexadecimal literals'); 25 + console.log('0x0:', 0x0); 26 + console.log('0xF:', 0xF); 27 + console.log('0x10:', 0x10); 28 + console.log('0xFF:', 0xFF); 29 + console.log('0xDEADBEEF:', 0xDEADBEEF); 30 + console.log('Hex 0x10 === 16:', 0x10 === 16 ? 'PASS' : 'FAIL'); 31 + console.log('Hex 0xFF === 255:', 0xFF === 255 ? 'PASS' : 'FAIL'); 32 + 33 + // Test 4: Uppercase variants 34 + console.log('\nTest 4: Uppercase variants'); 35 + console.log('0B1010:', 0B1010); 36 + console.log('0O755:', 0O755); 37 + console.log('0XFF:', 0XFF); 38 + console.log('Uppercase 0B1010 === 10:', 0B1010 === 10 ? 'PASS' : 'FAIL'); 39 + console.log('Uppercase 0O755 === 493:', 0O755 === 493 ? 'PASS' : 'FAIL'); 40 + console.log('Uppercase 0XFF === 255:', 0XFF === 255 ? 'PASS' : 'FAIL'); 41 + 42 + // Test 5: Mixed usage in expressions 43 + console.log('\nTest 5: Mixed usage in expressions'); 44 + const sum = 0b1010 + 0o12 + 0x0A; 45 + console.log('0b1010 + 0o12 + 0x0A =', sum); 46 + console.log('Sum === 30:', sum === 30 ? 'PASS' : 'FAIL'); 47 + 48 + const product = 0b10 * 0o10 * 0x10; 49 + console.log('0b10 * 0o10 * 0x10 =', product); 50 + console.log('Product === 256:', product === 256 ? 'PASS' : 'FAIL'); 51 + 52 + // Test 6: Bitwise operations with binary literals 53 + console.log('\nTest 6: Bitwise operations with binary literals'); 54 + console.log('0b1111 & 0b1010 =', (0b1111 & 0b1010).toString(2).padStart(4, '0')); 55 + console.log('0b1111 | 0b1010 =', (0b1111 | 0b1010).toString(2).padStart(4, '0')); 56 + console.log('0b1111 ^ 0b1010 =', (0b1111 ^ 0b1010).toString(2).padStart(4, '0')); 57 + console.log('AND result === 10:', (0b1111 & 0b1010) === 10 ? 'PASS' : 'FAIL'); 58 + console.log('OR result === 15:', (0b1111 | 0b1010) === 15 ? 'PASS' : 'FAIL'); 59 + console.log('XOR result === 5:', (0b1111 ^ 0b1010) === 5 ? 'PASS' : 'FAIL'); 60 + 61 + // Test 7: All representations of the same number 62 + console.log('\nTest 7: All representations of the same number'); 63 + console.log('Binary 0b1111 === 15:', 0b1111 === 15); 64 + console.log('Octal 0o17 === 15:', 0o17 === 15); 65 + console.log('Hex 0xF === 15:', 0xF === 15); 66 + console.log('Decimal 15 === 15:', 15 === 15); 67 + console.log('All equal:', (0b1111 === 0o17 && 0o17 === 0xF && 0xF === 15) ? 'PASS' : 'FAIL'); 68 + 69 + console.log('\n=== All number literal tests completed ===');