From 3e4638dc16636e509e02199b0359a2d49199c64a Mon Sep 17 00:00:00 2001 From: theMackabu Date: Fri, 6 Mar 2026 16:16:46 -0800 Subject: [PATCH] de-couple math --- include/internal.h | 17 +- include/modules/bigint.h | 29 ++ include/modules/math.h | 6 + src/ant.c | 880 +----------------------------------- src/main.c | 4 + src/modules/bigint.c | 779 +++++++++++++++++++++++++++++++ src/modules/math.c | 313 +++++++++++++ src/silver/ops/arithmetic.h | 2 + src/silver/ops/bitwise.h | 1 + src/silver/ops/comparison.h | 1 + 10 files changed, 1140 insertions(+), 892 deletions(-) create mode 100644 include/modules/bigint.h create mode 100644 include/modules/math.h create mode 100644 src/modules/bigint.c create mode 100644 src/modules/math.c diff --git a/include/internal.h b/include/internal.h index b7d8be3..20e36ee 100644 --- a/include/internal.h +++ b/include/internal.h @@ -251,12 +251,13 @@ ant_offset_t lkp_sym(ant_t *js, ant_value_t obj, ant_offset_t sym_off); ant_offset_t lkp_sym_proto(ant_t *js, ant_value_t obj, ant_offset_t sym_off); ant_offset_t vstr(ant_t *js, ant_value_t value, ant_offset_t *len); ant_offset_t vstrlen(ant_t *js, ant_value_t value); -ant_value_t rope_flatten(ant_t *js, ant_value_t rope); ant_offset_t str_len_fast(ant_t *js, ant_value_t str); +ant_offset_t js_alloc(ant_t *js, size_t size); ant_value_t mkarr(ant_t *js); ant_value_t mkval(uint8_t type, uint64_t data); ant_value_t mkobj(ant_t *js, ant_offset_t parent); +ant_value_t rope_flatten(ant_t *js, ant_value_t rope); ant_value_t js_for_in_keys(ant_t *js, ant_value_t obj); ant_value_t js_delete_prop(ant_t *js, ant_value_t obj, const char *key, size_t len); @@ -277,18 +278,4 @@ ant_value_t js_to_primitive(ant_t *js, ant_value_t value, int hint); ant_value_t do_instanceof(ant_t *js, ant_value_t l, ant_value_t r); ant_value_t do_in(ant_t *js, ant_value_t l, ant_value_t r); -ant_value_t bigint_add(ant_t *js, ant_value_t a, ant_value_t b); -ant_value_t bigint_sub(ant_t *js, ant_value_t a, ant_value_t b); -ant_value_t bigint_mul(ant_t *js, ant_value_t a, ant_value_t b); -ant_value_t bigint_div(ant_t *js, ant_value_t a, ant_value_t b); -ant_value_t bigint_mod(ant_t *js, ant_value_t a, ant_value_t b); -ant_value_t bigint_neg(ant_t *js, ant_value_t a); -ant_value_t bigint_exp(ant_t *js, ant_value_t base, ant_value_t exp); -int bigint_compare(ant_t *js, ant_value_t a, ant_value_t b); - -ant_value_t bigint_shift_left(ant_t *js, ant_value_t value, uint64_t shift); -ant_value_t bigint_shift_right(ant_t *js, ant_value_t value, uint64_t shift); -ant_value_t bigint_shift_right_logical(ant_t *js, ant_value_t value, uint64_t shift); -ant_value_t bigint_asint_bits(ant_t *js, ant_value_t arg, uint64_t *bits_out); - #endif diff --git a/include/modules/bigint.h b/include/modules/bigint.h new file mode 100644 index 0000000..6353c72 --- /dev/null +++ b/include/modules/bigint.h @@ -0,0 +1,29 @@ +#ifndef BIGINT_MODULE_H +#define BIGINT_MODULE_H + +#include "types.h" + +void init_bigint_module(void); + +ant_value_t bigint_add(ant_t *js, ant_value_t a, ant_value_t b); +ant_value_t bigint_sub(ant_t *js, ant_value_t a, ant_value_t b); +ant_value_t bigint_mul(ant_t *js, ant_value_t a, ant_value_t b); +ant_value_t bigint_div(ant_t *js, ant_value_t a, ant_value_t b); +ant_value_t bigint_mod(ant_t *js, ant_value_t a, ant_value_t b); +ant_value_t bigint_neg(ant_t *js, ant_value_t a); +ant_value_t bigint_exp(ant_t *js, ant_value_t base, ant_value_t exp); + +ant_value_t bigint_shift_left(ant_t *js, ant_value_t value, uint64_t shift); +ant_value_t bigint_shift_right(ant_t *js, ant_value_t value, uint64_t shift); +ant_value_t bigint_shift_right_logical(ant_t *js, ant_value_t value, uint64_t shift); +ant_value_t bigint_asint_bits(ant_t *js, ant_value_t arg, uint64_t *bits_out); + +bool bigint_is_negative(ant_t *js, ant_value_t v); +bool bigint_is_zero(ant_t *js, ant_value_t v); + + +size_t bigint_digits_len(ant_t *js, ant_value_t v); +size_t strbigint(ant_t *js, ant_value_t value, char *buf, size_t len); +size_t bigint_compare(ant_t *js, ant_value_t a, ant_value_t b); + +#endif diff --git a/include/modules/math.h b/include/modules/math.h new file mode 100644 index 0000000..b413bbc --- /dev/null +++ b/include/modules/math.h @@ -0,0 +1,6 @@ +#ifndef MATH_MODULE_H +#define MATH_MODULE_H + +void init_math_module(void); + +#endif diff --git a/src/ant.c b/src/ant.c index 225a12e..a07fc5e 100644 --- a/src/ant.c +++ b/src/ant.c @@ -50,6 +50,7 @@ #endif #include "modules/fs.h" +#include "modules/bigint.h" #include "modules/timer.h" #include "modules/fetch.h" #include "modules/symbol.h" @@ -309,7 +310,6 @@ ant_offset_t vstrlen(ant_t *js, ant_value_t v) { static ant_value_t proxy_read_target(ant_t *js, ant_value_t obj); static ant_offset_t proxy_aware_length(ant_t *js, ant_value_t obj); static ant_value_t proxy_aware_get_elem(ant_t *js, ant_value_t obj, const char *key, size_t key_len); -static bool bigint_is_zero(ant_t *js, ant_value_t v); static ant_offset_t get_dense_buf(ant_t *js, ant_value_t arr); static ant_offset_t dense_length(ant_t *js, ant_offset_t doff); @@ -324,7 +324,6 @@ static ant_offset_t lkp_with_setter(ant_t *js, ant_value_t obj, const char *buf, static ant_value_t call_proto_accessor(ant_t *js, ant_value_t prim, ant_value_t accessor, bool has_accessor, ant_value_t *arg, int arg_count, bool is_setter); static ant_value_t get_prototype_for_type(ant_t *js, uint8_t type); -static size_t strbigint(ant_t *js, ant_value_t value, char *buf, size_t len); static size_t tostr(ant_t *js, ant_value_t value, char *buf, size_t len); static size_t strpromise(ant_t *js, ant_value_t value, char *buf, size_t len); static ant_value_t js_call_valueOf(ant_t *js, ant_value_t value); @@ -350,8 +349,6 @@ static ant_value_t get_prototype_for_type(ant_t *js, uint8_t type); static ant_value_t get_ctor_proto(ant_t *js, const char *name, size_t len); static ant_offset_t lkp_interned(ant_t *js, ant_value_t obj, const char *search_intern, size_t len); -static const char *bigint_digits(ant_t *js, ant_value_t v, size_t *len); - typedef struct { ant_value_t handle; bool is_new; } ctor_t; static ctor_t get_constructor(ant_t *js, const char *name, size_t len) { @@ -436,12 +433,6 @@ size_t uint_to_str(char *buf, size_t bufsize, uint64_t val) { return len; } -static ant_value_t bigint_from_u64(ant_t *js, uint64_t value) { - char buf[32]; - size_t len = uint_to_str(buf, sizeof(buf), value); - return js_mkbigint(js, buf, len, false); -} - static ant_value_t stringify_stack[MAX_STRINGIFY_DEPTH]; static int stringify_depth = 0; static int stringify_indent = 0; @@ -1809,7 +1800,7 @@ ant_value_t js_tostring_val(ant_t *js, ant_value_t value) { } L_BIGINT: { - bigint_digits(js, value, &buflen); + buflen = bigint_digits_len(js, value); buf = (char *)ant_calloc(buflen + 2); len = strbigint(js, value, buf, buflen + 2); ant_value_t result = js_mkstr(js, buf, len); @@ -1909,7 +1900,7 @@ static void js_track_allocation(ant_t *js, size_t size) { if (js->gc_alloc_since > threshold) js->needs_gc = true; } -static inline ant_offset_t js_alloc(ant_t *js, size_t size) { +ant_offset_t js_alloc(ant_t *js, size_t size) { size = align64((ant_offset_t) size); if (!js_ensure_space(js, size)) return ~(ant_offset_t) 0; @@ -2113,562 +2104,6 @@ static ant_value_t js_mkrope(ant_t *js, ant_value_t left, ant_value_t right, ant return mkval(T_STR, ofs); } -static bool bigint_parse_abs_u64(ant_t *js, ant_value_t value, uint64_t *out) { - size_t len = 0; const char *digits = bigint_digits(js, value, &len); - uint64_t acc = 0; - - for (size_t i = 0; i < len; i++) { - char c = digits[i]; - if (!is_digit(c)) return false; - uint64_t digit = (uint64_t)(c - '0'); - if (acc > UINT64_MAX / 10 || (acc == UINT64_MAX / 10 && digit > (UINT64_MAX % 10))) { - return false; - } acc = acc * 10 + digit; - } - - *out = acc; - return true; -} - -static bool bigint_IsNegative(ant_t *js, ant_value_t v) { - ant_offset_t ofs = (ant_offset_t) vdata(v); - return js->mem[ofs + sizeof(ant_offset_t)] == 1; -} - -static bool bigint_parse_u64(ant_t *js, ant_value_t value, uint64_t *out) { - if (bigint_IsNegative(js, value)) return false; - return bigint_parse_abs_u64(js, value, out); -} - -ant_value_t js_mkbigint(ant_t *js, const char *digits, size_t len, bool negative) { - size_t total = len + 2; - ant_offset_t ofs = js_alloc(js, total + sizeof(ant_offset_t)); - if (ofs == (ant_offset_t) ~0) return js_mkerr(js, "oom"); - ant_offset_t header = (ant_offset_t) (total << 4); - memcpy(&js->mem[ofs], &header, sizeof(header)); - js->mem[ofs + sizeof(header)] = negative ? 1 : 0; - if (digits) memcpy(&js->mem[ofs + sizeof(header) + 1], digits, len); - js->mem[ofs + sizeof(header) + 1 + len] = 0; - return mkval(T_BIGINT, ofs); -} - -static const char *bigint_digits(ant_t *js, ant_value_t v, size_t *len) { - ant_offset_t ofs = (ant_offset_t) vdata(v); - ant_offset_t header = loadoff(js, ofs); - size_t total = (header >> 4) - 2; - if (len) *len = total; - return (const char *)&js->mem[ofs + sizeof(ant_offset_t) + 1]; -} - -static int bigint_cmp_abs(const char *a, size_t alen, const char *b, size_t blen) { - while (alen > 1 && a[0] == '0') { a++; alen--; } - while (blen > 1 && b[0] == '0') { b++; blen--; } - if (alen != blen) return alen > blen ? 1 : -1; - for (size_t i = 0; i < alen; i++) { - if (a[i] != b[i]) return a[i] > b[i] ? 1 : -1; - } - return 0; -} - -static char *bigint_add_abs(const char *a, size_t alen, const char *b, size_t blen, size_t *rlen) { - size_t maxlen = (alen > blen ? alen : blen) + 1; - char *result = (char *)malloc(maxlen + 1); - if (!result) return NULL; - int carry = 0; - size_t ri = 0; - for (size_t i = 0; i < maxlen; i++) { - int da = (i < alen) ? (a[alen - 1 - i] - '0') : 0; - int db = (i < blen) ? (b[blen - 1 - i] - '0') : 0; - int sum = da + db + carry; - carry = sum / 10; - result[ri++] = (char)('0' + (sum % 10)); - } - while (ri > 1 && result[ri - 1] == '0') ri--; - for (size_t i = 0; i < ri / 2; i++) { - char tmp = result[i]; result[i] = result[ri - 1 - i]; result[ri - 1 - i] = tmp; - } - result[ri] = 0; - *rlen = ri; - return result; -} - -static char *bigint_sub_abs(const char *a, size_t alen, const char *b, size_t blen, size_t *rlen) { - char *result = (char *)malloc(alen + 1); - if (!result) return NULL; - int borrow = 0; - size_t ri = 0; - for (size_t i = 0; i < alen; i++) { - int da = a[alen - 1 - i] - '0'; - int db = (i < blen) ? (b[blen - 1 - i] - '0') : 0; - int diff = da - db - borrow; - if (diff < 0) { diff += 10; borrow = 1; } else { borrow = 0; } - result[ri++] = (char)('0' + diff); - } - while (ri > 1 && result[ri - 1] == '0') ri--; - for (size_t i = 0; i < ri / 2; i++) { - char tmp = result[i]; result[i] = result[ri - 1 - i]; result[ri - 1 - i] = tmp; - } - result[ri] = 0; - *rlen = ri; - return result; -} - -static char *bigint_mul_abs(const char *a, size_t alen, const char *b, size_t blen, size_t *rlen) { - size_t reslen = alen + blen; - int *temp = (int *)calloc(reslen, sizeof(int)); - if (!temp) return NULL; - for (size_t i = 0; i < alen; i++) { - for (size_t j = 0; j < blen; j++) { - temp[i + j] += (a[alen - 1 - i] - '0') * (b[blen - 1 - j] - '0'); - } - } - for (size_t i = 0; i < reslen - 1; i++) { - temp[i + 1] += temp[i] / 10; - temp[i] %= 10; - } - size_t start = reslen - 1; - while (start > 0 && temp[start] == 0) start--; - char *result = (char *)malloc(start + 2); - if (!result) { free(temp); return NULL; } - for (size_t i = 0; i <= start; i++) result[i] = (char)('0' + temp[start - i]); - result[start + 1] = 0; - *rlen = start + 1; - free(temp); - return result; -} - -static char *bigint_div_abs(const char *a, size_t alen, const char *b, size_t blen, size_t *rlen, char **rem, size_t *remlen) { - if (blen == 1 && b[0] == '0') return NULL; - if (bigint_cmp_abs(a, alen, b, blen) < 0) { - char *result = (char *)malloc(2); result[0] = '0'; result[1] = 0; *rlen = 1; - if (rem) { *rem = (char *)malloc(alen + 1); memcpy(*rem, a, alen); (*rem)[alen] = 0; *remlen = alen; } - return result; - } - char *current = (char *)calloc(alen + 1, 1); - char *result = (char *)calloc(alen + 1, 1); - if (!current || !result) { free(current); free(result); return NULL; } - size_t curlen = 0, reslen = 0; - for (size_t i = 0; i < alen; i++) { - if (curlen == 1 && current[0] == '0') curlen = 0; - current[curlen++] = a[i]; current[curlen] = 0; - int count = 0; - while (bigint_cmp_abs(current, curlen, b, blen) >= 0) { - size_t sublen; - char *sub = bigint_sub_abs(current, curlen, b, blen, &sublen); - if (!sub) break; - memcpy(current, sub, sublen + 1); curlen = sublen; - free(sub); count++; - } - result[reslen++] = (char)('0' + count); - } - size_t start = 0; - while (start < reslen - 1 && result[start] == '0') start++; - memmove(result, result + start, reslen - start + 1); - *rlen = reslen - start; - if (rem) { *rem = current; *remlen = curlen; } else free(current); - return result; -} - -ant_value_t bigint_add(ant_t *js, ant_value_t a, ant_value_t b) { - bool aneg = bigint_IsNegative(js, a), bneg = bigint_IsNegative(js, b); - size_t alen, blen; - const char *ad = bigint_digits(js, a, &alen), *bd = bigint_digits(js, b, &blen); - char *result; size_t rlen; bool rneg; - if (aneg == bneg) { - result = bigint_add_abs(ad, alen, bd, blen, &rlen); rneg = aneg; - } else { - int cmp = bigint_cmp_abs(ad, alen, bd, blen); - if (cmp >= 0) { result = bigint_sub_abs(ad, alen, bd, blen, &rlen); rneg = aneg; } - else { result = bigint_sub_abs(bd, blen, ad, alen, &rlen); rneg = bneg; } - } - if (!result) return js_mkerr(js, "oom"); - if (rlen == 1 && result[0] == '0') rneg = false; - ant_value_t r = js_mkbigint(js, result, rlen, rneg); - free(result); - return r; -} - -ant_value_t bigint_sub(ant_t *js, ant_value_t a, ant_value_t b) { - bool aneg = bigint_IsNegative(js, a), bneg = bigint_IsNegative(js, b); - size_t alen, blen; - const char *ad = bigint_digits(js, a, &alen), *bd = bigint_digits(js, b, &blen); - char *result; size_t rlen; bool rneg; - if (aneg != bneg) { - result = bigint_add_abs(ad, alen, bd, blen, &rlen); rneg = aneg; - } else { - int cmp = bigint_cmp_abs(ad, alen, bd, blen); - if (cmp >= 0) { result = bigint_sub_abs(ad, alen, bd, blen, &rlen); rneg = aneg; } - else { result = bigint_sub_abs(bd, blen, ad, alen, &rlen); rneg = !aneg; } - } - if (!result) return js_mkerr(js, "oom"); - if (rlen == 1 && result[0] == '0') rneg = false; - ant_value_t r = js_mkbigint(js, result, rlen, rneg); - free(result); - return r; -} - -ant_value_t bigint_mul(ant_t *js, ant_value_t a, ant_value_t b) { - bool aneg = bigint_IsNegative(js, a), bneg = bigint_IsNegative(js, b); - size_t alen, blen; - const char *ad = bigint_digits(js, a, &alen), *bd = bigint_digits(js, b, &blen); - size_t rlen; - char *result = bigint_mul_abs(ad, alen, bd, blen, &rlen); - if (!result) return js_mkerr(js, "oom"); - bool rneg = (aneg != bneg) && !(rlen == 1 && result[0] == '0'); - ant_value_t r = js_mkbigint(js, result, rlen, rneg); - free(result); - return r; -} - -ant_value_t bigint_div(ant_t *js, ant_value_t a, ant_value_t b) { - bool aneg = bigint_IsNegative(js, a), bneg = bigint_IsNegative(js, b); - size_t alen, blen; - const char *ad = bigint_digits(js, a, &alen), *bd = bigint_digits(js, b, &blen); - if (blen == 1 && bd[0] == '0') return js_mkerr(js, "Division by zero"); - size_t rlen; - char *result = bigint_div_abs(ad, alen, bd, blen, &rlen, NULL, NULL); - if (!result) return js_mkerr(js, "oom"); - bool rneg = (aneg != bneg) && !(rlen == 1 && result[0] == '0'); - ant_value_t r = js_mkbigint(js, result, rlen, rneg); - free(result); - return r; -} - -ant_value_t bigint_mod(ant_t *js, ant_value_t a, ant_value_t b) { - bool aneg = bigint_IsNegative(js, a); - size_t alen, blen; - const char *ad = bigint_digits(js, a, &alen), *bd = bigint_digits(js, b, &blen); - if (blen == 1 && bd[0] == '0') return js_mkerr(js, "Division by zero"); - size_t rlen, remlen; char *rem; - char *result = bigint_div_abs(ad, alen, bd, blen, &rlen, &rem, &remlen); - if (!result) return js_mkerr(js, "oom"); - free(result); - bool rneg = aneg && !(remlen == 1 && rem[0] == '0'); - ant_value_t r = js_mkbigint(js, rem, remlen, rneg); - free(rem); - return r; -} - -ant_value_t bigint_neg(ant_t *js, ant_value_t a) { - size_t len; - const char *digits = bigint_digits(js, a, &len); - bool neg = bigint_IsNegative(js, a); - if (len == 1 && digits[0] == '0') return js_mkbigint(js, digits, len, false); - return js_mkbigint(js, digits, len, !neg); -} - -ant_value_t bigint_exp(ant_t *js, ant_value_t base, ant_value_t exp) { - if (bigint_IsNegative(js, exp)) return js_mkerr(js, "Exponent must be positive"); - size_t explen; - const char *expd = bigint_digits(js, exp, &explen); - if (explen == 1 && expd[0] == '0') return js_mkbigint(js, "1", 1, false); - ant_value_t result = js_mkbigint(js, "1", 1, false); - ant_value_t b = base; - ant_value_t e = exp; - ant_value_t two = js_mkbigint(js, "2", 1, false); - while (true) { - size_t elen; - const char *ed = bigint_digits(js, e, &elen); - if (elen == 1 && ed[0] == '0') break; - int last_digit = ed[elen - 1] - '0'; - if (last_digit % 2 == 1) { - result = bigint_mul(js, result, b); - if (is_err(result)) return result; - } - b = bigint_mul(js, b, b); - if (is_err(b)) return b; - e = bigint_div(js, e, two); - if (is_err(e)) return e; - } - return result; -} - -static inline ant_value_t bigint_pow2(ant_t *js, uint64_t bits) { - ant_value_t two = js_mkbigint(js, "2", 1, false); - if (is_err(two)) return two; - ant_value_t exp = bigint_from_u64(js, bits); - if (is_err(exp)) return exp; - return bigint_exp(js, two, exp); -} - -ant_value_t bigint_shift_left(ant_t *js, ant_value_t value, uint64_t shift) { - if (shift == 0) return value; - if (shift > 18446744073709551615ULL) return js_mkerr(js, "Shift count too large"); - - size_t digits_len; const char *digits = bigint_digits(js, value, &digits_len); - if (digits_len == 1 && digits[0] == '0') return js_mkbigint(js, "0", 1, false); - uint64_t u64 = 0; - if (!bigint_IsNegative(js, value) && shift < 64 && bigint_parse_u64(js, value, &u64)) { - if (u64 <= (UINT64_MAX >> shift)) return bigint_from_u64(js, u64 << shift); - } - - ant_value_t pow = bigint_pow2(js, shift); - if (is_err(pow)) return pow; - return bigint_mul(js, value, pow); -} - -ant_value_t bigint_shift_right(ant_t *js, ant_value_t value, uint64_t shift) { - if (shift == 0) return value; - if (shift > 18446744073709551615ULL) return js_mkerr(js, "Shift count too large"); - - size_t digits_len; const char *digits = bigint_digits(js, value, &digits_len); - if (digits_len == 1 && digits[0] == '0') return js_mkbigint(js, "0", 1, false); - uint64_t u64 = 0; - if (!bigint_IsNegative(js, value) && bigint_parse_u64(js, value, &u64)) { - if (shift >= 64) return js_mkbigint(js, "0", 1, false); - return bigint_from_u64(js, u64 >> shift); - } - - if (bigint_parse_abs_u64(js, value, &u64)) { - if (shift >= 64) return js_mkbigint( - js, bigint_IsNegative(js, value) ? "1" : "0", 1, - bigint_IsNegative(js, value) - ); - uint64_t shifted = u64 >> shift; - if (bigint_IsNegative(js, value)) { - if ((u64 & ((1ULL << shift) - 1)) != 0) shifted += 1; - ant_value_t pos = bigint_from_u64(js, shifted); - if (is_err(pos)) return pos; - return bigint_neg(js, pos); - } - return bigint_from_u64(js, shifted); - } - - ant_value_t pow = bigint_pow2(js, shift); - if (is_err(pow)) return pow; - return bigint_div(js, value, pow); -} - -ant_value_t bigint_shift_right_logical(ant_t *js, ant_value_t value, uint64_t shift) { - return js_mkerr_typed(js, JS_ERR_TYPE, "BigInts have no unsigned right shift, use >> instead"); -} - -int bigint_compare(ant_t *js, ant_value_t a, ant_value_t b) { - bool aneg = bigint_IsNegative(js, a), bneg = bigint_IsNegative(js, b); - size_t alen, blen; - const char *ad = bigint_digits(js, a, &alen), *bd = bigint_digits(js, b, &blen); - if (aneg && !bneg) return -1; - if (!aneg && bneg) return 1; - int cmp = bigint_cmp_abs(ad, alen, bd, blen); - return aneg ? -cmp : cmp; -} - -static bool bigint_is_zero(ant_t *js, ant_value_t v) { - size_t len; - const char *digits = bigint_digits(js, v, &len); - return len == 1 && digits[0] == '0'; -} - -static size_t strbigint(ant_t *js, ant_value_t value, char *buf, size_t len) { - bool neg = bigint_IsNegative(js, value); - size_t dlen; - const char *digits = bigint_digits(js, value, &dlen); - size_t n = 0; - if (neg) n += cpy(buf + n, REMAIN(n, len), "-", 1); - n += cpy(buf + n, REMAIN(n, len), digits, dlen); - return n; -} - -static ant_value_t builtin_BigInt(ant_t *js, ant_value_t *args, int nargs) { - if (vtype(js->new_target) != T_UNDEF) return js_mkerr_typed(js, JS_ERR_TYPE, "BigInt is not a constructor"); - if (nargs < 1) return js_mkbigint(js, "0", 1, false); - - ant_value_t arg = args[0]; - if (vtype(arg) == T_BIGINT) return arg; - if (vtype(arg) == T_NUM) { - double d = tod(arg); - if (!isfinite(d)) return js_mkerr(js, "Cannot convert Infinity or NaN to BigInt"); - if (d != trunc(d)) return js_mkerr(js, "Cannot convert non-integer to BigInt"); - bool neg = d < 0; - if (neg) d = -d; - char buf[64]; - snprintf(buf, sizeof(buf), "%.0f", d); - return js_mkbigint(js, buf, strlen(buf), neg); - } - if (vtype(arg) == T_STR) { - ant_offset_t slen, off = vstr(js, arg, &slen); - const char *str = (const char *)&js->mem[off]; - bool neg = false; - size_t i = 0; - if (slen > 0 && str[0] == '-') { neg = true; i++; } - else if (slen > 0 && str[0] == '+') { i++; } - while (i < slen && str[i] == '0') i++; - if (i >= slen) return js_mkbigint(js, "0", 1, false); - for (size_t j = i; j < slen; j++) { - if (!is_digit(str[j])) return js_mkerr(js, "Cannot convert string to BigInt"); - } - return js_mkbigint(js, str + i, slen - i, neg); - } - if (vtype(arg) == T_BOOL) { - return js_mkbigint(js, vdata(arg) ? "1" : "0", 1, false); - } - return js_mkerr(js, "Cannot convert to BigInt"); -} - -static ant_value_t bigint_to_u64(ant_t *js, ant_value_t value, uint64_t *out) { - if (!bigint_parse_u64(js, value, out)) { - return js_mkerr_typed(js, JS_ERR_RANGE, "Invalid bits"); - } - return js_mkundef(); -} - -ant_value_t bigint_asint_bits(ant_t *js, ant_value_t arg, uint64_t *bits_out) { - if (vtype(arg) == T_BIGINT) { - return bigint_to_u64(js, arg, bits_out); - } - double bits = js_to_number(js, arg); - if (!isfinite(bits) || bits < 0 || bits != floor(bits)) { - return js_mkerr_typed(js, JS_ERR_RANGE, "Invalid bits"); - } - if (bits > 18446744073709551615.0) { - return js_mkerr_typed(js, JS_ERR_RANGE, "Invalid bits"); - } - *bits_out = (uint64_t)bits; - return js_mkundef(); -} - -static ant_value_t builtin_BigInt_asIntN(ant_t *js, ant_value_t *args, int nargs) { - if (nargs < 2) return js_mkerr(js, "BigInt.asIntN requires 2 arguments"); - uint64_t bits = 0; - ant_value_t err = bigint_asint_bits(js, args[0], &bits); - if (is_err(err)) return err; - if (vtype(args[1]) != T_BIGINT) { - return js_mkerr_typed(js, JS_ERR_TYPE, "Cannot convert to BigInt"); - } - if (bits == 0) return js_mkbigint(js, "0", 1, false); - - ant_value_t mod = bigint_pow2(js, bits); - if (is_err(mod)) return mod; - ant_value_t res = bigint_mod(js, args[1], mod); - if (is_err(res)) return res; - if (bigint_IsNegative(js, res)) { - ant_value_t adj = bigint_add(js, res, mod); - if (is_err(adj)) return adj; - res = adj; - } - - ant_value_t threshold = bigint_pow2(js, bits - 1); - if (is_err(threshold)) return threshold; - if (bigint_compare(js, res, threshold) >= 0) { - ant_value_t adj = bigint_sub(js, res, mod); - if (is_err(adj)) return adj; - res = adj; - } - return res; -} - -static ant_value_t builtin_BigInt_asUintN(ant_t *js, ant_value_t *args, int nargs) { - if (nargs < 2) return js_mkerr(js, "BigInt.asUintN requires 2 arguments"); - uint64_t bits = 0; - ant_value_t err = bigint_asint_bits(js, args[0], &bits); - if (is_err(err)) return err; - if (vtype(args[1]) != T_BIGINT) { - return js_mkerr_typed(js, JS_ERR_TYPE, "Cannot convert to BigInt"); - } - if (bits == 0) return js_mkbigint(js, "0", 1, false); - - ant_value_t mod = bigint_pow2(js, bits); - if (is_err(mod)) return mod; - ant_value_t res = bigint_mod(js, args[1], mod); - if (is_err(res)) return res; - if (bigint_IsNegative(js, res)) { - ant_value_t adj = bigint_add(js, res, mod); - if (is_err(adj)) return adj; - res = adj; - } - return res; -} - -static ant_value_t builtin_bigint_toString(ant_t *js, ant_value_t *args, int nargs) { - ant_value_t val = js->this_val; - if (vtype(val) != T_BIGINT) return js_mkerr(js, "toString called on non-BigInt"); - - int radix = 10; - if (nargs >= 1 && vtype(args[0]) == T_NUM) { - radix = (int)tod(args[0]); - if (radix < 2 || radix > 36) return js_mkerr(js, "radix must be between 2 and 36"); - } - - bool neg = bigint_IsNegative(js, val); - size_t dlen; const char *digits = bigint_digits(js, val, &dlen); - - if (radix == 10) { - size_t buflen = dlen + 2; - char *buf = (char *)ant_calloc(buflen); - if (!buf) return js_mkerr(js, "oom"); - size_t n = 0; if (neg) buf[n++] = '-'; - memcpy(buf + n, digits, dlen); n += dlen; - ant_value_t ret = js_mkstr(js, buf, n); free(buf); - return ret; - } - - const uint32_t base = 1000000000U; - size_t result_cap = dlen * 4 + 16; - - char *result = (char *)ant_calloc(result_cap); - if (!result) return js_mkerr(js, "oom"); - - size_t rpos = result_cap - 1; - result[rpos] = '\0'; - - size_t limb_cap = (dlen + 8) / 9 + 1; - uint32_t *limbs = (uint32_t *)ant_calloc(limb_cap * sizeof(uint32_t)); - if (!limbs) { free(result); return js_mkerr(js, "oom"); } - size_t limb_len = 1; - - for (size_t i = 0; i < dlen; i++) { - uint64_t carry = (uint64_t)(digits[i] - '0'); - for (size_t j = 0; j < limb_len; j++) { - uint64_t cur = (uint64_t)limbs[j] * 10 + carry; - limbs[j] = (uint32_t)(cur % base); - carry = cur / base; - } - if (carry != 0) { - if (limb_len == limb_cap) { - size_t new_cap = limb_cap * 2; - uint32_t *new_limbs = (uint32_t *)ant_realloc(limbs, new_cap * sizeof(uint32_t)); - if (!new_limbs) { free(limbs); free(result); return js_mkerr(js, "oom"); } - limbs = new_limbs; - limb_cap = new_cap; - } - limbs[limb_len++] = (uint32_t)carry; - } - } - - static const char digit_map[] = "0123456789abcdefghijklmnopqrstuvwxyz"; - while (limb_len > 0 && !(limb_len == 1 && limbs[0] == 0)) { - uint64_t remainder = 0; - for (size_t i = limb_len; i-- > 0;) { - uint64_t cur = (uint64_t)limbs[i] + remainder * base; - limbs[i] = (uint32_t)(cur / (uint64_t)radix); - remainder = cur % (uint64_t)radix; - } - - while (limb_len > 0 && limbs[limb_len - 1] == 0) limb_len--; - if (rpos == 0) { - size_t new_cap = result_cap * 2; - char *new_result = (char *)ant_calloc(new_cap); - if (!new_result) { free(limbs); free(result); return js_mkerr(js, "oom"); } - - size_t used = result_cap - rpos; - memcpy(new_result + new_cap - used, result + rpos, used); - free(result); - - result = new_result; - rpos = new_cap - used; - result_cap = new_cap; - } - result[--rpos] = digit_map[remainder]; - } - - free(limbs); - - if (rpos == result_cap - 1) result[--rpos] = '0'; - if (neg) result[--rpos] = '-'; - - ant_value_t ret = js_mkstr(js, result + rpos, result_cap - 1 - rpos); - free(result); return ret; -} ant_value_t mkobj(ant_t *js, ant_offset_t parent) { ant_offset_t buf[2] = { parent, 0 }; @@ -5205,255 +4640,6 @@ static ant_value_t builtin_AggregateError(ant_t *js, ant_value_t *args, int narg return this_val; } -static ant_value_t builtin_Math_abs(ant_t *js, ant_value_t *args, int nargs) { - double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); - if (isnan(x)) return tov(JS_NAN); - return tov(fabs(x)); -} - -static ant_value_t builtin_Math_acos(ant_t *js, ant_value_t *args, int nargs) { - double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); - if (isnan(x)) return tov(JS_NAN); - return tov(acos(x)); -} - -static ant_value_t builtin_Math_acosh(ant_t *js, ant_value_t *args, int nargs) { - double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); - if (isnan(x)) return tov(JS_NAN); - return tov(acosh(x)); -} - -static ant_value_t builtin_Math_asin(ant_t *js, ant_value_t *args, int nargs) { - double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); - if (isnan(x)) return tov(JS_NAN); - return tov(asin(x)); -} - -static ant_value_t builtin_Math_asinh(ant_t *js, ant_value_t *args, int nargs) { - double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); - if (isnan(x)) return tov(JS_NAN); - return tov(asinh(x)); -} - -static ant_value_t builtin_Math_atan(ant_t *js, ant_value_t *args, int nargs) { - double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); - if (isnan(x)) return tov(JS_NAN); - return tov(atan(x)); -} - -static ant_value_t builtin_Math_atanh(ant_t *js, ant_value_t *args, int nargs) { - double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); - if (isnan(x)) return tov(JS_NAN); - return tov(atanh(x)); -} - -static ant_value_t builtin_Math_atan2(ant_t *js, ant_value_t *args, int nargs) { - double y = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); - double x = (nargs < 2) ? JS_NAN : js_to_number(js, args[1]); - if (isnan(y) || isnan(x)) return tov(JS_NAN); - return tov(atan2(y, x)); -} - -static ant_value_t builtin_Math_cbrt(ant_t *js, ant_value_t *args, int nargs) { - double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); - if (isnan(x)) return tov(JS_NAN); - return tov(cbrt(x)); -} - -static ant_value_t builtin_Math_ceil(ant_t *js, ant_value_t *args, int nargs) { - double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); - if (isnan(x)) return tov(JS_NAN); - return tov(ceil(x)); -} - -static ant_value_t builtin_Math_clz32(ant_t *js, ant_value_t *args, int nargs) { - if (nargs < 1) return tov(32); - double x = js_to_number(js, args[0]); - if (isnan(x) || isinf(x)) return tov(32); - uint32_t n = (uint32_t) x; - if (n == 0) return tov(32); - int count = 0; - while ((n & 0x80000000U) == 0) { count++; n <<= 1; } - return tov((double) count); -} - -static ant_value_t builtin_Math_cos(ant_t *js, ant_value_t *args, int nargs) { - double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); - if (isnan(x)) return tov(JS_NAN); - return tov(cos(x)); -} - -static ant_value_t builtin_Math_cosh(ant_t *js, ant_value_t *args, int nargs) { - double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); - if (isnan(x)) return tov(JS_NAN); - return tov(cosh(x)); -} - -static ant_value_t builtin_Math_exp(ant_t *js, ant_value_t *args, int nargs) { - double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); - if (isnan(x)) return tov(JS_NAN); - return tov(exp(x)); -} - -static ant_value_t builtin_Math_expm1(ant_t *js, ant_value_t *args, int nargs) { - double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); - if (isnan(x)) return tov(JS_NAN); - return tov(expm1(x)); -} - -static ant_value_t builtin_Math_floor(ant_t *js, ant_value_t *args, int nargs) { - double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); - if (isnan(x)) return tov(JS_NAN); - return tov(floor(x)); -} - -static ant_value_t builtin_Math_fround(ant_t *js, ant_value_t *args, int nargs) { - double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); - if (isnan(x)) return tov(JS_NAN); - return tov((double)(float)x); -} - -static ant_value_t builtin_Math_hypot(ant_t *js, ant_value_t *args, int nargs) { - if (nargs == 0) return tov(0.0); - double sum = 0.0; - for (int i = 0; i < nargs; i++) { - double v = js_to_number(js, args[i]); - if (isnan(v)) return tov(JS_NAN); - sum += v * v; - } - return tov(sqrt(sum)); -} - -static int32_t toInt32(double d) { - if (isnan(d) || isinf(d) || d == 0) return 0; - double int_val = trunc(d); - double two32 = (double)(1ULL << 32); - double two31 = (double)(1ULL << 31); - double mod_val = fmod(int_val, two32); - if (mod_val < 0) mod_val += two32; - if (mod_val >= two31) mod_val -= two32; - return (int32_t)mod_val; -} - -static ant_value_t builtin_Math_imul(ant_t *js, ant_value_t *args, int nargs) { - if (nargs < 2) return tov(0); - int32_t a = toInt32(js_to_number(js, args[0])); - int32_t b = toInt32(js_to_number(js, args[1])); - return tov((double)((int32_t)((uint32_t)a * (uint32_t)b))); -} - -static ant_value_t builtin_Math_log(ant_t *js, ant_value_t *args, int nargs) { - double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); - if (isnan(x)) return tov(JS_NAN); - return tov(log(x)); -} - -static ant_value_t builtin_Math_log1p(ant_t *js, ant_value_t *args, int nargs) { - double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); - if (isnan(x)) return tov(JS_NAN); - return tov(log1p(x)); -} - -static ant_value_t builtin_Math_log10(ant_t *js, ant_value_t *args, int nargs) { - double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); - if (isnan(x)) return tov(JS_NAN); - return tov(log10(x)); -} - -static ant_value_t builtin_Math_log2(ant_t *js, ant_value_t *args, int nargs) { - double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); - if (isnan(x)) return tov(JS_NAN); - return tov(log2(x)); -} - -static ant_value_t builtin_Math_max(ant_t *js, ant_value_t *args, int nargs) { - if (nargs == 0) return tov(JS_NEG_INF); - double max_val = JS_NEG_INF; - for (int i = 0; i < nargs; i++) { - double v = js_to_number(js, args[i]); - if (isnan(v)) return tov(JS_NAN); - if (v > max_val) max_val = v; - } - return tov(max_val); -} - -static ant_value_t builtin_Math_min(ant_t *js, ant_value_t *args, int nargs) { - if (nargs == 0) return tov(JS_INF); - double min_val = JS_INF; - for (int i = 0; i < nargs; i++) { - double v = js_to_number(js, args[i]); - if (isnan(v)) return tov(JS_NAN); - if (v < min_val) min_val = v; - } - return tov(min_val); -} - -static ant_value_t builtin_Math_pow(ant_t *js, ant_value_t *args, int nargs) { - double base = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); - double exp = (nargs < 2) ? JS_NAN : js_to_number(js, args[1]); - if (isnan(base) || isnan(exp)) return tov(JS_NAN); - return tov(pow(base, exp)); -} - -static bool random_seeded = false; - -static ant_value_t builtin_Math_random(ant_t *js, ant_value_t *args, int nargs) { - if (!random_seeded) { - srand((unsigned int) time(NULL)); - random_seeded = true; - } - return tov((double) rand() / ((double) RAND_MAX + 1.0)); -} - -static ant_value_t builtin_Math_round(ant_t *js, ant_value_t *args, int nargs) { - double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); - if (isnan(x) || isinf(x)) return tov(x); - return tov(floor(x + 0.5)); -} - -static ant_value_t builtin_Math_sign(ant_t *js, ant_value_t *args, int nargs) { - double v = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); - if (isnan(v)) return tov(JS_NAN); - if (v > 0) return tov(1.0); - if (v < 0) return tov(-1.0); - return tov(v); -} - -static ant_value_t builtin_Math_sin(ant_t *js, ant_value_t *args, int nargs) { - double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); - if (isnan(x)) return tov(JS_NAN); - return tov(sin(x)); -} - -static ant_value_t builtin_Math_sinh(ant_t *js, ant_value_t *args, int nargs) { - double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); - if (isnan(x)) return tov(JS_NAN); - return tov(sinh(x)); -} - -static ant_value_t builtin_Math_sqrt(ant_t *js, ant_value_t *args, int nargs) { - double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); - if (isnan(x)) return tov(JS_NAN); - return tov(sqrt(x)); -} - -static ant_value_t builtin_Math_tan(ant_t *js, ant_value_t *args, int nargs) { - double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); - if (isnan(x)) return tov(JS_NAN); - return tov(tan(x)); -} - -static ant_value_t builtin_Math_tanh(ant_t *js, ant_value_t *args, int nargs) { - double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); - if (isnan(x)) return tov(JS_NAN); - return tov(tanh(x)); -} - -static ant_value_t builtin_Math_trunc(ant_t *js, ant_value_t *args, int nargs) { - double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); - if (isnan(x)) return tov(JS_NAN); - return tov(trunc(x)); -} typedef ant_value_t (*dynamic_kv_mapper_fn)( ant_t *js, @@ -12029,10 +11215,6 @@ ant_t *js_create(void *buf, size_t len) { js_setprop(js, boolean_proto, js_mkstr(js, "valueOf", 7), js_mkfun(builtin_boolean_valueOf)); js_setprop(js, boolean_proto, js_mkstr(js, "toString", 8), js_mkfun(builtin_boolean_toString)); - ant_value_t bigint_proto = js_mkobj(js); - set_proto(js, bigint_proto, object_proto); - js_setprop(js, bigint_proto, js_mkstr(js, "toString", 8), js_mkfun(builtin_bigint_toString)); - ant_value_t error_proto = js_mkobj(js); set_proto(js, error_proto, object_proto); js_setprop(js, error_proto, ANT_STRING("name"), ANT_STRING("Error")); @@ -12229,15 +11411,6 @@ ant_t *js_create(void *buf, size_t len) { js_setprop(js, p_ctor_obj, ANT_STRING("name"), ANT_STRING("Promise")); js_setprop(js, glob, js_mkstr(js, "Promise", 7), js_obj_to_func(p_ctor_obj)); - ant_value_t bigint_ctor_obj = mkobj(js, 0); - set_proto(js, bigint_ctor_obj, function_proto); - set_slot(js, bigint_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_BigInt)); - js_setprop(js, bigint_ctor_obj, js_mkstr(js, "asIntN", 6), js_mkfun(builtin_BigInt_asIntN)); - js_setprop(js, bigint_ctor_obj, js_mkstr(js, "asUintN", 7), js_mkfun(builtin_BigInt_asUintN)); - js_setprop_nonconfigurable(js, bigint_ctor_obj, "prototype", 9, bigint_proto); - js_setprop(js, bigint_ctor_obj, ANT_STRING("name"), ANT_STRING("BigInt")); - js_setprop(js, glob, js_mkstr(js, "BigInt", 6), js_obj_to_func(bigint_ctor_obj)); - js_setprop(js, glob, js_mkstr(js, "parseInt", 8), js_mkfun(builtin_parseInt)); js_setprop(js, glob, js_mkstr(js, "parseFloat", 10), js_mkfun(builtin_parseFloat)); js_setprop(js, glob, js_mkstr(js, "eval", 4), js_mkfun(builtin_eval)); @@ -12252,53 +11425,6 @@ ant_t *js_create(void *buf, size_t len) { js_setprop(js, glob, js_mkstr(js, "undefined", 9), js_mkundef()); js_set_descriptor(js, glob, "undefined", 9, 0); - ant_value_t math_obj = mkobj(js, 0); - set_proto(js, math_obj, object_proto); - js_setprop(js, math_obj, js_mkstr(js, "E", 1), tov(M_E)); - js_setprop(js, math_obj, js_mkstr(js, "LN10", 4), tov(M_LN10)); - js_setprop(js, math_obj, js_mkstr(js, "LN2", 3), tov(M_LN2)); - js_setprop(js, math_obj, js_mkstr(js, "LOG10E", 6), tov(M_LOG10E)); - js_setprop(js, math_obj, js_mkstr(js, "LOG2E", 5), tov(M_LOG2E)); - js_setprop(js, math_obj, js_mkstr(js, "PI", 2), tov(M_PI)); - js_setprop(js, math_obj, js_mkstr(js, "SQRT1_2", 7), tov(M_SQRT1_2)); - js_setprop(js, math_obj, js_mkstr(js, "SQRT2", 5), tov(M_SQRT2)); - js_setprop(js, math_obj, js_mkstr(js, "abs", 3), js_mkfun(builtin_Math_abs)); - js_setprop(js, math_obj, js_mkstr(js, "acos", 4), js_mkfun(builtin_Math_acos)); - js_setprop(js, math_obj, js_mkstr(js, "acosh", 5), js_mkfun(builtin_Math_acosh)); - js_setprop(js, math_obj, js_mkstr(js, "asin", 4), js_mkfun(builtin_Math_asin)); - js_setprop(js, math_obj, js_mkstr(js, "asinh", 5), js_mkfun(builtin_Math_asinh)); - js_setprop(js, math_obj, js_mkstr(js, "atan", 4), js_mkfun(builtin_Math_atan)); - js_setprop(js, math_obj, js_mkstr(js, "atanh", 5), js_mkfun(builtin_Math_atanh)); - js_setprop(js, math_obj, js_mkstr(js, "atan2", 5), js_mkfun(builtin_Math_atan2)); - js_setprop(js, math_obj, js_mkstr(js, "cbrt", 4), js_mkfun(builtin_Math_cbrt)); - js_setprop(js, math_obj, js_mkstr(js, "ceil", 4), js_mkfun(builtin_Math_ceil)); - js_setprop(js, math_obj, js_mkstr(js, "clz32", 5), js_mkfun(builtin_Math_clz32)); - js_setprop(js, math_obj, js_mkstr(js, "cos", 3), js_mkfun(builtin_Math_cos)); - js_setprop(js, math_obj, js_mkstr(js, "cosh", 4), js_mkfun(builtin_Math_cosh)); - js_setprop(js, math_obj, js_mkstr(js, "exp", 3), js_mkfun(builtin_Math_exp)); - js_setprop(js, math_obj, js_mkstr(js, "expm1", 5), js_mkfun(builtin_Math_expm1)); - js_setprop(js, math_obj, js_mkstr(js, "floor", 5), js_mkfun(builtin_Math_floor)); - js_setprop(js, math_obj, js_mkstr(js, "fround", 6), js_mkfun(builtin_Math_fround)); - js_setprop(js, math_obj, js_mkstr(js, "hypot", 5), js_mkfun(builtin_Math_hypot)); - js_setprop(js, math_obj, js_mkstr(js, "imul", 4), js_mkfun(builtin_Math_imul)); - js_setprop(js, math_obj, js_mkstr(js, "log", 3), js_mkfun(builtin_Math_log)); - js_setprop(js, math_obj, js_mkstr(js, "log1p", 5), js_mkfun(builtin_Math_log1p)); - js_setprop(js, math_obj, js_mkstr(js, "log10", 5), js_mkfun(builtin_Math_log10)); - js_setprop(js, math_obj, js_mkstr(js, "log2", 4), js_mkfun(builtin_Math_log2)); - js_setprop(js, math_obj, js_mkstr(js, "max", 3), js_mkfun(builtin_Math_max)); - js_setprop(js, math_obj, js_mkstr(js, "min", 3), js_mkfun(builtin_Math_min)); - js_setprop(js, math_obj, js_mkstr(js, "pow", 3), js_mkfun(builtin_Math_pow)); - js_setprop(js, math_obj, js_mkstr(js, "random", 6), js_mkfun(builtin_Math_random)); - js_setprop(js, math_obj, js_mkstr(js, "round", 5), js_mkfun(builtin_Math_round)); - js_setprop(js, math_obj, js_mkstr(js, "sign", 4), js_mkfun(builtin_Math_sign)); - js_setprop(js, math_obj, js_mkstr(js, "sin", 3), js_mkfun(builtin_Math_sin)); - js_setprop(js, math_obj, js_mkstr(js, "sinh", 4), js_mkfun(builtin_Math_sinh)); - js_setprop(js, math_obj, js_mkstr(js, "sqrt", 4), js_mkfun(builtin_Math_sqrt)); - js_setprop(js, math_obj, js_mkstr(js, "tan", 3), js_mkfun(builtin_Math_tan)); - js_setprop(js, math_obj, js_mkstr(js, "tanh", 4), js_mkfun(builtin_Math_tanh)); - js_setprop(js, math_obj, js_mkstr(js, "trunc", 5), js_mkfun(builtin_Math_trunc)); - js_setprop(js, glob, js_mkstr(js, "Math", 4), math_obj); - ant_value_t import_obj = mkobj(js, 0); set_proto(js, import_obj, function_proto); diff --git a/src/main.c b/src/main.c index 863506f..adb9ffc 100644 --- a/src/main.c +++ b/src/main.c @@ -52,6 +52,8 @@ #include "modules/reflect.h" #include "modules/symbol.h" #include "modules/date.h" +#include "modules/math.h" +#include "modules/bigint.h" #include "modules/regex.h" #include "modules/textcodec.h" #include "modules/sessionstorage.h" @@ -568,6 +570,8 @@ int main(int argc, char *argv[]) { proc_argv = build_process_argv(argc, argv, module_file, script_tail); ant_runtime_init(js, proc_argv.argc, proc_argv.argv, localstorage_file); + init_math_module(); + init_bigint_module(); init_symbol_module(); init_date_module(); init_regex_module(); diff --git a/src/modules/bigint.c b/src/modules/bigint.c new file mode 100644 index 0000000..4b9a28e --- /dev/null +++ b/src/modules/bigint.c @@ -0,0 +1,779 @@ +#include +#include + +#include "ant.h" +#include "internal.h" +#include "errors.h" +#include "arena.h" +#include "runtime.h" + +static inline bool is_decimal_digit(char c) { + return c >= '0' && c <= '9'; +} + +bool bigint_is_negative(ant_t *js, ant_value_t v) { + ant_offset_t ofs = (ant_offset_t)vdata(v); + return js->mem[ofs + sizeof(ant_offset_t)] == 1; +} + +size_t bigint_digits_len(ant_t *js, ant_value_t v) { + ant_offset_t ofs = (ant_offset_t)vdata(v); + ant_offset_t header = loadoff(js, ofs); + return (size_t)((header >> 4) - 2); +} + +static const char *bigint_digits(ant_t *js, ant_value_t v, size_t *len) { + ant_offset_t ofs = (ant_offset_t)vdata(v); + size_t total = bigint_digits_len(js, v); + if (len) *len = total; + return (const char *)&js->mem[ofs + sizeof(ant_offset_t) + 1]; +} + +static ant_value_t bigint_from_u64(ant_t *js, uint64_t value) { + char buf[32]; + size_t len = uint_to_str(buf, sizeof(buf), value); + return js_mkbigint(js, buf, len, false); +} + +static bool bigint_parse_abs_u64(ant_t *js, ant_value_t value, uint64_t *out) { + size_t len = 0; + const char *digits = bigint_digits(js, value, &len); + uint64_t acc = 0; + + for (size_t i = 0; i < len; i++) { + char c = digits[i]; + if (!is_decimal_digit(c)) return false; + uint64_t digit = (uint64_t)(c - '0'); + if (acc > UINT64_MAX / 10 || (acc == UINT64_MAX / 10 && digit > (UINT64_MAX % 10))) { + return false; + } + acc = acc * 10 + digit; + } + + *out = acc; + return true; +} + +static bool bigint_parse_u64(ant_t *js, ant_value_t value, uint64_t *out) { + if (bigint_is_negative(js, value)) return false; + return bigint_parse_abs_u64(js, value, out); +} + +ant_value_t js_mkbigint(ant_t *js, const char *digits, size_t len, bool negative) { + size_t total = len + 2; + ant_offset_t ofs = js_alloc(js, total + sizeof(ant_offset_t)); + if (ofs == (ant_offset_t)~0) return js_mkerr(js, "oom"); + + ant_offset_t header = (ant_offset_t)(total << 4); + memcpy(&js->mem[ofs], &header, sizeof(header)); + js->mem[ofs + sizeof(header)] = negative ? 1 : 0; + if (digits) memcpy(&js->mem[ofs + sizeof(header) + 1], digits, len); + js->mem[ofs + sizeof(header) + 1 + len] = 0; + return mkval(T_BIGINT, ofs); +} + +static int bigint_cmp_abs(const char *a, size_t alen, const char *b, size_t blen) { + while (alen > 1 && a[0] == '0') { a++; alen--; } + while (blen > 1 && b[0] == '0') { b++; blen--; } + if (alen != blen) return alen > blen ? 1 : -1; + for (size_t i = 0; i < alen; i++) { + if (a[i] != b[i]) return a[i] > b[i] ? 1 : -1; + } + return 0; +} + +static char *bigint_add_abs(const char *a, size_t alen, const char *b, size_t blen, size_t *rlen) { + size_t maxlen = (alen > blen ? alen : blen) + 1; + char *result = (char *)malloc(maxlen + 1); + if (!result) return NULL; + + int carry = 0; + size_t ri = 0; + for (size_t i = 0; i < maxlen; i++) { + int da = (i < alen) ? (a[alen - 1 - i] - '0') : 0; + int db = (i < blen) ? (b[blen - 1 - i] - '0') : 0; + int sum = da + db + carry; + carry = sum / 10; + result[ri++] = (char)('0' + (sum % 10)); + } + + while (ri > 1 && result[ri - 1] == '0') ri--; + for (size_t i = 0; i < ri / 2; i++) { + char tmp = result[i]; + result[i] = result[ri - 1 - i]; + result[ri - 1 - i] = tmp; + } + + result[ri] = 0; + *rlen = ri; + return result; +} + +static char *bigint_sub_abs(const char *a, size_t alen, const char *b, size_t blen, size_t *rlen) { + char *result = (char *)malloc(alen + 1); + if (!result) return NULL; + + int borrow = 0; + size_t ri = 0; + for (size_t i = 0; i < alen; i++) { + int da = a[alen - 1 - i] - '0'; + int db = (i < blen) ? (b[blen - 1 - i] - '0') : 0; + int diff = da - db - borrow; + if (diff < 0) { + diff += 10; + borrow = 1; + } else { + borrow = 0; + } + result[ri++] = (char)('0' + diff); + } + + while (ri > 1 && result[ri - 1] == '0') ri--; + for (size_t i = 0; i < ri / 2; i++) { + char tmp = result[i]; + result[i] = result[ri - 1 - i]; + result[ri - 1 - i] = tmp; + } + + result[ri] = 0; + *rlen = ri; + return result; +} + +static char *bigint_mul_abs(const char *a, size_t alen, const char *b, size_t blen, size_t *rlen) { + size_t reslen = alen + blen; + int *temp = (int *)calloc(reslen, sizeof(int)); + if (!temp) return NULL; + + for (size_t i = 0; i < alen; i++) { + for (size_t j = 0; j < blen; j++) { + temp[i + j] += (a[alen - 1 - i] - '0') * (b[blen - 1 - j] - '0'); + } + } + + for (size_t i = 0; i < reslen - 1; i++) { + temp[i + 1] += temp[i] / 10; + temp[i] %= 10; + } + + size_t start = reslen - 1; + while (start > 0 && temp[start] == 0) start--; + + char *result = (char *)malloc(start + 2); + if (!result) { + free(temp); + return NULL; + } + + for (size_t i = 0; i <= start; i++) result[i] = (char)('0' + temp[start - i]); + result[start + 1] = 0; + *rlen = start + 1; + + free(temp); + return result; +} + +static char *bigint_div_abs( + const char *a, + size_t alen, + const char *b, + size_t blen, + size_t *rlen, + char **rem, + size_t *remlen +) { + if (blen == 1 && b[0] == '0') return NULL; + + if (bigint_cmp_abs(a, alen, b, blen) < 0) { + char *result = (char *)malloc(2); + result[0] = '0'; + result[1] = 0; + *rlen = 1; + if (rem) { + *rem = (char *)malloc(alen + 1); + memcpy(*rem, a, alen); + (*rem)[alen] = 0; + *remlen = alen; + } + return result; + } + + char *current = (char *)calloc(alen + 1, 1); + char *result = (char *)calloc(alen + 1, 1); + if (!current || !result) { + free(current); + free(result); + return NULL; + } + + size_t curlen = 0, reslen = 0; + for (size_t i = 0; i < alen; i++) { + if (curlen == 1 && current[0] == '0') curlen = 0; + current[curlen++] = a[i]; + current[curlen] = 0; + + int count = 0; + while (bigint_cmp_abs(current, curlen, b, blen) >= 0) { + size_t sublen; + char *sub = bigint_sub_abs(current, curlen, b, blen, &sublen); + if (!sub) break; + memcpy(current, sub, sublen + 1); + curlen = sublen; + free(sub); + count++; + } + + result[reslen++] = (char)('0' + count); + } + + size_t start = 0; + while (start < reslen - 1 && result[start] == '0') start++; + memmove(result, result + start, reslen - start + 1); + + *rlen = reslen - start; + if (rem) { + *rem = current; + *remlen = curlen; + } else { + free(current); + } + + return result; +} + +ant_value_t bigint_add(ant_t *js, ant_value_t a, ant_value_t b) { + bool aneg = bigint_is_negative(js, a); + bool bneg = bigint_is_negative(js, b); + size_t alen, blen; + const char *ad = bigint_digits(js, a, &alen); + const char *bd = bigint_digits(js, b, &blen); + + char *result; + size_t rlen; + bool rneg; + + if (aneg == bneg) { + result = bigint_add_abs(ad, alen, bd, blen, &rlen); + rneg = aneg; + } else { + int cmp = bigint_cmp_abs(ad, alen, bd, blen); + if (cmp >= 0) { + result = bigint_sub_abs(ad, alen, bd, blen, &rlen); + rneg = aneg; + } else { + result = bigint_sub_abs(bd, blen, ad, alen, &rlen); + rneg = bneg; + } + } + + if (!result) return js_mkerr(js, "oom"); + if (rlen == 1 && result[0] == '0') rneg = false; + + ant_value_t r = js_mkbigint(js, result, rlen, rneg); + free(result); + return r; +} + +ant_value_t bigint_sub(ant_t *js, ant_value_t a, ant_value_t b) { + bool aneg = bigint_is_negative(js, a); + bool bneg = bigint_is_negative(js, b); + size_t alen, blen; + const char *ad = bigint_digits(js, a, &alen); + const char *bd = bigint_digits(js, b, &blen); + + char *result; + size_t rlen; + bool rneg; + + if (aneg != bneg) { + result = bigint_add_abs(ad, alen, bd, blen, &rlen); + rneg = aneg; + } else { + int cmp = bigint_cmp_abs(ad, alen, bd, blen); + if (cmp >= 0) { + result = bigint_sub_abs(ad, alen, bd, blen, &rlen); + rneg = aneg; + } else { + result = bigint_sub_abs(bd, blen, ad, alen, &rlen); + rneg = !aneg; + } + } + + if (!result) return js_mkerr(js, "oom"); + if (rlen == 1 && result[0] == '0') rneg = false; + + ant_value_t r = js_mkbigint(js, result, rlen, rneg); + free(result); + return r; +} + +ant_value_t bigint_mul(ant_t *js, ant_value_t a, ant_value_t b) { + bool aneg = bigint_is_negative(js, a); + bool bneg = bigint_is_negative(js, b); + size_t alen, blen; + const char *ad = bigint_digits(js, a, &alen); + const char *bd = bigint_digits(js, b, &blen); + + size_t rlen; + char *result = bigint_mul_abs(ad, alen, bd, blen, &rlen); + if (!result) return js_mkerr(js, "oom"); + + bool rneg = (aneg != bneg) && !(rlen == 1 && result[0] == '0'); + ant_value_t r = js_mkbigint(js, result, rlen, rneg); + free(result); + return r; +} + +ant_value_t bigint_div(ant_t *js, ant_value_t a, ant_value_t b) { + bool aneg = bigint_is_negative(js, a); + bool bneg = bigint_is_negative(js, b); + size_t alen, blen; + const char *ad = bigint_digits(js, a, &alen); + const char *bd = bigint_digits(js, b, &blen); + + if (blen == 1 && bd[0] == '0') return js_mkerr(js, "Division by zero"); + + size_t rlen; + char *result = bigint_div_abs(ad, alen, bd, blen, &rlen, NULL, NULL); + if (!result) return js_mkerr(js, "oom"); + + bool rneg = (aneg != bneg) && !(rlen == 1 && result[0] == '0'); + ant_value_t r = js_mkbigint(js, result, rlen, rneg); + free(result); + return r; +} + +ant_value_t bigint_mod(ant_t *js, ant_value_t a, ant_value_t b) { + bool aneg = bigint_is_negative(js, a); + size_t alen, blen; + const char *ad = bigint_digits(js, a, &alen); + const char *bd = bigint_digits(js, b, &blen); + + if (blen == 1 && bd[0] == '0') return js_mkerr(js, "Division by zero"); + + size_t rlen, remlen; + char *rem; + char *result = bigint_div_abs(ad, alen, bd, blen, &rlen, &rem, &remlen); + if (!result) return js_mkerr(js, "oom"); + free(result); + + bool rneg = aneg && !(remlen == 1 && rem[0] == '0'); + ant_value_t r = js_mkbigint(js, rem, remlen, rneg); + free(rem); + return r; +} + +ant_value_t bigint_neg(ant_t *js, ant_value_t a) { + size_t len; + const char *digits = bigint_digits(js, a, &len); + bool neg = bigint_is_negative(js, a); + if (len == 1 && digits[0] == '0') return js_mkbigint(js, digits, len, false); + return js_mkbigint(js, digits, len, !neg); +} + +ant_value_t bigint_exp(ant_t *js, ant_value_t base, ant_value_t exp) { + if (bigint_is_negative(js, exp)) return js_mkerr(js, "Exponent must be positive"); + + size_t explen; + const char *expd = bigint_digits(js, exp, &explen); + if (explen == 1 && expd[0] == '0') return js_mkbigint(js, "1", 1, false); + + ant_value_t result = js_mkbigint(js, "1", 1, false); + ant_value_t b = base; + ant_value_t e = exp; + ant_value_t two = js_mkbigint(js, "2", 1, false); + + while (true) { + size_t elen; + const char *ed = bigint_digits(js, e, &elen); + if (elen == 1 && ed[0] == '0') break; + + int last_digit = ed[elen - 1] - '0'; + if (last_digit % 2 == 1) { + result = bigint_mul(js, result, b); + if (is_err(result)) return result; + } + + b = bigint_mul(js, b, b); + if (is_err(b)) return b; + + e = bigint_div(js, e, two); + if (is_err(e)) return e; + } + + return result; +} + +static inline ant_value_t bigint_pow2(ant_t *js, uint64_t bits) { + ant_value_t two = js_mkbigint(js, "2", 1, false); + if (is_err(two)) return two; + + ant_value_t exp = bigint_from_u64(js, bits); + if (is_err(exp)) return exp; + + return bigint_exp(js, two, exp); +} + +ant_value_t bigint_shift_left(ant_t *js, ant_value_t value, uint64_t shift) { + if (shift == 0) return value; + if (shift > 18446744073709551615ULL) return js_mkerr(js, "Shift count too large"); + + size_t digits_len; + const char *digits = bigint_digits(js, value, &digits_len); + if (digits_len == 1 && digits[0] == '0') return js_mkbigint(js, "0", 1, false); + + uint64_t u64 = 0; + if (!bigint_is_negative(js, value) && shift < 64 && bigint_parse_u64(js, value, &u64)) { + if (u64 <= (UINT64_MAX >> shift)) return bigint_from_u64(js, u64 << shift); + } + + ant_value_t pow = bigint_pow2(js, shift); + if (is_err(pow)) return pow; + return bigint_mul(js, value, pow); +} + +ant_value_t bigint_shift_right(ant_t *js, ant_value_t value, uint64_t shift) { + if (shift == 0) return value; + if (shift > 18446744073709551615ULL) return js_mkerr(js, "Shift count too large"); + + size_t digits_len; + const char *digits = bigint_digits(js, value, &digits_len); + if (digits_len == 1 && digits[0] == '0') return js_mkbigint(js, "0", 1, false); + + uint64_t u64 = 0; + if (!bigint_is_negative(js, value) && bigint_parse_u64(js, value, &u64)) { + if (shift >= 64) return js_mkbigint(js, "0", 1, false); + return bigint_from_u64(js, u64 >> shift); + } + + if (bigint_parse_abs_u64(js, value, &u64)) { + if (shift >= 64) { + return js_mkbigint(js, bigint_is_negative(js, value) ? "1" : "0", 1, bigint_is_negative(js, value)); + } + + uint64_t shifted = u64 >> shift; + if (bigint_is_negative(js, value)) { + if ((u64 & ((1ULL << shift) - 1)) != 0) shifted += 1; + ant_value_t pos = bigint_from_u64(js, shifted); + if (is_err(pos)) return pos; + return bigint_neg(js, pos); + } + + return bigint_from_u64(js, shifted); + } + + ant_value_t pow = bigint_pow2(js, shift); + if (is_err(pow)) return pow; + return bigint_div(js, value, pow); +} + +ant_value_t bigint_shift_right_logical(ant_t *js, ant_value_t value, uint64_t shift) { + return js_mkerr_typed(js, JS_ERR_TYPE, "BigInts have no unsigned right shift, use >> instead"); +} + +size_t bigint_compare(ant_t *js, ant_value_t a, ant_value_t b) { + bool aneg = bigint_is_negative(js, a); + bool bneg = bigint_is_negative(js, b); + + size_t alen, blen; + const char *ad = bigint_digits(js, a, &alen); + const char *bd = bigint_digits(js, b, &blen); + + if (aneg && !bneg) return -1; + if (!aneg && bneg) return 1; + + int cmp = bigint_cmp_abs(ad, alen, bd, blen); + return aneg ? -cmp : cmp; +} + +bool bigint_is_zero(ant_t *js, ant_value_t v) { + size_t len; + const char *digits = bigint_digits(js, v, &len); + return len == 1 && digits[0] == '0'; +} + +size_t strbigint(ant_t *js, ant_value_t value, char *buf, size_t len) { + bool neg = bigint_is_negative(js, value); + size_t dlen; + const char *digits = bigint_digits(js, value, &dlen); + size_t total = dlen + (neg ? 1 : 0); + + if (len == 0) return total; + + size_t n = 0; + if (neg && n < len - 1) buf[n] = '-'; + if (neg) n++; + + size_t avail = n < len ? len - n - 1 : 0; + size_t copy_len = dlen < avail ? dlen : avail; + if (copy_len > 0) memcpy(buf + n, digits, copy_len); + + size_t term = n + copy_len; + if (term >= len) term = len - 1; + buf[term] = '\0'; + return total; +} + +static ant_value_t builtin_BigInt(ant_t *js, ant_value_t *args, int nargs) { + if (vtype(js->new_target) != T_UNDEF) return js_mkerr_typed(js, JS_ERR_TYPE, "BigInt is not a constructor"); + if (nargs < 1) return js_mkbigint(js, "0", 1, false); + + ant_value_t arg = args[0]; + if (vtype(arg) == T_BIGINT) return arg; + + if (vtype(arg) == T_NUM) { + double d = tod(arg); + if (!isfinite(d)) return js_mkerr(js, "Cannot convert Infinity or NaN to BigInt"); + if (d != trunc(d)) return js_mkerr(js, "Cannot convert non-integer to BigInt"); + + bool neg = d < 0; + if (neg) d = -d; + + char buf[64]; + snprintf(buf, sizeof(buf), "%.0f", d); + return js_mkbigint(js, buf, strlen(buf), neg); + } + + if (vtype(arg) == T_STR) { + ant_offset_t slen; + ant_offset_t off = vstr(js, arg, &slen); + const char *str = (const char *)&js->mem[off]; + + bool neg = false; + size_t i = 0; + if (slen > 0 && str[0] == '-') { + neg = true; + i++; + } else if (slen > 0 && str[0] == '+') { + i++; + } + + while (i < slen && str[i] == '0') i++; + if (i >= slen) return js_mkbigint(js, "0", 1, false); + + for (size_t j = i; j < slen; j++) { + if (!is_decimal_digit(str[j])) return js_mkerr(js, "Cannot convert string to BigInt"); + } + + return js_mkbigint(js, str + i, slen - i, neg); + } + + if (vtype(arg) == T_BOOL) return js_mkbigint(js, vdata(arg) ? "1" : "0", 1, false); + + return js_mkerr(js, "Cannot convert to BigInt"); +} + +static ant_value_t bigint_to_u64(ant_t *js, ant_value_t value, uint64_t *out) { + if (!bigint_parse_u64(js, value, out)) { + return js_mkerr_typed(js, JS_ERR_RANGE, "Invalid bits"); + } + return js_mkundef(); +} + +ant_value_t bigint_asint_bits(ant_t *js, ant_value_t arg, uint64_t *bits_out) { + if (vtype(arg) == T_BIGINT) return bigint_to_u64(js, arg, bits_out); + + double bits = js_to_number(js, arg); + if (!isfinite(bits) || bits < 0 || bits != floor(bits)) { + return js_mkerr_typed(js, JS_ERR_RANGE, "Invalid bits"); + } + + if (bits > 18446744073709551615.0) { + return js_mkerr_typed(js, JS_ERR_RANGE, "Invalid bits"); + } + + *bits_out = (uint64_t)bits; + return js_mkundef(); +} + +static ant_value_t builtin_BigInt_asIntN(ant_t *js, ant_value_t *args, int nargs) { + if (nargs < 2) return js_mkerr(js, "BigInt.asIntN requires 2 arguments"); + + uint64_t bits = 0; + ant_value_t err = bigint_asint_bits(js, args[0], &bits); + if (is_err(err)) return err; + + if (vtype(args[1]) != T_BIGINT) return js_mkerr_typed(js, JS_ERR_TYPE, "Cannot convert to BigInt"); + if (bits == 0) return js_mkbigint(js, "0", 1, false); + + ant_value_t mod = bigint_pow2(js, bits); + if (is_err(mod)) return mod; + + ant_value_t res = bigint_mod(js, args[1], mod); + if (is_err(res)) return res; + + if (bigint_is_negative(js, res)) { + ant_value_t adj = bigint_add(js, res, mod); + if (is_err(adj)) return adj; + res = adj; + } + + ant_value_t threshold = bigint_pow2(js, bits - 1); + if (is_err(threshold)) return threshold; + + if (bigint_compare(js, res, threshold) >= 0) { + ant_value_t adj = bigint_sub(js, res, mod); + if (is_err(adj)) return adj; + res = adj; + } + + return res; +} + +static ant_value_t builtin_BigInt_asUintN(ant_t *js, ant_value_t *args, int nargs) { + if (nargs < 2) return js_mkerr(js, "BigInt.asUintN requires 2 arguments"); + + uint64_t bits = 0; + ant_value_t err = bigint_asint_bits(js, args[0], &bits); + if (is_err(err)) return err; + + if (vtype(args[1]) != T_BIGINT) return js_mkerr_typed(js, JS_ERR_TYPE, "Cannot convert to BigInt"); + if (bits == 0) return js_mkbigint(js, "0", 1, false); + + ant_value_t mod = bigint_pow2(js, bits); + if (is_err(mod)) return mod; + + ant_value_t res = bigint_mod(js, args[1], mod); + if (is_err(res)) return res; + + if (bigint_is_negative(js, res)) { + ant_value_t adj = bigint_add(js, res, mod); + if (is_err(adj)) return adj; + res = adj; + } + + return res; +} + +static ant_value_t builtin_bigint_toString(ant_t *js, ant_value_t *args, int nargs) { + ant_value_t val = js->this_val; + if (vtype(val) != T_BIGINT) return js_mkerr(js, "toString called on non-BigInt"); + + int radix = 10; + if (nargs >= 1 && vtype(args[0]) == T_NUM) { + radix = (int)tod(args[0]); + if (radix < 2 || radix > 36) return js_mkerr(js, "radix must be between 2 and 36"); + } + + bool neg = bigint_is_negative(js, val); + size_t dlen; + const char *digits = bigint_digits(js, val, &dlen); + + if (radix == 10) { + size_t buflen = dlen + 2; + char *buf = (char *)ant_calloc(buflen); + if (!buf) return js_mkerr(js, "oom"); + + size_t n = 0; + if (neg) buf[n++] = '-'; + memcpy(buf + n, digits, dlen); + n += dlen; + + ant_value_t ret = js_mkstr(js, buf, n); + free(buf); + return ret; + } + + const uint32_t base = 1000000000U; + size_t result_cap = dlen * 4 + 16; + char *result = (char *)ant_calloc(result_cap); + if (!result) return js_mkerr(js, "oom"); + + size_t rpos = result_cap - 1; + result[rpos] = '\0'; + + size_t limb_cap = (dlen + 8) / 9 + 1; + uint32_t *limbs = (uint32_t *)ant_calloc(limb_cap * sizeof(uint32_t)); + if (!limbs) { + free(result); + return js_mkerr(js, "oom"); + } + size_t limb_len = 1; + + for (size_t i = 0; i < dlen; i++) { + uint64_t carry = (uint64_t)(digits[i] - '0'); + for (size_t j = 0; j < limb_len; j++) { + uint64_t cur = (uint64_t)limbs[j] * 10 + carry; + limbs[j] = (uint32_t)(cur % base); + carry = cur / base; + } + + if (carry != 0) { + if (limb_len == limb_cap) { + size_t new_cap = limb_cap * 2; + uint32_t *new_limbs = (uint32_t *)ant_realloc(limbs, new_cap * sizeof(uint32_t)); + if (!new_limbs) { + free(limbs); + free(result); + return js_mkerr(js, "oom"); + } + limbs = new_limbs; + limb_cap = new_cap; + } + limbs[limb_len++] = (uint32_t)carry; + } + } + + static const char digit_map[] = "0123456789abcdefghijklmnopqrstuvwxyz"; + while (limb_len > 0 && !(limb_len == 1 && limbs[0] == 0)) { + uint64_t remainder = 0; + for (size_t i = limb_len; i-- > 0;) { + uint64_t cur = (uint64_t)limbs[i] + remainder * base; + limbs[i] = (uint32_t)(cur / (uint64_t)radix); + remainder = cur % (uint64_t)radix; + } + + while (limb_len > 0 && limbs[limb_len - 1] == 0) limb_len--; + + if (rpos == 0) { + size_t new_cap = result_cap * 2; + char *new_result = (char *)ant_calloc(new_cap); + if (!new_result) { + free(limbs); + free(result); + return js_mkerr(js, "oom"); + } + + size_t used = result_cap - rpos; + memcpy(new_result + new_cap - used, result + rpos, used); + free(result); + + result = new_result; + rpos = new_cap - used; + result_cap = new_cap; + } + + result[--rpos] = digit_map[remainder]; + } + + free(limbs); + + if (rpos == result_cap - 1) result[--rpos] = '0'; + if (neg) result[--rpos] = '-'; + + ant_value_t ret = js_mkstr(js, result + rpos, result_cap - 1 - rpos); + free(result); + return ret; +} + +void init_bigint_module(void) { + ant_t *js = rt->js; + + ant_value_t glob = js_glob(js); + ant_value_t object_proto = js->object; + ant_value_t function_proto = js_get_slot(js, glob, SLOT_FUNC_PROTO); + if (vtype(function_proto) == T_UNDEF) function_proto = js_get_ctor_proto(js, "Function", 8); + + ant_value_t bigint_proto = js_mkobj(js); + js_set_proto(js, bigint_proto, object_proto); + js_setprop(js, bigint_proto, js_mkstr(js, "toString", 8), js_mkfun(builtin_bigint_toString)); + + ant_value_t bigint_ctor_obj = mkobj(js, 0); + js_set_proto(js, bigint_ctor_obj, function_proto); + js_set_slot(js, bigint_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_BigInt)); + js_setprop(js, bigint_ctor_obj, js_mkstr(js, "asIntN", 6), js_mkfun(builtin_BigInt_asIntN)); + js_setprop(js, bigint_ctor_obj, js_mkstr(js, "asUintN", 7), js_mkfun(builtin_BigInt_asUintN)); + js_setprop_nonconfigurable(js, bigint_ctor_obj, "prototype", 9, bigint_proto); + js_setprop(js, bigint_ctor_obj, ANT_STRING("name"), ANT_STRING("BigInt")); + js_setprop(js, glob, js_mkstr(js, "BigInt", 6), js_obj_to_func(bigint_ctor_obj)); +} diff --git a/src/modules/math.c b/src/modules/math.c new file mode 100644 index 0000000..0e96067 --- /dev/null +++ b/src/modules/math.c @@ -0,0 +1,313 @@ +#include +#include + +#include "ant.h" +#include "internal.h" +#include "runtime.h" + +static ant_value_t builtin_Math_abs(ant_t *js, ant_value_t *args, int nargs) { + double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); + if (isnan(x)) return tov(JS_NAN); + return tov(fabs(x)); +} + +static ant_value_t builtin_Math_acos(ant_t *js, ant_value_t *args, int nargs) { + double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); + if (isnan(x)) return tov(JS_NAN); + return tov(acos(x)); +} + +static ant_value_t builtin_Math_acosh(ant_t *js, ant_value_t *args, int nargs) { + double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); + if (isnan(x)) return tov(JS_NAN); + return tov(acosh(x)); +} + +static ant_value_t builtin_Math_asin(ant_t *js, ant_value_t *args, int nargs) { + double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); + if (isnan(x)) return tov(JS_NAN); + return tov(asin(x)); +} + +static ant_value_t builtin_Math_asinh(ant_t *js, ant_value_t *args, int nargs) { + double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); + if (isnan(x)) return tov(JS_NAN); + return tov(asinh(x)); +} + +static ant_value_t builtin_Math_atan(ant_t *js, ant_value_t *args, int nargs) { + double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); + if (isnan(x)) return tov(JS_NAN); + return tov(atan(x)); +} + +static ant_value_t builtin_Math_atanh(ant_t *js, ant_value_t *args, int nargs) { + double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); + if (isnan(x)) return tov(JS_NAN); + return tov(atanh(x)); +} + +static ant_value_t builtin_Math_atan2(ant_t *js, ant_value_t *args, int nargs) { + double y = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); + double x = (nargs < 2) ? JS_NAN : js_to_number(js, args[1]); + if (isnan(y) || isnan(x)) return tov(JS_NAN); + return tov(atan2(y, x)); +} + +static ant_value_t builtin_Math_cbrt(ant_t *js, ant_value_t *args, int nargs) { + double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); + if (isnan(x)) return tov(JS_NAN); + return tov(cbrt(x)); +} + +static ant_value_t builtin_Math_ceil(ant_t *js, ant_value_t *args, int nargs) { + double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); + if (isnan(x)) return tov(JS_NAN); + return tov(ceil(x)); +} + +static ant_value_t builtin_Math_clz32(ant_t *js, ant_value_t *args, int nargs) { + if (nargs < 1) return tov(32); + double x = js_to_number(js, args[0]); + if (isnan(x) || isinf(x)) return tov(32); + uint32_t n = (uint32_t)x; + if (n == 0) return tov(32); + int count = 0; + while ((n & 0x80000000U) == 0) { count++; n <<= 1; } + return tov((double)count); +} + +static ant_value_t builtin_Math_cos(ant_t *js, ant_value_t *args, int nargs) { + double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); + if (isnan(x)) return tov(JS_NAN); + return tov(cos(x)); +} + +static ant_value_t builtin_Math_cosh(ant_t *js, ant_value_t *args, int nargs) { + double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); + if (isnan(x)) return tov(JS_NAN); + return tov(cosh(x)); +} + +static ant_value_t builtin_Math_exp(ant_t *js, ant_value_t *args, int nargs) { + double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); + if (isnan(x)) return tov(JS_NAN); + return tov(exp(x)); +} + +static ant_value_t builtin_Math_expm1(ant_t *js, ant_value_t *args, int nargs) { + double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); + if (isnan(x)) return tov(JS_NAN); + return tov(expm1(x)); +} + +static ant_value_t builtin_Math_floor(ant_t *js, ant_value_t *args, int nargs) { + double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); + if (isnan(x)) return tov(JS_NAN); + return tov(floor(x)); +} + +static ant_value_t builtin_Math_fround(ant_t *js, ant_value_t *args, int nargs) { + double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); + if (isnan(x)) return tov(JS_NAN); + return tov((double)(float)x); +} + +static ant_value_t builtin_Math_hypot(ant_t *js, ant_value_t *args, int nargs) { + if (nargs == 0) return tov(0.0); + double sum = 0.0; + for (int i = 0; i < nargs; i++) { + double v = js_to_number(js, args[i]); + if (isnan(v)) return tov(JS_NAN); + sum += v * v; + } + return tov(sqrt(sum)); +} + +static int32_t toInt32(double d) { + if (isnan(d) || isinf(d) || d == 0) return 0; + double int_val = trunc(d); + double two32 = (double)(1ULL << 32); + double two31 = (double)(1ULL << 31); + double mod_val = fmod(int_val, two32); + if (mod_val < 0) mod_val += two32; + if (mod_val >= two31) mod_val -= two32; + return (int32_t)mod_val; +} + +static ant_value_t builtin_Math_imul(ant_t *js, ant_value_t *args, int nargs) { + if (nargs < 2) return tov(0); + int32_t a = toInt32(js_to_number(js, args[0])); + int32_t b = toInt32(js_to_number(js, args[1])); + return tov((double)((int32_t)((uint32_t)a * (uint32_t)b))); +} + +static ant_value_t builtin_Math_log(ant_t *js, ant_value_t *args, int nargs) { + double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); + if (isnan(x)) return tov(JS_NAN); + return tov(log(x)); +} + +static ant_value_t builtin_Math_log1p(ant_t *js, ant_value_t *args, int nargs) { + double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); + if (isnan(x)) return tov(JS_NAN); + return tov(log1p(x)); +} + +static ant_value_t builtin_Math_log10(ant_t *js, ant_value_t *args, int nargs) { + double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); + if (isnan(x)) return tov(JS_NAN); + return tov(log10(x)); +} + +static ant_value_t builtin_Math_log2(ant_t *js, ant_value_t *args, int nargs) { + double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); + if (isnan(x)) return tov(JS_NAN); + return tov(log2(x)); +} + +static ant_value_t builtin_Math_max(ant_t *js, ant_value_t *args, int nargs) { + if (nargs == 0) return tov(JS_NEG_INF); + double max_val = JS_NEG_INF; + for (int i = 0; i < nargs; i++) { + double v = js_to_number(js, args[i]); + if (isnan(v)) return tov(JS_NAN); + if (v > max_val) max_val = v; + } + return tov(max_val); +} + +static ant_value_t builtin_Math_min(ant_t *js, ant_value_t *args, int nargs) { + if (nargs == 0) return tov(JS_INF); + double min_val = JS_INF; + for (int i = 0; i < nargs; i++) { + double v = js_to_number(js, args[i]); + if (isnan(v)) return tov(JS_NAN); + if (v < min_val) min_val = v; + } + return tov(min_val); +} + +static ant_value_t builtin_Math_pow(ant_t *js, ant_value_t *args, int nargs) { + double base = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); + double exp = (nargs < 2) ? JS_NAN : js_to_number(js, args[1]); + if (isnan(base) || isnan(exp)) return tov(JS_NAN); + return tov(pow(base, exp)); +} + +static bool random_seeded = false; + +static ant_value_t builtin_Math_random(ant_t *js, ant_value_t *args, int nargs) { + (void)js; + (void)args; + (void)nargs; + if (!random_seeded) { + srand((unsigned int)time(NULL)); + random_seeded = true; + } + return tov((double)rand() / ((double)RAND_MAX + 1.0)); +} + +static ant_value_t builtin_Math_round(ant_t *js, ant_value_t *args, int nargs) { + double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); + if (isnan(x) || isinf(x)) return tov(x); + return tov(floor(x + 0.5)); +} + +static ant_value_t builtin_Math_sign(ant_t *js, ant_value_t *args, int nargs) { + double v = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); + if (isnan(v)) return tov(JS_NAN); + if (v > 0) return tov(1.0); + if (v < 0) return tov(-1.0); + return tov(v); +} + +static ant_value_t builtin_Math_sin(ant_t *js, ant_value_t *args, int nargs) { + double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); + if (isnan(x)) return tov(JS_NAN); + return tov(sin(x)); +} + +static ant_value_t builtin_Math_sinh(ant_t *js, ant_value_t *args, int nargs) { + double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); + if (isnan(x)) return tov(JS_NAN); + return tov(sinh(x)); +} + +static ant_value_t builtin_Math_sqrt(ant_t *js, ant_value_t *args, int nargs) { + double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); + if (isnan(x)) return tov(JS_NAN); + return tov(sqrt(x)); +} + +static ant_value_t builtin_Math_tan(ant_t *js, ant_value_t *args, int nargs) { + double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); + if (isnan(x)) return tov(JS_NAN); + return tov(tan(x)); +} + +static ant_value_t builtin_Math_tanh(ant_t *js, ant_value_t *args, int nargs) { + double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); + if (isnan(x)) return tov(JS_NAN); + return tov(tanh(x)); +} + +static ant_value_t builtin_Math_trunc(ant_t *js, ant_value_t *args, int nargs) { + double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); + if (isnan(x)) return tov(JS_NAN); + return tov(trunc(x)); +} + +void init_math_module(void) { + ant_t *js = rt->js; + + ant_value_t glob = js_glob(js); + ant_value_t object_proto = js->object; + ant_value_t math_obj = mkobj(js, 0); + + js_set_proto(js, math_obj, object_proto); + js_setprop(js, math_obj, js_mkstr(js, "E", 1), tov(M_E)); + js_setprop(js, math_obj, js_mkstr(js, "LN10", 4), tov(M_LN10)); + js_setprop(js, math_obj, js_mkstr(js, "LN2", 3), tov(M_LN2)); + js_setprop(js, math_obj, js_mkstr(js, "LOG10E", 6), tov(M_LOG10E)); + js_setprop(js, math_obj, js_mkstr(js, "LOG2E", 5), tov(M_LOG2E)); + js_setprop(js, math_obj, js_mkstr(js, "PI", 2), tov(M_PI)); + js_setprop(js, math_obj, js_mkstr(js, "SQRT1_2", 7), tov(M_SQRT1_2)); + js_setprop(js, math_obj, js_mkstr(js, "SQRT2", 5), tov(M_SQRT2)); + js_setprop(js, math_obj, js_mkstr(js, "abs", 3), js_mkfun(builtin_Math_abs)); + js_setprop(js, math_obj, js_mkstr(js, "acos", 4), js_mkfun(builtin_Math_acos)); + js_setprop(js, math_obj, js_mkstr(js, "acosh", 5), js_mkfun(builtin_Math_acosh)); + js_setprop(js, math_obj, js_mkstr(js, "asin", 4), js_mkfun(builtin_Math_asin)); + js_setprop(js, math_obj, js_mkstr(js, "asinh", 5), js_mkfun(builtin_Math_asinh)); + js_setprop(js, math_obj, js_mkstr(js, "atan", 4), js_mkfun(builtin_Math_atan)); + js_setprop(js, math_obj, js_mkstr(js, "atanh", 5), js_mkfun(builtin_Math_atanh)); + js_setprop(js, math_obj, js_mkstr(js, "atan2", 5), js_mkfun(builtin_Math_atan2)); + js_setprop(js, math_obj, js_mkstr(js, "cbrt", 4), js_mkfun(builtin_Math_cbrt)); + js_setprop(js, math_obj, js_mkstr(js, "ceil", 4), js_mkfun(builtin_Math_ceil)); + js_setprop(js, math_obj, js_mkstr(js, "clz32", 5), js_mkfun(builtin_Math_clz32)); + js_setprop(js, math_obj, js_mkstr(js, "cos", 3), js_mkfun(builtin_Math_cos)); + js_setprop(js, math_obj, js_mkstr(js, "cosh", 4), js_mkfun(builtin_Math_cosh)); + js_setprop(js, math_obj, js_mkstr(js, "exp", 3), js_mkfun(builtin_Math_exp)); + js_setprop(js, math_obj, js_mkstr(js, "expm1", 5), js_mkfun(builtin_Math_expm1)); + js_setprop(js, math_obj, js_mkstr(js, "floor", 5), js_mkfun(builtin_Math_floor)); + js_setprop(js, math_obj, js_mkstr(js, "fround", 6), js_mkfun(builtin_Math_fround)); + js_setprop(js, math_obj, js_mkstr(js, "hypot", 5), js_mkfun(builtin_Math_hypot)); + js_setprop(js, math_obj, js_mkstr(js, "imul", 4), js_mkfun(builtin_Math_imul)); + js_setprop(js, math_obj, js_mkstr(js, "log", 3), js_mkfun(builtin_Math_log)); + js_setprop(js, math_obj, js_mkstr(js, "log1p", 5), js_mkfun(builtin_Math_log1p)); + js_setprop(js, math_obj, js_mkstr(js, "log10", 5), js_mkfun(builtin_Math_log10)); + js_setprop(js, math_obj, js_mkstr(js, "log2", 4), js_mkfun(builtin_Math_log2)); + js_setprop(js, math_obj, js_mkstr(js, "max", 3), js_mkfun(builtin_Math_max)); + js_setprop(js, math_obj, js_mkstr(js, "min", 3), js_mkfun(builtin_Math_min)); + js_setprop(js, math_obj, js_mkstr(js, "pow", 3), js_mkfun(builtin_Math_pow)); + js_setprop(js, math_obj, js_mkstr(js, "random", 6), js_mkfun(builtin_Math_random)); + js_setprop(js, math_obj, js_mkstr(js, "round", 5), js_mkfun(builtin_Math_round)); + js_setprop(js, math_obj, js_mkstr(js, "sign", 4), js_mkfun(builtin_Math_sign)); + js_setprop(js, math_obj, js_mkstr(js, "sin", 3), js_mkfun(builtin_Math_sin)); + js_setprop(js, math_obj, js_mkstr(js, "sinh", 4), js_mkfun(builtin_Math_sinh)); + js_setprop(js, math_obj, js_mkstr(js, "sqrt", 4), js_mkfun(builtin_Math_sqrt)); + js_setprop(js, math_obj, js_mkstr(js, "tan", 3), js_mkfun(builtin_Math_tan)); + js_setprop(js, math_obj, js_mkstr(js, "tanh", 4), js_mkfun(builtin_Math_tanh)); + js_setprop(js, math_obj, js_mkstr(js, "trunc", 5), js_mkfun(builtin_Math_trunc)); + js_setprop(js, glob, js_mkstr(js, "Math", 4), math_obj); +} diff --git a/src/silver/ops/arithmetic.h b/src/silver/ops/arithmetic.h index 1fa0412..d33aeec 100644 --- a/src/silver/ops/arithmetic.h +++ b/src/silver/ops/arithmetic.h @@ -4,7 +4,9 @@ #include #include "tokens.h" #include "errors.h" + #include "silver/engine.h" +#include "modules/bigint.h" static inline ant_value_t sv_op_add(sv_vm_t *vm, ant_t *js) { ant_value_t r = vm->stack[--vm->sp]; diff --git a/src/silver/ops/bitwise.h b/src/silver/ops/bitwise.h index d736f16..fa7c66f 100644 --- a/src/silver/ops/bitwise.h +++ b/src/silver/ops/bitwise.h @@ -3,6 +3,7 @@ #include "errors.h" #include "silver/engine.h" +#include "modules/bigint.h" static inline ant_value_t sv_op_band(sv_vm_t *vm, ant_t *js) { ant_value_t r = vm->stack[--vm->sp]; diff --git a/src/silver/ops/comparison.h b/src/silver/ops/comparison.h index c700b13..ac7b6ed 100644 --- a/src/silver/ops/comparison.h +++ b/src/silver/ops/comparison.h @@ -2,6 +2,7 @@ #define SV_COMPARISON_H #include "silver/engine.h" +#include "modules/bigint.h" static inline void sv_op_seq(sv_vm_t *vm, ant_t *js) { ant_value_t r = vm->stack[--vm->sp]; -- 2.51.2