@scriptc/runtime 0.0.7 → 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 +24 -1
- package/src/scr_island.c +109 -6
- package/src/scr_json.c +218 -0
- package/src/scr_lib.c +101 -3
- package/src/scr_library.c +119 -5
- package/src/scr_number.c +29 -19
- package/src/scr_object.c +18 -0
- package/src/scr_runtime.h +173 -7
- 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,10 +181,33 @@ 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 */
|
|
191
|
+
/* The ratified verbatim rule: a thrown message that ALREADY begins with
|
|
192
|
+
* the structured trap-teaching marker (0x01) is delivered exactly as
|
|
193
|
+
* authored — no "Uncaught " prefix, no added newline — which is how a
|
|
194
|
+
* facade-authored structured teaching rides the throw channel through to
|
|
195
|
+
* the sink. Both throw idioms qualify: a thrown string, and an Error
|
|
196
|
+
* whose .message starts with the marker. Everything else keeps the
|
|
197
|
+
* baseline "Uncaught ..." shape below, whose first byte is printable —
|
|
198
|
+
* the guarantee the marker's unambiguity rests on. */
|
|
199
|
+
const ScrStr *marked = NULL;
|
|
200
|
+
if (scr_exc_kind == SCR_EXC_STR) {
|
|
201
|
+
marked = (const ScrStr *)scr_exc_payload;
|
|
202
|
+
} else if (scr_exc_kind == SCR_EXC_OBJ && scr_error_is(scr_exc_payload)) {
|
|
203
|
+
marked = ((const ScrError *)scr_exc_payload)->message;
|
|
204
|
+
}
|
|
205
|
+
if (marked != NULL && marked->len > 0 && (uint8_t)marked->data[0] == 0x01) {
|
|
206
|
+
size_t take = marked->len > sizeof buf ? sizeof buf : marked->len;
|
|
207
|
+
memcpy(buf, marked->data, take);
|
|
208
|
+
scr_exc_reset(); /* the payload is released before the funnel poisons */
|
|
209
|
+
scr_trap_len(buf, take);
|
|
210
|
+
}
|
|
188
211
|
size_t n = 0;
|
|
189
212
|
const char prefix[] = "Uncaught ";
|
|
190
213
|
memcpy(buf, prefix, sizeof prefix - 1);
|
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) {
|
|
@@ -1412,8 +1440,27 @@ static JSValue isl_hostfn_invoke(JSContext *ctx, JSValueConst this_val, int argc
|
|
|
1412
1440
|
IslHostFn *b = JS_GetOpaque(func_data[0], isl_hostfn_class_id);
|
|
1413
1441
|
if (!b) return JS_ThrowTypeError(ctx, "detached scriptc host function");
|
|
1414
1442
|
ScrJsval *cells[ISL_HOSTFN_MAX_ARITY];
|
|
1415
|
-
|
|
1416
|
-
|
|
1443
|
+
int ncells;
|
|
1444
|
+
if (b->arity < 0) {
|
|
1445
|
+
/* The ISLAND-REST shape (negative arity = -(leading declared + 1)):
|
|
1446
|
+
* leading params pad/drop like any host call; the trailing cell is a
|
|
1447
|
+
* fresh ENGINE ARRAY of the surplus arguments — the closure's rest
|
|
1448
|
+
* binding IS the engine's own arguments array. */
|
|
1449
|
+
int leading = -b->arity - 1;
|
|
1450
|
+
for (int i = 0; i < leading; i++) {
|
|
1451
|
+
cells[i] = isl_cell_new(i < argc ? JS_DupValue(ctx, argv[i]) : JS_UNDEFINED);
|
|
1452
|
+
}
|
|
1453
|
+
JSValue rest = JS_NewArray(ctx);
|
|
1454
|
+
for (int i = leading; i < argc; i++) {
|
|
1455
|
+
JS_SetPropertyUint32(ctx, rest, (uint32_t)(i - leading), JS_DupValue(ctx, argv[i]));
|
|
1456
|
+
}
|
|
1457
|
+
cells[leading] = isl_cell_new(rest);
|
|
1458
|
+
ncells = leading + 1;
|
|
1459
|
+
} else {
|
|
1460
|
+
for (int i = 0; i < b->arity; i++) {
|
|
1461
|
+
cells[i] = isl_cell_new(i < argc ? JS_DupValue(ctx, argv[i]) : JS_UNDEFINED);
|
|
1462
|
+
}
|
|
1463
|
+
ncells = b->arity;
|
|
1417
1464
|
}
|
|
1418
1465
|
bool strayed_before = isl_anchor_strayed;
|
|
1419
1466
|
isl_host_depth++;
|
|
@@ -1429,7 +1476,7 @@ static JSValue isl_hostfn_invoke(JSContext *ctx, JSValueConst this_val, int argc
|
|
|
1429
1476
|
isl_anchor_here();
|
|
1430
1477
|
isl_anchor_strayed = strayed_before;
|
|
1431
1478
|
}
|
|
1432
|
-
for (int i = 0; i <
|
|
1479
|
+
for (int i = 0; i < ncells; i++) scr_jsval_release(cells[i]);
|
|
1433
1480
|
if (scr_exc_pending()) {
|
|
1434
1481
|
if (r) scr_jsval_release(r);
|
|
1435
1482
|
return isl_throw_pending(ctx);
|
|
@@ -1443,7 +1490,9 @@ static JSValue isl_hostfn_invoke(JSContext *ctx, JSValueConst this_val, int argc
|
|
|
1443
1490
|
ScrJsval *scr_jsval_from_closure(ScrClosure *c, int arity,
|
|
1444
1491
|
ScrJsval *(*adapt)(ScrClosure *, ScrJsval **)) {
|
|
1445
1492
|
isl_entry();
|
|
1446
|
-
|
|
1493
|
+
/* Negative arity = the island-rest shape; the CELL count is the leading
|
|
1494
|
+
* declared params + the one rest-array slot. */
|
|
1495
|
+
if ((arity < 0 ? -arity : arity) > ISL_HOSTFN_MAX_ARITY) {
|
|
1447
1496
|
fprintf(stderr, "scriptc: island callback arity %d exceeds %d\n", arity,
|
|
1448
1497
|
ISL_HOSTFN_MAX_ARITY);
|
|
1449
1498
|
abort(); /* the frontend fences this; reaching here is a compiler bug */
|
|
@@ -1459,7 +1508,7 @@ ScrJsval *scr_jsval_from_closure(ScrClosure *c, int arity,
|
|
|
1459
1508
|
JSValue box = JS_NewObjectClass(isl_ctx, isl_hostfn_class_id);
|
|
1460
1509
|
JS_SetOpaque(box, b);
|
|
1461
1510
|
JSValueConst data[1] = {box};
|
|
1462
|
-
JSValue fn = JS_NewCFunctionData(isl_ctx, isl_hostfn_invoke, arity, 0, 1, data);
|
|
1511
|
+
JSValue fn = JS_NewCFunctionData(isl_ctx, isl_hostfn_invoke, arity < 0 ? -arity - 1 : arity, 0, 1, data);
|
|
1463
1512
|
JS_FreeValue(isl_ctx, box); /* fn's func_data holds its own reference */
|
|
1464
1513
|
return isl_cell_new(fn);
|
|
1465
1514
|
}
|
|
@@ -1717,6 +1766,60 @@ ScrJsval *scr_jsval_obj_lit(int npairs, ScrJsval **kv) {
|
|
|
1717
1766
|
return isl_cell_new(o);
|
|
1718
1767
|
}
|
|
1719
1768
|
|
|
1769
|
+
/* The engine-native TemplateStringsArray for an island tag call: `kv`
|
|
1770
|
+
* carries n cooked strings then n raw strings — a fresh array whose
|
|
1771
|
+
* `.raw` property holds the raw spellings, exactly the object a tagged
|
|
1772
|
+
* template hands its tag (a JSON marshal would drop `.raw`, and tags
|
|
1773
|
+
* dispatch on it). */
|
|
1774
|
+
ScrJsval *scr_jsval_tpl_strings(int n, ScrJsval **kv) {
|
|
1775
|
+
isl_entry();
|
|
1776
|
+
JSValue cooked = JS_NewArray(isl_ctx);
|
|
1777
|
+
JSValue raw = JS_NewArray(isl_ctx);
|
|
1778
|
+
for (int i = 0; i < n; i++) {
|
|
1779
|
+
JS_SetPropertyUint32(isl_ctx, cooked, (uint32_t)i, JS_DupValue(isl_ctx, kv[i]->v));
|
|
1780
|
+
JS_SetPropertyUint32(isl_ctx, raw, (uint32_t)i, JS_DupValue(isl_ctx, kv[n + i]->v));
|
|
1781
|
+
}
|
|
1782
|
+
JS_SetPropertyStr(isl_ctx, cooked, "raw", raw); /* consumes raw */
|
|
1783
|
+
return isl_cell_new(cooked);
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
/* Spread completion for an island-native literal: copies `src`'s own
|
|
1787
|
+
* enumerable properties onto `obj` — the engine's own Object.assign (the
|
|
1788
|
+
* spec's CopyDataProperties; null/undefined sources spread nothing) — and
|
|
1789
|
+
* answers the target retained (+1). NULL with the exception pending when
|
|
1790
|
+
* a source getter throws. */
|
|
1791
|
+
ScrJsval *scr_jsval_obj_spread(ScrJsval *obj, ScrJsval *src) {
|
|
1792
|
+
isl_entry();
|
|
1793
|
+
if (JS_IsNull(src->v) || JS_IsUndefined(src->v)) return scr_jsval_retain(obj);
|
|
1794
|
+
JSValue global = JS_GetGlobalObject(isl_ctx);
|
|
1795
|
+
JSValue object_ctor = JS_GetPropertyStr(isl_ctx, global, "Object");
|
|
1796
|
+
JS_FreeValue(isl_ctx, global);
|
|
1797
|
+
JSValue assign = JS_GetPropertyStr(isl_ctx, object_ctor, "assign");
|
|
1798
|
+
JS_FreeValue(isl_ctx, object_ctor);
|
|
1799
|
+
JSValueConst args[2] = { obj->v, src->v };
|
|
1800
|
+
JSValue r = JS_Call(isl_ctx, assign, JS_UNDEFINED, 2, args);
|
|
1801
|
+
JS_FreeValue(isl_ctx, assign);
|
|
1802
|
+
if (JS_IsException(r)) {
|
|
1803
|
+
isl_bridge_exception();
|
|
1804
|
+
return NULL;
|
|
1805
|
+
}
|
|
1806
|
+
JS_FreeValue(isl_ctx, r); /* assign answers the target itself */
|
|
1807
|
+
return scr_jsval_retain(obj);
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
/* Getter completion for an island-native literal: defines `key` on `obj`
|
|
1811
|
+
* as an engine GETTER invoking `fn` (a marshaled host function), and
|
|
1812
|
+
* answers the object retained (+1) so builds chain. Enumerable +
|
|
1813
|
+
* configurable, no setter — exactly a JS object-literal `get k() {}`. */
|
|
1814
|
+
ScrJsval *scr_jsval_define_getter(ScrJsval *obj, ScrJsval *key, ScrJsval *fn) {
|
|
1815
|
+
isl_entry();
|
|
1816
|
+
JSAtom k = JS_ValueToAtom(isl_ctx, key->v);
|
|
1817
|
+
JS_DefinePropertyGetSet(isl_ctx, obj->v, k, JS_DupValue(isl_ctx, fn->v), JS_UNDEFINED,
|
|
1818
|
+
JS_PROP_ENUMERABLE | JS_PROP_CONFIGURABLE);
|
|
1819
|
+
JS_FreeAtom(isl_ctx, k);
|
|
1820
|
+
return scr_jsval_retain(obj);
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1720
1823
|
ScrJsval *scr_jsval_arr_lit(int n, ScrJsval **elems) {
|
|
1721
1824
|
isl_entry();
|
|
1722
1825
|
JSValue a = JS_NewArray(isl_ctx);
|