@scriptc/runtime 0.0.25 → 0.0.27
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_array.c +1 -1
- package/src/scr_assert.c +4 -4
- package/src/scr_async.c +235 -6
- package/src/scr_bytes.c +1 -1
- package/src/scr_child.c +2 -2
- package/src/scr_closure.c +2 -2
- package/src/scr_console.c +1 -1
- package/src/scr_cycle.c +14 -14
- package/src/scr_error.c +3 -3
- package/src/scr_events.c +48 -2
- package/src/scr_events_emitter.c +7 -7
- package/src/scr_exception.c +22 -12
- package/src/scr_http.c +1 -0
- package/src/scr_inspect.c +10 -10
- package/src/scr_island.c +8 -4
- package/src/scr_json.c +14 -14
- package/src/scr_lib.c +104 -28
- package/src/scr_library.c +33 -13
- package/src/scr_map.c +1 -1
- package/src/scr_musl.c +274 -0
- package/src/scr_net.c +118 -3
- package/src/scr_object.c +1 -1
- package/src/scr_regex.c +2 -2
- package/src/scr_runtime.h +82 -25
- package/src/scr_string.c +4 -4
- package/src/scr_symbol.c +1 -1
- package/src/scr_tls.c +22 -0
- package/src/scr_union.c +1 -1
- package/vendor/README.md +8 -0
package/package.json
CHANGED
package/src/scr_array.c
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
/* Live heap-array count for the RC audit lane (-DSCR_RC_AUDIT); same
|
|
9
9
|
* contract as scr_str_live_count in scr_string.c. */
|
|
10
10
|
#ifdef SCR_RC_AUDIT
|
|
11
|
-
static long scr_live_arrays = 0;
|
|
11
|
+
static SCR_TL long scr_live_arrays = 0;
|
|
12
12
|
long scr_arr_live_count(void) { return scr_live_arrays; }
|
|
13
13
|
#endif
|
|
14
14
|
|
package/src/scr_assert.c
CHANGED
|
@@ -158,9 +158,9 @@ void scr_assert_ok(bool pass, ScrStr *message) {
|
|
|
158
158
|
* PAIR already being compared answers equal (the coinductive step —
|
|
159
159
|
* Node's memo behavior exactly). The stack is global (comparisons never
|
|
160
160
|
* interleave; the emitted walks cannot throw mid-compare). */
|
|
161
|
-
static struct { const void *a, *b; } *g_deq_stack;
|
|
162
|
-
static size_t g_deq_len;
|
|
163
|
-
static size_t g_deq_cap;
|
|
161
|
+
static SCR_TL struct { const void *a, *b; } *g_deq_stack;
|
|
162
|
+
static SCR_TL size_t g_deq_len;
|
|
163
|
+
static SCR_TL size_t g_deq_cap;
|
|
164
164
|
|
|
165
165
|
bool scr_assert_deq_enter(const void *a, const void *b) {
|
|
166
166
|
for (size_t i = 0; i < g_deq_len; i++) {
|
|
@@ -1228,7 +1228,7 @@ typedef struct {
|
|
|
1228
1228
|
ScrStr *val; /* owned: the string value, or the regex's /source/flags */
|
|
1229
1229
|
} ScrShapeSlot;
|
|
1230
1230
|
|
|
1231
|
-
static struct {
|
|
1231
|
+
static SCR_TL struct {
|
|
1232
1232
|
ScrError *err; /* borrowed — alive across the straight-line sequence */
|
|
1233
1233
|
ScrShapeSlot slots[3];
|
|
1234
1234
|
} scr_shape;
|
package/src/scr_async.c
CHANGED
|
@@ -37,6 +37,8 @@
|
|
|
37
37
|
* served by a capped nanosleep — dispatch at the next turn's top, the
|
|
38
38
|
* cap bounding signal/stdin latency instead of a pollable wake fd. */
|
|
39
39
|
#include <windows.h>
|
|
40
|
+
#elif defined(__wasi__)
|
|
41
|
+
#include <poll.h>
|
|
40
42
|
#else
|
|
41
43
|
#include <poll.h>
|
|
42
44
|
#include <pthread.h>
|
|
@@ -45,6 +47,23 @@
|
|
|
45
47
|
#include <unistd.h>
|
|
46
48
|
#endif
|
|
47
49
|
|
|
50
|
+
#ifdef __wasi__
|
|
51
|
+
/* WASI Preview 1 has no process-spawn capability. The child unit is not
|
|
52
|
+
* linked for this target, but the generic loop keeps its quiescence hooks;
|
|
53
|
+
* their empty implementation makes the absence explicit at link time
|
|
54
|
+
* without weakening async/timer support. */
|
|
55
|
+
bool scr_children_pending(void) { return false; }
|
|
56
|
+
bool scr_children_reffed_pending(void) { return false; }
|
|
57
|
+
bool scr_children_failed_pending(void) { return false; }
|
|
58
|
+
void scr_children_poll(void) {}
|
|
59
|
+
int scr_children_wake_fd(void) { return -1; }
|
|
60
|
+
bool scr_children_wait(double max_wait_ms) {
|
|
61
|
+
(void)max_wait_ms;
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
void scr_children_teardown(void) {}
|
|
65
|
+
#endif
|
|
66
|
+
|
|
48
67
|
#if defined(__has_feature)
|
|
49
68
|
#if __has_feature(address_sanitizer)
|
|
50
69
|
#define SCR_ASAN_FIBERS 1
|
|
@@ -274,12 +293,18 @@ void scr_promise_trace_v(void *p, ScrTraceVisit visit, void *ctx) {
|
|
|
274
293
|
* so the slot only needs to name the destination; `from` is unused. */
|
|
275
294
|
#ifdef _WIN32
|
|
276
295
|
typedef void *ScrCtx;
|
|
296
|
+
#elif defined(__wasi__)
|
|
297
|
+
typedef unsigned char ScrCtx;
|
|
277
298
|
#else
|
|
278
299
|
typedef ucontext_t ScrCtx;
|
|
279
300
|
#endif
|
|
280
301
|
|
|
281
302
|
struct ScrFiber {
|
|
282
303
|
ScrCtx ctx;
|
|
304
|
+
/* wasm32 uses LLVM's stackless switched-coroutine frame instead of a
|
|
305
|
+
* native saved stack. The compiler emits these two generic wrappers in
|
|
306
|
+
* every WASI module. */
|
|
307
|
+
void *coro;
|
|
283
308
|
ScrCtx *return_to; /* whoever resumed us last (spawner or the loop) */
|
|
284
309
|
char *stack; /* POSIX only; Windows fibers own their stack (NULL) */
|
|
285
310
|
ScrPromise *promise; /* the promise this fiber settles (owned +1); NULL
|
|
@@ -350,6 +375,19 @@ long scr_abandoned_fiber_count(void) { return scr_fibers_abandoned; }
|
|
|
350
375
|
bool scr_on_fiber(void) { return scr_current != NULL; }
|
|
351
376
|
void *scr_fiber_self(void) { return scr_current; }
|
|
352
377
|
|
|
378
|
+
#ifdef __wasi__
|
|
379
|
+
extern void scr_wasi_coro_resume(void *handle);
|
|
380
|
+
extern void scr_wasi_coro_destroy(void *handle);
|
|
381
|
+
|
|
382
|
+
void scr_wasi_coro_started(void *handle) {
|
|
383
|
+
if (scr_current == NULL) {
|
|
384
|
+
fputs("scriptc: internal error: coroutine started outside a fiber\n", stderr);
|
|
385
|
+
abort();
|
|
386
|
+
}
|
|
387
|
+
scr_current->coro = handle;
|
|
388
|
+
}
|
|
389
|
+
#endif
|
|
390
|
+
|
|
353
391
|
/* Microtask queue: ready fibers, FIFO. */
|
|
354
392
|
static ScrFiber **scr_ready = NULL;
|
|
355
393
|
static size_t scr_ready_head = 0, scr_ready_len = 0, scr_ready_cap = 0;
|
|
@@ -867,6 +905,10 @@ static void scr_switch(ScrCtx *from, ScrCtx *to, ScrFiber *to_fiber) {
|
|
|
867
905
|
* fiber object — `from` has nothing to record. */
|
|
868
906
|
(void)from;
|
|
869
907
|
SwitchToFiber(*to);
|
|
908
|
+
#elif defined(__wasi__)
|
|
909
|
+
(void)from;
|
|
910
|
+
(void)to;
|
|
911
|
+
scr_trap("scriptc: internal error: native stack switch reached on WASI\n");
|
|
870
912
|
#elif defined(SCR_ASAN_FIBERS)
|
|
871
913
|
void **save = from_fiber ? &from_fiber->fake_stack : &scr_main_fake_stack;
|
|
872
914
|
const void *bottom = to_fiber ? to_fiber->stack : NULL;
|
|
@@ -1154,6 +1196,8 @@ static void scr_trampoline(void) {
|
|
|
1154
1196
|
static void scr_fiber_destroy(ScrFiber *f) {
|
|
1155
1197
|
#ifdef _WIN32
|
|
1156
1198
|
DeleteFiber(f->ctx);
|
|
1199
|
+
#elif defined(__wasi__)
|
|
1200
|
+
if (f->coro != NULL) scr_wasi_coro_destroy(f->coro);
|
|
1157
1201
|
#endif
|
|
1158
1202
|
scr_als_ctx_release(f->als);
|
|
1159
1203
|
free(f->stack);
|
|
@@ -1179,6 +1223,22 @@ ScrPromise *scr_async_spawn(void (*entry)(ScrFiber *, void *), void *argpack) {
|
|
|
1179
1223
|
ScrCtx here = scr_win_self();
|
|
1180
1224
|
f->ctx = CreateFiber(SCR_FIBER_STACK, scr_trampoline, NULL);
|
|
1181
1225
|
if (f->ctx == NULL) scr_oom();
|
|
1226
|
+
#elif defined(__wasi__)
|
|
1227
|
+
ScrFiber *spawner = scr_current;
|
|
1228
|
+
scr_current = f;
|
|
1229
|
+
scr_exc_swap_cell(&f->exc);
|
|
1230
|
+
scr_als_active = &f->als;
|
|
1231
|
+
entry(f, argpack); /* eager prefix; returns at suspend/final suspend */
|
|
1232
|
+
scr_current = spawner;
|
|
1233
|
+
scr_exc_swap_cell(spawner ? &spawner->exc : NULL);
|
|
1234
|
+
scr_als_active = spawner ? &spawner->als : &scr_als_main_slot;
|
|
1235
|
+
ScrPromise *result = scr_promise_retain(f->promise);
|
|
1236
|
+
if (f->done) {
|
|
1237
|
+
scr_promise_release(f->promise);
|
|
1238
|
+
scr_fiber_destroy(f);
|
|
1239
|
+
scr_fibers_live--;
|
|
1240
|
+
}
|
|
1241
|
+
return result;
|
|
1182
1242
|
#else
|
|
1183
1243
|
f->stack = malloc(SCR_FIBER_STACK);
|
|
1184
1244
|
if (!f->stack) scr_oom();
|
|
@@ -1190,6 +1250,7 @@ ScrPromise *scr_async_spawn(void (*entry)(ScrFiber *, void *), void *argpack) {
|
|
|
1190
1250
|
|
|
1191
1251
|
ucontext_t here;
|
|
1192
1252
|
#endif
|
|
1253
|
+
#ifndef __wasi__
|
|
1193
1254
|
f->return_to = &here;
|
|
1194
1255
|
ScrFiber *spawner = scr_current;
|
|
1195
1256
|
scr_switch(&here, &f->ctx, f);
|
|
@@ -1209,6 +1270,42 @@ ScrPromise *scr_async_spawn(void (*entry)(ScrFiber *, void *), void *argpack) {
|
|
|
1209
1270
|
scr_fibers_live--;
|
|
1210
1271
|
}
|
|
1211
1272
|
return result;
|
|
1273
|
+
#endif
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
/* A runtime-authored C continuation whose first operation awaits one known
|
|
1277
|
+
* dependency. Native fibers can park normally. wasm32 has no resumable C
|
|
1278
|
+
* stack, so queue the fiber only after the dependency settles; the entry's
|
|
1279
|
+
* await then extracts the result without attempting a stack switch. The
|
|
1280
|
+
* READY queue supplies the settled-await microtask hop in both cases. */
|
|
1281
|
+
ScrPromise *scr_async_spawn_after(ScrPromise *dependency,
|
|
1282
|
+
void (*entry)(ScrFiber *, void *),
|
|
1283
|
+
void *argpack) {
|
|
1284
|
+
#ifndef __wasi__
|
|
1285
|
+
(void)dependency;
|
|
1286
|
+
return scr_async_spawn(entry, argpack);
|
|
1287
|
+
#else
|
|
1288
|
+
ScrFiber *f = calloc(1, sizeof *f);
|
|
1289
|
+
if (!f) scr_oom();
|
|
1290
|
+
f->promise = scr_promise_new();
|
|
1291
|
+
f->entry = entry;
|
|
1292
|
+
f->argpack = argpack;
|
|
1293
|
+
f->als = scr_als_ctx_retain(*scr_als_active);
|
|
1294
|
+
scr_fibers_live++;
|
|
1295
|
+
|
|
1296
|
+
if (dependency->state == SCR_PROM_PENDING) {
|
|
1297
|
+
if (dependency->nwaiters == dependency->waiters_cap) {
|
|
1298
|
+
dependency->waiters_cap = dependency->waiters_cap ? dependency->waiters_cap * 2 : 4;
|
|
1299
|
+
dependency->waiters = realloc(
|
|
1300
|
+
dependency->waiters, dependency->waiters_cap * sizeof *dependency->waiters);
|
|
1301
|
+
if (!dependency->waiters) scr_oom();
|
|
1302
|
+
}
|
|
1303
|
+
dependency->waiters[dependency->nwaiters++] = f;
|
|
1304
|
+
} else {
|
|
1305
|
+
scr_ready_push(f);
|
|
1306
|
+
}
|
|
1307
|
+
return scr_promise_retain(f->promise);
|
|
1308
|
+
#endif
|
|
1212
1309
|
}
|
|
1213
1310
|
|
|
1214
1311
|
/* Parks the current fiber on `p` until it settles. Only fibers await. */
|
|
@@ -1228,6 +1325,42 @@ static void scr_await_park(ScrPromise *p) {
|
|
|
1228
1325
|
/* Resumed by the loop: return_to must now point at the loop's context. */
|
|
1229
1326
|
}
|
|
1230
1327
|
|
|
1328
|
+
#ifdef __wasi__
|
|
1329
|
+
/* The emitted coroutine suspends immediately after these preparation
|
|
1330
|
+
* calls. Settled promises still queue one ready turn; pending promises own
|
|
1331
|
+
* the continuation through their waiter list. */
|
|
1332
|
+
void scr_wasi_await_prepare(ScrFiber *self, ScrPromise *p) {
|
|
1333
|
+
if (p->state == SCR_PROM_PENDING) {
|
|
1334
|
+
if (p->nwaiters == p->waiters_cap) {
|
|
1335
|
+
p->waiters_cap = p->waiters_cap ? p->waiters_cap * 2 : 4;
|
|
1336
|
+
p->waiters = realloc(p->waiters, p->waiters_cap * sizeof *p->waiters);
|
|
1337
|
+
if (!p->waiters) scr_oom();
|
|
1338
|
+
}
|
|
1339
|
+
p->waiters[p->nwaiters++] = self;
|
|
1340
|
+
} else {
|
|
1341
|
+
scr_ready_push(self);
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
void scr_wasi_await_hop_prepare(ScrFiber *self) { scr_ready_push(self); }
|
|
1346
|
+
|
|
1347
|
+
bool scr_wasi_module_await_prepare(ScrFiber *self, ScrPromise *p) {
|
|
1348
|
+
if (p->state != SCR_PROM_PENDING) return false;
|
|
1349
|
+
scr_wasi_await_prepare(self, p);
|
|
1350
|
+
return true;
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
void scr_wasi_async_finish(ScrFiber *self) { scr_fiber_finish(self); }
|
|
1354
|
+
|
|
1355
|
+
void scr_wasi_gen_finish(ScrFiber *self) {
|
|
1356
|
+
if (scr_exc_genret_pending()) {
|
|
1357
|
+
scr_exc_clear();
|
|
1358
|
+
scr_gen_ret_to_out(self->gen);
|
|
1359
|
+
}
|
|
1360
|
+
scr_fiber_finish(self);
|
|
1361
|
+
}
|
|
1362
|
+
#endif
|
|
1363
|
+
|
|
1231
1364
|
/* One microtask hop: park the current fiber on the READY queue itself and
|
|
1232
1365
|
* yield. JS's `await` ALWAYS suspends — even on an already-settled promise
|
|
1233
1366
|
* the continuation runs as a microtask — so without this hop an awaiter of
|
|
@@ -1268,8 +1401,15 @@ static void scr_promise_rethrow(ScrPromise *p) {
|
|
|
1268
1401
|
|
|
1269
1402
|
/* Await result extraction. Rejection re-throws into the awaiter. */
|
|
1270
1403
|
static bool scr_await_settled(ScrPromise *p) {
|
|
1404
|
+
#ifdef __wasi__
|
|
1405
|
+
if (p->state == SCR_PROM_PENDING) {
|
|
1406
|
+
fputs("scriptc: internal error: resumed before awaited promise settled\n", stderr);
|
|
1407
|
+
abort();
|
|
1408
|
+
}
|
|
1409
|
+
#else
|
|
1271
1410
|
if (p->state != SCR_PROM_PENDING) scr_await_yield();
|
|
1272
1411
|
while (p->state == SCR_PROM_PENDING) scr_await_park(p);
|
|
1412
|
+
#endif
|
|
1273
1413
|
scr_prom_observe(p);
|
|
1274
1414
|
if (p->state == SCR_PROM_REJECTED) {
|
|
1275
1415
|
scr_promise_rethrow(p);
|
|
@@ -1295,7 +1435,9 @@ void scr_await_void(ScrPromise *p) { scr_await_settled(p); }
|
|
|
1295
1435
|
* dependency still parks this module fiber, and rejection propagates into
|
|
1296
1436
|
* it exactly like an ordinary await. */
|
|
1297
1437
|
void scr_module_await(ScrPromise *p) {
|
|
1438
|
+
#ifndef __wasi__
|
|
1298
1439
|
while (p->state == SCR_PROM_PENDING) scr_await_park(p);
|
|
1440
|
+
#endif
|
|
1299
1441
|
scr_prom_observe(p);
|
|
1300
1442
|
if (p->state == SCR_PROM_REJECTED) scr_promise_rethrow(p);
|
|
1301
1443
|
}
|
|
@@ -1524,7 +1666,7 @@ ScrPromise *scr_fsp_rename(ScrStr *oldpath, ScrStr *newpath) {
|
|
|
1524
1666
|
* libuv's default filesystem concurrency without creating one OS thread per
|
|
1525
1667
|
* request. A platform mutex publishes each result and the syscall's filesystem
|
|
1526
1668
|
* effects before the loop removes it from the completion queue. */
|
|
1527
|
-
#
|
|
1669
|
+
#if !defined(SCR_LIB) && !defined(__wasi__)
|
|
1528
1670
|
typedef struct ScrFsRenameOp {
|
|
1529
1671
|
ScrStr *oldpath;
|
|
1530
1672
|
ScrStr *newpath;
|
|
@@ -1678,6 +1820,49 @@ static bool scr_fs_renames_dispatch(void) {
|
|
|
1678
1820
|
scr_fs_rename_op_release(op);
|
|
1679
1821
|
return true;
|
|
1680
1822
|
}
|
|
1823
|
+
#elif defined(__wasi__)
|
|
1824
|
+
/* WASI Preview 1 has no threads, but rename itself is available. Queue the
|
|
1825
|
+
* request and perform it on the next loop turn so callback timing remains
|
|
1826
|
+
* asynchronous and the request remains a liveness handle. */
|
|
1827
|
+
typedef struct ScrFsRenameOp {
|
|
1828
|
+
ScrStr *oldpath;
|
|
1829
|
+
ScrStr *newpath;
|
|
1830
|
+
ScrClosure *cb;
|
|
1831
|
+
ScrFsRenameFn fn;
|
|
1832
|
+
struct ScrFsRenameOp *next;
|
|
1833
|
+
} ScrFsRenameOp;
|
|
1834
|
+
|
|
1835
|
+
static ScrFsRenameOp *scr_fs_rename_done = NULL;
|
|
1836
|
+
static ScrFsRenameOp **scr_fs_rename_done_tail = &scr_fs_rename_done;
|
|
1837
|
+
static size_t scr_fs_rename_pending_count = 0;
|
|
1838
|
+
|
|
1839
|
+
static bool scr_fs_renames_pending(void) { return scr_fs_rename_pending_count != 0; }
|
|
1840
|
+
|
|
1841
|
+
static bool scr_fs_renames_dispatch(void) {
|
|
1842
|
+
ScrFsRenameOp *op = scr_fs_rename_done;
|
|
1843
|
+
if (op == NULL) return false;
|
|
1844
|
+
scr_fs_rename_done = op->next;
|
|
1845
|
+
if (scr_fs_rename_done == NULL) scr_fs_rename_done_tail = &scr_fs_rename_done;
|
|
1846
|
+
scr_fs_rename_pending_count--;
|
|
1847
|
+
|
|
1848
|
+
int error = scr_fs_rename_raw(op->oldpath, op->newpath);
|
|
1849
|
+
ScrCaught *caught = NULL;
|
|
1850
|
+
ScrError *err = NULL;
|
|
1851
|
+
if (error != 0) {
|
|
1852
|
+
scr_fs_rename_error(error, op->oldpath, op->newpath);
|
|
1853
|
+
caught = scr_exc_take();
|
|
1854
|
+
if (caught && caught->kind == SCR_EXC_OBJ && scr_error_is(caught->payload)) {
|
|
1855
|
+
err = (ScrError *)caught->payload;
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
op->fn(op->cb, err);
|
|
1859
|
+
scr_caught_release(caught);
|
|
1860
|
+
scr_str_release(op->oldpath);
|
|
1861
|
+
scr_str_release(op->newpath);
|
|
1862
|
+
scr_closure_release(op->cb);
|
|
1863
|
+
free(op);
|
|
1864
|
+
return true;
|
|
1865
|
+
}
|
|
1681
1866
|
#else
|
|
1682
1867
|
static bool scr_fs_renames_pending(void) { return false; }
|
|
1683
1868
|
static bool scr_fs_renames_dispatch(void) { return false; }
|
|
@@ -1685,10 +1870,20 @@ static bool scr_fs_renames_dispatch(void) { return false; }
|
|
|
1685
1870
|
|
|
1686
1871
|
void scr_fs_rename_async(ScrStr *oldpath, ScrStr *newpath,
|
|
1687
1872
|
ScrClosure *cb, ScrFsRenameFn fn) {
|
|
1688
|
-
#
|
|
1873
|
+
#if defined(SCR_LIB)
|
|
1689
1874
|
(void)oldpath; (void)newpath; (void)fn;
|
|
1690
1875
|
scr_closure_release(cb);
|
|
1691
|
-
scr_trap("scriptc:
|
|
1876
|
+
scr_trap("scriptc: callback-style fs.rename is not supported by this target\n");
|
|
1877
|
+
#elif defined(__wasi__)
|
|
1878
|
+
ScrFsRenameOp *op = calloc(1, sizeof *op);
|
|
1879
|
+
if (!op) scr_trap("scriptc: out of memory\n");
|
|
1880
|
+
op->oldpath = scr_str_retain(oldpath);
|
|
1881
|
+
op->newpath = scr_str_retain(newpath);
|
|
1882
|
+
op->cb = cb;
|
|
1883
|
+
op->fn = fn;
|
|
1884
|
+
*scr_fs_rename_done_tail = op;
|
|
1885
|
+
scr_fs_rename_done_tail = &op->next;
|
|
1886
|
+
scr_fs_rename_pending_count++;
|
|
1692
1887
|
#else
|
|
1693
1888
|
if (!scr_fs_rename_shutdown_registered) {
|
|
1694
1889
|
if (atexit(scr_fs_renames_shutdown) != 0) {
|
|
@@ -1893,7 +2088,25 @@ static void scr_resume_fiber(ScrFiber *f) {
|
|
|
1893
2088
|
(void)scr_win_self();
|
|
1894
2089
|
#endif
|
|
1895
2090
|
f->return_to = &scr_loop_ctx;
|
|
2091
|
+
#ifdef __wasi__
|
|
2092
|
+
scr_current = f;
|
|
2093
|
+
scr_exc_swap_cell(&f->exc);
|
|
2094
|
+
scr_als_active = &f->als;
|
|
2095
|
+
if (f->coro != NULL) {
|
|
2096
|
+
scr_wasi_coro_resume(f->coro);
|
|
2097
|
+
} else {
|
|
2098
|
+
/* scr_async_spawn_after's runtime C continuation. Its dependency is
|
|
2099
|
+
* settled, so the entry can extract through scr_await_* without a
|
|
2100
|
+
* stack switch and then completes in this turn. */
|
|
2101
|
+
f->entry(f, f->argpack);
|
|
2102
|
+
scr_fiber_finish(f);
|
|
2103
|
+
}
|
|
2104
|
+
scr_current = NULL;
|
|
2105
|
+
scr_exc_swap_cell(NULL);
|
|
2106
|
+
scr_als_active = &scr_als_main_slot;
|
|
2107
|
+
#else
|
|
1896
2108
|
scr_switch(&scr_loop_ctx, &f->ctx, f);
|
|
2109
|
+
#endif
|
|
1897
2110
|
if (f->done) {
|
|
1898
2111
|
scr_promise_release(f->promise);
|
|
1899
2112
|
scr_fiber_destroy(f);
|
|
@@ -2222,8 +2435,9 @@ bool scr_loop_run(ScrPromise *top_level) {
|
|
|
2222
2435
|
now = scr_now_ms();
|
|
2223
2436
|
if (scr_ready_len > 0) continue; /* io callbacks woke fibers */
|
|
2224
2437
|
} else if (evw || net || dgram || watch) {
|
|
2225
|
-
#
|
|
2226
|
-
/* The win32 arm
|
|
2438
|
+
#if defined(_WIN32) || defined(__wasi__)
|
|
2439
|
+
/* The win32 arm, and WASI hosts whose poll_oneoff adapters do not
|
|
2440
|
+
* reliably wake for a closed inherited stdin pipe: the sleep is a capped nanosleep and
|
|
2227
2441
|
* the next turn's dispatch does the work — signal flags (set by the
|
|
2228
2442
|
* CRT handler on msvcrt's console-ctrl thread) and the stdin probe
|
|
2229
2443
|
* at SCR_SIGNAL_POLL_MS granularity (scr_events.c), socket/dgram
|
|
@@ -2315,7 +2529,7 @@ bool scr_loop_run(ScrPromise *top_level) {
|
|
|
2315
2529
|
* WNOHANG sweep at the next turn's top stays the reaper. */
|
|
2316
2530
|
if (kids) (void)scr_children_wait(0);
|
|
2317
2531
|
now = scr_now_ms();
|
|
2318
|
-
#endif /* !_WIN32 */
|
|
2532
|
+
#endif /* !_WIN32 && !__wasi__ */
|
|
2319
2533
|
} else if (kids && scr_children_wait(due > now ? due - now : 0)) {
|
|
2320
2534
|
now = scr_now_ms(); /* woke early on an exit, or hit the deadline */
|
|
2321
2535
|
} else {
|
|
@@ -2727,6 +2941,8 @@ ScrGen *scr_gen_new(void (*entry)(ScrFiber *, void *), void *argpack,
|
|
|
2727
2941
|
#ifdef _WIN32
|
|
2728
2942
|
f->ctx = CreateFiber(SCR_FIBER_STACK, scr_trampoline, NULL);
|
|
2729
2943
|
if (f->ctx == NULL) scr_oom();
|
|
2944
|
+
#elif defined(__wasi__)
|
|
2945
|
+
f->ctx = 0;
|
|
2730
2946
|
#else
|
|
2731
2947
|
f->stack = malloc(SCR_FIBER_STACK);
|
|
2732
2948
|
if (!f->stack) scr_oom();
|
|
@@ -2898,16 +3114,29 @@ static void scr_gen_switch_in(ScrGen *g) {
|
|
|
2898
3114
|
g->state = SCR_GEN_RUNNING;
|
|
2899
3115
|
#ifdef _WIN32
|
|
2900
3116
|
ScrCtx here = scr_win_self();
|
|
3117
|
+
#elif defined(__wasi__)
|
|
3118
|
+
ScrCtx here = 0;
|
|
2901
3119
|
#else
|
|
2902
3120
|
ucontext_t here;
|
|
2903
3121
|
#endif
|
|
2904
3122
|
f->return_to = &here;
|
|
2905
3123
|
ScrFiber *me = scr_current;
|
|
3124
|
+
#ifdef __wasi__
|
|
3125
|
+
scr_current = f;
|
|
3126
|
+
scr_exc_swap_cell(&f->exc);
|
|
3127
|
+
scr_als_active = &f->als;
|
|
3128
|
+
if (f->coro == NULL) f->entry(f, f->argpack);
|
|
3129
|
+
else scr_wasi_coro_resume(f->coro);
|
|
3130
|
+
scr_current = me;
|
|
3131
|
+
scr_exc_swap_cell(me != NULL ? &me->exc : NULL);
|
|
3132
|
+
scr_als_active = me != NULL ? &me->als : &scr_als_main_slot;
|
|
3133
|
+
#else
|
|
2906
3134
|
scr_switch(&here, &f->ctx, f);
|
|
2907
3135
|
/* Back on the consumer: restore identity + the consumer's cell (the
|
|
2908
3136
|
* yield/finish switch targeted NULL — main's cell). */
|
|
2909
3137
|
scr_current = me;
|
|
2910
3138
|
scr_exc_swap_cell(me != NULL ? &me->exc : NULL);
|
|
3139
|
+
#endif
|
|
2911
3140
|
if (f->done) {
|
|
2912
3141
|
g->state = SCR_GEN_DONE;
|
|
2913
3142
|
if (f->exc.kind != SCR_EXC_NONE) {
|
package/src/scr_bytes.c
CHANGED
package/src/scr_child.c
CHANGED
|
@@ -2292,7 +2292,7 @@ ScrStr *scr_exec_sync_core(ScrStr *cmd, ScrArr *args, const ScrExecOpts *o) {
|
|
|
2292
2292
|
} else if (o->stderr_mode != 3) {
|
|
2293
2293
|
posix_spawn_file_actions_addopen(&fa, 2, "/dev/null", O_WRONLY, 0);
|
|
2294
2294
|
} /* mode 3 (inherit): the child keeps the parent's fd 2 */
|
|
2295
|
-
#if defined(__APPLE__) || defined(__GLIBC__)
|
|
2295
|
+
#if defined(__APPLE__) || defined(__GLIBC__) || defined(SCR_MUSL)
|
|
2296
2296
|
if (o->cwd != NULL) {
|
|
2297
2297
|
posix_spawn_file_actions_addchdir_np(&fa, o->cwd->data);
|
|
2298
2298
|
}
|
|
@@ -3180,7 +3180,7 @@ ScrChild *scr_spawn_opts(ScrStr *cmd, ScrArr *args, double in_mode,
|
|
|
3180
3180
|
} else if ((int)err_mode == 3) {
|
|
3181
3181
|
posix_spawn_file_actions_adddup2(&fa, err_pipe[1], 2);
|
|
3182
3182
|
}
|
|
3183
|
-
#if defined(__APPLE__) || defined(__GLIBC__)
|
|
3183
|
+
#if defined(__APPLE__) || defined(__GLIBC__) || defined(SCR_MUSL)
|
|
3184
3184
|
if (cwd != NULL && cwd->len > 0) {
|
|
3185
3185
|
posix_spawn_file_actions_addchdir_np(&fa, cwd->data);
|
|
3186
3186
|
}
|
package/src/scr_closure.c
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
#include <string.h>
|
|
6
6
|
|
|
7
7
|
#ifdef SCR_RC_AUDIT
|
|
8
|
-
static long scr_live_boxes = 0;
|
|
9
|
-
static long scr_live_closures = 0;
|
|
8
|
+
static SCR_TL long scr_live_boxes = 0;
|
|
9
|
+
static SCR_TL long scr_live_closures = 0;
|
|
10
10
|
long scr_box_live_count(void) { return scr_live_boxes; }
|
|
11
11
|
long scr_closure_live_count(void) { return scr_live_closures; }
|
|
12
12
|
#endif
|
package/src/scr_console.c
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
/* Set by scr_async.c at loop exhaustion; lives here (unconditionally) so
|
|
12
12
|
* binaries that link the console without the async runtime still link, and
|
|
13
13
|
* plain builds satisfy scr_async's reference. */
|
|
14
|
-
static long scr_abandoned_fibers = 0;
|
|
14
|
+
static SCR_TL long scr_abandoned_fibers = 0;
|
|
15
15
|
void scr_note_abandoned_fibers(long n) { scr_abandoned_fibers = n; }
|
|
16
16
|
|
|
17
17
|
#ifndef SCR_LIB
|
package/src/scr_cycle.c
CHANGED
|
@@ -95,7 +95,7 @@ static void scr_cyc_oom(void) {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
/* Live cycle-headered objects — what the full-pass trigger watches. */
|
|
98
|
-
static size_t scr_cyc_live = 0;
|
|
98
|
+
static SCR_TL size_t scr_cyc_live = 0;
|
|
99
99
|
|
|
100
100
|
void *scr_cyc_alloc(size_t size, ScrTraceFn trace, ScrCycFreeFn free_fn) {
|
|
101
101
|
ScrCycHdr *h = calloc(1, sizeof(ScrCycHdr) + size);
|
|
@@ -137,25 +137,25 @@ static void scr_cyc_push(ScrVec *vec, void *obj) {
|
|
|
137
137
|
|
|
138
138
|
/* ── the candidate-root buffers (one per generation) ──────────────────── */
|
|
139
139
|
|
|
140
|
-
static ScrVec scr_roots[SCR_CYC_NGENS];
|
|
141
|
-
static bool scr_collecting = false;
|
|
140
|
+
static SCR_TL ScrVec scr_roots[SCR_CYC_NGENS];
|
|
141
|
+
static SCR_TL bool scr_collecting = false;
|
|
142
142
|
|
|
143
143
|
/* The candidates of the pass in flight, gathered across the generations it
|
|
144
144
|
* collects so the phases can walk them without re-filtering the buffers. */
|
|
145
|
-
static ScrVec scr_cands;
|
|
145
|
+
static SCR_TL ScrVec scr_cands;
|
|
146
146
|
|
|
147
147
|
/* Nursery survivors awaiting promotion (see scr_scan_black). */
|
|
148
|
-
static ScrVec scr_promote;
|
|
148
|
+
static SCR_TL ScrVec scr_promote;
|
|
149
149
|
|
|
150
150
|
/* The gathered white set (freed after the walk completes). */
|
|
151
|
-
static ScrVec scr_white;
|
|
151
|
+
static SCR_TL ScrVec scr_white;
|
|
152
152
|
|
|
153
153
|
/* Cross-generation targets pinned by the white set (see scr_xg_pin_visit),
|
|
154
154
|
* one entry per skipped edge. Drained after the teardowns. */
|
|
155
|
-
static ScrVec scr_xgen;
|
|
155
|
+
static SCR_TL ScrVec scr_xgen;
|
|
156
156
|
|
|
157
157
|
/* Objects above this generation are invisible to the pass in flight. */
|
|
158
|
-
static unsigned scr_gen_limit = SCR_CYC_MATURE;
|
|
158
|
+
static SCR_TL unsigned scr_gen_limit = SCR_CYC_MATURE;
|
|
159
159
|
|
|
160
160
|
static size_t scr_cyc_pass(unsigned gen_limit);
|
|
161
161
|
|
|
@@ -166,10 +166,10 @@ static size_t scr_cyc_pass(unsigned gen_limit);
|
|
|
166
166
|
#define SCR_CYC_FULL_FLOOR 4096 /* ...but not below this many objects */
|
|
167
167
|
|
|
168
168
|
/* Live count as of the end of the last full pass. */
|
|
169
|
-
static size_t scr_cyc_live_after_full = 0;
|
|
169
|
+
static SCR_TL size_t scr_cyc_live_after_full = 0;
|
|
170
170
|
|
|
171
171
|
static size_t scr_cyc_nursery_threshold(void) {
|
|
172
|
-
static size_t cached = 0;
|
|
172
|
+
static SCR_TL size_t cached = 0;
|
|
173
173
|
if (cached == 0) {
|
|
174
174
|
const char *env = getenv("SCR_CYCLE_THRESHOLD");
|
|
175
175
|
long v = env ? strtol(env, NULL, 10) : 0;
|
|
@@ -186,12 +186,12 @@ static size_t scr_cyc_nursery_threshold(void) {
|
|
|
186
186
|
* hysteresis a large mature buffer — which a nursery pass cannot drain —
|
|
187
187
|
* would re-arm on every single release. Both start at zero, so the first
|
|
188
188
|
* release runs one trivial pass that arms them properly. */
|
|
189
|
-
static size_t scr_cyc_nbuffered = 0;
|
|
190
|
-
static size_t scr_cyc_trigger = 0;
|
|
189
|
+
static SCR_TL size_t scr_cyc_nbuffered = 0;
|
|
190
|
+
static SCR_TL size_t scr_cyc_trigger = 0;
|
|
191
191
|
|
|
192
192
|
/* Scheduled nursery passes for which at least one mature root was waiting.
|
|
193
193
|
* A full pass resets the age; with no mature backlog there is nothing to age. */
|
|
194
|
-
static size_t scr_cyc_scheduled_mature_age = 0;
|
|
194
|
+
static SCR_TL size_t scr_cyc_scheduled_mature_age = 0;
|
|
195
195
|
|
|
196
196
|
static size_t scr_cyc_buffered(void) {
|
|
197
197
|
return scr_roots[SCR_CYC_NURSERY].n + scr_roots[SCR_CYC_MATURE].n;
|
|
@@ -404,7 +404,7 @@ static void scr_xg_pin_visit(void *child, void *ctx) {
|
|
|
404
404
|
}
|
|
405
405
|
|
|
406
406
|
/* Freed by the cross-generation drain, for the caller's fixpoint. */
|
|
407
|
-
static size_t scr_xg_freed = 0;
|
|
407
|
+
static SCR_TL size_t scr_xg_freed = 0;
|
|
408
408
|
|
|
409
409
|
static void scr_xg_release(void *obj);
|
|
410
410
|
static void scr_xg_child_visit(void *child, void *ctx) {
|
package/src/scr_error.c
CHANGED
|
@@ -17,7 +17,7 @@ static void scr_error_oom(void) {
|
|
|
17
17
|
scr_trap("scriptc: out of memory\n");
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
static bool scr_error_traced = false;
|
|
20
|
+
static SCR_TL bool scr_error_traced = false;
|
|
21
21
|
|
|
22
22
|
void scr_error_set_traced(void) { scr_error_traced = true; }
|
|
23
23
|
|
|
@@ -49,7 +49,7 @@ static void scr_error_gcfree(void *obj) {
|
|
|
49
49
|
* must stay linkable WITHOUT the checked-dynamic tree (the runtime C-unit tests link
|
|
50
50
|
* subsets), so the drop goes through a hook scr_json.c installs before
|
|
51
51
|
* any cause can exist (scr_domex_new is the only producer). */
|
|
52
|
-
static void (*scr_domex_cause_drop)(void *obj) = NULL;
|
|
52
|
+
static SCR_TL void (*scr_domex_cause_drop)(void *obj) = NULL;
|
|
53
53
|
|
|
54
54
|
void scr_domex_install_cause_drop(void (*fn)(void *obj)) {
|
|
55
55
|
scr_domex_cause_drop = fn;
|
|
@@ -90,7 +90,7 @@ static void scr_domex_reld(void *obj) {
|
|
|
90
90
|
|
|
91
91
|
/* Defaults cover only the instants before main() stamps the program's real
|
|
92
92
|
* preorder intervals; release is permanent. */
|
|
93
|
-
ScrVt scr_error_vts[5] = {
|
|
93
|
+
SCR_TL ScrVt scr_error_vts[5] = {
|
|
94
94
|
{0, 4, &scr_error_reld}, /* Error */
|
|
95
95
|
{1, 1, &scr_error_reld}, /* TypeError */
|
|
96
96
|
{2, 2, &scr_error_reld}, /* RangeError */
|