DataView setters, fresh-ArrayBuffer erasure, Object.is, and the en-US Intl slice

- The DataView setter family lands end-to-end; fresh ArrayBuffers erase into zero-filled typed-array views; resizable/shared surfaces fence by name
- Object.is compiles with the spec's SameValue across every kind, including union operands
- Composed en-US Intl.NumberFormat and toLocaleString print byte-exact through the Ryu digit core's shortest-round-trip rounding, 30k-double fuzz-verified; the rest of Intl fences by name with reasons
- DurationFormat deliberately fences: Node throws V8-internal Temporal errors with embedded source paths on plain inputs

# Conflicts:
#	packages/compiler/test/ts7/baselines/order-parity.json
This commit is contained in:
Chris Tate
2026-07-23 22:20:44 -05:00
29 changed files with 1292 additions and 197 deletions
+43
View File
@@ -312,6 +312,49 @@ double scr_dataview_get(const ScrBytes *b, double byte_off, ScrDataViewGet kind,
return 0; /* unreachable */
}
void scr_dataview_set(ScrBytes *b, double byte_off, double value, ScrDataViewGet kind, bool le) {
size_t width = scr_dataview_get_size(kind);
/* ToIndex + the view-relative bounds check — the getters' ONE message. */
double off = (byte_off != byte_off) ? 0 : trunc(byte_off);
if (!(off >= 0) || off > 9007199254740991.0 || off + (double)width > (double)b->len) {
static const char msg[] = "Offset is outside the bounds of the DataView";
scr_throw_error_msg(SCR_ERR_RANGE, msg, sizeof msg - 1);
return;
}
/* Coerce to the stored bit pattern. The integer kinds share ToUint32's
* 2^32 residue (signed/unsigned store the same bits; narrower widths
* take the low bytes — 2^width divides 2^32, so the residues agree);
* F32 rounds double→float to nearest even, exactly the spec. */
uint64_t u = 0;
switch (kind) {
case SCR_DV_U8:
case SCR_DV_I8:
case SCR_DV_U16:
case SCR_DV_I16:
case SCR_DV_U32:
case SCR_DV_I32:
u = (uint64_t)scr_bytes_to_u32(value);
break;
case SCR_DV_F32: {
float f = (float)value;
uint32_t bits;
memcpy(&bits, &f, 4);
u = bits;
break;
}
case SCR_DV_F64:
memcpy(&u, &value, 8);
break;
default:
return; /* BIG kinds never lower (bigint arguments) */
}
/* Scatter host-independently; le false is the JS big-endian default. */
uint8_t *p = b->data + (size_t)off;
for (size_t i = 0; i < width; i++) {
p[le ? i : width - 1 - i] = (uint8_t)(u >> (8 * i));
}
}
/* ── encodings (u8 only — the compiler routes only u8 receivers here) ──── */
static const char scr_hex_digits[] = "0123456789abcdef";
+98
View File
@@ -3486,6 +3486,104 @@ ScrStr *scr_num_to_fixed0(double x) {
return r;
}
/* Increment a decimal digit string in place. Returns true on overflow —
* the value becomes 1 followed by len zeros (the caller folds the zeros
* into its scale); an EMPTY string increments to "1" the same way (the
* round-up-from-nothing case: 0.0005 at 3 fraction digits). */
static bool scr_dec_inc(char *d, int len) {
for (int i = len - 1; i >= 0; i--) {
if (d[i] != '9') {
d[i]++;
return false;
}
d[i] = '0';
}
d[0] = '1';
return true;
}
/* Intl.NumberFormat("en-US").format(x) / x.toLocaleString("en-US") with
* DEFAULT options: decimal notation, minimum 0 / maximum 3 fraction
* digits, "," grouping every three integer digits, "∞"/"NaN" texts, and
* "-0" whenever the input is negative or negative zero even after
* rounding to zero. Rounding is half-up ON THE SHORTEST ROUND-TRIPPING
* DECIMAL — ICU's rounding input, probed against Node: format(1.0005) is
* "1.001" although the double is 1.000499... and toFixed(3) answers
* "1.000"; format(1e23) prints the shortest form's trailing zeros, not
* the double's exact expansion. The en-US/latn symbols (",", ".", "∞",
* "NaN", group size 3) are the whole embedded locale surface. Verified
* differentially against Node. Result +1; never throws. */
ScrStr *scr_intl_num_format_en_us(double x) {
if (isnan(x)) return scr_str_new("NaN", 3);
if (isinf(x)) {
return x < 0 ? scr_str_new("-\xE2\x88\x9E", 4) : scr_str_new("\xE2\x88\x9E", 3);
}
bool neg = signbit(x) != 0;
if (x == 0) return neg ? scr_str_new("-0", 2) : scr_str_new("0", 1);
double a = neg ? -x : x;
/* Shortest digits: value = 0.d × 10^n (no trailing zeros, k ≤ 17). */
char d[18];
int n;
int k = scr_f64_digits(a, d, &n);
/* Round at 3 fraction digits: fraction position p is digit index
* n+p-1, so index n+3 is the first DROPPED digit. Half-up on the
* decimal digits — the shortest string ends right after them, so
* "first dropped digit ≥ 5" IS the whole decision. */
int keep = n + 3;
if (keep < k) {
bool up = keep >= 0 && d[keep] >= '5';
k = keep < 0 ? 0 : keep;
if (up && scr_dec_inc(d, k)) {
/* Carried out (all nines, or the round-up-from-nothing 0.0005
* case): one leading 1, the dropped nines fold into the scale. */
k = 1;
n += 1;
} else if (k == 0) {
/* Everything rounded away: ±0 with the sign preserved. */
return neg ? scr_str_new("-0", 2) : scr_str_new("0", 1);
}
}
/* Assemble: integer digits (indices [0, n)), zero-padded past k, then
* the ≤ 3 fraction digits (indices n..n+2, '0' outside [0, k)) with
* trailing zeros trimmed, then commas every three integer digits. */
char frac[3];
int flen = 0;
for (int p = 1; p <= 3; p++) {
int idx = n + p - 1;
frac[flen++] = (idx >= 0 && idx < k) ? d[idx] : '0';
}
while (flen > 0 && frac[flen - 1] == '0') flen--;
char out[512];
int o = 0;
if (neg) out[o++] = '-';
if (n <= 0) {
out[o++] = '0';
} else {
for (int i = 0; i < n; i++) {
if (i > 0 && (n - i) % 3 == 0) out[o++] = ',';
out[o++] = (i < k) ? d[i] : '0';
}
}
if (flen > 0) {
out[o++] = '.';
memcpy(out + o, frac, (size_t)flen);
o += flen;
}
return scr_str_new(out, (size_t)o);
}
/* Object.is over two numbers — the spec's SameValue on doubles: NaN
* equals NaN, +0 differs from -0, everything else is ==. */
bool scr_num_same_value(double a, double b) {
if (a != a) return b != b;
if (a == 0 && b == 0) return signbit(a) == signbit(b);
return a == b;
}
bool scr_num_is_nan(double x) { return isnan(x) != 0; }
bool scr_num_is_integer(double x) { return isfinite(x) && trunc(x) == x; }
+29 -19
View File
@@ -24,27 +24,19 @@
* unchanged. Provides d2d(), d2d_small_int(), decimalLength17(), div10(). */
#include "../vendor/ryu/d2s.c"
size_t scr_f64_to_str(double x, char *buf) {
if (isnan(x)) return (size_t)(stpcpy(buf, "NaN") - buf);
if (x == 0) return (size_t)(stpcpy(buf, "0") - buf); /* covers -0 */
if (isinf(x)) {
return (size_t)(stpcpy(buf, x < 0 ? "-Infinity" : "Infinity") - buf);
}
char *out = buf;
if (x < 0) {
*out++ = '-';
x = -x;
}
/* Shortest round-tripping digits via Ryū. Mirrors d2s_buffered_n's
* dispatch: the exact small-integer fast path first (trailing decimal
* zeros folded into the exponent), the full algorithm otherwise. */
/* The Ryū digit core, shared by the ECMA placement below and the Intl
* en-US number formatter (scr_lib.c): the shortest round-tripping digit
* string for a positive finite double — value = 0.digits × 10^n with no
* trailing zeros. Returns k (the digit count, ≤ 17); digits is
* NUL-terminated. Mirrors d2s_buffered_n's dispatch: the exact
* small-integer fast path first (trailing decimal zeros folded into the
* exponent), the full algorithm otherwise. */
int scr_f64_digits(double x, char digits[18], int *n_out) {
uint64_t bits;
memcpy(&bits, &x, sizeof bits);
const uint64_t ieeeMantissa = bits & ((1ull << DOUBLE_MANTISSA_BITS) - 1);
const uint32_t ieeeExponent =
(uint32_t)(bits >> DOUBLE_MANTISSA_BITS); /* sign already stripped */
(uint32_t)(bits >> DOUBLE_MANTISSA_BITS); /* sign must be stripped */
floating_decimal_64 v;
if (d2d_small_int(ieeeMantissa, ieeeExponent, &v)) {
for (;;) {
@@ -60,9 +52,8 @@ size_t scr_f64_to_str(double x, char *buf) {
/* Ryū's (mantissa, exponent) → ECMA's (digits, k, n): the k mantissa
* digits have no trailing zeros, and value = 0.digits * 10^n. */
char digits[18];
int k = (int)decimalLength17(v.mantissa);
int n = v.exponent + k;
*n_out = v.exponent + k;
digits[k] = '\0';
uint64_t m = v.mantissa;
for (int i = k - 1; i >= 0; i--) {
@@ -70,6 +61,25 @@ size_t scr_f64_to_str(double x, char *buf) {
digits[i] = (char)('0' + (uint32_t)m - 10 * (uint32_t)q);
m = q;
}
return k;
}
size_t scr_f64_to_str(double x, char *buf) {
if (isnan(x)) return (size_t)(stpcpy(buf, "NaN") - buf);
if (x == 0) return (size_t)(stpcpy(buf, "0") - buf); /* covers -0 */
if (isinf(x)) {
return (size_t)(stpcpy(buf, x < 0 ? "-Infinity" : "Infinity") - buf);
}
char *out = buf;
if (x < 0) {
*out++ = '-';
x = -x;
}
char digits[18];
int n;
int k = scr_f64_digits(x, digits, &n);
if (k <= n && n <= 21) {
/* Integer: digits followed by n-k zeros. */
+26
View File
@@ -3896,6 +3896,12 @@ void scr_jsval_cast_fail(ScrJsval *v, const ScrStr *target);
*/
size_t scr_f64_to_str(double x, char *buf);
/* The Ryū digit core (scr_number.c), shared with the Intl en-US number
* formatter: the shortest round-tripping digits of a POSITIVE finite
* double — value = 0.digits × 10^n, no trailing zeros, NUL-terminated.
* Returns k, the digit count (≤ 17). */
int scr_f64_digits(double x, char digits[18], int *n_out);
/* ToString for template literals / string coercion. Returns +1. */
ScrStr *scr_f64_to_scrstr(double x);
ScrStr *scr_bool_to_scrstr(bool b); /* interned "true"/"false" */
@@ -3927,6 +3933,18 @@ bool scr_num_is_safe_integer(double x);
ScrStr *scr_num_to_exponential(double x);
ScrStr *scr_num_to_fixed0(double x);
/* Object.is over two numbers — the spec's SameValue on doubles: NaN
* equals NaN, +0 differs from -0, everything else is ==. Never throws. */
bool scr_num_same_value(double a, double b);
/* Intl.NumberFormat("en-US").format(x) / x.toLocaleString("en-US") with
* default options: decimal notation, 0–3 fraction digits rounded half-up
* on the shortest round-tripping decimal (ICU's rounding input — NOT
* toFixed's exact-value rounding), "," grouping every three integer
* digits, "∞"/"NaN" texts, "-0" for negative inputs rounding to zero.
* en-US is the one embedded locale. Result +1; never throws. */
ScrStr *scr_intl_num_format_en_us(double x);
/* ── Date, the composed slice (scr_lib.c) ─────────────────────────────
* Date values have no representation; the runtime surface is exactly
* Date.now() and toISOString over a millisecond time value. */
@@ -4081,6 +4099,14 @@ typedef enum ScrDataViewGet {
} ScrDataViewGet;
double scr_dataview_get(const ScrBytes *b, double byte_off, ScrDataViewGet kind, bool le);
/* DataView setters (the integer/float kinds only — the BIG kinds never
* lower: bigint arguments have no representation). Values coerce
* JS-exactly: the integer kinds by modular truncation (ToUint32's residue
* — the narrower widths store its low bytes, the same 2^width residue),
* F32 by double→float round-to-nearest-even. Offsets go through ToIndex
* with the getters' one RangeError. */
void scr_dataview_set(ScrBytes *b, double byte_off, double value, ScrDataViewGet kind, bool le);
/* Element read/write. Any invalid index — negative, fractional, NaN, or
* out of bounds — TRAPS like the array runtime (SEMANTICS.md documents
* the divergence from JS's undefined-read/ignored-write). Writes coerce