@scriptc/runtime 0.0.8 → 0.0.10
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_assert.c +43 -4
- package/src/scr_bytes.c +65 -18
- package/src/scr_bytes_io.c +507 -1
- package/src/scr_dc.c +6 -0
- package/src/scr_dgram.c +176 -1
- package/src/scr_dyn_handle.c +8 -66
- package/src/scr_dyn_invoke.c +96 -2
- 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_inspect.c +27 -3
- package/src/scr_island.c +417 -3
- package/src/scr_json.c +643 -6
- package/src/scr_lib.c +122 -4
- package/src/scr_library.c +105 -2
- package/src/scr_net.c +112 -0
- package/src/scr_number.c +29 -19
- package/src/scr_runtime.h +358 -9
- package/src/scr_stream.c +49 -2
- package/src/scr_tls.c +172 -0
package/src/scr_dgram.c
CHANGED
|
@@ -64,6 +64,7 @@
|
|
|
64
64
|
|
|
65
65
|
#include <errno.h>
|
|
66
66
|
#include <fcntl.h>
|
|
67
|
+
#include <math.h>
|
|
67
68
|
#include <stdio.h>
|
|
68
69
|
#include <stdlib.h>
|
|
69
70
|
#include <string.h>
|
|
@@ -466,7 +467,18 @@ ScrDgramSocket *scr_dgram_create(bool reuse_addr) {
|
|
|
466
467
|
}
|
|
467
468
|
|
|
468
469
|
static void scr_dgram_throw(const char *msg) {
|
|
469
|
-
|
|
470
|
+
/* Node's state errors carry their ERR_SOCKET_* codes; the message IS
|
|
471
|
+
* the discriminant (each arm throws exactly one text). */
|
|
472
|
+
const char *code =
|
|
473
|
+
strcmp(msg, "Already connected") == 0 ? "ERR_SOCKET_DGRAM_IS_CONNECTED"
|
|
474
|
+
: strcmp(msg, "Not running") == 0 ? "ERR_SOCKET_DGRAM_NOT_RUNNING"
|
|
475
|
+
: strcmp(msg, "Socket is already bound") == 0 ? "ERR_SOCKET_ALREADY_BOUND"
|
|
476
|
+
: NULL;
|
|
477
|
+
if (code != NULL) {
|
|
478
|
+
scr_throw_error_msg_code(0 /* Error */, msg, strlen(msg), code);
|
|
479
|
+
} else {
|
|
480
|
+
scr_throw_error_msg(0 /* Error */, msg, strlen(msg));
|
|
481
|
+
}
|
|
470
482
|
}
|
|
471
483
|
|
|
472
484
|
/* Resolve a numeric host into a sockaddr_in; "" means any (bind's
|
|
@@ -1005,3 +1017,166 @@ void scr_dgram_install(void) {
|
|
|
1005
1017
|
atexit(scr_dgram_cleanup_atexit);
|
|
1006
1018
|
scr_loop_set_dgram(&scr_dgram_pending, &scr_dgram_dispatch, &scr_dgram_pollfd);
|
|
1007
1019
|
}
|
|
1020
|
+
|
|
1021
|
+
/* ── the send argument-validation ladder (checked-dynamic lane) ─────────
|
|
1022
|
+
* Node's Socket.prototype.send signature shuffle and validation order
|
|
1023
|
+
* over DOM arguments, byte-for-byte: the connected/unconnected split
|
|
1024
|
+
* decides whether (a1, a2) are an offset/length slice or the port/address
|
|
1025
|
+
* pair; sliceBuffer validates the buffer's type and bounds
|
|
1026
|
+
* (ERR_BUFFER_OUT_OF_BOUNDS); list payloads validate per element with the
|
|
1027
|
+
* LIST as the Received tail; unconnected sends validate the port
|
|
1028
|
+
* (ERR_SOCKET_BAD_PORT, > 0 and < 65536 with the specific-type tail and
|
|
1029
|
+
* Node's trailing period) and the address's string contract; a send with
|
|
1030
|
+
* a port or address on a connected socket answers ERR_SOCKET_DGRAM_IS_
|
|
1031
|
+
* CONNECTED. A fully-validated unconnected single-payload send RUNS —
|
|
1032
|
+
* the callback form and the connected sends keep the compiler-rendered
|
|
1033
|
+
* fence. All dyn arguments borrowed. */
|
|
1034
|
+
|
|
1035
|
+
static bool scr_dgram_dyn_truthy(const ScrDyn *v) {
|
|
1036
|
+
switch (v->kind) {
|
|
1037
|
+
case SCR_DYN_UNDEF:
|
|
1038
|
+
case SCR_DYN_NULL: return false;
|
|
1039
|
+
case SCR_DYN_BOOL: return v->v.b;
|
|
1040
|
+
case SCR_DYN_NUM: return v->v.num == v->v.num && v->v.num != 0;
|
|
1041
|
+
case SCR_DYN_STR: return v->v.str->len > 0;
|
|
1042
|
+
default: return true;
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
static uint32_t scr_dgram_to_u32(const ScrDyn *v) {
|
|
1047
|
+
if (v->kind != SCR_DYN_NUM) return 0; /* >>> 0 over the ladder's shapes */
|
|
1048
|
+
double t = v->v.num;
|
|
1049
|
+
if (t != t || isinf(t)) return 0;
|
|
1050
|
+
t = trunc(t);
|
|
1051
|
+
t = fmod(t, 4294967296.0);
|
|
1052
|
+
if (t < 0) t += 4294967296.0;
|
|
1053
|
+
return (uint32_t)t;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
static const char SCR_DGRAM_BUF_EXPECTED[] =
|
|
1057
|
+
"of type string or an instance of Buffer, TypedArray, or DataView";
|
|
1058
|
+
|
|
1059
|
+
void scr_dgram_send_chk(ScrDgramSocket *s, const ScrDyn *buffer, const ScrDyn *a1,
|
|
1060
|
+
const ScrDyn *a2, const ScrDyn *a3, const ScrDyn *a4,
|
|
1061
|
+
const ScrStr *fence) {
|
|
1062
|
+
const ScrDyn *offset = a1, *length = a2, *port = a3, *address = a4;
|
|
1063
|
+
const ScrDyn *callback = scr_dyn_undefined();
|
|
1064
|
+
bool connected = s->connected;
|
|
1065
|
+
bool sliced = false;
|
|
1066
|
+
if (!connected) {
|
|
1067
|
+
if (scr_dgram_dyn_truthy(address) ||
|
|
1068
|
+
(scr_dgram_dyn_truthy(port) && port->kind != SCR_DYN_FUNC)) {
|
|
1069
|
+
sliced = true;
|
|
1070
|
+
} else {
|
|
1071
|
+
callback = port;
|
|
1072
|
+
port = offset;
|
|
1073
|
+
address = length;
|
|
1074
|
+
}
|
|
1075
|
+
} else {
|
|
1076
|
+
if (length->kind == SCR_DYN_NUM) {
|
|
1077
|
+
sliced = true;
|
|
1078
|
+
if (port->kind == SCR_DYN_FUNC) callback = port;
|
|
1079
|
+
} else {
|
|
1080
|
+
callback = offset;
|
|
1081
|
+
port = a3;
|
|
1082
|
+
address = a4;
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
size_t slice_off = 0, slice_len = 0;
|
|
1086
|
+
if (sliced) {
|
|
1087
|
+
double bytelen;
|
|
1088
|
+
if (buffer->kind == SCR_DYN_STR) bytelen = (double)buffer->v.str->len;
|
|
1089
|
+
else if (buffer->kind == SCR_DYN_BYTES) bytelen = scr_bytes_byte_len(buffer->v.bytes);
|
|
1090
|
+
else {
|
|
1091
|
+
scr_dyn_arg_type_fail("buffer", SCR_DGRAM_BUF_EXPECTED, buffer);
|
|
1092
|
+
return;
|
|
1093
|
+
}
|
|
1094
|
+
uint32_t off = scr_dgram_to_u32(offset);
|
|
1095
|
+
uint32_t len = scr_dgram_to_u32(length);
|
|
1096
|
+
if ((double)off > bytelen) {
|
|
1097
|
+
static const char msg[] = "\"offset\" is outside of buffer bounds";
|
|
1098
|
+
scr_throw_error_msg_code(SCR_ERR_RANGE, msg, sizeof msg - 1, "ERR_BUFFER_OUT_OF_BOUNDS");
|
|
1099
|
+
return;
|
|
1100
|
+
}
|
|
1101
|
+
if ((double)off + (double)len > bytelen) {
|
|
1102
|
+
static const char msg[] = "\"length\" is outside of buffer bounds";
|
|
1103
|
+
scr_throw_error_msg_code(SCR_ERR_RANGE, msg, sizeof msg - 1, "ERR_BUFFER_OUT_OF_BOUNDS");
|
|
1104
|
+
return;
|
|
1105
|
+
}
|
|
1106
|
+
slice_off = off;
|
|
1107
|
+
slice_len = len;
|
|
1108
|
+
} else if (buffer->kind == SCR_DYN_ARR) {
|
|
1109
|
+
for (size_t i = 0; i < buffer->v.arr.len; i++) {
|
|
1110
|
+
const ScrDyn *e = buffer->v.arr.items[i];
|
|
1111
|
+
if (e->kind != SCR_DYN_STR && e->kind != SCR_DYN_BYTES) {
|
|
1112
|
+
scr_dyn_arg_type_fail("buffer list arguments", SCR_DGRAM_BUF_EXPECTED, buffer);
|
|
1113
|
+
return;
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
} else if (buffer->kind != SCR_DYN_STR && buffer->kind != SCR_DYN_BYTES) {
|
|
1117
|
+
scr_dyn_arg_type_fail("buffer", SCR_DGRAM_BUF_EXPECTED, buffer);
|
|
1118
|
+
return;
|
|
1119
|
+
}
|
|
1120
|
+
if (connected) {
|
|
1121
|
+
if (scr_dgram_dyn_truthy(port) || scr_dgram_dyn_truthy(address)) {
|
|
1122
|
+
scr_dgram_throw("Already connected");
|
|
1123
|
+
return;
|
|
1124
|
+
}
|
|
1125
|
+
scr_throw_lowering_fence(fence); /* connected sends have no lowering yet */
|
|
1126
|
+
return;
|
|
1127
|
+
}
|
|
1128
|
+
/* validatePort(port, 'Port', false): integers (or numeric strings)
|
|
1129
|
+
* strictly between 0 and 65536; everything else renders the specific
|
|
1130
|
+
* type with Node's trailing period. */
|
|
1131
|
+
double portnum = -1;
|
|
1132
|
+
{
|
|
1133
|
+
bool ok = false;
|
|
1134
|
+
if (port->kind == SCR_DYN_NUM && trunc(port->v.num) == port->v.num &&
|
|
1135
|
+
port->v.num > 0 && port->v.num < 65536) {
|
|
1136
|
+
ok = true;
|
|
1137
|
+
portnum = port->v.num;
|
|
1138
|
+
} else if (port->kind == SCR_DYN_STR && port->v.str->len > 0) {
|
|
1139
|
+
ScrStr *ps = scr_str_retain(port->v.str);
|
|
1140
|
+
double n = scr_string_to_number(ps);
|
|
1141
|
+
scr_str_release(ps);
|
|
1142
|
+
if (n == n && trunc(n) == n && n > 0 && n < 65536) {
|
|
1143
|
+
ok = true;
|
|
1144
|
+
portnum = n;
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
if (!ok) {
|
|
1148
|
+
char detail[64], msg[160];
|
|
1149
|
+
const char *d = scr_dyn_specific_type(port, detail, sizeof detail);
|
|
1150
|
+
int len = snprintf(msg, sizeof msg, "Port should be > 0 and < 65536. Received %s.", d);
|
|
1151
|
+
scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)len, "ERR_SOCKET_BAD_PORT");
|
|
1152
|
+
return;
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
if (address->kind == SCR_DYN_FUNC) {
|
|
1156
|
+
callback = address;
|
|
1157
|
+
address = scr_dyn_undefined();
|
|
1158
|
+
} else if (address->kind != SCR_DYN_UNDEF && address->kind != SCR_DYN_NULL &&
|
|
1159
|
+
address->kind != SCR_DYN_STR) {
|
|
1160
|
+
/* Node's gate is null/undefined-only: a falsy 0 still throws. */
|
|
1161
|
+
scr_dyn_arg_type_fail("address", "of type string", address);
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
1164
|
+
if (callback->kind == SCR_DYN_FUNC || buffer->kind == SCR_DYN_ARR) {
|
|
1165
|
+
/* completion callbacks and list concatenation have no lowering yet —
|
|
1166
|
+
* refuse loudly after the full validation ladder, never drop */
|
|
1167
|
+
scr_throw_lowering_fence(fence);
|
|
1168
|
+
return;
|
|
1169
|
+
}
|
|
1170
|
+
const char *data = buffer->kind == SCR_DYN_STR ? buffer->v.str->data
|
|
1171
|
+
: (const char *)buffer->v.bytes->data;
|
|
1172
|
+
size_t datalen = buffer->kind == SCR_DYN_STR ? buffer->v.str->len
|
|
1173
|
+
: (size_t)scr_bytes_byte_len(buffer->v.bytes);
|
|
1174
|
+
if (sliced) {
|
|
1175
|
+
data += slice_off;
|
|
1176
|
+
datalen = slice_len;
|
|
1177
|
+
}
|
|
1178
|
+
ScrStr *host = address->kind == SCR_DYN_STR ? scr_str_retain(address->v.str)
|
|
1179
|
+
: scr_str_new("127.0.0.1", 9);
|
|
1180
|
+
scr_dgram_send_raw(s, data, datalen, portnum, host);
|
|
1181
|
+
scr_str_release(host);
|
|
1182
|
+
}
|
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_dyn_invoke.c
CHANGED
|
@@ -114,6 +114,11 @@ static void scr_dyn_display_buf(ScrJsonBuf *b, const ScrDyn *d) {
|
|
|
114
114
|
* for a bare promise is "[object Promise]". */
|
|
115
115
|
scr_jb_puts(b, "[object Promise]");
|
|
116
116
|
return;
|
|
117
|
+
case SCR_DYN_JSVAL:
|
|
118
|
+
/* The engine's own ToString (a bridged failure leaves the exception
|
|
119
|
+
* pending and appends nothing — the loud path). */
|
|
120
|
+
scr_dyn_isl_tostr_buf(b, d);
|
|
121
|
+
return;
|
|
117
122
|
}
|
|
118
123
|
}
|
|
119
124
|
|
|
@@ -186,6 +191,12 @@ static ScrDyn *dyn_call_cb(ScrDyn *cb, ScrDyn *item, size_t i, ScrDyn *recv) {
|
|
|
186
191
|
static bool dyn_cb_check(ScrDyn *const *args, size_t argc) {
|
|
187
192
|
ScrDyn *cb = argc > 0 ? args[0] : scr_dyn_undefined();
|
|
188
193
|
if (cb->kind == SCR_DYN_FUNC) return true;
|
|
194
|
+
/* An ENGINE function is callable — scr_dyn_call's JSVAL arm routes it
|
|
195
|
+
* (the loops below call through scr_dyn_call, which converts the DOM
|
|
196
|
+
* element arguments per the uniform crossing). A wrapped NON-function
|
|
197
|
+
* falls through to the display path: String(cb) renders through the
|
|
198
|
+
* engine, exactly Node's message. */
|
|
199
|
+
if (cb->kind == SCR_DYN_JSVAL && scr_dyn_isl_typeof_is(cb, "function")) return true;
|
|
189
200
|
ScrJsonBuf b;
|
|
190
201
|
scr_jb_init(&b);
|
|
191
202
|
scr_dyn_display_buf(&b, cb);
|
|
@@ -289,7 +300,7 @@ static bool dyn_arr_sort(ScrDyn *recv, ScrDyn *cmp) {
|
|
|
289
300
|
/* Names each prototype declares BEYOND what's implemented here — these
|
|
290
301
|
* fence loudly instead of mis-answering "is not a function". */
|
|
291
302
|
static bool dyn_arr_proto_unimpl(const char *m) {
|
|
292
|
-
static const char *names[] = { "splice", "reduce", "reduceRight", "flat",
|
|
303
|
+
static const char *names[] = { "splice", "reduce", "reduceRight", "flat",
|
|
293
304
|
"fill", "copyWithin", "keys", "values", "entries", "toReversed", "toSorted", "toSpliced",
|
|
294
305
|
"with", "toString", "toLocaleString", NULL };
|
|
295
306
|
for (size_t i = 0; names[i]; i++) if (dyn_name_is(m, names[i])) return true;
|
|
@@ -325,11 +336,25 @@ ScrDyn *scr_dyn_invoke(ScrDyn *recv, const char *method, ScrDyn *const *args, si
|
|
|
325
336
|
return scr_dynh_dispatch(recv, method, args, argc, what);
|
|
326
337
|
}
|
|
327
338
|
|
|
339
|
+
/* Island-held receivers: the ENGINE runs its own prototypes (JS-exact
|
|
340
|
+
* flatMap/map/forEach/filter — Array.prototype is the engine's) through
|
|
341
|
+
* scr_jsval_call_method; arguments cross per the uniform conversion
|
|
342
|
+
* (wrapped cells by reference, DOM data deep-copied, FUNC boxes through
|
|
343
|
+
* the host shim), a missing member throws the engine's own TypeError,
|
|
344
|
+
* and the result wraps back scalar-normalized. `what` is unused — the
|
|
345
|
+
* engine's message names the failure. */
|
|
346
|
+
if (recv->kind == SCR_DYN_JSVAL) {
|
|
347
|
+
return scr_dyn_jsval_ops()->invoke(recv->v.jsval.cell, method, args, argc, what);
|
|
348
|
+
}
|
|
349
|
+
|
|
328
350
|
/* OBJ: the own member calls (own properties shadow prototypes in JS
|
|
329
351
|
* too); anything else is Node's is-not-a-function. */
|
|
330
352
|
if (recv->kind == SCR_DYN_OBJ) {
|
|
331
353
|
ScrDyn *m = scr_dyn_obj_get(recv, method, strlen(method));
|
|
332
|
-
if (m && m->kind == SCR_DYN_FUNC
|
|
354
|
+
if (m && (m->kind == SCR_DYN_FUNC ||
|
|
355
|
+
/* a WRAPPED engine function stored as a DOM member: the
|
|
356
|
+
* routed call (scr_dyn_call's JSVAL arm) runs it. */
|
|
357
|
+
(m->kind == SCR_DYN_JSVAL && scr_dyn_isl_typeof_is(m, "function")))) {
|
|
333
358
|
/* JS binds the receiver for the call (`obj.method()` — this === obj):
|
|
334
359
|
* the ambient-receiver window (scr_runtime.h). */
|
|
335
360
|
scr_dyn_this_push_dyn(recv);
|
|
@@ -374,6 +399,23 @@ ScrDyn *scr_dyn_invoke(ScrDyn *recv, const char *method, ScrDyn *const *args, si
|
|
|
374
399
|
dyn_throw_unsupported("Function", method);
|
|
375
400
|
return NULL;
|
|
376
401
|
}
|
|
402
|
+
/* An OWN property on the FUNC box (defineProperties writes — the
|
|
403
|
+
* mustCall-wrapper expando family): a callable member runs with the
|
|
404
|
+
* box bound (JS's o.m() receiver), everything else keeps Node's
|
|
405
|
+
* is-not-a-function. */
|
|
406
|
+
{
|
|
407
|
+
ScrDyn *own = scr_dyn_fn_get(recv, method, strlen(method));
|
|
408
|
+
if (own) {
|
|
409
|
+
if (own->kind == SCR_DYN_FUNC || own->kind == SCR_DYN_JSVAL) {
|
|
410
|
+
scr_dyn_this_push_dyn(recv);
|
|
411
|
+
ScrDyn *r = scr_dyn_call(own, args, argc, what);
|
|
412
|
+
scr_dyn_this_pop();
|
|
413
|
+
scr_dyn_release(own);
|
|
414
|
+
return r;
|
|
415
|
+
}
|
|
416
|
+
scr_dyn_release(own);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
377
419
|
dyn_throw_not_fn(what);
|
|
378
420
|
return NULL;
|
|
379
421
|
}
|
|
@@ -541,6 +583,53 @@ ScrDyn *scr_dyn_invoke(ScrDyn *recv, const char *method, ScrDyn *const *args, si
|
|
|
541
583
|
if (dyn_name_is(method, "findIndex")) return scr_dyn_new_num(-1);
|
|
542
584
|
return scr_dyn_retain(scr_dyn_undefined()); /* forEach */
|
|
543
585
|
}
|
|
586
|
+
if (dyn_name_is(method, "flatMap")) {
|
|
587
|
+
/* JS Array.prototype.flatMap over a DOM array: map + a depth-1
|
|
588
|
+
* flatten. Native DOM-array results flatten element-by-element; a
|
|
589
|
+
* WRAPPED engine array flattens through the routed keyed reads
|
|
590
|
+
* (elements wrap back scalar-normalized); everything else pushes
|
|
591
|
+
* as a single element (JS keeps non-array callback results whole).
|
|
592
|
+
* JS's spec snapshots the length up front (elements appended by
|
|
593
|
+
* the callback are not visited). */
|
|
594
|
+
if (!dyn_cb_check(args, argc)) return NULL;
|
|
595
|
+
ScrDyn *cb = args[0];
|
|
596
|
+
ScrDyn *out = scr_dyn_new_arr();
|
|
597
|
+
size_t n = recv->v.arr.len;
|
|
598
|
+
for (size_t i = 0; i < n && i < recv->v.arr.len; i++) {
|
|
599
|
+
ScrDyn *item = scr_dyn_retain(recv->v.arr.items[i]);
|
|
600
|
+
ScrDyn *r = dyn_call_cb(cb, item, i, recv);
|
|
601
|
+
scr_dyn_release(item);
|
|
602
|
+
if (!r) { scr_dyn_release(out); return NULL; }
|
|
603
|
+
if (r->kind == SCR_DYN_ARR) {
|
|
604
|
+
for (size_t j = 0; j < r->v.arr.len; j++) {
|
|
605
|
+
scr_dyn_arr_push(out, scr_dyn_retain(r->v.arr.items[j]));
|
|
606
|
+
}
|
|
607
|
+
scr_dyn_release(r);
|
|
608
|
+
} else if (scr_dyn_isl_is_array(r)) {
|
|
609
|
+
/* An engine-array result: length + element reads through the
|
|
610
|
+
* routed engine ops (a bridged surprise unwinds). */
|
|
611
|
+
ScrStr *lk = scr_str_new("length", 6);
|
|
612
|
+
ScrDyn *lenv = scr_dyn_isl_key_get(r, lk);
|
|
613
|
+
scr_str_release(lk);
|
|
614
|
+
if (!lenv) { scr_dyn_release(r); scr_dyn_release(out); return NULL; }
|
|
615
|
+
size_t rn = lenv->kind == SCR_DYN_NUM ? (size_t)lenv->v.num : 0;
|
|
616
|
+
scr_dyn_release(lenv);
|
|
617
|
+
for (size_t j = 0; j < rn; j++) {
|
|
618
|
+
char idx[24];
|
|
619
|
+
int ilen = snprintf(idx, sizeof idx, "%zu", j);
|
|
620
|
+
ScrStr *jk = scr_str_new(idx, (size_t)ilen);
|
|
621
|
+
ScrDyn *el = scr_dyn_isl_key_get(r, jk);
|
|
622
|
+
scr_str_release(jk);
|
|
623
|
+
if (!el) { scr_dyn_release(r); scr_dyn_release(out); return NULL; }
|
|
624
|
+
scr_dyn_arr_push(out, el); /* ownership moves in */
|
|
625
|
+
}
|
|
626
|
+
scr_dyn_release(r);
|
|
627
|
+
} else {
|
|
628
|
+
scr_dyn_arr_push(out, r); /* ownership moves in */
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
return out;
|
|
632
|
+
}
|
|
544
633
|
if (dyn_name_is(method, "sort")) {
|
|
545
634
|
ScrDyn *cmp = argc > 0 ? args[0] : scr_dyn_undefined();
|
|
546
635
|
if (cmp->kind != SCR_DYN_UNDEF && cmp->kind != SCR_DYN_FUNC) {
|
|
@@ -598,6 +687,11 @@ ScrDyn *scr_dyn_invoke(ScrDyn *recv, const char *method, ScrDyn *const *args, si
|
|
|
598
687
|
* (DOM properties are plain data properties — SEMANTICS.md); get/set
|
|
599
688
|
* throw the loud unsupported Error, never a silent drop. */
|
|
600
689
|
ScrDyn *scr_dyn_define_props(ScrDyn *target, ScrDyn *descs) {
|
|
690
|
+
/* Island-held operands ARE objects to Node — the non-object TypeError
|
|
691
|
+
* below would be a wrong claim. Loud fence (lane dyn-routing-ops). */
|
|
692
|
+
scr_dyn_isl_fence(target, "Object.defineProperties");
|
|
693
|
+
if (!scr_exc_pending()) scr_dyn_isl_fence(descs, "Object.defineProperties");
|
|
694
|
+
if (scr_exc_pending()) return NULL;
|
|
601
695
|
if (target->kind != SCR_DYN_OBJ && target->kind != SCR_DYN_FUNC) {
|
|
602
696
|
scr_throw_error_msg(SCR_ERR_TYPE, "Object.defineProperties called on non-object",
|
|
603
697
|
strlen("Object.defineProperties called on non-object"));
|
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_inspect.c
CHANGED
|
@@ -775,8 +775,18 @@ ScrStr *scr_insp_dyn(ScrDyn *d, double recurse, double depth) {
|
|
|
775
775
|
return out;
|
|
776
776
|
}
|
|
777
777
|
case SCR_DYN_OBJ: {
|
|
778
|
-
|
|
779
|
-
|
|
778
|
+
/* Object.create(null)'s dictionary: Node prefixes the rendering
|
|
779
|
+
* with "[Object: null prototype]" (formatValue's constructor-less
|
|
780
|
+
* base), the empty form included, and the beyond-depth answer IS
|
|
781
|
+
* the bare marker (where a plain object says [Object]). */
|
|
782
|
+
if (d->v.obj.len == 0) {
|
|
783
|
+
return d->null_proto ? scr_str_new("[Object: null prototype] {}", 27)
|
|
784
|
+
: scr_str_new("{}", 2);
|
|
785
|
+
}
|
|
786
|
+
if (recurse > depth) {
|
|
787
|
+
return d->null_proto ? scr_str_new("[Object: null prototype]", 24)
|
|
788
|
+
: scr_str_new("[Object]", 8);
|
|
789
|
+
}
|
|
780
790
|
scr_insp_begin(recurse + 1);
|
|
781
791
|
for (size_t i = 0; i < d->v.obj.len; i++) {
|
|
782
792
|
ScrDynEntry *ent = &d->v.obj.entries[i];
|
|
@@ -790,7 +800,8 @@ ScrStr *scr_insp_dyn(ScrDyn *d, double recurse, double depth) {
|
|
|
790
800
|
scr_insp_entry(entry, false);
|
|
791
801
|
scr_str_release(entry);
|
|
792
802
|
}
|
|
793
|
-
ScrStr *base = scr_str_new("",
|
|
803
|
+
ScrStr *base = d->null_proto ? scr_str_new("[Object: null prototype]", 24)
|
|
804
|
+
: scr_str_new("", 0);
|
|
794
805
|
ScrStr *b0 = scr_str_new("{", 1);
|
|
795
806
|
ScrStr *b1 = scr_str_new("}", 1);
|
|
796
807
|
ScrStr *out = scr_insp_end(base, b0, b1, recurse + 1, false, false);
|
|
@@ -825,6 +836,19 @@ ScrStr *scr_insp_dyn(ScrDyn *d, double recurse, double depth) {
|
|
|
825
836
|
scr_throw_error(SCR_ERR_ERROR, ib_take(&out));
|
|
826
837
|
return scr_str_new("", 0);
|
|
827
838
|
}
|
|
839
|
+
case SCR_DYN_JSVAL: {
|
|
840
|
+
/* An island value inside the DOM: Node prints the engine object's
|
|
841
|
+
* property dump — fence loudly, naming the engine typeof (the
|
|
842
|
+
* scr_insp_jsval wording for bare 'any' composites). */
|
|
843
|
+
ScrStr *t = scr_dyn_typeof(d); /* routes to the engine; +1 */
|
|
844
|
+
InspBuf out = {0};
|
|
845
|
+
ib_cstr(&out, "util.inspect of a composite 'any' value (typeof '");
|
|
846
|
+
ib_bytes(&out, t->data, t->len);
|
|
847
|
+
ib_cstr(&out, "') is not supported yet");
|
|
848
|
+
scr_str_release(t);
|
|
849
|
+
scr_throw_error(SCR_ERR_ERROR, ib_take(&out));
|
|
850
|
+
return scr_str_new("", 0);
|
|
851
|
+
}
|
|
828
852
|
case SCR_DYN_PROMISE: {
|
|
829
853
|
/* Node renders Promise { <pending> } / Promise { value } — the
|
|
830
854
|
* settled-value rendering pulls in the whole inspector; fence
|