@scriptc/runtime 0.0.8 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/scr_bytes.c +58 -12
- package/src/scr_bytes_io.c +97 -1
- package/src/scr_dyn_handle.c +8 -66
- package/src/scr_error.c +15 -0
- package/src/scr_events_emitter.c +34 -0
- package/src/scr_exception.c +4 -1
- package/src/scr_island.c +29 -1
- package/src/scr_json.c +156 -0
- package/src/scr_lib.c +101 -3
- package/src/scr_library.c +105 -2
- package/src/scr_number.c +29 -19
- package/src/scr_runtime.h +122 -5
- package/src/scr_stream.c +49 -2
package/package.json
CHANGED
package/src/scr_bytes.c
CHANGED
|
@@ -312,6 +312,49 @@ double scr_dataview_get(const ScrBytes *b, double byte_off, ScrDataViewGet kind,
|
|
|
312
312
|
return 0; /* unreachable */
|
|
313
313
|
}
|
|
314
314
|
|
|
315
|
+
void scr_dataview_set(ScrBytes *b, double byte_off, double value, ScrDataViewGet kind, bool le) {
|
|
316
|
+
size_t width = scr_dataview_get_size(kind);
|
|
317
|
+
/* ToIndex + the view-relative bounds check — the getters' ONE message. */
|
|
318
|
+
double off = (byte_off != byte_off) ? 0 : trunc(byte_off);
|
|
319
|
+
if (!(off >= 0) || off > 9007199254740991.0 || off + (double)width > (double)b->len) {
|
|
320
|
+
static const char msg[] = "Offset is outside the bounds of the DataView";
|
|
321
|
+
scr_throw_error_msg(SCR_ERR_RANGE, msg, sizeof msg - 1);
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
/* Coerce to the stored bit pattern. The integer kinds share ToUint32's
|
|
325
|
+
* 2^32 residue (signed/unsigned store the same bits; narrower widths
|
|
326
|
+
* take the low bytes — 2^width divides 2^32, so the residues agree);
|
|
327
|
+
* F32 rounds double→float to nearest even, exactly the spec. */
|
|
328
|
+
uint64_t u = 0;
|
|
329
|
+
switch (kind) {
|
|
330
|
+
case SCR_DV_U8:
|
|
331
|
+
case SCR_DV_I8:
|
|
332
|
+
case SCR_DV_U16:
|
|
333
|
+
case SCR_DV_I16:
|
|
334
|
+
case SCR_DV_U32:
|
|
335
|
+
case SCR_DV_I32:
|
|
336
|
+
u = (uint64_t)scr_bytes_to_u32(value);
|
|
337
|
+
break;
|
|
338
|
+
case SCR_DV_F32: {
|
|
339
|
+
float f = (float)value;
|
|
340
|
+
uint32_t bits;
|
|
341
|
+
memcpy(&bits, &f, 4);
|
|
342
|
+
u = bits;
|
|
343
|
+
break;
|
|
344
|
+
}
|
|
345
|
+
case SCR_DV_F64:
|
|
346
|
+
memcpy(&u, &value, 8);
|
|
347
|
+
break;
|
|
348
|
+
default:
|
|
349
|
+
return; /* BIG kinds never lower (bigint arguments) */
|
|
350
|
+
}
|
|
351
|
+
/* Scatter host-independently; le false is the JS big-endian default. */
|
|
352
|
+
uint8_t *p = b->data + (size_t)off;
|
|
353
|
+
for (size_t i = 0; i < width; i++) {
|
|
354
|
+
p[le ? i : width - 1 - i] = (uint8_t)(u >> (8 * i));
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
315
358
|
/* ── encodings (u8 only — the compiler routes only u8 receivers here) ──── */
|
|
316
359
|
|
|
317
360
|
static const char scr_hex_digits[] = "0123456789abcdef";
|
|
@@ -886,16 +929,19 @@ ScrBytes *scr_bytes_from_arr(ScrBytesElem elem, const ScrArr *arr) {
|
|
|
886
929
|
|
|
887
930
|
static size_t scr_bytes_received(double v, char out[48]); /* the numeric section below */
|
|
888
931
|
|
|
889
|
-
/* validateOffset(name, 0, max): non-integers are 'an integer'
|
|
890
|
-
*
|
|
891
|
-
*
|
|
892
|
-
|
|
893
|
-
|
|
932
|
+
/* validateOffset(name, 0, max): non-integers are 'an integer' (Number.
|
|
933
|
+
* isInteger — ±Infinity render 'an integer' too), the rest '>= 0 && <=
|
|
934
|
+
* max' (Node spells '&&' here, unlike the read/write families' 'and').
|
|
935
|
+
* max < 0 means copy's no-upper-bound '>= 0' render. Exported: the
|
|
936
|
+
* checked-dynamic compare/equals validators (scr_bytes_io.c) run the
|
|
937
|
+
* same ladder after their own type gate. */
|
|
938
|
+
bool scr_bytes_validate_off(const char *name, double value, double max) {
|
|
939
|
+
if (isfinite(value) && floor(value) == value && value >= 0 && (max < 0 || value <= max)) return true;
|
|
894
940
|
char recv[48];
|
|
895
941
|
scr_bytes_received(value, recv);
|
|
896
942
|
char msg[160];
|
|
897
943
|
int mlen;
|
|
898
|
-
if (floor(value) != value) {
|
|
944
|
+
if (floor(value) != value || !isfinite(value)) {
|
|
899
945
|
mlen = snprintf(msg, sizeof msg,
|
|
900
946
|
"The value of \"%s\" is out of range. It must be an integer. Received %s",
|
|
901
947
|
name, recv);
|
|
@@ -909,7 +955,7 @@ static bool scr_bytes_validate_off(const char *name, double value, double max) {
|
|
|
909
955
|
"The value of \"%s\" is out of range. It must be >= 0 && <= %s. Received %s",
|
|
910
956
|
name, maxb, recv);
|
|
911
957
|
}
|
|
912
|
-
|
|
958
|
+
scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)mlen, "ERR_OUT_OF_RANGE");
|
|
913
959
|
return false;
|
|
914
960
|
}
|
|
915
961
|
|
|
@@ -994,7 +1040,7 @@ static ScrBytes *scr_bytes_fill_core(ScrBytes *b, const uint8_t *pat, size_t pat
|
|
|
994
1040
|
bool zero_ok, double nargs, double offset, double end) {
|
|
995
1041
|
if (patn == 0 && !zero_ok) {
|
|
996
1042
|
static const char msg[] = "The argument 'value' is invalid. Received <Buffer >";
|
|
997
|
-
|
|
1043
|
+
scr_throw_error_msg_code(SCR_ERR_TYPE, msg, sizeof msg - 1, "ERR_INVALID_ARG_VALUE");
|
|
998
1044
|
return NULL;
|
|
999
1045
|
}
|
|
1000
1046
|
size_t n = (size_t)nargs;
|
|
@@ -1065,7 +1111,7 @@ ScrBytes *scr_bytes_swap(ScrBytes *b, double width) {
|
|
|
1065
1111
|
if (b->len % w != 0) {
|
|
1066
1112
|
char msg[64];
|
|
1067
1113
|
int mlen = snprintf(msg, sizeof msg, "Buffer size must be a multiple of %zu-bits", w * 8);
|
|
1068
|
-
|
|
1114
|
+
scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)mlen, "ERR_INVALID_BUFFER_SIZE");
|
|
1069
1115
|
return NULL;
|
|
1070
1116
|
}
|
|
1071
1117
|
for (size_t g = 0; g < b->len; g += w) {
|
|
@@ -1244,7 +1290,7 @@ static void scr_bytes_bounds_error(double value, double length, const char *type
|
|
|
1244
1290
|
type ? type : "offset", recv);
|
|
1245
1291
|
} else if (length < 0) {
|
|
1246
1292
|
static const char oob[] = "Attempt to access memory outside buffer bounds";
|
|
1247
|
-
|
|
1293
|
+
scr_throw_error_msg_code(SCR_ERR_RANGE, oob, sizeof oob - 1, "ERR_BUFFER_OUT_OF_BOUNDS");
|
|
1248
1294
|
return;
|
|
1249
1295
|
} else {
|
|
1250
1296
|
char lenbuf[32];
|
|
@@ -1253,7 +1299,7 @@ static void scr_bytes_bounds_error(double value, double length, const char *type
|
|
|
1253
1299
|
"The value of \"%s\" is out of range. It must be >= %d and <= %s. Received %s",
|
|
1254
1300
|
type ? type : "offset", type ? 1 : 0, lenbuf, recv);
|
|
1255
1301
|
}
|
|
1256
|
-
|
|
1302
|
+
scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)mlen, "ERR_OUT_OF_RANGE");
|
|
1257
1303
|
}
|
|
1258
1304
|
|
|
1259
1305
|
/* The shared offset gate: width bytes at offset must lie inside b. */
|
|
@@ -1297,7 +1343,7 @@ static bool scr_bytes_check_int(const ScrBytes *b, double value, double offset,
|
|
|
1297
1343
|
"The value of \"value\" is out of range. It must be >= %s and <= %s. Received %s",
|
|
1298
1344
|
minb, maxb, recv);
|
|
1299
1345
|
}
|
|
1300
|
-
|
|
1346
|
+
scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)mlen, "ERR_OUT_OF_RANGE");
|
|
1301
1347
|
return false;
|
|
1302
1348
|
}
|
|
1303
1349
|
return scr_bytes_rw_check(b, offset, width);
|
package/src/scr_bytes_io.c
CHANGED
|
@@ -7,9 +7,11 @@
|
|
|
7
7
|
#include "scr_runtime.h"
|
|
8
8
|
|
|
9
9
|
#include <errno.h>
|
|
10
|
+
#include <math.h>
|
|
10
11
|
#include <stdio.h>
|
|
11
12
|
#include <stdlib.h>
|
|
12
13
|
#include <string.h>
|
|
14
|
+
#include <time.h>
|
|
13
15
|
|
|
14
16
|
static void scr_bytes_io_oom(void) {
|
|
15
17
|
scr_trap("scriptc: out of memory\n");
|
|
@@ -138,7 +140,7 @@ ScrBytes *scr_crypto_random_bytes(double n) {
|
|
|
138
140
|
msg, sizeof msg,
|
|
139
141
|
"The value of \"size\" is out of range. It must be >= 0 && <= 2147483647. Received %.*s",
|
|
140
142
|
(int)numlen, num);
|
|
141
|
-
|
|
143
|
+
scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)mlen, "ERR_OUT_OF_RANGE");
|
|
142
144
|
return NULL;
|
|
143
145
|
}
|
|
144
146
|
ScrBytes *b = scr_bytes_new(SCR_BYTES_U8, n);
|
|
@@ -158,3 +160,97 @@ bool scr_process_stderr_write_bytes(const ScrBytes *b) {
|
|
|
158
160
|
fwrite(b->data, 1, b->len * scr_bytes_elem_size(b->elem), stderr);
|
|
159
161
|
return true;
|
|
160
162
|
}
|
|
163
|
+
|
|
164
|
+
/* ── the checked-dynamic Buffer compare/equals validators ──────────────
|
|
165
|
+
* Node's argument ladders for buf.equals / buf.compare / Buffer.compare
|
|
166
|
+
* over DOM-boxed arguments (the invalid-input probes: string needles,
|
|
167
|
+
* '0' offsets, null/object range args). A well-typed dyn still computes
|
|
168
|
+
* the real answer — validation, not a constant fence. */
|
|
169
|
+
|
|
170
|
+
/* A bytes payload or the API's own ERR_INVALID_ARG_TYPE (borrowed). */
|
|
171
|
+
static ScrBytes *scr_bytes_chk_u8(const ScrDyn *d, const char *argname) {
|
|
172
|
+
if (d->kind != SCR_DYN_BYTES) {
|
|
173
|
+
scr_dyn_arg_type_fail(argname, "an instance of Buffer or Uint8Array", d);
|
|
174
|
+
return NULL;
|
|
175
|
+
}
|
|
176
|
+
return d->v.bytes;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
double scr_buffer_compare_chk(const ScrDyn *a, const ScrDyn *b) {
|
|
180
|
+
ScrBytes *b1 = scr_bytes_chk_u8(a, "buf1");
|
|
181
|
+
if (!b1) return 0;
|
|
182
|
+
ScrBytes *b2 = scr_bytes_chk_u8(b, "buf2");
|
|
183
|
+
if (!b2) return 0;
|
|
184
|
+
return scr_bytes_compare(b1, b2, 0, 0, 0, 0, 0);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
bool scr_bytes_equals_chk(const ScrBytes *recv, const ScrDyn *other) {
|
|
188
|
+
ScrBytes *o = scr_bytes_chk_u8(other, "otherBuffer");
|
|
189
|
+
if (!o) return false;
|
|
190
|
+
return scr_bytes_equals(recv, o);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/* One offset slot: undefined takes the Node default, non-numbers throw
|
|
194
|
+
* ERR_INVALID_ARG_TYPE "of type number", numbers run validateOffset. */
|
|
195
|
+
static bool scr_bytes_chk_off(const ScrDyn *d, const char *name, double max,
|
|
196
|
+
double dflt, double *out) {
|
|
197
|
+
if (d->kind == SCR_DYN_UNDEF) {
|
|
198
|
+
*out = dflt;
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
if (d->kind != SCR_DYN_NUM) {
|
|
202
|
+
scr_dyn_arg_type_fail(name, "of type number", d);
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
*out = d->v.num;
|
|
206
|
+
return scr_bytes_validate_off(name, *out, max);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
double scr_bytes_compare_chk(const ScrBytes *src, const ScrDyn *target,
|
|
210
|
+
const ScrDyn *ts, const ScrDyn *te,
|
|
211
|
+
const ScrDyn *ss, const ScrDyn *se) {
|
|
212
|
+
ScrBytes *t = scr_bytes_chk_u8(target, "target");
|
|
213
|
+
if (!t) return 0;
|
|
214
|
+
double tsv, tev, ssv, sev;
|
|
215
|
+
if (!scr_bytes_chk_off(ts, "targetStart", 9007199254740991.0, 0, &tsv)) return 0;
|
|
216
|
+
if (!scr_bytes_chk_off(te, "targetEnd", (double)t->len, (double)t->len, &tev)) return 0;
|
|
217
|
+
if (!scr_bytes_chk_off(ss, "sourceStart", 9007199254740991.0, 0, &ssv)) return 0;
|
|
218
|
+
if (!scr_bytes_chk_off(se, "sourceEnd", (double)src->len, (double)src->len, &sev)) return 0;
|
|
219
|
+
/* Every slot validated or defaulted above; nargs 4 revalidates the
|
|
220
|
+
* now-known-good numbers (a no-op) and keeps one comparison core. */
|
|
221
|
+
return scr_bytes_compare(src, t, 4, tsv, tev, ssv, sev);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/* new Buffer(number, encoding) — the deprecated ctor's string arm with a
|
|
225
|
+
* non-string first argument: Node's exact ERR_INVALID_ARG_TYPE (the
|
|
226
|
+
* throwing path never fires DEP0005, so compiled silence matches). */
|
|
227
|
+
ScrBytes *scr_buffer_new_string_fail(const ScrDyn *got) {
|
|
228
|
+
scr_dyn_arg_type_fail("string", "of type string", got);
|
|
229
|
+
return NULL;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/* fs._toUnixTimestamp — the seconds coercion the utimes family runs on
|
|
233
|
+
* its time arguments (fs.js's toUnixTimestamp, underscore-exported):
|
|
234
|
+
* numeric STRINGS pass ToNumber's loose-equality gate (+time == time —
|
|
235
|
+
* whitespace-only strings answer 0), finite numbers pass (negatives
|
|
236
|
+
* answer now/1000, Node's "past times" shape), everything else throws
|
|
237
|
+
* Node's exact ERR_INVALID_ARG_TYPE. Borrowed; the throw is pending on
|
|
238
|
+
* the dummy 0 return. */
|
|
239
|
+
double scr_fs_to_unix_timestamp(const ScrDyn *t) {
|
|
240
|
+
if (t->kind == SCR_DYN_STR) {
|
|
241
|
+
double n = scr_string_to_number(t->v.str);
|
|
242
|
+
/* +time == time: NaN fails; every parsed number loosely equals its
|
|
243
|
+
* own source string by construction of ToNumber. */
|
|
244
|
+
if (n == n) return n;
|
|
245
|
+
}
|
|
246
|
+
if (t->kind == SCR_DYN_NUM && isfinite(t->v.num)) {
|
|
247
|
+
if (t->v.num < 0) {
|
|
248
|
+
struct timespec ts;
|
|
249
|
+
clock_gettime(CLOCK_REALTIME, &ts);
|
|
250
|
+
return ((double)ts.tv_sec * 1000.0 + (double)ts.tv_nsec / 1e6) / 1000.0;
|
|
251
|
+
}
|
|
252
|
+
return t->v.num;
|
|
253
|
+
}
|
|
254
|
+
scr_dyn_arg_type_fail("time", "an instance of Date or an Time in seconds", t);
|
|
255
|
+
return 0;
|
|
256
|
+
}
|
package/src/scr_dyn_handle.c
CHANGED
|
@@ -1,77 +1,19 @@
|
|
|
1
1
|
/* The checked-dynamic HANDLE support unit — everything the handle
|
|
2
2
|
* dispatchers (scr_http.c / scr_net.c) and the emitter unit's dyn
|
|
3
|
-
* registrations share BEYOND the DOM core:
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
3
|
+
* registrations share BEYOND the DOM core: the listener gate over the
|
|
4
|
+
* ERR_INVALID_ARG_TYPE throwers (the throwers themselves live in
|
|
5
|
+
* scr_json.c beside the DOM core — the always-linked bytes/fs argument
|
|
6
|
+
* validators call them too), and the runtime-built listener adapter
|
|
7
|
+
* closures whose fire thunks box event tuples back into the DOM. Split
|
|
8
|
+
* out of scr_json.c so handle-free binaries keep their exact size
|
|
9
|
+
* class: cc.ts compiles this unit exactly when a user of it links (the
|
|
10
|
+
* net or emitter gate — http implies net).
|
|
10
11
|
*/
|
|
11
12
|
#include "scr_runtime.h"
|
|
12
13
|
|
|
13
14
|
#include <stdio.h>
|
|
14
15
|
#include <string.h>
|
|
15
16
|
|
|
16
|
-
/* errors.js's determineSpecificType over a DOM value — the "Received
|
|
17
|
-
* ..." tail of Node's ERR_INVALID_ARG_TYPE messages. Renders into buf
|
|
18
|
-
* when the shape needs a payload; returns the text either way. */
|
|
19
|
-
const char *scr_dyn_specific_type(const ScrDyn *cb, char *detail, size_t cap) {
|
|
20
|
-
const char *d = detail;
|
|
21
|
-
switch (cb->kind) {
|
|
22
|
-
case SCR_DYN_NULL: d = "null"; break;
|
|
23
|
-
case SCR_DYN_UNDEF: d = "undefined"; break;
|
|
24
|
-
case SCR_DYN_OBJ: d = "an instance of Object"; break;
|
|
25
|
-
case SCR_DYN_ARR: d = "an instance of Array"; break;
|
|
26
|
-
case SCR_DYN_BYTES: d = "an instance of Uint8Array"; break;
|
|
27
|
-
case SCR_DYN_FUNC: d = "function"; break; /* callers usually return before this */
|
|
28
|
-
case SCR_DYN_HANDLE:
|
|
29
|
-
snprintf(detail, cap, "an instance of %s", scr_dyn_handle_cls(cb));
|
|
30
|
-
break;
|
|
31
|
-
case SCR_DYN_PROMISE: d = "an instance of Promise"; break;
|
|
32
|
-
case SCR_DYN_BOOL:
|
|
33
|
-
snprintf(detail, cap, "type boolean (%s)", cb->v.b ? "true" : "false");
|
|
34
|
-
break;
|
|
35
|
-
case SCR_DYN_NUM: {
|
|
36
|
-
char num[32];
|
|
37
|
-
size_t n = scr_f64_to_str(cb->v.num, num);
|
|
38
|
-
snprintf(detail, cap, "type number (%.*s)", (int)n, num);
|
|
39
|
-
break;
|
|
40
|
-
}
|
|
41
|
-
case SCR_DYN_STR: {
|
|
42
|
-
const ScrStr *sv = cb->v.str;
|
|
43
|
-
char insp[32];
|
|
44
|
-
size_t n = 0;
|
|
45
|
-
insp[n++] = '\'';
|
|
46
|
-
for (size_t i = 0; i < sv->len && n < 28; i++) insp[n++] = sv->data[i];
|
|
47
|
-
if (sv->len + 2 > 28) {
|
|
48
|
-
n = 25;
|
|
49
|
-
memcpy(insp + n, "...", 3);
|
|
50
|
-
n += 3;
|
|
51
|
-
} else {
|
|
52
|
-
insp[n++] = '\'';
|
|
53
|
-
}
|
|
54
|
-
snprintf(detail, cap, "type string (%.*s)", (int)n, insp);
|
|
55
|
-
break;
|
|
56
|
-
}
|
|
57
|
-
default: d = "an instance of Object"; break;
|
|
58
|
-
}
|
|
59
|
-
return d;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/* Node's ERR_INVALID_ARG_TYPE thrower ("The \"chunk\" argument must be
|
|
63
|
-
* of type string or an instance of Buffer or Uint8Array. Received type
|
|
64
|
-
* number (5)") — the handle dispatchers' per-arg gates. `expected` is
|
|
65
|
-
* the full "of type ..."/"an instance of ..." clause. */
|
|
66
|
-
void scr_dyn_arg_type_fail(const char *argname, const char *expected, const ScrDyn *got) {
|
|
67
|
-
char detail[64];
|
|
68
|
-
const char *d = scr_dyn_specific_type(got, detail, sizeof detail);
|
|
69
|
-
char msg[224];
|
|
70
|
-
int len = snprintf(msg, sizeof msg,
|
|
71
|
-
"The \"%s\" argument must be %s. Received %s", argname, expected, d);
|
|
72
|
-
scr_throw_error_msg_code(SCR_ERR_TYPE, msg, (size_t)len, "ERR_INVALID_ARG_TYPE");
|
|
73
|
-
}
|
|
74
|
-
|
|
75
17
|
/* Node's ERR_INVALID_ARG_TYPE listener gate (errors.js's
|
|
76
18
|
* determineSpecificType shapes — the scr_emitter_check_listener wording,
|
|
77
19
|
* shared here so the gated units need not link the emitter unit). */
|
package/src/scr_error.c
CHANGED
|
@@ -237,6 +237,21 @@ void scr_throw_error_msg_code(int kind, const char *message, size_t len,
|
|
|
237
237
|
scr_error_traced ? &scr_error_trace : NULL);
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
+
/* A compiler-resolved Node-parity throw (the always-throwing lowered
|
|
241
|
+
* arms: ERR_INVALID_THIS receivers, ERR_MISSING_ARGS ladders, the
|
|
242
|
+
* symbol-to-string TypeError): builds the builtin error of `kind` with
|
|
243
|
+
* `code` stamped when non-empty. Borrows both strings; always throws
|
|
244
|
+
* (catchably — call sites are compiler-emitted pending checks). */
|
|
245
|
+
void scr_throw_node_coded(double kind, const ScrStr *code, const ScrStr *msg) {
|
|
246
|
+
ScrError *e = scr_error_new((int)kind, (ScrStr *)msg); /* borrowed in */
|
|
247
|
+
if (code->len > 0) {
|
|
248
|
+
scr_str_release(e->code); /* NULL-safe */
|
|
249
|
+
e->code = scr_str_retain((ScrStr *)code);
|
|
250
|
+
}
|
|
251
|
+
scr_throw_obj(e, &scr_error_retain_v, &scr_error_release_v,
|
|
252
|
+
scr_error_traced ? &scr_error_trace : NULL);
|
|
253
|
+
}
|
|
254
|
+
|
|
240
255
|
/* A read of a `declare`d const nothing defines (the bundler-define
|
|
241
256
|
* pattern — __VERSION__): Node running the source throws ReferenceError
|
|
242
257
|
* "<name> is not defined" at the access. Borrows `name`; always throws
|
package/src/scr_events_emitter.c
CHANGED
|
@@ -662,6 +662,18 @@ ScrArr *scr_emitter_listeners(ScrEmitter *em, ScrStr *name) {
|
|
|
662
662
|
|
|
663
663
|
/* ── max listeners ────────────────────────────────────────────────────── */
|
|
664
664
|
|
|
665
|
+
/* The checked-dynamic setMaxListeners ladder: non-numbers throw Node's
|
|
666
|
+
* ERR_INVALID_ARG_TYPE ("The \"setMaxListeners\" argument must be of
|
|
667
|
+
* type number. Received ..."), numbers run the range gate below.
|
|
668
|
+
* Borrowed dyn; +1 receiver either way (the pending check unwinds). */
|
|
669
|
+
ScrEmitter *scr_emitter_set_max_chk(ScrEmitter *em, const ScrDyn *n) {
|
|
670
|
+
if (n->kind != SCR_DYN_NUM) {
|
|
671
|
+
scr_dyn_arg_type_fail("setMaxListeners", "of type number", n);
|
|
672
|
+
return scr_emitter_retain(em);
|
|
673
|
+
}
|
|
674
|
+
return scr_emitter_set_max(em, n->v.num);
|
|
675
|
+
}
|
|
676
|
+
|
|
665
677
|
ScrEmitter *scr_emitter_set_max(ScrEmitter *em, double n) {
|
|
666
678
|
if (!(n >= 0)) { /* negatives and NaN — Node's validateNumber */
|
|
667
679
|
char num[32];
|
|
@@ -682,6 +694,28 @@ double scr_emitter_get_max(ScrEmitter *em) {
|
|
|
682
694
|
return scr_emitter_default_max;
|
|
683
695
|
}
|
|
684
696
|
|
|
697
|
+
/* The checked-dynamic defaultMaxListeners ladder — `name` picks the
|
|
698
|
+
* message's slot ("setMaxListeners" for the static call,
|
|
699
|
+
* "defaultMaxListeners" for the module-property assignment, Node's own
|
|
700
|
+
* split). Borrowed. */
|
|
701
|
+
void scr_emitter_set_default_max_chk(const ScrDyn *n, const ScrStr *name) {
|
|
702
|
+
if (n->kind != SCR_DYN_NUM) {
|
|
703
|
+
scr_dyn_arg_type_fail(name->data, "of type number", n);
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
if (!(n->v.num >= 0)) {
|
|
707
|
+
char num[32];
|
|
708
|
+
size_t nlen = scr_f64_to_str(n->v.num, num);
|
|
709
|
+
char msg[128];
|
|
710
|
+
int len = snprintf(msg, sizeof msg,
|
|
711
|
+
"The value of \"%s\" is out of range. It must be >= 0. Received %.*s",
|
|
712
|
+
name->data, (int)nlen, num);
|
|
713
|
+
scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)len, "ERR_OUT_OF_RANGE");
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
scr_emitter_set_default_max(n->v.num);
|
|
717
|
+
}
|
|
718
|
+
|
|
685
719
|
void scr_emitter_set_default_max(double n) {
|
|
686
720
|
if (!(n >= 0)) { /* negatives and NaN — Node's validateNumber(n, "setMaxListeners", 0) */
|
|
687
721
|
char num[32];
|
package/src/scr_exception.c
CHANGED
|
@@ -181,7 +181,10 @@ ScrStr *scr_caught_to_string(const ScrCaught *c) {
|
|
|
181
181
|
* contract-shaped failure exactly like a trap. Render the same "Uncaught
|
|
182
182
|
* ..." first line the executable epilogue prints (scr_exc_print_uncaught's
|
|
183
183
|
* arms, buffer-writing), release the payload, and route the text through
|
|
184
|
-
* the trap funnel — one failure channel at the boundary.
|
|
184
|
+
* the trap funnel — one failure channel at the boundary. The funnel
|
|
185
|
+
* (scr_library.c) assembles the rendered line into the structured
|
|
186
|
+
* trap-teaching form (code SC4013, the trapping entry's symbol) before
|
|
187
|
+
* delivery; only the 0x01-led verbatim path below bypasses assembly. */
|
|
185
188
|
void scr_library_check_exc(void) {
|
|
186
189
|
if (!scr_exc_pending()) return;
|
|
187
190
|
static char buf[1024]; /* the message is copied out before the payload dies */
|
package/src/scr_island.c
CHANGED
|
@@ -379,6 +379,7 @@ enum {
|
|
|
379
379
|
ISL_H_DESTRCHECK,
|
|
380
380
|
ISL_H_ITERN,
|
|
381
381
|
ISL_H_ITER,
|
|
382
|
+
ISL_H_CALLSPREAD,
|
|
382
383
|
ISL_H_COUNT,
|
|
383
384
|
};
|
|
384
385
|
|
|
@@ -417,7 +418,17 @@ static const char isl_prelude[] =
|
|
|
417
418
|
"else if(typeof v===\"number\")d=\"number \"+v;else if(typeof v===\"boolean\")d=\"boolean \"+v;"
|
|
418
419
|
"else if(typeof v===\"function\")d=\"function\";else d=\"object\";"
|
|
419
420
|
"throw new TypeError(d+\" is not iterable (cannot read property Symbol(Symbol.iterator))\")}"
|
|
420
|
-
"return v[Symbol.iterator]()}
|
|
421
|
+
"return v[Symbol.iterator]()},"
|
|
422
|
+
/* ISL_H_CALLSPREAD: spread application (`f(...pre, ...s)` — the
|
|
423
|
+
* rest-forwarding idiom's call): REAL spread syntax, so iterator
|
|
424
|
+
* protocols are the engine's own; the guards front-run V8's exact
|
|
425
|
+
* spread-call TypeError texts (nullish spells the spread expression
|
|
426
|
+
* `w`, everything else the generic Spread-syntax text). */
|
|
427
|
+
"(f,p,s,w)=>{if(s===undefined||s===null)"
|
|
428
|
+
"throw new TypeError(w+\" is not iterable (cannot read property \"+s+\")\");"
|
|
429
|
+
"if(typeof s[Symbol.iterator]!==\"function\")"
|
|
430
|
+
"throw new TypeError(\"Spread syntax requires ...iterable[Symbol.iterator] to be a function\");"
|
|
431
|
+
"return f(...p,...s)}]";
|
|
421
432
|
|
|
422
433
|
static void isl_free_boot(void);
|
|
423
434
|
static void isl_prom_wraps_teardown(void);
|
|
@@ -1274,6 +1285,23 @@ ScrJsval *scr_jsval_call(ScrJsval *f, int argc, ScrJsval **argv) {
|
|
|
1274
1285
|
return isl_cell_new(r);
|
|
1275
1286
|
}
|
|
1276
1287
|
|
|
1288
|
+
/* Spread application on an island callee (jsOp callSpread) — the prelude
|
|
1289
|
+
* helper's real `f(...pre, ...spread)`, so iterator protocols are the
|
|
1290
|
+
* engine's own and the guards front-run V8's exact spread-call TypeError
|
|
1291
|
+
* texts (`what` is the spread expression's source spelling). Borrows
|
|
1292
|
+
* everything; +1 out, or NULL with the engine exception bridged. */
|
|
1293
|
+
ScrJsval *scr_jsval_call_spread(ScrJsval *f, ScrJsval *pre, ScrJsval *spread, const ScrStr *what) {
|
|
1294
|
+
isl_entry();
|
|
1295
|
+
JSValue argv[4] = {f->v, pre->v, spread->v, JS_NewStringLen(isl_ctx, what->data, what->len)};
|
|
1296
|
+
JSValue r = JS_Call(isl_ctx, isl_helpers[ISL_H_CALLSPREAD], JS_UNDEFINED, 4, argv);
|
|
1297
|
+
JS_FreeValue(isl_ctx, argv[3]);
|
|
1298
|
+
if (JS_IsException(r)) {
|
|
1299
|
+
isl_bridge_exception();
|
|
1300
|
+
return NULL;
|
|
1301
|
+
}
|
|
1302
|
+
return isl_cell_new(r);
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1277
1305
|
/* `new X(...)` on an island callee (jsOp construct). Borrows everything;
|
|
1278
1306
|
* +1 out, or NULL with the engine exception bridged. */
|
|
1279
1307
|
ScrJsval *scr_jsval_construct(ScrJsval *f, int argc, ScrJsval **argv) {
|
package/src/scr_json.c
CHANGED
|
@@ -424,6 +424,52 @@ void scr_dyn_arr_push(ScrDyn *arr, ScrDyn *item) {
|
|
|
424
424
|
arr->v.arr.items[arr->v.arr.len++] = item; /* ownership moves in */
|
|
425
425
|
}
|
|
426
426
|
|
|
427
|
+
/* Spread completion for a runtime-arity argument list (`f(...xs)` in the
|
|
428
|
+
* checked-dynamic tier): JS's spread over the DOM's iterable kinds —
|
|
429
|
+
* arrays element-by-element (retained), strings by code POINT (the string
|
|
430
|
+
* iterator; astral chars arrive unsplit), bytes by byte; every other kind
|
|
431
|
+
* throws V8's exact SPREAD-CALL TypeError (catchable, pending — callers
|
|
432
|
+
* check): nullish sources spell the spread expression (`what`) — "v is
|
|
433
|
+
* not iterable (cannot read property undefined)" — and everything else is
|
|
434
|
+
* the generic "Spread syntax requires ...iterable[Symbol.iterator] to be
|
|
435
|
+
* a function". Borrows src. */
|
|
436
|
+
void scr_dyn_arr_push_spread(ScrDyn *arr, const ScrDyn *src, const char *what) {
|
|
437
|
+
if (src->kind == SCR_DYN_ARR) {
|
|
438
|
+
for (size_t i = 0; i < src->v.arr.len; i++) {
|
|
439
|
+
scr_dyn_arr_push(arr, scr_dyn_retain(src->v.arr.items[i]));
|
|
440
|
+
}
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
if (src->kind == SCR_DYN_BYTES) {
|
|
444
|
+
for (size_t i = 0; i < src->v.bytes->len; i++) {
|
|
445
|
+
scr_dyn_arr_push(arr, scr_dyn_new_num((double)src->v.bytes->data[i]));
|
|
446
|
+
}
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
if (src->kind == SCR_DYN_STR) {
|
|
450
|
+
double len = scr_str_utf16_len(src->v.str);
|
|
451
|
+
for (double at = 0; at < len;) {
|
|
452
|
+
ScrStr *cp = scr_str_cp_at(src->v.str, at);
|
|
453
|
+
at += scr_str_utf16_len(cp);
|
|
454
|
+
scr_dyn_arr_push(arr, scr_dyn_new_str(cp));
|
|
455
|
+
scr_str_release(cp);
|
|
456
|
+
}
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
if (src->kind == SCR_DYN_UNDEF || src->kind == SCR_DYN_NULL) {
|
|
460
|
+
ScrJsonBuf b;
|
|
461
|
+
scr_jb_init(&b);
|
|
462
|
+
scr_jb_puts(&b, what);
|
|
463
|
+
scr_jb_puts(&b, " is not iterable (cannot read property ");
|
|
464
|
+
scr_jb_puts(&b, src->kind == SCR_DYN_UNDEF ? "undefined" : "null");
|
|
465
|
+
scr_jb_puts(&b, ")");
|
|
466
|
+
scr_throw_error(SCR_ERR_TYPE, scr_jb_finish(&b));
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
static const char msg[] = "Spread syntax requires ...iterable[Symbol.iterator] to be a function";
|
|
470
|
+
scr_throw_error_msg(SCR_ERR_TYPE, msg, sizeof msg - 1);
|
|
471
|
+
}
|
|
472
|
+
|
|
427
473
|
/* Takes ownership of key (malloc'd) and value. Duplicate keys: the LATER
|
|
428
474
|
* value wins (like JS JSON.parse) — the old value is released and the new
|
|
429
475
|
* key buffer freed (the surviving entry keeps its original, equal key). */
|
|
@@ -551,6 +597,13 @@ ScrDyn *scr_dyn_call(const ScrDyn *d, ScrDyn *const *args, size_t argc, const ch
|
|
|
551
597
|
return d->v.fn.thunk(d->v.fn.clo, args, argc);
|
|
552
598
|
}
|
|
553
599
|
|
|
600
|
+
/* scr_dyn_call over a DOM ARRAY's elements — the spread-application form
|
|
601
|
+
* (`f(...args)` after the emitted argument array is built). Borrows both;
|
|
602
|
+
* result owned (+1), or NULL with the exception pending. */
|
|
603
|
+
ScrDyn *scr_dyn_apply(const ScrDyn *d, const ScrDyn *args, const char *what) {
|
|
604
|
+
return scr_dyn_call(d, args->v.arr.items, args->v.arr.len, what);
|
|
605
|
+
}
|
|
606
|
+
|
|
554
607
|
/* ── native handles in the DOM (SCR_DYN_HANDLE) ───────────────────────
|
|
555
608
|
* Per-tag ops stamped by the owning units at main() (scr_http_dyn_install
|
|
556
609
|
* / scr_net_dyn_install — the scr_net_install hook story), so this
|
|
@@ -582,6 +635,76 @@ const ScrDynHandleOps *scr_dyn_handle_ops_of(const ScrDyn *d) {
|
|
|
582
635
|
return scr_dyn_handle_ops(d->v.handle.tag);
|
|
583
636
|
}
|
|
584
637
|
|
|
638
|
+
/* errors.js's determineSpecificType over a DOM value — the "Received
|
|
639
|
+
* ..." tail of Node's ERR_INVALID_ARG_TYPE messages. Renders into buf
|
|
640
|
+
* when the shape needs a payload; returns the text either way. Lives
|
|
641
|
+
* beside the DOM core (not the gated handle unit) because the always-
|
|
642
|
+
* linked argument validators (bytes, fs) render through it too. */
|
|
643
|
+
const char *scr_dyn_specific_type(const ScrDyn *cb, char *detail, size_t cap) {
|
|
644
|
+
const char *d = detail;
|
|
645
|
+
switch (cb->kind) {
|
|
646
|
+
case SCR_DYN_NULL: d = "null"; break;
|
|
647
|
+
case SCR_DYN_UNDEF: d = "undefined"; break;
|
|
648
|
+
case SCR_DYN_OBJ: d = "an instance of Object"; break;
|
|
649
|
+
case SCR_DYN_ARR: d = "an instance of Array"; break;
|
|
650
|
+
case SCR_DYN_BYTES: d = "an instance of Uint8Array"; break;
|
|
651
|
+
case SCR_DYN_FUNC: d = "function"; break; /* callers usually return before this */
|
|
652
|
+
case SCR_DYN_HANDLE:
|
|
653
|
+
snprintf(detail, cap, "an instance of %s", scr_dyn_handle_cls(cb));
|
|
654
|
+
break;
|
|
655
|
+
case SCR_DYN_PROMISE: d = "an instance of Promise"; break;
|
|
656
|
+
case SCR_DYN_BOOL:
|
|
657
|
+
snprintf(detail, cap, "type boolean (%s)", cb->v.b ? "true" : "false");
|
|
658
|
+
break;
|
|
659
|
+
case SCR_DYN_NUM: {
|
|
660
|
+
char num[32];
|
|
661
|
+
size_t n = scr_f64_to_str(cb->v.num, num);
|
|
662
|
+
snprintf(detail, cap, "type number (%.*s)", (int)n, num);
|
|
663
|
+
break;
|
|
664
|
+
}
|
|
665
|
+
case SCR_DYN_STR: {
|
|
666
|
+
const ScrStr *sv = cb->v.str;
|
|
667
|
+
char insp[32];
|
|
668
|
+
size_t n = 0;
|
|
669
|
+
insp[n++] = '\'';
|
|
670
|
+
for (size_t i = 0; i < sv->len && n < 28; i++) insp[n++] = sv->data[i];
|
|
671
|
+
if (sv->len + 2 > 28) {
|
|
672
|
+
n = 25;
|
|
673
|
+
memcpy(insp + n, "...", 3);
|
|
674
|
+
n += 3;
|
|
675
|
+
} else {
|
|
676
|
+
insp[n++] = '\'';
|
|
677
|
+
}
|
|
678
|
+
snprintf(detail, cap, "type string (%.*s)", (int)n, insp);
|
|
679
|
+
break;
|
|
680
|
+
}
|
|
681
|
+
default: d = "an instance of Object"; break;
|
|
682
|
+
}
|
|
683
|
+
return d;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
/* Node's ERR_INVALID_ARG_TYPE thrower ("The \"chunk\" argument must be
|
|
687
|
+
* of type string or an instance of Buffer or Uint8Array. Received type
|
|
688
|
+
* number (5)") — the handle dispatchers' and argument validators'
|
|
689
|
+
* per-arg gates. `expected` is the full "of type ..."/"an instance of
|
|
690
|
+
* ..." clause. */
|
|
691
|
+
/* The compiler-resolved ERR_INVALID_ARG_TYPE throw with a RUNTIME-
|
|
692
|
+
* rendered Received tail (error.argTypeThrow — the always-throwing
|
|
693
|
+
* lowered arms whose offending value is not a literal). Borrows all
|
|
694
|
+
* three; always throws catchably. */
|
|
695
|
+
void scr_throw_arg_type(const ScrStr *argname, const ScrStr *expected, const ScrDyn *got) {
|
|
696
|
+
scr_dyn_arg_type_fail(argname->data, expected->data, got);
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
void scr_dyn_arg_type_fail(const char *argname, const char *expected, const ScrDyn *got) {
|
|
700
|
+
char detail[64];
|
|
701
|
+
const char *d = scr_dyn_specific_type(got, detail, sizeof detail);
|
|
702
|
+
char msg[224];
|
|
703
|
+
int len = snprintf(msg, sizeof msg,
|
|
704
|
+
"The \"%s\" argument must be %s. Received %s", argname, expected, d);
|
|
705
|
+
scr_throw_error_msg_code(SCR_ERR_TYPE, msg, (size_t)len, "ERR_INVALID_ARG_TYPE");
|
|
706
|
+
}
|
|
707
|
+
|
|
585
708
|
static void scr_dyn_handle_release(void *h, ScrDynHandleTag tag) {
|
|
586
709
|
scr_dyn_handle_ops(tag)->release(h);
|
|
587
710
|
}
|
|
@@ -1066,6 +1189,39 @@ ScrStr *scr_dyn_string_coerce(const ScrDyn *d) {
|
|
|
1066
1189
|
return scr_dyn_to_string(d, NULL);
|
|
1067
1190
|
}
|
|
1068
1191
|
|
|
1192
|
+
/* JS ToString over a DOM value WITH the object protocol (the WHATWG
|
|
1193
|
+
* USVString conversions — URLSearchParams names/values): an OBJ whose
|
|
1194
|
+
* own 'toString' member is callable is invoked with zero arguments (its
|
|
1195
|
+
* throw propagates, catchably); a non-primitive answer falls through to
|
|
1196
|
+
* 'valueOf' (ToPrimitive's string hint); exhaustion is the spec's
|
|
1197
|
+
* "Cannot convert object to primitive value" TypeError. Every other
|
|
1198
|
+
* kind matches scr_dyn_string_coerce (units RENDER — ToString(null) is
|
|
1199
|
+
* "null"). Borrows; +1, or NULL with the exception pending. */
|
|
1200
|
+
ScrStr *scr_dyn_string_coerce_js(const ScrDyn *d) {
|
|
1201
|
+
if (d->kind == SCR_DYN_OBJ) {
|
|
1202
|
+
static const char *const hint[2] = { "toString", "valueOf" };
|
|
1203
|
+
for (int i = 0; i < 2; i++) {
|
|
1204
|
+
ScrDyn *m = scr_dyn_obj_get(d, hint[i], strlen(hint[i])); /* borrowed */
|
|
1205
|
+
if (!m || m->kind != SCR_DYN_FUNC) continue;
|
|
1206
|
+
ScrDyn *r = scr_dyn_call(m, NULL, 0, hint[i]);
|
|
1207
|
+
if (!r) return NULL; /* the method threw — pending */
|
|
1208
|
+
if (r->kind == SCR_DYN_OBJ || r->kind == SCR_DYN_ARR ||
|
|
1209
|
+
r->kind == SCR_DYN_FUNC || r->kind == SCR_DYN_HANDLE ||
|
|
1210
|
+
r->kind == SCR_DYN_PROMISE) {
|
|
1211
|
+
scr_dyn_release(r); /* non-primitive answer: try the next method */
|
|
1212
|
+
continue;
|
|
1213
|
+
}
|
|
1214
|
+
ScrStr *s = scr_dyn_string_coerce(r);
|
|
1215
|
+
scr_dyn_release(r);
|
|
1216
|
+
return s;
|
|
1217
|
+
}
|
|
1218
|
+
static const char msg[] = "Cannot convert object to primitive value";
|
|
1219
|
+
scr_throw_error_msg(SCR_ERR_TYPE, msg, sizeof msg - 1);
|
|
1220
|
+
return NULL;
|
|
1221
|
+
}
|
|
1222
|
+
return scr_dyn_string_coerce(d);
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1069
1225
|
/* The checked-dynamic keyed WRITE (`h.k = v` on a dyn receiver): OBJ sets
|
|
1070
1226
|
* the member (later writes win, insertion order — JS); undefined/null
|
|
1071
1227
|
* throws Node's "Cannot set properties of ..."; every other kind throws
|
package/src/scr_lib.c
CHANGED
|
@@ -1647,7 +1647,7 @@ double scr_fs_read_sync(double fd, ScrBytes *buf, double offset, double length)
|
|
|
1647
1647
|
mlen = snprintf(msg, sizeof msg,
|
|
1648
1648
|
"The value of \"offset\" is out of range. It must be >= 0 && <= 9007199254740991. Received %s",
|
|
1649
1649
|
numbuf);
|
|
1650
|
-
|
|
1650
|
+
scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)mlen, "ERR_OUT_OF_RANGE");
|
|
1651
1651
|
return 0;
|
|
1652
1652
|
}
|
|
1653
1653
|
size_t off = (size_t)offset;
|
|
@@ -1656,7 +1656,7 @@ double scr_fs_read_sync(double fd, ScrBytes *buf, double offset, double length)
|
|
|
1656
1656
|
mlen = snprintf(msg, sizeof msg,
|
|
1657
1657
|
"The value of \"length\" is out of range. It must be <= %zu. Received %s",
|
|
1658
1658
|
bytelen - off, numbuf);
|
|
1659
|
-
|
|
1659
|
+
scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)mlen, "ERR_OUT_OF_RANGE");
|
|
1660
1660
|
return 0;
|
|
1661
1661
|
}
|
|
1662
1662
|
size_t want = (size_t)length;
|
|
@@ -2528,7 +2528,7 @@ ScrStr *scr_crypto_random_string(double n, ScrStr *enc) {
|
|
|
2528
2528
|
int mlen = snprintf(msg, sizeof msg,
|
|
2529
2529
|
"The value of \"size\" is out of range. It must be >= 0 && <= 2147483647. Received %.*s",
|
|
2530
2530
|
(int)numlen, num);
|
|
2531
|
-
|
|
2531
|
+
scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)mlen, "ERR_OUT_OF_RANGE");
|
|
2532
2532
|
return NULL;
|
|
2533
2533
|
}
|
|
2534
2534
|
size_t size = (size_t)n;
|
|
@@ -3486,6 +3486,104 @@ ScrStr *scr_num_to_fixed0(double x) {
|
|
|
3486
3486
|
return r;
|
|
3487
3487
|
}
|
|
3488
3488
|
|
|
3489
|
+
/* Increment a decimal digit string in place. Returns true on overflow —
|
|
3490
|
+
* the value becomes 1 followed by len zeros (the caller folds the zeros
|
|
3491
|
+
* into its scale); an EMPTY string increments to "1" the same way (the
|
|
3492
|
+
* round-up-from-nothing case: 0.0005 at 3 fraction digits). */
|
|
3493
|
+
static bool scr_dec_inc(char *d, int len) {
|
|
3494
|
+
for (int i = len - 1; i >= 0; i--) {
|
|
3495
|
+
if (d[i] != '9') {
|
|
3496
|
+
d[i]++;
|
|
3497
|
+
return false;
|
|
3498
|
+
}
|
|
3499
|
+
d[i] = '0';
|
|
3500
|
+
}
|
|
3501
|
+
d[0] = '1';
|
|
3502
|
+
return true;
|
|
3503
|
+
}
|
|
3504
|
+
|
|
3505
|
+
/* Intl.NumberFormat("en-US").format(x) / x.toLocaleString("en-US") with
|
|
3506
|
+
* DEFAULT options: decimal notation, minimum 0 / maximum 3 fraction
|
|
3507
|
+
* digits, "," grouping every three integer digits, "∞"/"NaN" texts, and
|
|
3508
|
+
* "-0" whenever the input is negative or negative zero even after
|
|
3509
|
+
* rounding to zero. Rounding is half-up ON THE SHORTEST ROUND-TRIPPING
|
|
3510
|
+
* DECIMAL — ICU's rounding input, probed against Node: format(1.0005) is
|
|
3511
|
+
* "1.001" although the double is 1.000499... and toFixed(3) answers
|
|
3512
|
+
* "1.000"; format(1e23) prints the shortest form's trailing zeros, not
|
|
3513
|
+
* the double's exact expansion. The en-US/latn symbols (",", ".", "∞",
|
|
3514
|
+
* "NaN", group size 3) are the whole embedded locale surface. Verified
|
|
3515
|
+
* differentially against Node. Result +1; never throws. */
|
|
3516
|
+
ScrStr *scr_intl_num_format_en_us(double x) {
|
|
3517
|
+
if (isnan(x)) return scr_str_new("NaN", 3);
|
|
3518
|
+
if (isinf(x)) {
|
|
3519
|
+
return x < 0 ? scr_str_new("-\xE2\x88\x9E", 4) : scr_str_new("\xE2\x88\x9E", 3);
|
|
3520
|
+
}
|
|
3521
|
+
bool neg = signbit(x) != 0;
|
|
3522
|
+
if (x == 0) return neg ? scr_str_new("-0", 2) : scr_str_new("0", 1);
|
|
3523
|
+
double a = neg ? -x : x;
|
|
3524
|
+
|
|
3525
|
+
/* Shortest digits: value = 0.d × 10^n (no trailing zeros, k ≤ 17). */
|
|
3526
|
+
char d[18];
|
|
3527
|
+
int n;
|
|
3528
|
+
int k = scr_f64_digits(a, d, &n);
|
|
3529
|
+
|
|
3530
|
+
/* Round at 3 fraction digits: fraction position p is digit index
|
|
3531
|
+
* n+p-1, so index n+3 is the first DROPPED digit. Half-up on the
|
|
3532
|
+
* decimal digits — the shortest string ends right after them, so
|
|
3533
|
+
* "first dropped digit ≥ 5" IS the whole decision. */
|
|
3534
|
+
int keep = n + 3;
|
|
3535
|
+
if (keep < k) {
|
|
3536
|
+
bool up = keep >= 0 && d[keep] >= '5';
|
|
3537
|
+
k = keep < 0 ? 0 : keep;
|
|
3538
|
+
if (up && scr_dec_inc(d, k)) {
|
|
3539
|
+
/* Carried out (all nines, or the round-up-from-nothing 0.0005
|
|
3540
|
+
* case): one leading 1, the dropped nines fold into the scale. */
|
|
3541
|
+
k = 1;
|
|
3542
|
+
n += 1;
|
|
3543
|
+
} else if (k == 0) {
|
|
3544
|
+
/* Everything rounded away: ±0 with the sign preserved. */
|
|
3545
|
+
return neg ? scr_str_new("-0", 2) : scr_str_new("0", 1);
|
|
3546
|
+
}
|
|
3547
|
+
}
|
|
3548
|
+
|
|
3549
|
+
/* Assemble: integer digits (indices [0, n)), zero-padded past k, then
|
|
3550
|
+
* the ≤ 3 fraction digits (indices n..n+2, '0' outside [0, k)) with
|
|
3551
|
+
* trailing zeros trimmed, then commas every three integer digits. */
|
|
3552
|
+
char frac[3];
|
|
3553
|
+
int flen = 0;
|
|
3554
|
+
for (int p = 1; p <= 3; p++) {
|
|
3555
|
+
int idx = n + p - 1;
|
|
3556
|
+
frac[flen++] = (idx >= 0 && idx < k) ? d[idx] : '0';
|
|
3557
|
+
}
|
|
3558
|
+
while (flen > 0 && frac[flen - 1] == '0') flen--;
|
|
3559
|
+
|
|
3560
|
+
char out[512];
|
|
3561
|
+
int o = 0;
|
|
3562
|
+
if (neg) out[o++] = '-';
|
|
3563
|
+
if (n <= 0) {
|
|
3564
|
+
out[o++] = '0';
|
|
3565
|
+
} else {
|
|
3566
|
+
for (int i = 0; i < n; i++) {
|
|
3567
|
+
if (i > 0 && (n - i) % 3 == 0) out[o++] = ',';
|
|
3568
|
+
out[o++] = (i < k) ? d[i] : '0';
|
|
3569
|
+
}
|
|
3570
|
+
}
|
|
3571
|
+
if (flen > 0) {
|
|
3572
|
+
out[o++] = '.';
|
|
3573
|
+
memcpy(out + o, frac, (size_t)flen);
|
|
3574
|
+
o += flen;
|
|
3575
|
+
}
|
|
3576
|
+
return scr_str_new(out, (size_t)o);
|
|
3577
|
+
}
|
|
3578
|
+
|
|
3579
|
+
/* Object.is over two numbers — the spec's SameValue on doubles: NaN
|
|
3580
|
+
* equals NaN, +0 differs from -0, everything else is ==. */
|
|
3581
|
+
bool scr_num_same_value(double a, double b) {
|
|
3582
|
+
if (a != a) return b != b;
|
|
3583
|
+
if (a == 0 && b == 0) return signbit(a) == signbit(b);
|
|
3584
|
+
return a == b;
|
|
3585
|
+
}
|
|
3586
|
+
|
|
3489
3587
|
bool scr_num_is_nan(double x) { return isnan(x) != 0; }
|
|
3490
3588
|
|
|
3491
3589
|
bool scr_num_is_integer(double x) { return isfinite(x) && trunc(x) == x; }
|
package/src/scr_library.c
CHANGED
|
@@ -40,7 +40,19 @@ void scr_library_set_sink(ScrLibSinkFn fn, void *ctx) {
|
|
|
40
40
|
* conforming survival pattern), then deliver exactly once, then abort:
|
|
41
41
|
* before registration, or if the sink returns (the ruled host-contract
|
|
42
42
|
* violation). The address is the funnel frame's return address — the trap
|
|
43
|
-
* site — 0 where the toolchain cannot supply one.
|
|
43
|
+
* site — 0 where the toolchain cannot supply one.
|
|
44
|
+
*
|
|
45
|
+
* Delivery shape (the ratified structured trap-teaching encoding): a
|
|
46
|
+
* message that already begins with the 0x01 marker — a facade-authored
|
|
47
|
+
* structured throw riding the verbatim rule, or the wrapper's compile-
|
|
48
|
+
* time-assembled SC4012 contract trap — is delivered byte-for-byte. Every
|
|
49
|
+
* OTHER message is a trap the runtime DETECTED, and the funnel assembles
|
|
50
|
+
* it here into 0x01 text 0x1F code 0x1F symbol [0x1F remediation]: the
|
|
51
|
+
* baseline human line becomes field 0 unchanged (so plain-text hosts read
|
|
52
|
+
* exactly what they always read), the code classifies the trap kind, the
|
|
53
|
+
* symbol is the entry the trapping call came through (recorded by the
|
|
54
|
+
* entry prologue below), and the remediation is the profile's for that
|
|
55
|
+
* code when the program TU's overlay table declares one. */
|
|
44
56
|
|
|
45
57
|
#if defined(__GNUC__) || defined(__clang__)
|
|
46
58
|
#define SCR_TRAP_ADDR() ((uint64_t)(uintptr_t)__builtin_return_address(0))
|
|
@@ -48,8 +60,95 @@ void scr_library_set_sink(ScrLibSinkFn fn, void *ctx) {
|
|
|
48
60
|
#define SCR_TRAP_ADDR() ((uint64_t)0)
|
|
49
61
|
#endif
|
|
50
62
|
|
|
63
|
+
/* The current-entry slot: every generated entry's prologue records its
|
|
64
|
+
* external symbol before dispatching into core code. A single static slot
|
|
65
|
+
* is sound — exactly one core is live per process (the one-live-core rule),
|
|
66
|
+
* entries never nest, and a trap can only fire while an entry is on the
|
|
67
|
+
* stack. NULL (never entered) renders as the empty symbol field. */
|
|
68
|
+
static const char *scr_library_entry_symbol = NULL;
|
|
69
|
+
|
|
70
|
+
/* Detected-trap classification: the runtime's trap sites self-classify
|
|
71
|
+
* through their message conventions (the exact bytes the executable lane
|
|
72
|
+
* prints), so the kind → code mapping keys on those prefixes. The codes are
|
|
73
|
+
* the compiler registry's runtime family (diagnostics/diagnostic.ts —
|
|
74
|
+
* documented beside SC4012); SC4019 is the family's residual for detected
|
|
75
|
+
* traps outside the named kinds (environment failures, unsupported
|
|
76
|
+
* operations, the RC audit). */
|
|
77
|
+
static const struct {
|
|
78
|
+
const char *prefix;
|
|
79
|
+
const char *code;
|
|
80
|
+
} scr_library_trap_kinds[] = {
|
|
81
|
+
{"Uncaught ", "SC4013"}, /* escaped exception at an entry */
|
|
82
|
+
{"scriptc: RangeError: ", "SC4014"}, /* range trap */
|
|
83
|
+
{"scriptc: TypeError: ", "SC4015"}, /* type trap */
|
|
84
|
+
{"scriptc: SyntaxError: ", "SC4016"}, /* syntax trap (regex compile) */
|
|
85
|
+
{"scriptc: out of memory", "SC4017"}, /* allocation failure */
|
|
86
|
+
{"scriptc: internal error: ", "SC4018"}, /* internal invariant failure */
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
static const char *scr_library_trap_code(const char *msg, size_t len) {
|
|
90
|
+
for (size_t i = 0; i < sizeof scr_library_trap_kinds / sizeof scr_library_trap_kinds[0]; i++) {
|
|
91
|
+
size_t plen = strlen(scr_library_trap_kinds[i].prefix);
|
|
92
|
+
if (len >= plen && memcmp(msg, scr_library_trap_kinds[i].prefix, plen) == 0) {
|
|
93
|
+
return scr_library_trap_kinds[i].code;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return "SC4019"; /* other detected trap */
|
|
97
|
+
}
|
|
98
|
+
|
|
51
99
|
static _Noreturn void scr_library_trap_deliver(const char *msg, size_t len, uint64_t addr) {
|
|
52
100
|
scr_library_poisoned = true;
|
|
101
|
+
if (!(len > 0 && (uint8_t)msg[0] == 0x01)) {
|
|
102
|
+
/* A detected trap: assemble the structured message. Static buffer —
|
|
103
|
+
* no malloc on the failure path; text truncates before structure ever
|
|
104
|
+
* would (codes and symbols are short; an oversized remediation drops
|
|
105
|
+
* whole, never split). */
|
|
106
|
+
static char buf[2048];
|
|
107
|
+
const char *code = scr_library_trap_code(msg, len);
|
|
108
|
+
const char *text = msg;
|
|
109
|
+
size_t text_len = len;
|
|
110
|
+
const char *rem = NULL;
|
|
111
|
+
for (size_t i = 0; i < scr_library_trap_overlays_len; i++) {
|
|
112
|
+
const char *const *t = &scr_library_trap_overlays[3 * i];
|
|
113
|
+
if (strcmp(t[0], code) == 0) {
|
|
114
|
+
if (t[1] != NULL) {
|
|
115
|
+
text = t[1];
|
|
116
|
+
text_len = strlen(t[1]);
|
|
117
|
+
}
|
|
118
|
+
rem = t[2];
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
const char *sym = scr_library_entry_symbol != NULL ? scr_library_entry_symbol : "";
|
|
123
|
+
size_t tail = 2 + strlen(code) + strlen(sym);
|
|
124
|
+
if (rem != NULL) {
|
|
125
|
+
if (tail + 1 + strlen(rem) > sizeof buf - 1) rem = NULL; /* drop whole, keep structure */
|
|
126
|
+
else tail += 1 + strlen(rem);
|
|
127
|
+
}
|
|
128
|
+
size_t n = 0;
|
|
129
|
+
buf[n++] = '\x01';
|
|
130
|
+
size_t cap = sizeof buf - 1 - tail;
|
|
131
|
+
if (text_len > cap) text_len = cap;
|
|
132
|
+
for (size_t i = 0; i < text_len; i++) {
|
|
133
|
+
/* The encoding reserves 0x01/0x1F; runtime messages never contain
|
|
134
|
+
* them, but an escaped exception's rendered text embeds user bytes. */
|
|
135
|
+
char c = text[i];
|
|
136
|
+
buf[n++] = (c == '\x01' || c == '\x1f') ? ' ' : c;
|
|
137
|
+
}
|
|
138
|
+
buf[n++] = '\x1f';
|
|
139
|
+
memcpy(buf + n, code, strlen(code));
|
|
140
|
+
n += strlen(code);
|
|
141
|
+
buf[n++] = '\x1f';
|
|
142
|
+
memcpy(buf + n, sym, strlen(sym));
|
|
143
|
+
n += strlen(sym);
|
|
144
|
+
if (rem != NULL) {
|
|
145
|
+
buf[n++] = '\x1f';
|
|
146
|
+
memcpy(buf + n, rem, strlen(rem));
|
|
147
|
+
n += strlen(rem);
|
|
148
|
+
}
|
|
149
|
+
msg = buf;
|
|
150
|
+
len = n;
|
|
151
|
+
}
|
|
53
152
|
if (scr_library_sink != NULL) {
|
|
54
153
|
scr_library_sink(scr_library_sink_ctx, (const uint8_t *)msg, len, addr);
|
|
55
154
|
}
|
|
@@ -80,7 +179,11 @@ __attribute__((noinline)) _Noreturn void scr_trap_fmt(const char *fmt, ...) {
|
|
|
80
179
|
|
|
81
180
|
/* ── entry prologues ──────────────────────────────────────────────────── */
|
|
82
181
|
|
|
83
|
-
void scr_library_entry(bool reset_arena) {
|
|
182
|
+
void scr_library_entry(bool reset_arena, const char *entry_symbol) {
|
|
183
|
+
/* Record the entry symbol FIRST so even a poisoned-abort's core dump
|
|
184
|
+
* names the entry; a trap anywhere below (the arena reset's OOM
|
|
185
|
+
* included) then reports the right symbol. */
|
|
186
|
+
scr_library_entry_symbol = entry_symbol;
|
|
84
187
|
/* A poisoned library's entries abort deterministically — never through the
|
|
85
188
|
* sink again (it received its exactly-once message when the trap fired),
|
|
86
189
|
* never into a heap whose invariants already failed. */
|
package/src/scr_number.c
CHANGED
|
@@ -24,27 +24,19 @@
|
|
|
24
24
|
* unchanged. Provides d2d(), d2d_small_int(), decimalLength17(), div10(). */
|
|
25
25
|
#include "../vendor/ryu/d2s.c"
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if (x < 0) {
|
|
36
|
-
*out++ = '-';
|
|
37
|
-
x = -x;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/* Shortest round-tripping digits via Ryū. Mirrors d2s_buffered_n's
|
|
41
|
-
* dispatch: the exact small-integer fast path first (trailing decimal
|
|
42
|
-
* zeros folded into the exponent), the full algorithm otherwise. */
|
|
27
|
+
/* The Ryū digit core, shared by the ECMA placement below and the Intl
|
|
28
|
+
* en-US number formatter (scr_lib.c): the shortest round-tripping digit
|
|
29
|
+
* string for a positive finite double — value = 0.digits × 10^n with no
|
|
30
|
+
* trailing zeros. Returns k (the digit count, ≤ 17); digits is
|
|
31
|
+
* NUL-terminated. Mirrors d2s_buffered_n's dispatch: the exact
|
|
32
|
+
* small-integer fast path first (trailing decimal zeros folded into the
|
|
33
|
+
* exponent), the full algorithm otherwise. */
|
|
34
|
+
int scr_f64_digits(double x, char digits[18], int *n_out) {
|
|
43
35
|
uint64_t bits;
|
|
44
36
|
memcpy(&bits, &x, sizeof bits);
|
|
45
37
|
const uint64_t ieeeMantissa = bits & ((1ull << DOUBLE_MANTISSA_BITS) - 1);
|
|
46
38
|
const uint32_t ieeeExponent =
|
|
47
|
-
(uint32_t)(bits >> DOUBLE_MANTISSA_BITS); /* sign
|
|
39
|
+
(uint32_t)(bits >> DOUBLE_MANTISSA_BITS); /* sign must be stripped */
|
|
48
40
|
floating_decimal_64 v;
|
|
49
41
|
if (d2d_small_int(ieeeMantissa, ieeeExponent, &v)) {
|
|
50
42
|
for (;;) {
|
|
@@ -60,9 +52,8 @@ size_t scr_f64_to_str(double x, char *buf) {
|
|
|
60
52
|
|
|
61
53
|
/* Ryū's (mantissa, exponent) → ECMA's (digits, k, n): the k mantissa
|
|
62
54
|
* digits have no trailing zeros, and value = 0.digits * 10^n. */
|
|
63
|
-
char digits[18];
|
|
64
55
|
int k = (int)decimalLength17(v.mantissa);
|
|
65
|
-
|
|
56
|
+
*n_out = v.exponent + k;
|
|
66
57
|
digits[k] = '\0';
|
|
67
58
|
uint64_t m = v.mantissa;
|
|
68
59
|
for (int i = k - 1; i >= 0; i--) {
|
|
@@ -70,6 +61,25 @@ size_t scr_f64_to_str(double x, char *buf) {
|
|
|
70
61
|
digits[i] = (char)('0' + (uint32_t)m - 10 * (uint32_t)q);
|
|
71
62
|
m = q;
|
|
72
63
|
}
|
|
64
|
+
return k;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
size_t scr_f64_to_str(double x, char *buf) {
|
|
68
|
+
if (isnan(x)) return (size_t)(stpcpy(buf, "NaN") - buf);
|
|
69
|
+
if (x == 0) return (size_t)(stpcpy(buf, "0") - buf); /* covers -0 */
|
|
70
|
+
if (isinf(x)) {
|
|
71
|
+
return (size_t)(stpcpy(buf, x < 0 ? "-Infinity" : "Infinity") - buf);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
char *out = buf;
|
|
75
|
+
if (x < 0) {
|
|
76
|
+
*out++ = '-';
|
|
77
|
+
x = -x;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
char digits[18];
|
|
81
|
+
int n;
|
|
82
|
+
int k = scr_f64_digits(x, digits, &n);
|
|
73
83
|
|
|
74
84
|
if (k <= n && n <= 21) {
|
|
75
85
|
/* Integer: digits followed by n-k zeros. */
|
package/src/scr_runtime.h
CHANGED
|
@@ -48,8 +48,10 @@ void scr_init(void);
|
|
|
48
48
|
* the host-registered panic sink and abort only as the last resort: before
|
|
49
49
|
* registration, or if the sink returns (the ruled host-contract violation —
|
|
50
50
|
* a conforming sink longjmps to a host frame BELOW the entry, never back
|
|
51
|
-
* into library frames). Messages keep their trailing newline in both lanes
|
|
52
|
-
* the funnel
|
|
51
|
+
* into library frames). Messages keep their trailing newline in both lanes;
|
|
52
|
+
* the library funnel additionally assembles every DETECTED trap into the
|
|
53
|
+
* structured trap-teaching form before delivery (a message that already
|
|
54
|
+
* begins with the 0x01 marker passes verbatim) — see ScrLibSinkFn below. */
|
|
53
55
|
_Noreturn void scr_trap(const char *msg);
|
|
54
56
|
_Noreturn void scr_trap_fmt(const char *fmt, ...);
|
|
55
57
|
|
|
@@ -82,7 +84,17 @@ typedef struct ScrBytes ScrBytes;
|
|
|
82
84
|
* diagnostic code, 2 the trapping symbol as the host linked it, 3 the
|
|
83
85
|
* remediation; a missing or empty field means none; ignore any field past
|
|
84
86
|
* the fourth. Fields are (pointer, length) — never assume NUL termination.
|
|
85
|
-
* A plain-text host may print the whole buffer: the teaching leads it.
|
|
87
|
+
* A plain-text host may print the whole buffer: the teaching leads it.
|
|
88
|
+
*
|
|
89
|
+
* Every trap the runtime DETECTS arrives structured: the funnel assembles
|
|
90
|
+
* the baseline human line into field 0 unchanged, a stable code for the
|
|
91
|
+
* trap kind (the compiler registry's SC4013–SC4019 runtime family,
|
|
92
|
+
* classified in scr_library.c), the entry symbol recorded by the trapping
|
|
93
|
+
* entry's prologue, and the profile's remediation for that code when the
|
|
94
|
+
* program TU's overlay table declares one (the whole fourth field is
|
|
95
|
+
* absent otherwise). A message that already begins with the marker — a
|
|
96
|
+
* facade-authored structured throw, or the wrapper's compile-time-
|
|
97
|
+
* assembled SC4012 contract trap — passes through byte-for-byte. */
|
|
86
98
|
typedef void (*ScrLibSinkFn)(void *ctx, const uint8_t *msg, size_t msg_len,
|
|
87
99
|
uint64_t address);
|
|
88
100
|
void scr_library_set_sink(ScrLibSinkFn fn, void *ctx); /* latest wins */
|
|
@@ -90,8 +102,15 @@ void scr_library_set_sink(ScrLibSinkFn fn, void *ctx); /* latest wins */
|
|
|
90
102
|
/* Entry prologue: aborts deterministically when the library is poisoned (a
|
|
91
103
|
* trap already fired — no profile entry may run again; recovery is process
|
|
92
104
|
* restart). reset_arena additionally drops the result arena (the
|
|
93
|
-
* auto-reset posture, and the reset/collect entries' shared body).
|
|
94
|
-
|
|
105
|
+
* auto-reset posture, and the reset/collect entries' shared body).
|
|
106
|
+
* entry_symbol is the generated entry's external symbol exactly as the
|
|
107
|
+
* host linked it (a static string in the program TU): the prologue records
|
|
108
|
+
* it in the funnel's current-entry slot so a detected trap's structured
|
|
109
|
+
* message can name the trapping entry — sound as a single static slot
|
|
110
|
+
* because exactly one core is ever live and entries never nest. Init and
|
|
111
|
+
* the mode entries (reset, collect) record theirs too; the identity
|
|
112
|
+
* getters and sink registration touch no runtime and never trap. */
|
|
113
|
+
void scr_library_entry(bool reset_arena, const char *entry_symbol);
|
|
95
114
|
void scr_library_arena_reset(void);
|
|
96
115
|
/* The mode-provided collect entry's body: arena reset + a full cycle
|
|
97
116
|
* collection (snapshot-invariant by construction — collection frees only
|
|
@@ -124,6 +143,16 @@ void scr_library_check_exc(void);
|
|
|
124
143
|
* structured message is length-delimited, never NUL-scanned. */
|
|
125
144
|
_Noreturn void scr_trap_len(const char *msg, size_t len);
|
|
126
145
|
|
|
146
|
+
/* The runtime-trap overlay table, DEFINED by the generated program TU
|
|
147
|
+
* (both emissions emit identical data) and consumed by the funnel when it
|
|
148
|
+
* assembles a detected trap's structured message: flat triples of
|
|
149
|
+
* (code, teaching-or-NULL, remediation-or-NULL), one per runtime trap code
|
|
150
|
+
* (SC4013–SC4019 family) the profile declares text for; _len counts
|
|
151
|
+
* triples. A declared teaching replaces the baseline human line as field 0;
|
|
152
|
+
* a declared remediation becomes the optional fourth field. */
|
|
153
|
+
extern const char *const scr_library_trap_overlays[];
|
|
154
|
+
extern const size_t scr_library_trap_overlays_len;
|
|
155
|
+
|
|
127
156
|
/* Marshalling helpers the generated wrappers call (both emissions share
|
|
128
157
|
* these bodies, which is how the two lanes stay identical by
|
|
129
158
|
* construction). Inbound is borrowed-and-copied; outbound values MOVE into
|
|
@@ -449,6 +478,9 @@ void scr_undef_global_read(ScrStr *name);
|
|
|
449
478
|
void scr_error_set_code(ScrError *e, const char *code);
|
|
450
479
|
ScrStr *scr_error_code(ScrError *e);
|
|
451
480
|
void scr_throw_error_msg_code(int kind, const char *message, size_t len, const char *code);
|
|
481
|
+
/* The compiler-resolved Node-parity throw (error.nodeThrow): builtin
|
|
482
|
+
* error of `kind`, `code` stamped when non-empty. Borrows both. */
|
|
483
|
+
void scr_throw_node_coded(double kind, const ScrStr *code, const ScrStr *msg);
|
|
452
484
|
|
|
453
485
|
/* ── string methods ─────────────────────────────────────────────────
|
|
454
486
|
* ECMA-262 observable semantics (UTF-16 code units) computed over the
|
|
@@ -1552,6 +1584,13 @@ bool scr_stream_push_dyn(ScrStream *s, const ScrDyn *d); /* borrows d; tag
|
|
|
1552
1584
|
bool scr_stream_write_dyn(ScrStream *s, const ScrDyn *d, ScrClosure *cb); /* cb moves */
|
|
1553
1585
|
ScrPromise *scr_stream_next_chunk(ScrStream *s);
|
|
1554
1586
|
ScrPromise *scr_stream_next_chunk_dyn(ScrStream *s);
|
|
1587
|
+
/* node:stream/promises — the promise forms over the finished/pipeline
|
|
1588
|
+
* machinery above: a pending void promise the terminal watcher settles
|
|
1589
|
+
* (fulfilled on a clean finish, rejected with the finish status
|
|
1590
|
+
* otherwise — the stream's error or ERR_STREAM_PREMATURE_CLOSE).
|
|
1591
|
+
* Streams borrowed; +1 promises. */
|
|
1592
|
+
ScrPromise *scr_sp_finished(ScrStream *s);
|
|
1593
|
+
ScrPromise *scr_sp_pipeline(double n, ScrStream **streams);
|
|
1555
1594
|
/* Readable.from(array): +1 fully-seeded object-entry stream (one WHOLE
|
|
1556
1595
|
* chunk per element — strings or Buffers per the flag; hwm 1, already
|
|
1557
1596
|
* EOF'd). Borrows arr. */
|
|
@@ -2706,6 +2745,14 @@ ScrDyn *scr_dyn_new_buffer_copy(const ScrBytes *b);
|
|
|
2706
2745
|
* extraction (`u as Uint8Array`). */
|
|
2707
2746
|
ScrBytes *scr_dyn_bytes_copy_out(const ScrDyn *d);
|
|
2708
2747
|
void scr_dyn_arr_push(ScrDyn *arr, ScrDyn *item);
|
|
2748
|
+
/* Spread completion for a runtime-arity argument list (`f(...xs)` in the
|
|
2749
|
+
* checked-dynamic tier): flattens `src` into `arr` per JS's spread over the
|
|
2750
|
+
* DOM's iterable kinds — arrays element-by-element (retained), strings by
|
|
2751
|
+
* code POINT (the string iterator), bytes by byte — and throws V8's exact
|
|
2752
|
+
* SPREAD-CALL TypeError for every other kind (pending; callers check):
|
|
2753
|
+
* nullish sources spell the spread expression (`what`), everything else is
|
|
2754
|
+
* the generic "Spread syntax requires ..." text. Borrows src. */
|
|
2755
|
+
void scr_dyn_arr_push_spread(ScrDyn *arr, const ScrDyn *src, const char *what);
|
|
2709
2756
|
void scr_dyn_obj_set(ScrDyn *obj, const char *key, size_t key_len, ScrDyn *value);
|
|
2710
2757
|
/* The checked-dynamic keyed WRITE (`h.k = v` on a dyn receiver): OBJ sets
|
|
2711
2758
|
* the member (JS: later writes win, insertion order); undefined/null and
|
|
@@ -2722,6 +2769,10 @@ ScrStr *scr_dyn_to_string(const ScrDyn *d, const ScrStr *enc);
|
|
|
2722
2769
|
/* JS String() over the DOM kind (units render "null"/"undefined" where
|
|
2723
2770
|
* scr_dyn_to_string throws) — the web globals' WebIDL ToString. +1. */
|
|
2724
2771
|
ScrStr *scr_dyn_string_coerce(const ScrDyn *d);
|
|
2772
|
+
/* JS ToString WITH the object protocol (user toString/valueOf members
|
|
2773
|
+
* called, their throws propagating) — the WHATWG USVString conversions.
|
|
2774
|
+
* Borrows; +1 or NULL with the exception pending. */
|
|
2775
|
+
ScrStr *scr_dyn_string_coerce_js(const ScrDyn *d);
|
|
2725
2776
|
|
|
2726
2777
|
/* `d instanceof TypeError` (and the other builtin error classes) on a
|
|
2727
2778
|
* checked-dynamic value: the from_error cache resolves the DOM encoding
|
|
@@ -2792,6 +2843,10 @@ ScrDyn *scr_dyn_new_func(ScrClosure *clo, ScrDynThunk thunk, uint32_t arity, con
|
|
|
2792
2843
|
* boxed thunk (per-arg checks live there). `args` entries are BORROWED;
|
|
2793
2844
|
* the result is owned (+1), or NULL with the exception pending. */
|
|
2794
2845
|
ScrDyn *scr_dyn_call(const ScrDyn *d, ScrDyn *const *args, size_t argc, const char *what);
|
|
2846
|
+
/* scr_dyn_call over a DOM ARRAY's elements (the spread-application form —
|
|
2847
|
+
* `f(...args)` after the emitted argument array is built): argv IS the
|
|
2848
|
+
* array's items. Borrows both; result owned (+1), or NULL pending. */
|
|
2849
|
+
ScrDyn *scr_dyn_apply(const ScrDyn *d, const ScrDyn *args, const char *what);
|
|
2795
2850
|
|
|
2796
2851
|
/* Path spine for dynCheck error messages — a compile-time-shaped linked
|
|
2797
2852
|
* list the emitted builders stack-allocate per recursion level: `key`
|
|
@@ -2932,6 +2987,9 @@ void scr_dyn_check_listener(const ScrDyn *cb, const char *argname);
|
|
|
2932
2987
|
* generic ERR_INVALID_ARG_TYPE thrower over it (`expected` is the whole
|
|
2933
2988
|
* "of type ..." clause). The handle dispatchers' per-arg gates. */
|
|
2934
2989
|
const char *scr_dyn_specific_type(const ScrDyn *v, char *buf, size_t cap);
|
|
2990
|
+
/* ERR_INVALID_ARG_TYPE with the runtime-rendered Received tail (the
|
|
2991
|
+
* error.argTypeThrow libCall). Borrows all three; always throws. */
|
|
2992
|
+
void scr_throw_arg_type(const ScrStr *argname, const ScrStr *expected, const ScrDyn *got);
|
|
2935
2993
|
void scr_dyn_arg_type_fail(const char *argname, const char *expected, const ScrDyn *got);
|
|
2936
2994
|
/* Listener-closure builders for the handle dispatchers' .on(...) paths:
|
|
2937
2995
|
* a runtime-built ScrClosure whose capture is the boxed dyn listener and
|
|
@@ -3720,6 +3778,13 @@ ScrJsval *scr_jsval_call_method(ScrJsval *o, const ScrStr *name, int argc, ScrJs
|
|
|
3720
3778
|
* anything else calls with this = o (non-callables throw in the engine). */
|
|
3721
3779
|
ScrJsval *scr_jsval_opt_call_method(ScrJsval *o, const ScrStr *name, int argc, ScrJsval **argv);
|
|
3722
3780
|
ScrJsval *scr_jsval_call(ScrJsval *f, int argc, ScrJsval **argv);
|
|
3781
|
+
/* Spread application on an island callee — `f(...pre, ...spread)` through
|
|
3782
|
+
* the prelude helper's REAL spread syntax (iterator protocols are the
|
|
3783
|
+
* engine's own; the guards front-run V8's exact spread-call TypeError
|
|
3784
|
+
* texts). `pre` is the engine array of leading fixed arguments; `what` the
|
|
3785
|
+
* spread expression's source spelling (the nullish text spells it).
|
|
3786
|
+
* Borrows everything; +1 out, or NULL with the exception bridged. */
|
|
3787
|
+
ScrJsval *scr_jsval_call_spread(ScrJsval *f, ScrJsval *pre, ScrJsval *spread, const ScrStr *what);
|
|
3723
3788
|
/* `new X(...)` on an island callee (jsOp construct) — JS_CallConstructor.
|
|
3724
3789
|
* Borrows everything; +1 out, or NULL with the exception bridged. */
|
|
3725
3790
|
ScrJsval *scr_jsval_construct(ScrJsval *f, int argc, ScrJsval **argv);
|
|
@@ -3867,6 +3932,12 @@ void scr_jsval_cast_fail(ScrJsval *v, const ScrStr *target);
|
|
|
3867
3932
|
*/
|
|
3868
3933
|
size_t scr_f64_to_str(double x, char *buf);
|
|
3869
3934
|
|
|
3935
|
+
/* The Ryū digit core (scr_number.c), shared with the Intl en-US number
|
|
3936
|
+
* formatter: the shortest round-tripping digits of a POSITIVE finite
|
|
3937
|
+
* double — value = 0.digits × 10^n, no trailing zeros, NUL-terminated.
|
|
3938
|
+
* Returns k, the digit count (≤ 17). */
|
|
3939
|
+
int scr_f64_digits(double x, char digits[18], int *n_out);
|
|
3940
|
+
|
|
3870
3941
|
/* ToString for template literals / string coercion. Returns +1. */
|
|
3871
3942
|
ScrStr *scr_f64_to_scrstr(double x);
|
|
3872
3943
|
ScrStr *scr_bool_to_scrstr(bool b); /* interned "true"/"false" */
|
|
@@ -3898,6 +3969,18 @@ bool scr_num_is_safe_integer(double x);
|
|
|
3898
3969
|
ScrStr *scr_num_to_exponential(double x);
|
|
3899
3970
|
ScrStr *scr_num_to_fixed0(double x);
|
|
3900
3971
|
|
|
3972
|
+
/* Object.is over two numbers — the spec's SameValue on doubles: NaN
|
|
3973
|
+
* equals NaN, +0 differs from -0, everything else is ==. Never throws. */
|
|
3974
|
+
bool scr_num_same_value(double a, double b);
|
|
3975
|
+
|
|
3976
|
+
/* Intl.NumberFormat("en-US").format(x) / x.toLocaleString("en-US") with
|
|
3977
|
+
* default options: decimal notation, 0–3 fraction digits rounded half-up
|
|
3978
|
+
* on the shortest round-tripping decimal (ICU's rounding input — NOT
|
|
3979
|
+
* toFixed's exact-value rounding), "," grouping every three integer
|
|
3980
|
+
* digits, "∞"/"NaN" texts, "-0" for negative inputs rounding to zero.
|
|
3981
|
+
* en-US is the one embedded locale. Result +1; never throws. */
|
|
3982
|
+
ScrStr *scr_intl_num_format_en_us(double x);
|
|
3983
|
+
|
|
3901
3984
|
/* ── Date, the composed slice (scr_lib.c) ─────────────────────────────
|
|
3902
3985
|
* Date values have no representation; the runtime surface is exactly
|
|
3903
3986
|
* Date.now() and toISOString over a millisecond time value. */
|
|
@@ -4052,6 +4135,14 @@ typedef enum ScrDataViewGet {
|
|
|
4052
4135
|
} ScrDataViewGet;
|
|
4053
4136
|
double scr_dataview_get(const ScrBytes *b, double byte_off, ScrDataViewGet kind, bool le);
|
|
4054
4137
|
|
|
4138
|
+
/* DataView setters (the integer/float kinds only — the BIG kinds never
|
|
4139
|
+
* lower: bigint arguments have no representation). Values coerce
|
|
4140
|
+
* JS-exactly: the integer kinds by modular truncation (ToUint32's residue
|
|
4141
|
+
* — the narrower widths store its low bytes, the same 2^width residue),
|
|
4142
|
+
* F32 by double→float round-to-nearest-even. Offsets go through ToIndex
|
|
4143
|
+
* with the getters' one RangeError. */
|
|
4144
|
+
void scr_dataview_set(ScrBytes *b, double byte_off, double value, ScrDataViewGet kind, bool le);
|
|
4145
|
+
|
|
4055
4146
|
/* Element read/write. Any invalid index — negative, fractional, NaN, or
|
|
4056
4147
|
* out of bounds — TRAPS like the array runtime (SEMANTICS.md documents
|
|
4057
4148
|
* the divergence from JS's undefined-read/ignored-write). Writes coerce
|
|
@@ -4122,6 +4213,32 @@ bool scr_bytes_is_encoding(const ScrStr *s);
|
|
|
4122
4213
|
bool scr_bytes_equals(const ScrBytes *a, const ScrBytes *b);
|
|
4123
4214
|
double scr_bytes_compare(const ScrBytes *src, const ScrBytes *target, double nargs,
|
|
4124
4215
|
double ts, double te, double ss, double se);
|
|
4216
|
+
/* Node's validateOffset ladder (buffer.js): non-integers (±Infinity and
|
|
4217
|
+
* NaN included) throw the 'an integer' ERR_OUT_OF_RANGE RangeError, the
|
|
4218
|
+
* rest the '>= 0 && <= max' render; max < 0 drops the upper bound.
|
|
4219
|
+
* Returns false after arming the pending throw. */
|
|
4220
|
+
bool scr_bytes_validate_off(const char *name, double value, double max);
|
|
4221
|
+
/* The checked-dynamic compare/equals validators (scr_bytes_io.c): Node's
|
|
4222
|
+
* argument ladder over DOM-boxed arguments — a non-bytes value throws
|
|
4223
|
+
* ERR_INVALID_ARG_TYPE with the API's own argument name ("buf1"/"buf2",
|
|
4224
|
+
* "otherBuffer", "target"), non-number offsets ERR_INVALID_ARG_TYPE
|
|
4225
|
+
* "of type number", out-of-range numbers the validateOffset RangeError;
|
|
4226
|
+
* an undefined offset takes its Node default. All arguments BORROWED. */
|
|
4227
|
+
double scr_buffer_compare_chk(const ScrDyn *a, const ScrDyn *b);
|
|
4228
|
+
bool scr_bytes_equals_chk(const ScrBytes *recv, const ScrDyn *other);
|
|
4229
|
+
double scr_bytes_compare_chk(const ScrBytes *src, const ScrDyn *target,
|
|
4230
|
+
const ScrDyn *ts, const ScrDyn *te,
|
|
4231
|
+
const ScrDyn *ss, const ScrDyn *se);
|
|
4232
|
+
/* new Buffer(number, encoding)'s string-arm type error (always throws;
|
|
4233
|
+
* borrowed). */
|
|
4234
|
+
ScrBytes *scr_buffer_new_string_fail(const ScrDyn *got);
|
|
4235
|
+
/* fs._toUnixTimestamp over a DOM time value: numeric strings and finite
|
|
4236
|
+
* numbers coerce (negatives answer now/1000), the rest throw Node's
|
|
4237
|
+
* ERR_INVALID_ARG_TYPE. Borrowed. */
|
|
4238
|
+
double scr_fs_to_unix_timestamp(const ScrDyn *t);
|
|
4239
|
+
/* The checked-dynamic max-listeners ladders (scr_events_emitter.c). */
|
|
4240
|
+
ScrEmitter *scr_emitter_set_max_chk(ScrEmitter *em, const ScrDyn *n);
|
|
4241
|
+
void scr_emitter_set_default_max_chk(const ScrDyn *n, const ScrStr *name);
|
|
4125
4242
|
double scr_bytes_index_of(const ScrBytes *b, const ScrBytes *needle, double off, double align, bool fwd);
|
|
4126
4243
|
double scr_bytes_index_of_num(const ScrBytes *b, double v, double off, bool fwd);
|
|
4127
4244
|
ScrBytes *scr_bytes_fill(ScrBytes *b, const ScrBytes *pattern, double nargs, double offset, double end);
|
package/src/scr_stream.c
CHANGED
|
@@ -683,7 +683,9 @@ static void *scr_stream_read_n(ScrStream *s, double size) {
|
|
|
683
683
|
st->r.hwm = h;
|
|
684
684
|
}
|
|
685
685
|
}
|
|
686
|
-
|
|
686
|
+
/* Node's read(): `if (n !== 0) state.emittedReadable = false` — the
|
|
687
|
+
* absent form is NaN there, which also clears; only read(0) keeps it. */
|
|
688
|
+
if (absent || n != 0) st->r.emitted_readable = false;
|
|
687
689
|
if (!absent && n == 0 && st->r.need_readable &&
|
|
688
690
|
(st->r.length >= st->r.hwm || st->r.ended)) {
|
|
689
691
|
if (st->r.length == 0 && st->r.ended) scr_stream_end_readable(s);
|
|
@@ -2148,6 +2150,49 @@ ScrStream *scr_stream_pipeline(double n_d, ScrStream **streams /*borrowed*/,
|
|
|
2148
2150
|
return scr_stream_retain(streams[n - 1]);
|
|
2149
2151
|
}
|
|
2150
2152
|
|
|
2153
|
+
/* ── node:stream/promises ─────────────────────────────────────────────
|
|
2154
|
+
* The promise forms ride the callback machinery above with a settling
|
|
2155
|
+
* watcher: caps[0] boxes the pending promise; the terminal status
|
|
2156
|
+
* fulfills (NULL) or rejects (the error moves through the exception
|
|
2157
|
+
* cell, the reject-pending pattern). */
|
|
2158
|
+
|
|
2159
|
+
static void scr_sp_settle_inv(ScrClosure *cb, ScrStream *s, ScrError *err) {
|
|
2160
|
+
(void)s;
|
|
2161
|
+
ScrPromise *p = scr_box_get_ref(cb->caps[0]); /* +1 */
|
|
2162
|
+
if (err != NULL) {
|
|
2163
|
+
scr_throw_obj(scr_error_retain(err), &scr_error_retain_v, &scr_error_release_v,
|
|
2164
|
+
scr_error_trace_arg());
|
|
2165
|
+
scr_promise_reject_pending(p);
|
|
2166
|
+
} else {
|
|
2167
|
+
scr_promise_fulfill_void(p);
|
|
2168
|
+
}
|
|
2169
|
+
scr_promise_release(p);
|
|
2170
|
+
}
|
|
2171
|
+
|
|
2172
|
+
static ScrClosure *scr_sp_watcher(ScrPromise *p /*borrowed*/) {
|
|
2173
|
+
ScrClosure *w = scr_closure_new((void *)&scr_sp_settle_inv, 1);
|
|
2174
|
+
w->caps[0] = scr_box_new_obj(scr_promise_retain_v, scr_promise_release_v, scr_promise_trace_v);
|
|
2175
|
+
scr_box_set_ref(w->caps[0], scr_promise_retain(p));
|
|
2176
|
+
return w;
|
|
2177
|
+
}
|
|
2178
|
+
|
|
2179
|
+
ScrPromise *scr_sp_finished(ScrStream *s) {
|
|
2180
|
+
ScrPromise *p = scr_promise_new();
|
|
2181
|
+
/* The cleanup closure is the callback form's return value; the promise
|
|
2182
|
+
* form exposes no unhook, so it drops here (the watcher stays parked —
|
|
2183
|
+
* scr_stream_finished retained it into the stream's list). */
|
|
2184
|
+
ScrClosure *cleanup = scr_stream_finished(s, scr_sp_watcher(p), &scr_sp_settle_inv);
|
|
2185
|
+
scr_closure_release(cleanup);
|
|
2186
|
+
return p;
|
|
2187
|
+
}
|
|
2188
|
+
|
|
2189
|
+
ScrPromise *scr_sp_pipeline(double n, ScrStream **streams) {
|
|
2190
|
+
ScrPromise *p = scr_promise_new();
|
|
2191
|
+
ScrStream *dst = scr_stream_pipeline(n, streams, scr_sp_watcher(p), &scr_sp_settle_inv);
|
|
2192
|
+
scr_stream_release(dst);
|
|
2193
|
+
return p;
|
|
2194
|
+
}
|
|
2195
|
+
|
|
2151
2196
|
/* ── the readable surface ─────────────────────────────────────────────── */
|
|
2152
2197
|
|
|
2153
2198
|
bool scr_stream_push(ScrStream *s, ScrBytes *chunk) {
|
|
@@ -2664,10 +2709,12 @@ double scr_stream_prop(ScrStream *s, const char *name) {
|
|
|
2664
2709
|
* arrives outside a _read call). */
|
|
2665
2710
|
static void scr_stream_emit_readable_now(ScrStream *s) {
|
|
2666
2711
|
ScrStreamState *st = s->st;
|
|
2667
|
-
|
|
2712
|
+
/* Node's emitReadable_ clears emittedReadable AFTER the emit (a
|
|
2713
|
+
* 'readable' listener observes true) and only when it really fired. */
|
|
2668
2714
|
if (!st->destroyed && !st->errored && (st->r.length > 0 || st->r.ended)) {
|
|
2669
2715
|
scr_stream_emit0(s, "readable");
|
|
2670
2716
|
if (scr_exc_pending()) return;
|
|
2717
|
+
st->r.emitted_readable = false;
|
|
2671
2718
|
}
|
|
2672
2719
|
st->r.need_readable = st->r.flowing != 1 && !st->r.ended &&
|
|
2673
2720
|
st->r.length <= st->r.hwm;
|