@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/src/scr_events.c CHANGED
@@ -30,6 +30,9 @@
30
30
  #include <io.h>
31
31
  #include <unistd.h> /* mingw-w64 ships one: read */
32
32
  #include <windows.h>
33
+ #elif defined(__wasi__)
34
+ #include <poll.h>
35
+ #include <unistd.h>
33
36
  #else
34
37
  #include <poll.h>
35
38
  #include <unistd.h>
@@ -40,6 +43,21 @@ static void scr_events_oom(void) {
40
43
  abort();
41
44
  }
42
45
 
46
+ #ifdef __wasi__
47
+ /* The native child unit normally owns these generic Error-listener adapters.
48
+ * WASI cannot spawn children and therefore does not link that unit, while
49
+ * stdin's error event intentionally shares the same adapter ABI. */
50
+ void scr_child_err_thunk0(ScrClosure *cb, ScrStr *msg) {
51
+ (void)msg;
52
+ ((void (*)(ScrClosure *))cb->fn)(cb);
53
+ }
54
+
55
+ void scr_child_err_thunk_error(ScrClosure *cb, ScrStr *msg) {
56
+ ScrError *error = scr_error_new(0 /* Error */, msg);
57
+ ((void (*)(ScrClosure *, ScrError *))cb->fn)(cb, error);
58
+ }
59
+ #endif
60
+
43
61
  /* ── process signal events (process.on("SIGINT" | "SIGTERM")) ─────────
44
62
  * Classic self-pipe integration: a watched signal's sigaction handler
45
63
  * (installed WITHOUT SA_RESTART) sets a per-signal flag and writes one
@@ -62,7 +80,7 @@ typedef struct {
62
80
 
63
81
  typedef struct ScrSigReg {
64
82
  int sig;
65
- #ifdef _WIN32
83
+ #if defined(_WIN32) || defined(__wasi__)
66
84
  void (*prev)(int); /* restored when the last listener leaves */
67
85
  #else
68
86
  struct sigaction prev; /* restored when the last listener leaves */
@@ -116,7 +134,7 @@ static void scr_sig_cleanup(void) {
116
134
  }
117
135
 
118
136
  static void scr_wake_pipe_init(void) {
119
- #ifdef _WIN32
137
+ #if defined(_WIN32) || defined(__wasi__)
120
138
  /* No self-pipe: the win32 loop never polls fds (scr_async.c's capped
121
139
  * nanosleep takes the idle sleep), so the handler's flag is the whole
122
140
  * wake path and the fds stay -1 — every pipe use below is guarded. */
@@ -141,6 +159,15 @@ static void scr_wake_pipe_drain(void) {
141
159
  }
142
160
 
143
161
  void scr_signal_on(double signum, ScrClosure *cb /*moves*/, bool once) {
162
+ #ifdef __wasi__
163
+ (void)signum;
164
+ (void)once;
165
+ scr_closure_release(cb);
166
+ /* The compiler rejects this capability before linking. This guard keeps
167
+ * the optional events unit honest if hand-authored IR reaches it. */
168
+ scr_trap("scriptc: internal error: OS signal surface reached on WASI\n");
169
+ return;
170
+ #else
144
171
  int sig = (int)signum;
145
172
  if (sig <= 0 || sig >= SCR_SIG_MAX) {
146
173
  scr_closure_release(cb); /* frontend only emits classic signals */
@@ -176,11 +203,15 @@ void scr_signal_on(double signum, ScrClosure *cb /*moves*/, bool once) {
176
203
  reg->ls[reg->n].cb = cb;
177
204
  reg->ls[reg->n].once = once;
178
205
  reg->n++;
206
+ #endif
179
207
  }
180
208
 
181
209
  static void scr_sig_reg_drop(ScrSigReg *reg) {
182
210
  #ifdef _WIN32
183
211
  signal(reg->sig, reg->prev); /* default disposition back */
212
+ #elif defined(__wasi__)
213
+ /* No signal disposition exists in WASI Preview 1. The compiler refuses
214
+ * signal registrations, so this arm is only the cleanup safety net. */
184
215
  #else
185
216
  sigaction(reg->sig, &reg->prev, NULL); /* default disposition back */
186
217
  #endif
@@ -193,6 +224,12 @@ static void scr_sig_reg_drop(ScrSigReg *reg) {
193
224
  }
194
225
 
195
226
  void scr_signal_off(double signum, ScrClosure *cb /*borrowed*/) {
227
+ #ifdef __wasi__
228
+ (void)signum;
229
+ (void)cb;
230
+ scr_trap("scriptc: internal error: OS signal surface reached on WASI\n");
231
+ return;
232
+ #else
196
233
  int sig = (int)signum;
197
234
  ScrSigReg *reg = scr_sig_regs;
198
235
  while (reg && reg->sig != sig) reg = reg->next;
@@ -206,6 +243,7 @@ void scr_signal_off(double signum, ScrClosure *cb /*borrowed*/) {
206
243
  return;
207
244
  }
208
245
  }
246
+ #endif
209
247
  }
210
248
 
211
249
  /* One dispatch pass: every flagged signal fires its listener SNAPSHOT
@@ -486,6 +524,10 @@ static void scr_stdin_service(void) {
486
524
  if (WaitForSingleObject(h, 0) != WAIT_OBJECT_0) return;
487
525
  }
488
526
  }
527
+ #elif defined(__wasi__)
528
+ /* install() puts fd 0 in nonblocking mode, so a direct read is both the
529
+ * readiness probe and the operation. This also observes a closed pipe on
530
+ * WASI hosts whose poll_oneoff adapter does not report POLLHUP. */
489
531
  #else
490
532
  struct pollfd pfd = {0 /* stdin */, POLLIN, 0};
491
533
  int rc = poll(&pfd, 1, 0);
@@ -705,6 +747,10 @@ void scr_events_install(void) {
705
747
  static bool installed = false;
706
748
  if (installed) return;
707
749
  installed = true;
750
+ #ifdef __wasi__
751
+ int stdin_flags = fcntl(0, F_GETFL, 0);
752
+ if (stdin_flags >= 0) (void)fcntl(0, F_SETFL, stdin_flags | O_NONBLOCK);
753
+ #endif
708
754
  atexit(scr_events_cleanup_atexit);
709
755
  atexit(scr_exit_atexit);
710
756
  scr_loop_set_events(&scr_events_pending, &scr_events_watching, &scr_events_dispatch,
@@ -97,11 +97,11 @@ struct ScrEeReg {
97
97
  * stamps pre/post from the program's preorder numbering (the
98
98
  * scr_error_vts precedent). release is the direct teardown below. */
99
99
  static void scr_emitter_release_direct(void *obj);
100
- ScrVt scr_emitter_vt = {0, 0, &scr_emitter_release_direct};
100
+ SCR_TL ScrVt scr_emitter_vt = {0, 0, &scr_emitter_release_direct};
101
101
 
102
- double scr_emitter_default_max = 10; /* EventEmitter.defaultMaxListeners */
102
+ SCR_TL double scr_emitter_default_max = 10; /* EventEmitter.defaultMaxListeners */
103
103
 
104
- static bool scr_ee_trace_hint_shown = false;
104
+ static SCR_TL bool scr_ee_trace_hint_shown = false;
105
105
 
106
106
  /* Post-registration hook (scr_runtime.h): the stream unit's flow kick.
107
107
  * NULL in stream-free builds — behavior byte-identical. */
@@ -390,10 +390,10 @@ ScrEmitter *scr_emitter_on_via(ScrEmitter *em, ScrStr *name, ScrClosure *orig /*
390
390
 
391
391
  /* ── fixed-arity invoke shims (scr_runtime.h's contract) ──────────────
392
392
  * Read k pointer-size slots and call the fixed-signature adapter behind
393
- * cb->fn. The LLVM lane's emit sites pass every tuple argument pointer-
394
- * classed (f64 as its i64 bit pattern, bool zero-extended) and the
395
- * runtime's own emits are pointer-only, so va_arg(ap, void *) reads every
396
- * tuple a fixed-registered listener can observe. */
393
+ * cb->fn. The LLVM lane's emit sites pass scalars by pointer to typed stack
394
+ * slots and references directly; the runtime's own emits are pointer-only,
395
+ * so va_arg(ap, void *) reads every tuple a fixed-registered listener can
396
+ * observe on 32- and 64-bit targets. */
397
397
  void scr_ee_inv_fixed0(ScrClosure *cb, va_list ap) {
398
398
  (void)ap;
399
399
  ((void (*)(ScrClosure *))cb->fn)(cb);
@@ -35,28 +35,38 @@ _Noreturn void scr_trap_fmt(const char *fmt, ...) {
35
35
  }
36
36
  #endif /* !SCR_LIB */
37
37
 
38
- static ScrExcCell scr_main_exc; /* fiber zero (the main stack) */
38
+ static SCR_TL ScrExcCell scr_main_exc; /* fiber zero (the main stack) */
39
+ #if defined(SCR_LIB) && defined(SCR_THREAD_INSTANCES)
40
+ /* A thread-local pointer cannot be initialized with &scr_main_exc — the
41
+ * address of a thread-local is not a constant expression — so NULL stands
42
+ * for the main cell and every read resolves it. Fibers never exist in
43
+ * library artifacts, so the cell only ever IS the main cell here. */
44
+ static SCR_TL ScrExcCell *scr_cur;
45
+ #define SCR_EXC_CUR() (scr_cur ? scr_cur : &scr_main_exc)
46
+ #else
39
47
  static ScrExcCell *scr_cur = &scr_main_exc;
48
+ #define SCR_EXC_CUR() (scr_cur)
49
+ #endif
40
50
 
41
51
  /* Fiber switching (scr_async.c) points the exception machinery at the
42
52
  * incoming fiber's cell; returns the previous cell. */
43
53
  ScrExcCell *scr_exc_swap_cell(ScrExcCell *cell) {
44
- ScrExcCell *prev = scr_cur;
54
+ ScrExcCell *prev = SCR_EXC_CUR();
45
55
  scr_cur = cell ? cell : &scr_main_exc;
46
56
  return prev;
47
57
  }
48
58
 
49
59
  /* The ACTIVE cell, for runtime code that moves a pending payload out of it
50
60
  * (a new-Promise executor throw rejecting the promise). */
51
- ScrExcCell *scr_exc_current_cell(void) { return scr_cur; }
61
+ ScrExcCell *scr_exc_current_cell(void) { return SCR_EXC_CUR(); }
52
62
 
53
- #define scr_exc_kind (scr_cur->kind)
54
- #define scr_exc_f64 (scr_cur->f64)
55
- #define scr_exc_bool (scr_cur->b)
56
- #define scr_exc_payload (scr_cur->payload)
57
- #define scr_exc_retain_fn (scr_cur->retain_fn)
58
- #define scr_exc_release_fn (scr_cur->release_fn)
59
- #define scr_exc_trace_fn (scr_cur->trace_fn)
63
+ #define scr_exc_kind (SCR_EXC_CUR()->kind)
64
+ #define scr_exc_f64 (SCR_EXC_CUR()->f64)
65
+ #define scr_exc_bool (SCR_EXC_CUR()->b)
66
+ #define scr_exc_payload (SCR_EXC_CUR()->payload)
67
+ #define scr_exc_retain_fn (SCR_EXC_CUR()->retain_fn)
68
+ #define scr_exc_release_fn (SCR_EXC_CUR()->release_fn)
69
+ #define scr_exc_trace_fn (SCR_EXC_CUR()->trace_fn)
60
70
 
61
71
  bool scr_exc_pending(void) { return scr_exc_kind != SCR_EXC_NONE; }
62
72
 
@@ -187,7 +197,7 @@ ScrStr *scr_caught_to_string(const ScrCaught *c) {
187
197
  * delivery; only the 0x01-led verbatim path below bypasses assembly. */
188
198
  void scr_library_check_exc(void) {
189
199
  if (!scr_exc_pending()) return;
190
- static char buf[1024]; /* the message is copied out before the payload dies */
200
+ static SCR_TL char buf[1024]; /* the message is copied out before the payload dies */
191
201
  /* The ratified verbatim rule: a thrown message that ALREADY begins with
192
202
  * the structured trap-teaching marker (0x01) is delivered exactly as
193
203
  * authored — no "Uncaught " prefix, no added newline — which is how a
@@ -301,7 +311,7 @@ void scr_throw_obj(void *v, void *(*retain)(void *), void (*release)(void *),
301
311
  * events unit's atexit, when linked) must see that code, like Node's.
302
312
  * Lives HERE so the unit is self-contained (the reporters below and in
303
313
  * scr_async.c both note it; scr_events.c reads it). */
304
- static int scr_exit_code_hint = 0;
314
+ static SCR_TL int scr_exit_code_hint = 0;
305
315
  void scr_exit_code_note(int code) { scr_exit_code_hint = code; }
306
316
  int scr_exit_code_hint_get(void) { return scr_exit_code_hint; }
307
317
 
package/src/scr_http.c CHANGED
@@ -2009,6 +2009,7 @@ void scr_http2_stream_undef_call(ScrStr *member) {
2009
2009
  ScrNetServer *scr_http_create_server(ScrClosure *handler /*moves, nullable*/, ScrHttpReqFn fn) {
2010
2010
  scr_http_install();
2011
2011
  ScrNetServer *s = scr_net_create_server(NULL, NULL);
2012
+ scr_net_server_enable_http_timeout_surface(s);
2012
2013
  ScrHttpSrvCtx *ctx = calloc(1, sizeof *ctx);
2013
2014
  if (!ctx) scr_http_oom();
2014
2015
  ctx->proto = SCR_NET_PROTO_HTTP1;
package/src/scr_inspect.c CHANGED
@@ -237,11 +237,11 @@ typedef struct {
237
237
  bool all_num; /* every entry so far flagged numeric (grouping order) */
238
238
  } InspFrame;
239
239
 
240
- static InspFrame *g_frames;
241
- static size_t g_nframes;
242
- static size_t g_frames_cap;
243
- static size_t g_indent; /* ctx.indentationLvl */
244
- static double g_cur_depth; /* ctx.currentDepth (last-entered composite) */
240
+ static SCR_TL InspFrame *g_frames;
241
+ static SCR_TL size_t g_nframes;
242
+ static SCR_TL size_t g_frames_cap;
243
+ static SCR_TL size_t g_indent; /* ctx.indentationLvl */
244
+ static SCR_TL double g_cur_depth; /* ctx.currentDepth (last-entered composite) */
245
245
 
246
246
  static InspFrame *insp_top(void) { return &g_frames[g_nframes - 1]; }
247
247
 
@@ -297,12 +297,12 @@ typedef struct {
297
297
  int id; /* circular id (0 while only on the stack) */
298
298
  } InspSeenEnt;
299
299
 
300
- static InspSeenEnt *g_seen;
301
- static size_t g_nseen;
302
- static size_t g_seen_cap;
300
+ static SCR_TL InspSeenEnt *g_seen;
301
+ static SCR_TL size_t g_nseen;
302
+ static SCR_TL size_t g_seen_cap;
303
303
  /* Detection-numbered circular targets (persist across the call). */
304
- static const void *g_circ[64];
305
- static int g_ncirc;
304
+ static SCR_TL const void *g_circ[64];
305
+ static SCR_TL int g_ncirc;
306
306
 
307
307
  static void insp_circ_reset(void) {
308
308
  g_nseen = 0;
package/src/scr_island.c CHANGED
@@ -118,7 +118,7 @@ static void *isl_realloc_fn(void *opaque, void *ptr, size_t size) {
118
118
  if (!ptr && p) isl_live_allocs++;
119
119
  return p;
120
120
  }
121
- static size_t isl_usable_size(const void *ptr) { return isl_malloc_size(ptr); }
121
+ static size_t isl_usable_size(const void *ptr) { return isl_malloc_size((void *)ptr); }
122
122
 
123
123
  static const JSMallocFunctions isl_mf = {
124
124
  isl_calloc, isl_malloc, isl_free, isl_realloc_fn, isl_usable_size,
@@ -2107,7 +2107,7 @@ ScrJsval *scr_jsval_from_promise(ScrPromise *p, int payload) {
2107
2107
  w->payload = payload;
2108
2108
  w->next = isl_prom_wraps;
2109
2109
  isl_prom_wraps = w;
2110
- ScrPromise *waiter = scr_async_spawn(isl_prom_wrap_entry, w);
2110
+ ScrPromise *waiter = scr_async_spawn_after(w->p, isl_prom_wrap_entry, w);
2111
2111
  scr_promise_release(waiter); /* the waiter never rejects; nobody awaits it */
2112
2112
  return isl_cell_new(prom);
2113
2113
  }
@@ -3352,7 +3352,7 @@ static JSValue isl_host_ids(JSContext *ctx, JSValueConst this_val, int argc,
3352
3352
  (void)argc;
3353
3353
  (void)argv;
3354
3354
  JSValue arr = JS_NewArray(ctx);
3355
- #ifdef _WIN32
3355
+ #if defined(_WIN32) || defined(__wasi__)
3356
3356
  JS_SetPropertyUint32(ctx, arr, 0, JS_NewInt32(ctx, -1));
3357
3357
  JS_SetPropertyUint32(ctx, arr, 1, JS_NewInt32(ctx, -1));
3358
3358
  #else
@@ -3370,7 +3370,7 @@ static JSValue isl_host_umask(JSContext *ctx, JSValueConst this_val, int argc,
3370
3370
  (void)this_val;
3371
3371
  (void)argc;
3372
3372
  (void)argv;
3373
- #ifdef _WIN32
3373
+ #if defined(_WIN32) || defined(__wasi__)
3374
3374
  return JS_NewInt32(ctx, 0);
3375
3375
  #else
3376
3376
  mode_t m = umask(0);
@@ -3495,11 +3495,15 @@ static JSValue isl_host_hostname(JSContext *ctx, JSValueConst this_val, int argc
3495
3495
  * counted like the socket units' own WSAStartup calls (ws2_32 is on
3496
3496
  * every win32 link line). */
3497
3497
  char buf[256];
3498
+ #if defined(__wasi__)
3499
+ buf[0] = '\0';
3500
+ #else
3498
3501
  #ifdef _WIN32
3499
3502
  WSADATA wsa;
3500
3503
  if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) return JS_NewString(ctx, "");
3501
3504
  #endif
3502
3505
  if (gethostname(buf, sizeof buf) != 0) buf[0] = '\0';
3506
+ #endif
3503
3507
  buf[sizeof buf - 1] = '\0';
3504
3508
  return JS_NewString(ctx, buf);
3505
3509
  }
package/src/scr_json.c CHANGED
@@ -31,7 +31,7 @@
31
31
  /* Live dyn-node count for the RC audit lane (-DSCR_RC_AUDIT); same contract
32
32
  * as scr_str_live_count in scr_string.c. */
33
33
  #ifdef SCR_RC_AUDIT
34
- static long scr_live_dyns = 0;
34
+ static SCR_TL long scr_live_dyns = 0;
35
35
  long scr_dyn_live_count(void) { return scr_live_dyns; }
36
36
  #endif
37
37
 
@@ -49,7 +49,7 @@ static void scr_json_oom(void) {
49
49
  /* The ScrStr block behind a non-empty buffer. */
50
50
  #define SCR_JB_STR(b) ((ScrStr *)((char *)(b)->data - offsetof(ScrStr, data)))
51
51
 
52
- static size_t scr_jb_hint = 64;
52
+ static SCR_TL size_t scr_jb_hint = 64;
53
53
 
54
54
  void scr_jb_init(ScrJsonBuf *b) {
55
55
  b->data = NULL;
@@ -280,9 +280,9 @@ void scr_jb_edge_idx(ScrJsonBuf *b, size_t i) {
280
280
  * (SEMANTICS.md documents the divergence). The emitted converters over
281
281
  * cycle-capable containers bracket their walks with enter/leave; the
282
282
  * stack is global (conversions never interleave). */
283
- static const void **g_td_seen;
284
- static size_t g_td_nseen;
285
- static size_t g_td_cap;
283
+ static SCR_TL const void **g_td_seen;
284
+ static SCR_TL size_t g_td_nseen;
285
+ static SCR_TL size_t g_td_cap;
286
286
 
287
287
  void scr_dyn_from_enter(const void *v) {
288
288
  for (size_t i = 0; i < g_td_nseen; i++) {
@@ -315,8 +315,8 @@ void scr_dyn_from_leave(void) {
315
315
  * strict alloc/free balance.
316
316
  */
317
317
  #ifndef SCR_RC_AUDIT
318
- static ScrDyn *scr_dyn_free_arr, *scr_dyn_free_obj, *scr_dyn_free_misc;
319
- static size_t scr_dyn_free_count;
318
+ static SCR_TL ScrDyn *scr_dyn_free_arr, *scr_dyn_free_obj, *scr_dyn_free_misc;
319
+ static SCR_TL size_t scr_dyn_free_count;
320
320
  #define SCR_DYN_FREE_MAX 8192
321
321
  #endif
322
322
 
@@ -818,7 +818,7 @@ ScrBytes *scr_dyn_bytes_copy_out(const ScrDyn *d) {
818
818
  * Per-chunk utf8 decode: a multibyte character split across chunks does
819
819
  * not re-join (Node's StringDecoder holds the partial byte); ASCII-clean
820
820
  * bodies — the overwhelming test shape — are exact. */
821
- static bool scr_dyn_chunk_utf8;
821
+ static SCR_TL bool scr_dyn_chunk_utf8;
822
822
  void scr_dyn_chunk_enc(bool utf8) { scr_dyn_chunk_utf8 = utf8; }
823
823
 
824
824
  /* One 'data' payload as the dyn value the current window dictates:
@@ -1122,7 +1122,7 @@ ScrDyn *scr_dyn_alloc_promise(void (*release_fn)(ScrPromise *p)) {
1122
1122
  * this allocator view and installs the engine-routing ops — the
1123
1123
  * scr_dyn_alloc_promise story: a dynamic-free link never references
1124
1124
  * engine symbols, and JSVAL nodes exist only after an install. */
1125
- static const ScrDynJsvalOps *scr_dynjs_ops = NULL;
1125
+ static SCR_TL const ScrDynJsvalOps *scr_dynjs_ops = NULL;
1126
1126
 
1127
1127
  ScrDyn *scr_dyn_alloc_jsval(ScrJsval *cell, const ScrDynJsvalOps *ops) {
1128
1128
  scr_dynjs_ops = ops;
@@ -1256,8 +1256,8 @@ typedef struct {
1256
1256
  ScrDyn *dv; /* dyn entry (+1); NULL for handle/undefined entries */
1257
1257
  } ScrDynThisEnt;
1258
1258
 
1259
- static ScrDynThisEnt *scr_dyn_this_stack;
1260
- static size_t scr_dyn_this_n, scr_dyn_this_cap;
1259
+ static SCR_TL ScrDynThisEnt *scr_dyn_this_stack;
1260
+ static SCR_TL size_t scr_dyn_this_n, scr_dyn_this_cap;
1261
1261
 
1262
1262
  static ScrDynThisEnt *scr_dyn_this_grow(void) {
1263
1263
  if (scr_dyn_this_n == scr_dyn_this_cap) {
@@ -1526,9 +1526,9 @@ typedef struct {
1526
1526
  ScrDyn *dyn; /* retained */
1527
1527
  } ScrErrDynEnt;
1528
1528
 
1529
- static ScrErrDynEnt *scr_errdyn_cache = NULL;
1530
- static size_t scr_errdyn_n = 0, scr_errdyn_cap = 0;
1531
- static bool scr_errdyn_teardown_registered = false;
1529
+ static SCR_TL ScrErrDynEnt *scr_errdyn_cache = NULL;
1530
+ static SCR_TL size_t scr_errdyn_n = 0, scr_errdyn_cap = 0;
1531
+ static SCR_TL bool scr_errdyn_teardown_registered = false;
1532
1532
 
1533
1533
  static void scr_errdyn_teardown(void) {
1534
1534
  for (size_t i = 0; i < scr_errdyn_n; i++) {