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.

getRandomValues, .replace callback

+364 -18
+5
examples/advanced.js
··· 1 + const uuid = '10000000-1000-4000-8000-100000000000'.replace(/[018]/g, c => 2 + (+c ^ (Ant.Crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (+c / 4)))).toString(16) 3 + ); 4 + 5 + console.log(uuid);
+3
include/modules/buffer.h
··· 1 1 #ifndef BUFFER_H 2 2 #define BUFFER_H 3 3 4 + #include "ant.h" 4 5 #include <stdint.h> 5 6 #include <stddef.h> 6 7 ··· 41 42 size_t byte_offset; 42 43 size_t byte_length; 43 44 } DataViewData; 45 + 46 + void sync_typedarray_indices(struct js *js, jsval_t obj, TypedArrayData *ta_data); 44 47 45 48 #endif
+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.20') 77 + version_conf.set('ANT_VERSION', '0.0.8.21') 78 78 version_conf.set('ANT_GIT_HASH', git_hash) 79 79 version_conf.set('ANT_BUILD_DATE', build_date) 80 80
+134 -16
src/ant.c
··· 8879 8879 8880 8880 bool is_regex = false; 8881 8881 bool global_flag = false; 8882 + bool is_func_replacement = (vtype(replacement) == T_FUNC); 8882 8883 char pattern_buf[256]; 8883 8884 jsoff_t pattern_len = 0; 8884 8885 ··· 8909 8910 } 8910 8911 not_regex: 8911 8912 8912 - if (vtype(replacement) != T_STR) return str; 8913 - jsoff_t repl_len, repl_off = vstr(js, replacement, &repl_len); 8914 - const char *repl_ptr = (char *) &js->mem[repl_off]; 8913 + jsoff_t repl_len = 0; 8914 + const char *repl_ptr = NULL; 8915 + if (!is_func_replacement) { 8916 + if (vtype(replacement) != T_STR) return str; 8917 + jsoff_t repl_off; 8918 + repl_off = vstr(js, replacement, &repl_len); 8919 + repl_ptr = (char *) &js->mem[repl_off]; 8920 + } 8915 8921 8916 8922 char result[4096]; 8917 8923 jsoff_t result_len = 0; ··· 8946 8952 result_len += before_len; 8947 8953 } 8948 8954 8949 - if (result_len + repl_len < sizeof(result)) { 8950 - memcpy(result + result_len, repl_ptr, repl_len); 8951 - result_len += repl_len; 8955 + jsoff_t match_len = (jsoff_t)(match.rm_eo - match.rm_so); 8956 + 8957 + if (is_func_replacement) { 8958 + jsval_t match_str = js_mkstr(js, str_buf + pos + match.rm_so, match_len); 8959 + jsval_t cb_args[1] = { match_str }; 8960 + jsval_t cb_result = js_call(js, replacement, cb_args, 1); 8961 + 8962 + if (vtype(cb_result) == T_ERR) { 8963 + regfree(&regex); 8964 + return cb_result; 8965 + } 8966 + 8967 + if (vtype(cb_result) == T_STR) { 8968 + jsoff_t cb_len, cb_off = vstr(js, cb_result, &cb_len); 8969 + if (result_len + cb_len < sizeof(result)) { 8970 + memcpy(result + result_len, &js->mem[cb_off], cb_len); 8971 + result_len += cb_len; 8972 + } 8973 + } else { 8974 + char numbuf[32]; 8975 + size_t n = tostr(js, cb_result, numbuf, sizeof(numbuf)); 8976 + if (result_len + n < sizeof(result)) { 8977 + memcpy(result + result_len, numbuf, n); 8978 + result_len += (jsoff_t)n; 8979 + } 8980 + } 8981 + } else { 8982 + if (result_len + repl_len < sizeof(result)) { 8983 + memcpy(result + result_len, repl_ptr, repl_len); 8984 + result_len += repl_len; 8985 + } 8952 8986 } 8953 8987 8954 - jsoff_t match_len = (jsoff_t)match.rm_eo; 8955 - if (match_len == 0) { 8988 + if (match.rm_eo == 0) { 8956 8989 if (pos < str_len && result_len < sizeof(result)) { 8957 8990 result[result_len++] = str_buf[pos]; 8958 8991 } 8959 8992 pos++; 8960 8993 } else { 8961 - pos += match_len; 8994 + pos += (jsoff_t)match.rm_eo; 8962 8995 } 8963 8996 8964 8997 replaced = true; ··· 8990 9023 memcpy(result + result_len, str_ptr, i); 8991 9024 result_len += i; 8992 9025 } 8993 - if (result_len + repl_len < sizeof(result)) { 8994 - memcpy(result + result_len, repl_ptr, repl_len); 8995 - result_len += repl_len; 9026 + 9027 + if (is_func_replacement) { 9028 + jsval_t match_str = js_mkstr(js, search_ptr, search_len); 9029 + jsval_t cb_args[1] = { match_str }; 9030 + jsval_t cb_result = js_call(js, replacement, cb_args, 1); 9031 + 9032 + if (vtype(cb_result) == T_ERR) return cb_result; 9033 + 9034 + if (vtype(cb_result) == T_STR) { 9035 + jsoff_t cb_len, cb_off = vstr(js, cb_result, &cb_len); 9036 + if (result_len + cb_len < sizeof(result)) { 9037 + memcpy(result + result_len, &js->mem[cb_off], cb_len); 9038 + result_len += cb_len; 9039 + } 9040 + } else { 9041 + char numbuf[32]; 9042 + size_t n = tostr(js, cb_result, numbuf, sizeof(numbuf)); 9043 + if (result_len + n < sizeof(result)) { 9044 + memcpy(result + result_len, numbuf, n); 9045 + result_len += (jsoff_t)n; 9046 + } 9047 + } 9048 + } else { 9049 + if (result_len + repl_len < sizeof(result)) { 9050 + memcpy(result + result_len, repl_ptr, repl_len); 9051 + result_len += repl_len; 9052 + } 8996 9053 } 9054 + 8997 9055 jsoff_t after_start = i + search_len; 8998 9056 jsoff_t after_len = str_len - after_start; 8999 9057 if (after_len > 0 && result_len + after_len < sizeof(result)) { ··· 9266 9324 } 9267 9325 9268 9326 static jsval_t builtin_number_toString(struct js *js, jsval_t *args, int nargs) { 9269 - (void) args; (void) nargs; 9270 9327 jsval_t num = js->this_val; 9271 9328 if (vtype(num) != T_NUM) return js_mkerr(js, "toString called on non-number"); 9272 9329 9273 - char buf[64]; 9274 - size_t len = strnum(num, buf, sizeof(buf)); 9275 - return js_mkstr(js, buf, len); 9330 + int radix = 10; 9331 + if (nargs >= 1 && vtype(args[0]) == T_NUM) { 9332 + radix = (int)tod(args[0]); 9333 + if (radix < 2 || radix > 36) { 9334 + return js_mkerr(js, "radix must be between 2 and 36"); 9335 + } 9336 + } 9337 + 9338 + if (radix == 10) { 9339 + char buf[64]; 9340 + size_t len = strnum(num, buf, sizeof(buf)); 9341 + return js_mkstr(js, buf, len); 9342 + } 9343 + 9344 + double val = tod(num); 9345 + 9346 + if (isnan(val)) return js_mkstr(js, "NaN", 3); 9347 + if (isinf(val)) return val > 0 ? js_mkstr(js, "Infinity", 8) : js_mkstr(js, "-Infinity", 9); 9348 + 9349 + char buf[128]; 9350 + char *p = buf + sizeof(buf) - 1; 9351 + *p = '\0'; 9352 + 9353 + bool negative = val < 0; 9354 + if (negative) val = -val; 9355 + 9356 + long long int_part = (long long)val; 9357 + double frac_part = val - (double)int_part; 9358 + 9359 + if (int_part == 0) { 9360 + *--p = '0'; 9361 + } else { 9362 + while (int_part > 0 && p > buf) { 9363 + int digit = int_part % radix; 9364 + *--p = digit < 10 ? '0' + digit : 'a' + (digit - 10); 9365 + int_part /= radix; 9366 + } 9367 + } 9368 + 9369 + if (negative && p > buf) { 9370 + *--p = '-'; 9371 + } 9372 + 9373 + size_t int_len = strlen(p); 9374 + 9375 + if (frac_part > 0.0000001) { 9376 + char frac_buf[64]; 9377 + int frac_pos = 0; 9378 + frac_buf[frac_pos++] = '.'; 9379 + 9380 + for (int i = 0; i < 16 && frac_part > 0.0000001 && frac_pos < 63; i++) { 9381 + frac_part *= radix; 9382 + int digit = (int)frac_part; 9383 + frac_buf[frac_pos++] = digit < 10 ? '0' + digit : 'a' + (digit - 10); 9384 + frac_part -= digit; 9385 + } 9386 + frac_buf[frac_pos] = '\0'; 9387 + 9388 + char result[192]; 9389 + snprintf(result, sizeof(result), "%s%s", p, frac_buf); 9390 + return js_mkstr(js, result, strlen(result)); 9391 + } 9392 + 9393 + return js_mkstr(js, p, int_len); 9276 9394 } 9277 9395 9278 9396 static jsval_t builtin_number_toFixed(struct js *js, jsval_t *args, int nargs) {
+45 -1
src/modules/buffer.c
··· 3 3 #include <string.h> 4 4 #include <ctype.h> 5 5 6 - #include "ant.h" 7 6 #include "runtime.h" 8 7 #include "modules/buffer.h" 9 8 ··· 194 193 return new_obj; 195 194 } 196 195 196 + void sync_typedarray_indices(struct js *js, jsval_t obj, TypedArrayData *ta_data) { 197 + uint8_t *data = ta_data->buffer->data + ta_data->byte_offset; 198 + size_t element_size = get_element_size(ta_data->type); 199 + 200 + for (size_t i = 0; i < ta_data->length; i++) { 201 + char key[16]; 202 + snprintf(key, sizeof(key), "%zu", i); 203 + 204 + double value = 0; 205 + switch (ta_data->type) { 206 + case TYPED_ARRAY_INT8: 207 + value = (double)((int8_t*)data)[i]; 208 + break; 209 + case TYPED_ARRAY_UINT8: 210 + case TYPED_ARRAY_UINT8_CLAMPED: 211 + value = (double)data[i]; 212 + break; 213 + case TYPED_ARRAY_INT16: 214 + value = (double)((int16_t*)(data))[i]; 215 + break; 216 + case TYPED_ARRAY_UINT16: 217 + value = (double)((uint16_t*)(data))[i]; 218 + break; 219 + case TYPED_ARRAY_INT32: 220 + value = (double)((int32_t*)(data))[i]; 221 + break; 222 + case TYPED_ARRAY_UINT32: 223 + value = (double)((uint32_t*)(data))[i]; 224 + break; 225 + case TYPED_ARRAY_FLOAT32: 226 + value = (double)((float*)(data))[i]; 227 + break; 228 + case TYPED_ARRAY_FLOAT64: 229 + value = ((double*)(data))[i]; 230 + break; 231 + default: 232 + value = (double)data[i * element_size]; 233 + break; 234 + } 235 + js_set(js, obj, key, js_mknum(value)); 236 + } 237 + } 238 + 197 239 static jsval_t create_typed_array(struct js *js, TypedArrayType type, ArrayBufferData *buffer, size_t byte_offset, size_t length, const char *type_name) { 198 240 TypedArrayData *ta_data = malloc(sizeof(TypedArrayData)); 199 241 if (!ta_data) return js_mkerr(js, "Failed to allocate TypedArray"); ··· 214 256 js_set(js, obj, "BYTES_PER_ELEMENT", js_mknum((double)element_size)); 215 257 js_set(js, obj, "slice", js_mkfun(js_typedarray_slice)); 216 258 js_set(js, obj, "subarray", js_mkfun(js_typedarray_subarray)); 259 + 260 + sync_typedarray_indices(js, obj, ta_data); 217 261 218 262 return obj; 219 263 }
+34
src/modules/crypto.c
··· 7 7 #include "ant.h" 8 8 #include "runtime.h" 9 9 #include "modules/crypto.h" 10 + #include "modules/buffer.h" 10 11 11 12 static int ensure_crypto_init(struct js *js) { 12 13 static int crypto_initialized = 0; ··· 129 130 return js_mkstr(js, uuid_str, strlen(uuid_str)); 130 131 } 131 132 133 + // Ant.Crypto.getRandomValues(typedArray) 134 + static jsval_t js_crypto_get_random_values(struct js *js, jsval_t *args, int nargs) { 135 + if (nargs < 1) { 136 + return js_mkerr(js, "getRandomValues requires a TypedArray argument"); 137 + } 138 + 139 + if (ensure_crypto_init(js) < 0) { 140 + return js_mkerr(js, "libsodium initialization failed"); 141 + } 142 + 143 + jsval_t ta_data_val = js_get(js, args[0], "_typedarray_data"); 144 + if (js_type(ta_data_val) != JS_NUM) { 145 + return js_mkerr(js, "argument must be a TypedArray"); 146 + } 147 + 148 + TypedArrayData *ta_data = (TypedArrayData *)(uintptr_t)js_getnum(ta_data_val); 149 + if (!ta_data || !ta_data->buffer) { 150 + return js_mkerr(js, "invalid TypedArray"); 151 + } 152 + 153 + if (ta_data->byte_length > 65536) { 154 + return js_mkerr(js, "TypedArray byte length exceeds 65536"); 155 + } 156 + 157 + uint8_t *ptr = ta_data->buffer->data + ta_data->byte_offset; 158 + randombytes_buf(ptr, ta_data->byte_length); 159 + 160 + sync_typedarray_indices(js, args[0], ta_data); 161 + 162 + return args[0]; 163 + } 164 + 132 165 void init_crypto_module() { 133 166 struct js *js = rt->js; 134 167 jsval_t ant_obj = rt->ant_obj; ··· 139 172 js_set(js, crypto_obj, "randomBytes", js_mkfun(js_crypto_random_bytes)); 140 173 js_set(js, crypto_obj, "randomUUID", js_mkfun(js_crypto_random_uuid)); 141 174 js_set(js, crypto_obj, "randomUUIDv7", js_mkfun(js_crypto_random_uuidv7)); 175 + js_set(js, crypto_obj, "getRandomValues", js_mkfun(js_crypto_get_random_values)); 142 176 143 177 js_set(js, crypto_obj, "@@toStringTag", js_mkstr(js, "Crypto", 6)); 144 178 js_set(js, ant_obj, "Crypto", crypto_obj);
+142
tests/test_number_radix.cjs
··· 1 + // Test Number.prototype.toString with radix parameter 2 + 3 + console.log("Testing Number.prototype.toString(radix):"); 4 + 5 + // Base 16 (hexadecimal) 6 + console.log("\nHexadecimal (base 16):"); 7 + console.log("(0).toString(16):", (0).toString(16), "expected: 0"); 8 + console.log("(10).toString(16):", (10).toString(16), "expected: a"); 9 + console.log("(15).toString(16):", (15).toString(16), "expected: f"); 10 + console.log("(16).toString(16):", (16).toString(16), "expected: 10"); 11 + console.log("(255).toString(16):", (255).toString(16), "expected: ff"); 12 + console.log("(256).toString(16):", (256).toString(16), "expected: 100"); 13 + console.log("(4095).toString(16):", (4095).toString(16), "expected: fff"); 14 + console.log("(65535).toString(16):", (65535).toString(16), "expected: ffff"); 15 + 16 + // Base 2 (binary) 17 + console.log("\nBinary (base 2):"); 18 + console.log("(0).toString(2):", (0).toString(2), "expected: 0"); 19 + console.log("(1).toString(2):", (1).toString(2), "expected: 1"); 20 + console.log("(2).toString(2):", (2).toString(2), "expected: 10"); 21 + console.log("(8).toString(2):", (8).toString(2), "expected: 1000"); 22 + console.log("(16).toString(2):", (16).toString(2), "expected: 10000"); 23 + console.log("(255).toString(2):", (255).toString(2), "expected: 11111111"); 24 + 25 + // Base 8 (octal) 26 + console.log("\nOctal (base 8):"); 27 + console.log("(0).toString(8):", (0).toString(8), "expected: 0"); 28 + console.log("(7).toString(8):", (7).toString(8), "expected: 7"); 29 + console.log("(8).toString(8):", (8).toString(8), "expected: 10"); 30 + console.log("(64).toString(8):", (64).toString(8), "expected: 100"); 31 + console.log("(511).toString(8):", (511).toString(8), "expected: 777"); 32 + 33 + // Base 10 (decimal - default) 34 + console.log("\nDecimal (base 10):"); 35 + console.log("(123).toString():", (123).toString(), "expected: 123"); 36 + console.log("(123).toString(10):", (123).toString(10), "expected: 123"); 37 + 38 + // Base 36 (maximum) 39 + console.log("\nBase 36:"); 40 + console.log("(35).toString(36):", (35).toString(36), "expected: z"); 41 + console.log("(36).toString(36):", (36).toString(36), "expected: 10"); 42 + 43 + // Negative numbers 44 + console.log("\nNegative numbers:"); 45 + console.log("(-10).toString(16):", (-10).toString(16), "expected: -a"); 46 + console.log("(-255).toString(16):", (-255).toString(16), "expected: -ff"); 47 + console.log("(-8).toString(2):", (-8).toString(2), "expected: -1000"); 48 + 49 + // Special values 50 + console.log("\nSpecial values:"); 51 + console.log("(NaN).toString(16):", (NaN).toString(16), "expected: NaN"); 52 + console.log("(Infinity).toString(16):", (Infinity).toString(16), "expected: Infinity"); 53 + console.log("(-Infinity).toString(16):", (-Infinity).toString(16), "expected: -Infinity"); 54 + 55 + // parseInt with radix 56 + console.log("\n--- parseInt with radix ---"); 57 + 58 + // Hexadecimal 59 + console.log("\nparseInt hexadecimal:"); 60 + console.log("parseInt('0', 16):", parseInt('0', 16), "expected: 0"); 61 + console.log("parseInt('a', 16):", parseInt('a', 16), "expected: 10"); 62 + console.log("parseInt('A', 16):", parseInt('A', 16), "expected: 10"); 63 + console.log("parseInt('f', 16):", parseInt('f', 16), "expected: 15"); 64 + console.log("parseInt('ff', 16):", parseInt('ff', 16), "expected: 255"); 65 + console.log("parseInt('FF', 16):", parseInt('FF', 16), "expected: 255"); 66 + console.log("parseInt('100', 16):", parseInt('100', 16), "expected: 256"); 67 + console.log("parseInt('ffff', 16):", parseInt('ffff', 16), "expected: 65535"); 68 + 69 + // Binary 70 + console.log("\nparseInt binary:"); 71 + console.log("parseInt('0', 2):", parseInt('0', 2), "expected: 0"); 72 + console.log("parseInt('1', 2):", parseInt('1', 2), "expected: 1"); 73 + console.log("parseInt('10', 2):", parseInt('10', 2), "expected: 2"); 74 + console.log("parseInt('1000', 2):", parseInt('1000', 2), "expected: 8"); 75 + console.log("parseInt('11111111', 2):", parseInt('11111111', 2), "expected: 255"); 76 + 77 + // Octal 78 + console.log("\nparseInt octal:"); 79 + console.log("parseInt('0', 8):", parseInt('0', 8), "expected: 0"); 80 + console.log("parseInt('7', 8):", parseInt('7', 8), "expected: 7"); 81 + console.log("parseInt('10', 8):", parseInt('10', 8), "expected: 8"); 82 + console.log("parseInt('100', 8):", parseInt('100', 8), "expected: 64"); 83 + console.log("parseInt('777', 8):", parseInt('777', 8), "expected: 511"); 84 + 85 + // Base 36 86 + console.log("\nparseInt base 36:"); 87 + console.log("parseInt('z', 36):", parseInt('z', 36), "expected: 35"); 88 + console.log("parseInt('Z', 36):", parseInt('Z', 36), "expected: 35"); 89 + console.log("parseInt('10', 36):", parseInt('10', 36), "expected: 36"); 90 + 91 + // Default base 10 92 + console.log("\nparseInt decimal:"); 93 + console.log("parseInt('123'):", parseInt('123'), "expected: 123"); 94 + console.log("parseInt('123', 10):", parseInt('123', 10), "expected: 123"); 95 + 96 + // Validation tests 97 + console.log("\n--- Validation ---"); 98 + let passed = 0; 99 + let failed = 0; 100 + 101 + function test(name, actual, expected) { 102 + if (actual === expected) { 103 + passed++; 104 + } else { 105 + failed++; 106 + console.log("FAIL:", name, "got:", actual, "expected:", expected); 107 + } 108 + } 109 + 110 + // toString tests 111 + test("toString hex 0", (0).toString(16), "0"); 112 + test("toString hex 10", (10).toString(16), "a"); 113 + test("toString hex 15", (15).toString(16), "f"); 114 + test("toString hex 255", (255).toString(16), "ff"); 115 + test("toString bin 8", (8).toString(2), "1000"); 116 + test("toString bin 255", (255).toString(2), "11111111"); 117 + test("toString oct 64", (64).toString(8), "100"); 118 + test("toString base36 35", (35).toString(36), "z"); 119 + test("toString neg hex", (-15).toString(16), "-f"); 120 + test("toString decimal default", (123).toString(), "123"); 121 + 122 + // parseInt tests 123 + test("parseInt hex ff", parseInt('ff', 16), 255); 124 + test("parseInt hex FF", parseInt('FF', 16), 255); 125 + test("parseInt bin 1010", parseInt('1010', 2), 10); 126 + test("parseInt oct 777", parseInt('777', 8), 511); 127 + test("parseInt base36 z", parseInt('z', 36), 35); 128 + test("parseInt base36 Z", parseInt('Z', 36), 35); 129 + test("parseInt decimal", parseInt('123', 10), 123); 130 + test("parseInt default", parseInt('456'), 456); 131 + 132 + // Round-trip tests (toString then parseInt) 133 + test("roundtrip hex 255", parseInt((255).toString(16), 16), 255); 134 + test("roundtrip bin 42", parseInt((42).toString(2), 2), 42); 135 + test("roundtrip oct 100", parseInt((100).toString(8), 8), 100); 136 + test("roundtrip base36 1000", parseInt((1000).toString(36), 36), 1000); 137 + 138 + console.log("\nResults:", passed, "passed,", failed, "failed"); 139 + 140 + if (failed === 0) { 141 + console.log("All radix tests passed!"); 142 + }