@scriptc/runtime 0.0.9 → 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 +7 -6
- package/src/scr_bytes_io.c +410 -0
- package/src/scr_dc.c +6 -0
- package/src/scr_dgram.c +176 -1
- package/src/scr_dyn_invoke.c +96 -2
- package/src/scr_inspect.c +27 -3
- package/src/scr_island.c +389 -3
- package/src/scr_json.c +488 -7
- package/src/scr_lib.c +21 -1
- package/src/scr_net.c +112 -0
- package/src/scr_runtime.h +236 -4
- package/src/scr_tls.c +172 -0
package/src/scr_island.c
CHANGED
|
@@ -380,6 +380,9 @@ enum {
|
|
|
380
380
|
ISL_H_ITERN,
|
|
381
381
|
ISL_H_ITER,
|
|
382
382
|
ISL_H_CALLSPREAD,
|
|
383
|
+
ISL_H_OBJWALK,
|
|
384
|
+
ISL_H_HASOWN,
|
|
385
|
+
ISL_H_ASSIGN,
|
|
383
386
|
ISL_H_COUNT,
|
|
384
387
|
};
|
|
385
388
|
|
|
@@ -428,7 +431,13 @@ static const char isl_prelude[] =
|
|
|
428
431
|
"throw new TypeError(w+\" is not iterable (cannot read property \"+s+\")\");"
|
|
429
432
|
"if(typeof s[Symbol.iterator]!==\"function\")"
|
|
430
433
|
"throw new TypeError(\"Spread syntax requires ...iterable[Symbol.iterator] to be a function\");"
|
|
431
|
-
"return f(...p,...s)}
|
|
434
|
+
"return f(...p,...s)},"
|
|
435
|
+
/* ISL_H_OBJWALK / ISL_H_HASOWN / ISL_H_ASSIGN: the Object statics a
|
|
436
|
+
* wrapped (SCR_DYN_JSVAL) receiver routes here — the engine's own
|
|
437
|
+
* semantics (own-key order, getters running, ToObject refusals). */
|
|
438
|
+
"(o,m)=>m===0?Object.keys(o):m===1?Object.values(o):Object.entries(o),"
|
|
439
|
+
"(o,k)=>Object.hasOwn(o,k),"
|
|
440
|
+
"(t,s)=>Object.assign(t,s)]";
|
|
432
441
|
|
|
433
442
|
static void isl_free_boot(void);
|
|
434
443
|
static void isl_prom_wraps_teardown(void);
|
|
@@ -963,8 +972,9 @@ ScrJsval *scr_jsval_from_json(const ScrStr *json) {
|
|
|
963
972
|
* STATIC code the engine cannot re-enter through a data copy). */
|
|
964
973
|
static const char *isl_dyn_unmarshalable(const ScrDyn *d) {
|
|
965
974
|
switch (d->kind) {
|
|
966
|
-
|
|
967
|
-
|
|
975
|
+
/* SCR_DYN_FUNC is NOT here: a boxed function crosses as the generic
|
|
976
|
+
* host-function shim (isl_dynfn_new below) — the routed-call lane's
|
|
977
|
+
* uniform argument conversion. */
|
|
968
978
|
case SCR_DYN_HANDLE:
|
|
969
979
|
return "a runtime handle";
|
|
970
980
|
case SCR_DYN_PROMISE:
|
|
@@ -986,8 +996,16 @@ static const char *isl_dyn_unmarshalable(const ScrDyn *d) {
|
|
|
986
996
|
}
|
|
987
997
|
}
|
|
988
998
|
|
|
999
|
+
static JSValue isl_dynfn_new(const ScrDyn *d); /* the DOM-function shim, below */
|
|
1000
|
+
|
|
989
1001
|
static JSValue isl_from_dyn(const ScrDyn *d) {
|
|
990
1002
|
switch (d->kind) {
|
|
1003
|
+
case SCR_DYN_FUNC:
|
|
1004
|
+
/* A boxed DOM function enters as ONE generic host-function shim over
|
|
1005
|
+
* its uniform call thunk (ScrDynThunk): engine args wrap as DOM
|
|
1006
|
+
* values, the thunk runs, the DOM result converts back. Each
|
|
1007
|
+
* crossing mints a fresh engine function (documented). */
|
|
1008
|
+
return isl_dynfn_new(d);
|
|
991
1009
|
case SCR_DYN_UNDEF:
|
|
992
1010
|
return JS_UNDEFINED;
|
|
993
1011
|
case SCR_DYN_NULL:
|
|
@@ -1028,6 +1046,10 @@ static JSValue isl_from_dyn(const ScrDyn *d) {
|
|
|
1028
1046
|
}
|
|
1029
1047
|
return obj;
|
|
1030
1048
|
}
|
|
1049
|
+
case SCR_DYN_JSVAL:
|
|
1050
|
+
/* An engine value riding inside DOM data: embed the SAME engine
|
|
1051
|
+
* value by reference — the identity round trip, member position. */
|
|
1052
|
+
return JS_DupValue(isl_ctx, d->v.jsval.cell->v);
|
|
1031
1053
|
default:
|
|
1032
1054
|
/* Pre-scanned away — defensive. */
|
|
1033
1055
|
return JS_ThrowTypeError(isl_ctx, "unmarshalable dynamic value");
|
|
@@ -1035,6 +1057,11 @@ static JSValue isl_from_dyn(const ScrDyn *d) {
|
|
|
1035
1057
|
}
|
|
1036
1058
|
|
|
1037
1059
|
ScrJsval *scr_jsval_from_dyn(const ScrDyn *d) {
|
|
1060
|
+
/* The identity round trip: an engine value that crossed into the DOM
|
|
1061
|
+
* (scr_dyn_from_jsval) and back is the SAME engine value, by reference
|
|
1062
|
+
* — the one direction that used to throw (SEMANTICS.md supersedes the
|
|
1063
|
+
* "one unbridgeable mix"). */
|
|
1064
|
+
if (d->kind == SCR_DYN_JSVAL) return scr_jsval_retain(d->v.jsval.cell);
|
|
1038
1065
|
isl_entry();
|
|
1039
1066
|
const char *bad = isl_dyn_unmarshalable(d);
|
|
1040
1067
|
if (bad != NULL) {
|
|
@@ -1052,6 +1079,279 @@ ScrJsval *scr_jsval_from_dyn(const ScrDyn *d) {
|
|
|
1052
1079
|
return isl_cell_new(v);
|
|
1053
1080
|
}
|
|
1054
1081
|
|
|
1082
|
+
/* ── the jsval→DOM crossing (SCR_DYN_JSVAL's constructor + ops) ───────
|
|
1083
|
+
* The reverse direction: an 'any'-typed engine value flowing into an
|
|
1084
|
+
* 'unknown'/'object'/JS-residue slot wraps BY REFERENCE as the DOM's
|
|
1085
|
+
* island kind. The DOM core (scr_json.c) stays island-free — it routes
|
|
1086
|
+
* the armed operations (typeof/truthy/String()/===, the narrowing
|
|
1087
|
+
* tests) through these installed ops; everything un-armed there keeps
|
|
1088
|
+
* the loud "not supported yet" ladder. */
|
|
1089
|
+
|
|
1090
|
+
static void isl_dynjs_release(ScrJsval *cell) { scr_jsval_release(cell); }
|
|
1091
|
+
static ScrStr *isl_dynjs_typeof(ScrJsval *cell) { return scr_jsval_typeof(cell); }
|
|
1092
|
+
static bool isl_dynjs_truthy(ScrJsval *cell) { return scr_jsval_truthy(cell) != 0; }
|
|
1093
|
+
static ScrStr *isl_dynjs_to_str(ScrJsval *cell) { return scr_jsval_to_str(cell); }
|
|
1094
|
+
static bool isl_dynjs_strict_eq(ScrJsval *a, ScrJsval *b) {
|
|
1095
|
+
/* The engine's === through the pinned helper (a bridged surprise
|
|
1096
|
+
* answers false — strict equality itself cannot throw in JS). */
|
|
1097
|
+
return scr_jsval_cmp(SCR_JSOP_EQ, a, b) == 1;
|
|
1098
|
+
}
|
|
1099
|
+
static bool isl_dynjs_is_array(ScrJsval *cell) { return JS_IsArray(cell->v); }
|
|
1100
|
+
static bool isl_dynjs_is_error(ScrJsval *cell) { return JS_IsError(cell->v); }
|
|
1101
|
+
|
|
1102
|
+
static const ScrDynJsvalOps isl_dynjs_ops;
|
|
1103
|
+
|
|
1104
|
+
/* The jsval→DOM wrap over a RAW engine value (BORROWED) — the scalar
|
|
1105
|
+
* normalization the cell constructor applies, shared by the routed-op
|
|
1106
|
+
* result conversions (which hold a JSValue, not a cell). Composites mint
|
|
1107
|
+
* a fresh cell over the SAME engine value (identity is the value — the
|
|
1108
|
+
* engine's === and the unwrap both go through it). */
|
|
1109
|
+
static ScrDyn *isl_dyn_from_value(JSValue v) {
|
|
1110
|
+
if (JS_IsUndefined(v)) return scr_dyn_retain(scr_dyn_undefined());
|
|
1111
|
+
if (JS_IsNull(v)) return scr_dyn_new_null();
|
|
1112
|
+
if (JS_IsBool(v)) return scr_dyn_new_bool(JS_ToBool(isl_ctx, v) > 0);
|
|
1113
|
+
if (JS_IsNumber(v)) {
|
|
1114
|
+
double num = 0;
|
|
1115
|
+
JS_ToFloat64(isl_ctx, &num, v); /* cannot fail on a number */
|
|
1116
|
+
return scr_dyn_new_num(num);
|
|
1117
|
+
}
|
|
1118
|
+
if (JS_IsString(v)) {
|
|
1119
|
+
ScrStr *s = isl_js_to_str(v); /* cannot bridge on a string */
|
|
1120
|
+
ScrDyn *d = scr_dyn_new_str(s);
|
|
1121
|
+
scr_str_release(s);
|
|
1122
|
+
return d;
|
|
1123
|
+
}
|
|
1124
|
+
return scr_dyn_alloc_jsval(isl_cell_new(JS_DupValue(isl_ctx, v)), &isl_dynjs_ops);
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
/* ── the routed operation set (the ScrDynJsvalOps rows scr_json.c and
|
|
1128
|
+
* scr_dyn_invoke.c dispatch through) ─────────────────────────────────
|
|
1129
|
+
* Keys enter as engine strings through the GETIDX/SETIDX helpers (any
|
|
1130
|
+
* byte content, canonical-index semantics are the engine's own); dyn
|
|
1131
|
+
* arguments cross through scr_jsval_from_dyn (wrapped cells by
|
|
1132
|
+
* reference, data as the usual deep copy, FUNC boxes through the shim);
|
|
1133
|
+
* results wrap back scalar-normalized. Engine throws bridge catchably
|
|
1134
|
+
* with the engine's message. */
|
|
1135
|
+
|
|
1136
|
+
static ScrDyn *isl_dynjs_key_get(ScrJsval *cell, const ScrStr *k) {
|
|
1137
|
+
isl_entry();
|
|
1138
|
+
JSValue key = JS_NewStringLen(isl_ctx, k->data, k->len);
|
|
1139
|
+
JSValue argv[2] = {cell->v, key};
|
|
1140
|
+
JSValue r = JS_Call(isl_ctx, isl_helpers[ISL_H_GETIDX], JS_UNDEFINED, 2, argv);
|
|
1141
|
+
JS_FreeValue(isl_ctx, key);
|
|
1142
|
+
if (JS_IsException(r)) {
|
|
1143
|
+
isl_bridge_exception();
|
|
1144
|
+
return NULL;
|
|
1145
|
+
}
|
|
1146
|
+
ScrDyn *d = isl_dyn_from_value(r);
|
|
1147
|
+
JS_FreeValue(isl_ctx, r);
|
|
1148
|
+
return d;
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
static bool isl_dynjs_key_set(ScrJsval *cell, const ScrStr *k, const ScrDyn *v) {
|
|
1152
|
+
ScrJsval *vj = scr_jsval_from_dyn(v);
|
|
1153
|
+
if (!vj) return false; /* unmarshalable value — the catchable TypeError */
|
|
1154
|
+
isl_entry();
|
|
1155
|
+
JSValue key = JS_NewStringLen(isl_ctx, k->data, k->len);
|
|
1156
|
+
JSValue argv[3] = {cell->v, key, vj->v};
|
|
1157
|
+
JSValue r = JS_Call(isl_ctx, isl_helpers[ISL_H_SETIDX], JS_UNDEFINED, 3, argv);
|
|
1158
|
+
JS_FreeValue(isl_ctx, key);
|
|
1159
|
+
scr_jsval_release(vj);
|
|
1160
|
+
if (JS_IsException(r)) {
|
|
1161
|
+
isl_bridge_exception();
|
|
1162
|
+
return false;
|
|
1163
|
+
}
|
|
1164
|
+
JS_FreeValue(isl_ctx, r);
|
|
1165
|
+
return true;
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
/* Convert argc dyn arguments for a routed call; frees what it built on
|
|
1169
|
+
* failure. Returns false with the exception pending. */
|
|
1170
|
+
static bool isl_dynjs_args_in(ScrDyn *const *args, size_t argc, ScrJsval **cells) {
|
|
1171
|
+
for (size_t i = 0; i < argc; i++) {
|
|
1172
|
+
cells[i] = scr_jsval_from_dyn(args[i]);
|
|
1173
|
+
if (!cells[i]) {
|
|
1174
|
+
for (size_t j = 0; j < i; j++) scr_jsval_release(cells[j]);
|
|
1175
|
+
return false;
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
return true;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
static ScrDyn *isl_dynjs_call(ScrJsval *cell, ScrDyn *const *args, size_t argc) {
|
|
1182
|
+
ScrJsval *stack_cells[8];
|
|
1183
|
+
ScrJsval **cells = argc <= 8 ? stack_cells : malloc(argc * sizeof *cells);
|
|
1184
|
+
if (!isl_dynjs_args_in(args, argc, cells)) {
|
|
1185
|
+
if (cells != stack_cells) free(cells);
|
|
1186
|
+
return NULL;
|
|
1187
|
+
}
|
|
1188
|
+
ScrJsval *r = scr_jsval_call(cell, (int)argc, cells);
|
|
1189
|
+
for (size_t i = 0; i < argc; i++) scr_jsval_release(cells[i]);
|
|
1190
|
+
if (cells != stack_cells) free(cells);
|
|
1191
|
+
if (!r) return NULL;
|
|
1192
|
+
ScrDyn *d = isl_dyn_from_value(r->v);
|
|
1193
|
+
scr_jsval_release(r);
|
|
1194
|
+
return d;
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
static ScrDyn *isl_dynjs_invoke(ScrJsval *cell, const char *method, ScrDyn *const *args, size_t argc, const char *what) {
|
|
1198
|
+
isl_entry();
|
|
1199
|
+
JSValue fn = JS_GetPropertyStr(isl_ctx, cell->v, method); /* owned */
|
|
1200
|
+
if (JS_IsException(fn)) {
|
|
1201
|
+
isl_bridge_exception();
|
|
1202
|
+
return NULL;
|
|
1203
|
+
}
|
|
1204
|
+
if (!JS_IsFunction(isl_ctx, fn)) {
|
|
1205
|
+
/* Node's spelled TypeError (V8's text), front-run before the
|
|
1206
|
+
* engine's terser "not a function". */
|
|
1207
|
+
JS_FreeValue(isl_ctx, fn);
|
|
1208
|
+
ScrJsonBuf b;
|
|
1209
|
+
scr_jb_init(&b);
|
|
1210
|
+
scr_jb_puts(&b, what);
|
|
1211
|
+
scr_jb_puts(&b, " is not a function");
|
|
1212
|
+
scr_throw_error(SCR_ERR_TYPE, scr_jb_finish(&b));
|
|
1213
|
+
return NULL;
|
|
1214
|
+
}
|
|
1215
|
+
ScrJsval *stack_cells[8];
|
|
1216
|
+
ScrJsval **cells = argc <= 8 ? stack_cells : malloc(argc * sizeof *cells);
|
|
1217
|
+
if (!isl_dynjs_args_in(args, argc, cells)) {
|
|
1218
|
+
JS_FreeValue(isl_ctx, fn);
|
|
1219
|
+
if (cells != stack_cells) free(cells);
|
|
1220
|
+
return NULL;
|
|
1221
|
+
}
|
|
1222
|
+
JSValue stack_args[8];
|
|
1223
|
+
JSValue *argv = argc <= 8 ? stack_args : malloc(argc * sizeof(JSValue));
|
|
1224
|
+
for (size_t i = 0; i < argc; i++) argv[i] = cells[i]->v;
|
|
1225
|
+
JSValue r = JS_Call(isl_ctx, fn, cell->v, (int)argc, argv); /* this = receiver */
|
|
1226
|
+
if (argv != stack_args) free(argv);
|
|
1227
|
+
JS_FreeValue(isl_ctx, fn);
|
|
1228
|
+
for (size_t i = 0; i < argc; i++) scr_jsval_release(cells[i]);
|
|
1229
|
+
if (cells != stack_cells) free(cells);
|
|
1230
|
+
if (JS_IsException(r)) {
|
|
1231
|
+
isl_bridge_exception();
|
|
1232
|
+
return NULL;
|
|
1233
|
+
}
|
|
1234
|
+
ScrDyn *d = isl_dyn_from_value(r);
|
|
1235
|
+
JS_FreeValue(isl_ctx, r);
|
|
1236
|
+
return d;
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
static bool isl_dynjs_is_nullish(ScrJsval *cell) {
|
|
1240
|
+
return JS_IsUndefined(cell->v) || JS_IsNull(cell->v);
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
static ScrDyn *isl_dynjs_obj_walk(ScrJsval *cell, int mode) {
|
|
1244
|
+
isl_entry();
|
|
1245
|
+
JSValue m = JS_NewInt32(isl_ctx, mode);
|
|
1246
|
+
JSValue argv[2] = {cell->v, m};
|
|
1247
|
+
JSValue r = JS_Call(isl_ctx, isl_helpers[ISL_H_OBJWALK], JS_UNDEFINED, 2, argv);
|
|
1248
|
+
if (JS_IsException(r)) {
|
|
1249
|
+
isl_bridge_exception();
|
|
1250
|
+
return NULL;
|
|
1251
|
+
}
|
|
1252
|
+
/* The engine array unpacks into a NATIVE DOM array (keys are DOM
|
|
1253
|
+
* strings, values wrap per element, entries become native pairs) so
|
|
1254
|
+
* the results iterate/index/measure at native speed. */
|
|
1255
|
+
int64_t len = 0;
|
|
1256
|
+
JSValue lv = JS_GetPropertyStr(isl_ctx, r, "length");
|
|
1257
|
+
JS_ToInt64(isl_ctx, &len, lv);
|
|
1258
|
+
JS_FreeValue(isl_ctx, lv);
|
|
1259
|
+
ScrDyn *out = scr_dyn_new_arr();
|
|
1260
|
+
for (int64_t i = 0; i < len; i++) {
|
|
1261
|
+
JSValue e = JS_GetPropertyUint32(isl_ctx, r, (uint32_t)i);
|
|
1262
|
+
if (mode == 2) {
|
|
1263
|
+
JSValue k = JS_GetPropertyUint32(isl_ctx, e, 0);
|
|
1264
|
+
JSValue v = JS_GetPropertyUint32(isl_ctx, e, 1);
|
|
1265
|
+
ScrDyn *pair = scr_dyn_new_arr();
|
|
1266
|
+
scr_dyn_arr_push(pair, isl_dyn_from_value(k));
|
|
1267
|
+
scr_dyn_arr_push(pair, isl_dyn_from_value(v));
|
|
1268
|
+
scr_dyn_arr_push(out, pair);
|
|
1269
|
+
JS_FreeValue(isl_ctx, k);
|
|
1270
|
+
JS_FreeValue(isl_ctx, v);
|
|
1271
|
+
} else {
|
|
1272
|
+
scr_dyn_arr_push(out, isl_dyn_from_value(e));
|
|
1273
|
+
}
|
|
1274
|
+
JS_FreeValue(isl_ctx, e);
|
|
1275
|
+
}
|
|
1276
|
+
JS_FreeValue(isl_ctx, r);
|
|
1277
|
+
return out;
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
static int isl_dynjs_has_own(ScrJsval *cell, const ScrStr *k) {
|
|
1281
|
+
isl_entry();
|
|
1282
|
+
JSValue key = JS_NewStringLen(isl_ctx, k->data, k->len);
|
|
1283
|
+
JSValue argv[2] = {cell->v, key};
|
|
1284
|
+
JSValue r = JS_Call(isl_ctx, isl_helpers[ISL_H_HASOWN], JS_UNDEFINED, 2, argv);
|
|
1285
|
+
JS_FreeValue(isl_ctx, key);
|
|
1286
|
+
if (JS_IsException(r)) {
|
|
1287
|
+
isl_bridge_exception();
|
|
1288
|
+
return -1;
|
|
1289
|
+
}
|
|
1290
|
+
int b = JS_ToBool(isl_ctx, r);
|
|
1291
|
+
JS_FreeValue(isl_ctx, r);
|
|
1292
|
+
return b > 0 ? 1 : 0;
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
static bool isl_dynjs_assign(ScrJsval *cell, const ScrDyn *src) {
|
|
1296
|
+
ScrJsval *sj = scr_jsval_from_dyn(src);
|
|
1297
|
+
if (!sj) return false;
|
|
1298
|
+
isl_entry();
|
|
1299
|
+
JSValue argv[2] = {cell->v, sj->v};
|
|
1300
|
+
JSValue r = JS_Call(isl_ctx, isl_helpers[ISL_H_ASSIGN], JS_UNDEFINED, 2, argv);
|
|
1301
|
+
scr_jsval_release(sj);
|
|
1302
|
+
if (JS_IsException(r)) {
|
|
1303
|
+
isl_bridge_exception();
|
|
1304
|
+
return false;
|
|
1305
|
+
}
|
|
1306
|
+
JS_FreeValue(isl_ctx, r);
|
|
1307
|
+
return true;
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
static ScrStr *isl_dynjs_to_json(ScrJsval *cell) { return scr_jsval_to_json(cell); }
|
|
1311
|
+
|
|
1312
|
+
static const ScrDynJsvalOps isl_dynjs_ops = {
|
|
1313
|
+
isl_dynjs_release,
|
|
1314
|
+
isl_dynjs_typeof,
|
|
1315
|
+
isl_dynjs_truthy,
|
|
1316
|
+
isl_dynjs_to_str,
|
|
1317
|
+
isl_dynjs_strict_eq,
|
|
1318
|
+
isl_dynjs_is_array,
|
|
1319
|
+
isl_dynjs_is_error,
|
|
1320
|
+
isl_dynjs_key_get,
|
|
1321
|
+
isl_dynjs_key_set,
|
|
1322
|
+
isl_dynjs_call,
|
|
1323
|
+
isl_dynjs_invoke,
|
|
1324
|
+
isl_dynjs_is_nullish,
|
|
1325
|
+
isl_dynjs_obj_walk,
|
|
1326
|
+
isl_dynjs_has_own,
|
|
1327
|
+
isl_dynjs_assign,
|
|
1328
|
+
isl_dynjs_to_json,
|
|
1329
|
+
};
|
|
1330
|
+
|
|
1331
|
+
ScrDyn *scr_dyn_from_jsval(ScrJsval *cell) {
|
|
1332
|
+
isl_entry();
|
|
1333
|
+
JSValue v = cell->v;
|
|
1334
|
+
/* Scalar normalization: engine-reported scalars become the NATIVE DOM
|
|
1335
|
+
* kinds at wrap time (the strict exits cannot fail on them), so every
|
|
1336
|
+
* scalar path in the DOM core — ===, typeof tests, JSON of leaves —
|
|
1337
|
+
* stays untouched and the JSVAL kind never competes with them. */
|
|
1338
|
+
if (JS_IsUndefined(v)) return scr_dyn_retain(scr_dyn_undefined());
|
|
1339
|
+
if (JS_IsNull(v)) return scr_dyn_new_null();
|
|
1340
|
+
if (JS_IsBool(v)) return scr_dyn_new_bool(JS_ToBool(isl_ctx, v) > 0);
|
|
1341
|
+
if (JS_IsNumber(v)) {
|
|
1342
|
+
double num = 0;
|
|
1343
|
+
JS_ToFloat64(isl_ctx, &num, v); /* cannot fail on a number */
|
|
1344
|
+
return scr_dyn_new_num(num);
|
|
1345
|
+
}
|
|
1346
|
+
if (JS_IsString(v)) {
|
|
1347
|
+
ScrStr *s = isl_js_to_str(v); /* cannot bridge on a string */
|
|
1348
|
+
ScrDyn *d = scr_dyn_new_str(s);
|
|
1349
|
+
scr_str_release(s);
|
|
1350
|
+
return d;
|
|
1351
|
+
}
|
|
1352
|
+
return scr_dyn_alloc_jsval(scr_jsval_retain(cell), &isl_dynjs_ops);
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1055
1355
|
/* ── operators (through the pinned prelude helpers) ───────────────────── */
|
|
1056
1356
|
|
|
1057
1357
|
ScrJsval *scr_jsval_binop(int op, ScrJsval *a, ScrJsval *b) {
|
|
@@ -1353,9 +1653,14 @@ static const JSClassDef isl_hostfn_class = {
|
|
|
1353
1653
|
.finalizer = isl_hostfn_finalizer,
|
|
1354
1654
|
};
|
|
1355
1655
|
|
|
1656
|
+
static JSClassID isl_dynfn_class_id; /* the DOM-function shim, below */
|
|
1657
|
+
static const JSClassDef isl_dynfn_class;
|
|
1658
|
+
|
|
1356
1659
|
static void isl_register_hostfn_class(void) {
|
|
1357
1660
|
JS_NewClassID(isl_rt, &isl_hostfn_class_id);
|
|
1358
1661
|
JS_NewClass(isl_rt, isl_hostfn_class_id, &isl_hostfn_class);
|
|
1662
|
+
JS_NewClassID(isl_rt, &isl_dynfn_class_id);
|
|
1663
|
+
JS_NewClass(isl_rt, isl_dynfn_class_id, &isl_dynfn_class);
|
|
1359
1664
|
isl_register_bridge_class();
|
|
1360
1665
|
}
|
|
1361
1666
|
|
|
@@ -1513,6 +1818,87 @@ ScrJsval *scr_jsval_from_closure(ScrClosure *c, int arity,
|
|
|
1513
1818
|
return isl_cell_new(fn);
|
|
1514
1819
|
}
|
|
1515
1820
|
|
|
1821
|
+
/* ── the generic DOM-function shim (a boxed SCR_DYN_FUNC entering the
|
|
1822
|
+
* island) ─────────────────────────────────────────────────────────────
|
|
1823
|
+
* ONE shim serves every boxed function because the box's call thunk is a
|
|
1824
|
+
* single uniform C signature (ScrDynThunk): engine arguments wrap as DOM
|
|
1825
|
+
* values (scalar-normalizing — the jsval→DOM constructor's stance), the
|
|
1826
|
+
* thunk validates them against the closure's declared parameter types
|
|
1827
|
+
* and runs it, and the DOM result converts back through the from_dyn
|
|
1828
|
+
* rules (wrapped cells by reference, data as a deep copy, nested
|
|
1829
|
+
* functions through this same shim). OWNERSHIP: the engine function's
|
|
1830
|
+
* opaque box owns ONE reference on the whole ScrDyn FUNC node (closure
|
|
1831
|
+
* and descriptor ride inside); the engine finalizer releases it — at
|
|
1832
|
+
* teardown that runs before the RC audit, the isl_hostfn story. A
|
|
1833
|
+
* scriptc exception thrown inside reverse-bridges to an engine throw;
|
|
1834
|
+
* the receiver (`this`) is deliberately not forwarded (the typed
|
|
1835
|
+
* host-function stance — dyn thunks read the ambient receiver, which no
|
|
1836
|
+
* engine call site binds). Each crossing mints a FRESH engine function:
|
|
1837
|
+
* re-crossing identity is not preserved (SEMANTICS.md). */
|
|
1838
|
+
|
|
1839
|
+
static JSClassID isl_dynfn_class_id = 0;
|
|
1840
|
+
|
|
1841
|
+
static void isl_dynfn_finalizer(JSRuntime *rt, JSValueConst val) {
|
|
1842
|
+
(void)rt;
|
|
1843
|
+
ScrDyn *box = JS_GetOpaque(val, isl_dynfn_class_id);
|
|
1844
|
+
if (box) scr_dyn_release(box);
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
static const JSClassDef isl_dynfn_class = {
|
|
1848
|
+
.class_name = "ScrDynFn",
|
|
1849
|
+
.finalizer = isl_dynfn_finalizer,
|
|
1850
|
+
};
|
|
1851
|
+
|
|
1852
|
+
static JSValue isl_dynfn_invoke(JSContext *ctx, JSValueConst this_val, int argc,
|
|
1853
|
+
JSValueConst *argv, int magic, JSValueConst *func_data) {
|
|
1854
|
+
(void)this_val;
|
|
1855
|
+
(void)magic;
|
|
1856
|
+
ScrDyn *box = JS_GetOpaque(func_data[0], isl_dynfn_class_id);
|
|
1857
|
+
if (!box) return JS_ThrowTypeError(ctx, "detached scriptc function");
|
|
1858
|
+
ScrDyn *stack_args[8];
|
|
1859
|
+
ScrDyn **dargs = argc <= 8 ? stack_args : malloc((size_t)argc * sizeof *dargs);
|
|
1860
|
+
for (int i = 0; i < argc; i++) dargs[i] = isl_dyn_from_value(argv[i]);
|
|
1861
|
+
bool strayed_before = isl_anchor_strayed;
|
|
1862
|
+
isl_host_depth++;
|
|
1863
|
+
ScrDyn *r = box->v.fn.thunk(box->v.fn.clo, dargs, (size_t)argc);
|
|
1864
|
+
isl_host_depth--;
|
|
1865
|
+
if (isl_anchor_strayed && !strayed_before) {
|
|
1866
|
+
/* The isl_hostfn_invoke re-anchor dance: a fiber the callback spawned
|
|
1867
|
+
* moved the engine's overflow anchor off this stack. */
|
|
1868
|
+
isl_anchor_here();
|
|
1869
|
+
isl_anchor_strayed = strayed_before;
|
|
1870
|
+
}
|
|
1871
|
+
for (int i = 0; i < argc; i++) scr_dyn_release(dargs[i]);
|
|
1872
|
+
if (dargs != stack_args) free(dargs);
|
|
1873
|
+
if (scr_exc_pending()) {
|
|
1874
|
+
if (r) scr_dyn_release(r);
|
|
1875
|
+
return isl_throw_pending(ctx);
|
|
1876
|
+
}
|
|
1877
|
+
if (!r) return JS_UNDEFINED;
|
|
1878
|
+
/* The thunk's DOM result converts back per the from_dyn rules; a kind
|
|
1879
|
+
* with no crossing (a handle, a promise) throws the same catchable
|
|
1880
|
+
* TypeError from_dyn would. */
|
|
1881
|
+
const char *bad = isl_dyn_unmarshalable(r);
|
|
1882
|
+
if (bad != NULL) {
|
|
1883
|
+
scr_dyn_release(r);
|
|
1884
|
+
return JS_ThrowTypeError(ctx, "an 'unknown' value holding %s cannot enter dynamically-executed code", bad);
|
|
1885
|
+
}
|
|
1886
|
+
JSValue out = isl_from_dyn(r);
|
|
1887
|
+
scr_dyn_release(r);
|
|
1888
|
+
return out; /* JS_EXCEPTION passes through as the engine throw */
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
/* A fresh engine function over a boxed DOM function (borrows d — the
|
|
1892
|
+
* opaque box retains it). */
|
|
1893
|
+
static JSValue isl_dynfn_new(const ScrDyn *d) {
|
|
1894
|
+
JSValue boxv = JS_NewObjectClass(isl_ctx, isl_dynfn_class_id);
|
|
1895
|
+
JS_SetOpaque(boxv, scr_dyn_retain((ScrDyn *)d));
|
|
1896
|
+
JSValueConst data[1] = {boxv};
|
|
1897
|
+
JSValue fn = JS_NewCFunctionData(isl_ctx, isl_dynfn_invoke, (int)d->v.fn.arity, 0, 1, data);
|
|
1898
|
+
JS_FreeValue(isl_ctx, boxv); /* fn's func_data holds its own reference */
|
|
1899
|
+
return fn;
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1516
1902
|
/* The typed adapters' absence test for `T | undefined` parameters. */
|
|
1517
1903
|
bool scr_jsval_is_undefined(ScrJsval *v) { return JS_IsUndefined(v->v); }
|
|
1518
1904
|
|