@scriptc/runtime 0.0.25 → 0.0.26

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_lib.c CHANGED
@@ -175,6 +175,8 @@ ScrStr *scr_process_platform(void) {
175
175
  scr_platform_str = scr_str_new("darwin", 6);
176
176
  #elif defined(__linux__)
177
177
  scr_platform_str = scr_str_new("linux", 5);
178
+ #elif defined(__wasi__)
179
+ scr_platform_str = scr_str_new("wasi", 4);
178
180
  #elif defined(_WIN32)
179
181
  scr_platform_str = scr_str_new("win32", 5);
180
182
  #else
@@ -188,7 +190,11 @@ ScrStr *scr_process_platform(void) {
188
190
  * Node spells its own build's arch. Interned like process.platform. */
189
191
  ScrStr *scr_process_arch(void) {
190
192
  if (!scr_arch_str) {
191
- #if defined(__aarch64__) || defined(_M_ARM64)
193
+ #if defined(__wasm32__)
194
+ scr_arch_str = scr_str_new("wasm32", 6);
195
+ #elif defined(__wasm64__)
196
+ scr_arch_str = scr_str_new("wasm64", 6);
197
+ #elif defined(__aarch64__) || defined(_M_ARM64)
192
198
  scr_arch_str = scr_str_new("arm64", 5);
193
199
  #elif defined(__x86_64__) || defined(_M_X64)
194
200
  scr_arch_str = scr_str_new("x64", 3);
@@ -350,11 +356,11 @@ ScrArr *scr_env_pairs(void) {
350
356
 
351
357
  double scr_process_pid(void) { return (double)getpid(); }
352
358
 
353
- #ifdef _WIN32
359
+ #if defined(_WIN32) || defined(__wasi__)
354
360
  /* No uids/gids exist on Windows: Node's process object simply has no
355
361
  * getuid/getgid members there, so a call is the property-access TypeError
356
362
  * below — thrown catchably, exactly what `process.getuid()` does under
357
- * Windows Node. */
363
+ * Windows Node. WASI has no uid/gid process model either. */
358
364
  double scr_process_getuid(void) {
359
365
  scr_throw_error_msg(SCR_ERR_TYPE, "process.getuid is not a function", 32);
360
366
  return 0;
@@ -532,6 +538,14 @@ static int scr_win_kill(int pid, int sig) {
532
538
  return 0;
533
539
  }
534
540
  #define scr_sys_kill(pid, sig) scr_win_kill((int)(pid), (sig))
541
+ #elif defined(__wasi__)
542
+ static int scr_wasi_kill(int pid, int sig) {
543
+ (void)pid;
544
+ (void)sig;
545
+ errno = ENOSYS;
546
+ return -1;
547
+ }
548
+ #define scr_sys_kill(pid, sig) scr_wasi_kill((int)(pid), (sig))
535
549
  #else
536
550
  #define scr_sys_kill(pid, sig) kill((pid_t)(pid), (sig))
537
551
  #endif
@@ -689,6 +703,27 @@ ScrStr *scr_os_tmpdir(void) {
689
703
  if (len > 1 && (buf[len - 1] == '\\' || buf[len - 1] == '/')) len--;
690
704
  return scr_str_new(buf, len);
691
705
  }
706
+ #elif defined(__wasi__)
707
+ /* WASI has an inherited environment but no passwd database, uname, or
708
+ * physical-memory query. Keep the useful environment-backed answers and
709
+ * spell the platform explicitly for the rest. */
710
+ ScrStr *scr_os_homedir(void) {
711
+ const char *home = getenv("HOME");
712
+ return scr_str_new(home ? home : "", home ? strlen(home) : 0);
713
+ }
714
+ ScrStr *scr_os_user_name(void) {
715
+ const char *user = getenv("USER");
716
+ return scr_str_new(user ? user : "", user ? strlen(user) : 0);
717
+ }
718
+ ScrStr *scr_os_user_shell(void) { return scr_str_new("", 0); }
719
+ ScrStr *scr_os_user_homedir(void) { return scr_os_homedir(); }
720
+ ScrStr *scr_os_release(void) { return scr_str_new("", 0); }
721
+ ScrStr *scr_os_type(void) { return scr_str_new("WASI", 4); }
722
+ double scr_os_totalmem(void) { return 0; }
723
+ /* The guest temp namespace is stable across hosts. `scriptc run` preopens
724
+ * the host's /tmp at this path; other WASI hosts can provide the same
725
+ * capability without leaking a host-specific TMPDIR into the module. */
726
+ ScrStr *scr_os_tmpdir(void) { return scr_str_new("/tmp", 4); }
692
727
  #else
693
728
  ScrStr *scr_os_homedir(void) {
694
729
  /* uv_os_homedir: $HOME when set (even empty is "set" only if non-NULL;
@@ -1226,7 +1261,7 @@ double scr_thread_cpu_system_diff(double prev) { return scr_thread_cpu_system()
1226
1261
  * Darwin's bytes by 1024; the rest are getrusage's own counters, zero
1227
1262
  * where the platform never fills them). */
1228
1263
  double scr_process_rusage(double idx) {
1229
- #ifdef _WIN32
1264
+ #if defined(_WIN32) || defined(__wasi__)
1230
1265
  /* uv fills only the CPU times and page-fault/maxRSS slice on Windows;
1231
1266
  * everything else answers 0 — Node's own shape there. */
1232
1267
  switch ((int)idx) {
@@ -1336,6 +1371,11 @@ double scr_process_umask(double mask) {
1336
1371
  _umask_s((int)mask, &prev);
1337
1372
  }
1338
1373
  return (double)prev;
1374
+ #elif defined(__wasi__)
1375
+ static mode_t current = 022;
1376
+ mode_t prev = current;
1377
+ if (mask >= 0) current = (mode_t)mask;
1378
+ return (double)prev;
1339
1379
  #else
1340
1380
  mode_t prev;
1341
1381
  if (mask < 0) {
@@ -1653,9 +1693,10 @@ void scr_fs_chmod(ScrStr *path, double mode) {
1653
1693
  }
1654
1694
 
1655
1695
  void scr_fs_chown(ScrStr *path, double uid, double gid) {
1656
- #ifdef _WIN32
1696
+ #if defined(_WIN32) || defined(__wasi__)
1657
1697
  /* libuv's uv_fs_chown on Windows is an unconditional no-op success —
1658
- * Node's chownSync "works" and changes nothing there; same here. */
1698
+ * Node's chownSync "works" and changes nothing there; WASI likewise has
1699
+ * no ownership model. */
1659
1700
  (void)path; (void)uid; (void)gid;
1660
1701
  #else
1661
1702
  /* Node passes the ids straight to chown(2); -1 is the POSIX "leave
@@ -1863,7 +1904,7 @@ static bool scr_fs_write_fd_valid(double fd) {
1863
1904
  * call, preserving any signal that was already pending. */
1864
1905
  static ssize_t scr_fs_write_once(int fd, const void *data, size_t length,
1865
1906
  bool positioned, double position) {
1866
- #ifdef _WIN32
1907
+ #if defined(_WIN32) || defined(__wasi__)
1867
1908
  return positioned
1868
1909
  ? scr_fs_pwrite(fd, data, length, position)
1869
1910
  : write(fd, data, length);
@@ -2520,23 +2561,23 @@ void scr_fs_rm_opts_retry(ScrStr *path, bool recursive, bool force, double max_r
2520
2561
  scr_str_release(f.path);
2521
2562
  }
2522
2563
 
2523
- #ifdef _WIN32
2524
- /* No mkdtemp in the CRT: libuv's own fallback shape — six random
2564
+ #if defined(_WIN32) || defined(__wasi__)
2565
+ /* No mkdtemp in the Windows CRT or WASI libc: libuv's own fallback shape — six random
2525
2566
  * [a-z0-9] name characters from the CSPRNG, retried on EEXIST. */
2526
- static char *scr_win_mkdtemp(char *tmpl) {
2567
+ static char *scr_portable_mkdtemp(char *tmpl) {
2527
2568
  static const char cs[] = "abcdefghijklmnopqrstuvwxyz0123456789";
2528
2569
  size_t len = strlen(tmpl);
2529
2570
  for (int tries = 0; tries < 32; tries++) {
2530
2571
  unsigned char r[6];
2531
2572
  arc4random_buf(r, sizeof r);
2532
2573
  for (size_t i = 0; i < 6; i++) tmpl[len - 6 + i] = cs[r[i] % 36];
2533
- if (mkdir(tmpl) == 0) return tmpl;
2574
+ if (scr_sys_mkdir(tmpl, 0777) == 0) return tmpl;
2534
2575
  if (errno != EEXIST) break;
2535
2576
  }
2536
2577
  memcpy(tmpl + len - 6, "XXXXXX", 6); /* the error message shows the template */
2537
2578
  return NULL;
2538
2579
  }
2539
- #define mkdtemp scr_win_mkdtemp
2580
+ #define mkdtemp scr_portable_mkdtemp
2540
2581
  #endif
2541
2582
 
2542
2583
  ScrStr *scr_fs_mkdtemp(ScrStr *prefix) {
@@ -2688,6 +2729,8 @@ double scr_process_columns(double fd) {
2688
2729
  CONSOLE_SCREEN_BUFFER_INFO info;
2689
2730
  if (h == INVALID_HANDLE_VALUE || !GetConsoleScreenBufferInfo(h, &info)) return -1;
2690
2731
  return (double)(info.srWindow.Right - info.srWindow.Left + 1);
2732
+ #elif defined(__wasi__)
2733
+ return -1;
2691
2734
  #else
2692
2735
  struct winsize ws;
2693
2736
  if (ioctl((int)fd, TIOCGWINSZ, &ws) != 0) return -1;
@@ -2728,6 +2771,12 @@ void scr_process_stdin_set_raw_mode(bool raw) {
2728
2771
  (void)SetConsoleMode(h, scr_stdin_cooked);
2729
2772
  }
2730
2773
  }
2774
+ #elif defined(__wasi__)
2775
+ void scr_process_stdin_set_raw_mode(bool raw) {
2776
+ (void)raw;
2777
+ const char msg[] = "process.stdin.setRawMode is not a function";
2778
+ scr_throw_error_msg(SCR_ERR_TYPE, msg, sizeof msg - 1);
2779
+ }
2731
2780
  #else
2732
2781
  static struct termios scr_stdin_cooked;
2733
2782
  static bool scr_stdin_cooked_saved = false;
package/src/scr_musl.c ADDED
@@ -0,0 +1,274 @@
1
+ /* musl libc shim — compiled into linux-musl target builds only (cc.ts adds
2
+ * this TU together with -DSCR_MUSL). musl deliberately provides no libc
3
+ * identification macro, so the target-selected project macro is the guard.
4
+ *
5
+ * The x86_64 ucontext implementation below is derived from libucontext
6
+ * commit 49e671dd52ff6791295d8161ad3b6da7dc5f6f9d:
7
+ * https://github.com/kaniini/libucontext
8
+ *
9
+ * Copyright (c) 2018-2025 Ariadne Conill <ariadne@dereferenced.org>
10
+ *
11
+ * Permission to use, copy, modify, and/or distribute this software for any
12
+ * purpose with or without fee is hereby granted, provided that the above
13
+ * copyright notice and this permission notice appear in all copies.
14
+ *
15
+ * This software is provided 'as is' and without any warranty, express or
16
+ * implied. In no event shall the authors be liable for any damages arising
17
+ * from the use of this software. */
18
+ #ifdef SCR_MUSL
19
+
20
+ #include "scr_runtime.h"
21
+
22
+ #include <errno.h>
23
+ #include <stdlib.h>
24
+ #include <sys/random.h>
25
+ #ifndef SCR_LIB
26
+ #include <stdarg.h>
27
+ #include <ucontext.h>
28
+ #endif
29
+
30
+ #if !defined(__x86_64__)
31
+ #error "scriptc's musl runtime currently supports x86_64 only"
32
+ #endif
33
+
34
+ /* The scriptc runtime uses arc4random_buf as its infallible CSPRNG contract.
35
+ * Linux getrandom has the same kernel source and needs neither mutable state
36
+ * nor an fd. Retry interrupts and short reads; any other failure enters the
37
+ * runtime trap funnel rather than returning predictable or partially
38
+ * initialized bytes. Executables still print and abort there, while library
39
+ * artifacts deliver the failure to their registered panic sink. */
40
+ void arc4random_buf(void *buf, size_t n) {
41
+ unsigned char *p = buf;
42
+ while (n > 0) {
43
+ ssize_t got = getrandom(p, n, 0);
44
+ if (got > 0) {
45
+ p += (size_t)got;
46
+ n -= (size_t)got;
47
+ continue;
48
+ }
49
+ if (got < 0 && errno == EINTR) continue;
50
+ scr_trap("scriptc: getrandom failed\n");
51
+ }
52
+ }
53
+
54
+ /* Library artifacts contain no fibers or event loop, so they need only the
55
+ * CSPRNG shim above and must not acquire context-switching definitions. */
56
+ #ifndef SCR_LIB
57
+
58
+ /* musl exposes the POSIX ucontext types but intentionally omits these legacy
59
+ * functions. scriptc's async/generator fibers need only user-space register
60
+ * swaps; they never use a context to mutate the process signal mask. This is
61
+ * libucontext's fast (non-POSIX-signal-mask) x86_64 implementation, emitted
62
+ * under the standard symbol names that musl leaves unresolved. */
63
+ _Static_assert(offsetof(ucontext_t, uc_mcontext.gregs) == 40,
64
+ "unexpected musl x86_64 ucontext layout");
65
+ _Static_assert(offsetof(ucontext_t, uc_mcontext.fpregs) == 224,
66
+ "unexpected musl x86_64 fpregs layout");
67
+ _Static_assert(offsetof(ucontext_t, __fpregs_mem) == 424,
68
+ "unexpected musl x86_64 fpregs storage layout");
69
+
70
+ /* Keep the assembler independent of whether the sysroot exposes glibc's
71
+ * REG_* spellings as enum constants, self-referential macros, or neither. */
72
+ #undef REG_R8
73
+ #undef REG_R9
74
+ #undef REG_R10
75
+ #undef REG_R11
76
+ #undef REG_R12
77
+ #undef REG_R13
78
+ #undef REG_R14
79
+ #undef REG_R15
80
+ #undef REG_RDI
81
+ #undef REG_RSI
82
+ #undef REG_RBP
83
+ #undef REG_RBX
84
+ #undef REG_RDX
85
+ #undef REG_RAX
86
+ #undef REG_RCX
87
+ #undef REG_RSP
88
+ #undef REG_RIP
89
+ #define REG_R8 0
90
+ #define REG_R9 1
91
+ #define REG_R10 2
92
+ #define REG_R11 3
93
+ #define REG_R12 4
94
+ #define REG_R13 5
95
+ #define REG_R14 6
96
+ #define REG_R15 7
97
+ #define REG_RDI 8
98
+ #define REG_RSI 9
99
+ #define REG_RBP 10
100
+ #define REG_RBX 11
101
+ #define REG_RDX 12
102
+ #define REG_RAX 13
103
+ #define REG_RCX 14
104
+ #define REG_RSP 15
105
+ #define REG_RIP 16
106
+
107
+ #define SCR_STR_INNER(x) #x
108
+ #define SCR_STR(x) SCR_STR_INNER(x)
109
+ #define SCR_UC_GREG(reg) (40 + ((reg) * 8))
110
+
111
+ __asm__(
112
+ ".text\n"
113
+ ".global getcontext\n"
114
+ "getcontext:\n"
115
+ " movq %r8, " SCR_STR(SCR_UC_GREG(REG_R8)) "(%rdi)\n"
116
+ " movq %r9, " SCR_STR(SCR_UC_GREG(REG_R9)) "(%rdi)\n"
117
+ " movq %r10, " SCR_STR(SCR_UC_GREG(REG_R10)) "(%rdi)\n"
118
+ " movq %r11, " SCR_STR(SCR_UC_GREG(REG_R11)) "(%rdi)\n"
119
+ " movq %r12, " SCR_STR(SCR_UC_GREG(REG_R12)) "(%rdi)\n"
120
+ " movq %r13, " SCR_STR(SCR_UC_GREG(REG_R13)) "(%rdi)\n"
121
+ " movq %r14, " SCR_STR(SCR_UC_GREG(REG_R14)) "(%rdi)\n"
122
+ " movq %r15, " SCR_STR(SCR_UC_GREG(REG_R15)) "(%rdi)\n"
123
+ " movq %rdi, " SCR_STR(SCR_UC_GREG(REG_RDI)) "(%rdi)\n"
124
+ " movq %rsi, " SCR_STR(SCR_UC_GREG(REG_RSI)) "(%rdi)\n"
125
+ " movq %rbp, " SCR_STR(SCR_UC_GREG(REG_RBP)) "(%rdi)\n"
126
+ " movq %rbx, " SCR_STR(SCR_UC_GREG(REG_RBX)) "(%rdi)\n"
127
+ " movq %rdx, " SCR_STR(SCR_UC_GREG(REG_RDX)) "(%rdi)\n"
128
+ " movq %rax, " SCR_STR(SCR_UC_GREG(REG_RAX)) "(%rdi)\n"
129
+ " movq %rcx, " SCR_STR(SCR_UC_GREG(REG_RCX)) "(%rdi)\n"
130
+ " movq (%rsp), %rcx\n"
131
+ " movq %rcx, " SCR_STR(SCR_UC_GREG(REG_RIP)) "(%rdi)\n"
132
+ " leaq 8(%rsp), %rcx\n"
133
+ " movq %rcx, " SCR_STR(SCR_UC_GREG(REG_RSP)) "(%rdi)\n"
134
+ " leaq 424(%rdi), %rcx\n"
135
+ " movq %rcx, 224(%rdi)\n"
136
+ " fnstenv (%rcx)\n"
137
+ " fldenv (%rcx)\n"
138
+ " stmxcsr 448(%rdi)\n"
139
+ " xorl %eax, %eax\n"
140
+ " ret\n"
141
+
142
+ ".global setcontext\n"
143
+ "setcontext:\n"
144
+ " movq 224(%rdi), %rcx\n"
145
+ " fldenv (%rcx)\n"
146
+ " ldmxcsr 448(%rdi)\n"
147
+ " movq " SCR_STR(SCR_UC_GREG(REG_R8)) "(%rdi), %r8\n"
148
+ " movq " SCR_STR(SCR_UC_GREG(REG_R9)) "(%rdi), %r9\n"
149
+ " movq " SCR_STR(SCR_UC_GREG(REG_R10)) "(%rdi), %r10\n"
150
+ " movq " SCR_STR(SCR_UC_GREG(REG_R11)) "(%rdi), %r11\n"
151
+ " movq " SCR_STR(SCR_UC_GREG(REG_R12)) "(%rdi), %r12\n"
152
+ " movq " SCR_STR(SCR_UC_GREG(REG_R13)) "(%rdi), %r13\n"
153
+ " movq " SCR_STR(SCR_UC_GREG(REG_R14)) "(%rdi), %r14\n"
154
+ " movq " SCR_STR(SCR_UC_GREG(REG_R15)) "(%rdi), %r15\n"
155
+ " movq " SCR_STR(SCR_UC_GREG(REG_RSI)) "(%rdi), %rsi\n"
156
+ " movq " SCR_STR(SCR_UC_GREG(REG_RBP)) "(%rdi), %rbp\n"
157
+ " movq " SCR_STR(SCR_UC_GREG(REG_RBX)) "(%rdi), %rbx\n"
158
+ " movq " SCR_STR(SCR_UC_GREG(REG_RDX)) "(%rdi), %rdx\n"
159
+ " movq " SCR_STR(SCR_UC_GREG(REG_RAX)) "(%rdi), %rax\n"
160
+ " movq " SCR_STR(SCR_UC_GREG(REG_RCX)) "(%rdi), %rcx\n"
161
+ " movq " SCR_STR(SCR_UC_GREG(REG_RSP)) "(%rdi), %rsp\n"
162
+ " pushq " SCR_STR(SCR_UC_GREG(REG_RIP)) "(%rdi)\n"
163
+ " movq " SCR_STR(SCR_UC_GREG(REG_RDI)) "(%rdi), %rdi\n"
164
+ " xorl %eax, %eax\n"
165
+ " ret\n"
166
+
167
+ ".global swapcontext\n"
168
+ "swapcontext:\n"
169
+ " movq %r8, " SCR_STR(SCR_UC_GREG(REG_R8)) "(%rdi)\n"
170
+ " movq %r9, " SCR_STR(SCR_UC_GREG(REG_R9)) "(%rdi)\n"
171
+ " movq %r10, " SCR_STR(SCR_UC_GREG(REG_R10)) "(%rdi)\n"
172
+ " movq %r11, " SCR_STR(SCR_UC_GREG(REG_R11)) "(%rdi)\n"
173
+ " movq %r12, " SCR_STR(SCR_UC_GREG(REG_R12)) "(%rdi)\n"
174
+ " movq %r13, " SCR_STR(SCR_UC_GREG(REG_R13)) "(%rdi)\n"
175
+ " movq %r14, " SCR_STR(SCR_UC_GREG(REG_R14)) "(%rdi)\n"
176
+ " movq %r15, " SCR_STR(SCR_UC_GREG(REG_R15)) "(%rdi)\n"
177
+ " movq %rdi, " SCR_STR(SCR_UC_GREG(REG_RDI)) "(%rdi)\n"
178
+ " movq %rsi, " SCR_STR(SCR_UC_GREG(REG_RSI)) "(%rdi)\n"
179
+ " movq %rbp, " SCR_STR(SCR_UC_GREG(REG_RBP)) "(%rdi)\n"
180
+ " movq %rbx, " SCR_STR(SCR_UC_GREG(REG_RBX)) "(%rdi)\n"
181
+ " movq %rdx, " SCR_STR(SCR_UC_GREG(REG_RDX)) "(%rdi)\n"
182
+ " movq %rax, " SCR_STR(SCR_UC_GREG(REG_RAX)) "(%rdi)\n"
183
+ " movq %rcx, " SCR_STR(SCR_UC_GREG(REG_RCX)) "(%rdi)\n"
184
+ " movq (%rsp), %rcx\n"
185
+ " movq %rcx, " SCR_STR(SCR_UC_GREG(REG_RIP)) "(%rdi)\n"
186
+ " leaq 8(%rsp), %rcx\n"
187
+ " movq %rcx, " SCR_STR(SCR_UC_GREG(REG_RSP)) "(%rdi)\n"
188
+ " leaq 424(%rdi), %rcx\n"
189
+ " movq %rcx, 224(%rdi)\n"
190
+ " fnstenv (%rcx)\n"
191
+ " fldenv (%rcx)\n"
192
+ " stmxcsr 448(%rdi)\n"
193
+ " movq 224(%rsi), %rcx\n"
194
+ " fldenv (%rcx)\n"
195
+ " ldmxcsr 448(%rsi)\n"
196
+ " movq " SCR_STR(SCR_UC_GREG(REG_R8)) "(%rsi), %r8\n"
197
+ " movq " SCR_STR(SCR_UC_GREG(REG_R9)) "(%rsi), %r9\n"
198
+ " movq " SCR_STR(SCR_UC_GREG(REG_R10)) "(%rsi), %r10\n"
199
+ " movq " SCR_STR(SCR_UC_GREG(REG_R11)) "(%rsi), %r11\n"
200
+ " movq " SCR_STR(SCR_UC_GREG(REG_R12)) "(%rsi), %r12\n"
201
+ " movq " SCR_STR(SCR_UC_GREG(REG_R13)) "(%rsi), %r13\n"
202
+ " movq " SCR_STR(SCR_UC_GREG(REG_R14)) "(%rsi), %r14\n"
203
+ " movq " SCR_STR(SCR_UC_GREG(REG_R15)) "(%rsi), %r15\n"
204
+ " movq " SCR_STR(SCR_UC_GREG(REG_RDI)) "(%rsi), %rdi\n"
205
+ " movq " SCR_STR(SCR_UC_GREG(REG_RBP)) "(%rsi), %rbp\n"
206
+ " movq " SCR_STR(SCR_UC_GREG(REG_RBX)) "(%rsi), %rbx\n"
207
+ " movq " SCR_STR(SCR_UC_GREG(REG_RDX)) "(%rsi), %rdx\n"
208
+ " movq " SCR_STR(SCR_UC_GREG(REG_RAX)) "(%rsi), %rax\n"
209
+ " movq " SCR_STR(SCR_UC_GREG(REG_RCX)) "(%rsi), %rcx\n"
210
+ " movq " SCR_STR(SCR_UC_GREG(REG_RSP)) "(%rsi), %rsp\n"
211
+ " pushq " SCR_STR(SCR_UC_GREG(REG_RIP)) "(%rsi)\n"
212
+ " movq " SCR_STR(SCR_UC_GREG(REG_RSI)) "(%rsi), %rsi\n"
213
+ " xorl %eax, %eax\n"
214
+ " ret\n"
215
+
216
+ ".hidden scr_musl_context_trampoline\n"
217
+ "scr_musl_context_trampoline:\n"
218
+ " movq (%rbx), %rdi\n"
219
+ " testq %rdi, %rdi\n"
220
+ " je 1f\n"
221
+ " call setcontext\n"
222
+ " ud2\n"
223
+ "1:\n"
224
+ " xorl %edi, %edi\n"
225
+ " call exit\n"
226
+ " ud2\n");
227
+
228
+ extern void scr_musl_context_trampoline(void);
229
+
230
+ void makecontext(ucontext_t *ucp, void (*func)(void), int argc, ...) {
231
+ greg_t *sp;
232
+ va_list va;
233
+ int i;
234
+ unsigned int link_slot = (unsigned int)(argc > 6 ? argc - 6 : 0) + 1;
235
+
236
+ sp = (greg_t *)((uintptr_t)ucp->uc_stack.ss_sp + ucp->uc_stack.ss_size);
237
+ sp -= link_slot;
238
+ sp = (greg_t *)(((uintptr_t)sp & ~(uintptr_t)15) - 8);
239
+
240
+ ucp->uc_mcontext.fpregs = (void *)&ucp->__fpregs_mem;
241
+ ucp->uc_mcontext.gregs[REG_RIP] = (greg_t)(uintptr_t)func;
242
+ ucp->uc_mcontext.gregs[REG_RBX] = (greg_t)(uintptr_t)&sp[link_slot];
243
+ ucp->uc_mcontext.gregs[REG_RSP] = (greg_t)(uintptr_t)sp;
244
+
245
+ sp[0] = (greg_t)(uintptr_t)&scr_musl_context_trampoline;
246
+ sp[link_slot] = (greg_t)(uintptr_t)ucp->uc_link;
247
+
248
+ va_start(va, argc);
249
+ for (i = 0; i < argc; i++) {
250
+ greg_t arg = va_arg(va, greg_t);
251
+ switch (i) {
252
+ case 0: ucp->uc_mcontext.gregs[REG_RDI] = arg; break;
253
+ case 1: ucp->uc_mcontext.gregs[REG_RSI] = arg; break;
254
+ case 2: ucp->uc_mcontext.gregs[REG_RDX] = arg; break;
255
+ case 3: ucp->uc_mcontext.gregs[REG_RCX] = arg; break;
256
+ case 4: ucp->uc_mcontext.gregs[REG_R8] = arg; break;
257
+ case 5: ucp->uc_mcontext.gregs[REG_R9] = arg; break;
258
+ default: sp[i - 5] = arg; break;
259
+ }
260
+ }
261
+ va_end(va);
262
+ }
263
+
264
+ #undef SCR_UC_GREG
265
+ #undef SCR_STR
266
+ #undef SCR_STR_INNER
267
+
268
+ #endif /* !SCR_LIB */
269
+
270
+ #else /* !SCR_MUSL */
271
+
272
+ typedef int scr_musl_unused;
273
+
274
+ #endif /* SCR_MUSL */
package/src/scr_net.c CHANGED
@@ -389,6 +389,16 @@ struct ScrNetServer {
389
389
  bool closing; /* close() called; 'close' fires when conns drain */
390
390
  bool close_emitted; /* settled: off the registry, listeners dropped */
391
391
  bool emit_listening;
392
+ /* The writable http.Server timeout property values. Kept on the shared
393
+ * server handle because http/https servers retain that handle identity;
394
+ * plain net servers never expose these fields through the static type
395
+ * surface. Indices match lower-server.ts's selector ABI. */
396
+ double http_timeouts[5];
397
+ /* A dynamic write may store any JS value on these ordinary writable
398
+ * properties. Numeric writes stay in http_timeouts; every other value
399
+ * is retained here so dynamic reads preserve kind and identity. */
400
+ ScrDyn *http_timeout_dyn[5];
401
+ bool http_timeout_surface; /* true only for HTTP/1 and HTTPS servers */
392
402
  bool defer_conn; /* TLS: 'connection' fires post-handshake, not at accept */
393
403
  bool bound_v6; /* the bound family (address()'s 'IPv6'/'IPv4' split) */
394
404
  ScrStr *bound_host; /* the explicit bind host (NULL = the host-less any:
@@ -556,6 +566,7 @@ void scr_net_server_release(ScrNetServer *s) {
556
566
  scr_net_ls_drop(&s->close_ls);
557
567
  scr_net_ls_drop(&s->listening_cbs);
558
568
  scr_closure_release(s->close_override);
569
+ for (size_t i = 0; i < 5; i++) scr_dyn_release(s->http_timeout_dyn[i]);
559
570
  scr_str_release(s->bound_host);
560
571
  scr_str_release(s->pending_err);
561
572
  if (s->fd >= 0) scr_net_close_fd_raw(s->fd);
@@ -1200,6 +1211,11 @@ static ScrNetServer *scr_net_server_new(void) {
1200
1211
  s->kind = SCR_NET_K_SERVER;
1201
1212
  s->rc = 1;
1202
1213
  s->fd = -1;
1214
+ s->http_timeouts[0] = 0; /* timeout */
1215
+ s->http_timeouts[1] = 5000; /* keepAliveTimeout */
1216
+ s->http_timeouts[2] = 60000; /* headersTimeout */
1217
+ s->http_timeouts[3] = 300000; /* requestTimeout */
1218
+ s->http_timeouts[4] = 1000; /* keepAliveTimeoutBuffer */
1203
1219
  #ifdef SCR_RC_AUDIT
1204
1220
  scr_net_live++;
1205
1221
  #endif
@@ -1378,6 +1394,79 @@ void scr_net_listen_opts(ScrNetServer *s, double port, ScrStr *host /*borrowed*/
1378
1394
 
1379
1395
  double scr_net_server_port(ScrNetServer *s) { return (double)s->port; }
1380
1396
 
1397
+ double scr_net_server_timeout_get(ScrNetServer *s, double field) {
1398
+ static const char *const names[] = {
1399
+ "timeout", "keepAliveTimeout", "headersTimeout", "requestTimeout",
1400
+ "keepAliveTimeoutBuffer",
1401
+ };
1402
+ int i = (int)field;
1403
+ if (i < 0 || i >= 5) abort(); /* compiler/runtime ABI violation */
1404
+ if (s->http_timeout_dyn[i] != NULL) {
1405
+ scr_dyn_arg_type_fail(names[i], "of type number", s->http_timeout_dyn[i]);
1406
+ return 0; /* pending exception; typed reads validate an any-written value */
1407
+ }
1408
+ return s->http_timeouts[i];
1409
+ }
1410
+
1411
+ void scr_net_server_timeout_set(ScrNetServer *s, double field, double value) {
1412
+ int i = (int)field;
1413
+ if (i < 0 || i >= 5) abort(); /* compiler/runtime ABI violation */
1414
+ scr_dyn_release(s->http_timeout_dyn[i]);
1415
+ s->http_timeout_dyn[i] = NULL;
1416
+ s->http_timeouts[i] = value;
1417
+ }
1418
+
1419
+ void scr_net_server_enable_http_timeout_surface(ScrNetServer *s) {
1420
+ s->http_timeout_surface = true;
1421
+ }
1422
+
1423
+ /* Constructor-option validation is intentionally separate from the plain
1424
+ * writable-property setter above. Node validates these option values as
1425
+ * non-negative safe integers, while later property assignments store the
1426
+ * number directly. */
1427
+ bool scr_net_server_timeout_option_check(double field, double value) {
1428
+ static const char *const names[] = {
1429
+ "timeout", "keepAliveTimeout", "headersTimeout", "requestTimeout",
1430
+ "keepAliveTimeoutBuffer",
1431
+ };
1432
+ int i = (int)field;
1433
+ if (i < 0 || i >= 5) abort(); /* compiler/runtime ABI violation */
1434
+ char recv[48], msg[224];
1435
+ if (!(isfinite(value) && trunc(value) == value)) {
1436
+ scr_num_received(value, recv);
1437
+ int len = snprintf(msg, sizeof msg,
1438
+ "The value of \"%s\" is out of range. It must be an integer. Received %s",
1439
+ names[i], recv);
1440
+ scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)len, "ERR_OUT_OF_RANGE");
1441
+ return false;
1442
+ }
1443
+ if (value < 0 || value > 9007199254740991.0) {
1444
+ scr_num_received(value, recv);
1445
+ int len = snprintf(msg, sizeof msg,
1446
+ "The value of \"%s\" is out of range. It must be >= 0 && <= 9007199254740991. Received %s",
1447
+ names[i], recv);
1448
+ scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)len, "ERR_OUT_OF_RANGE");
1449
+ return false;
1450
+ }
1451
+ return true;
1452
+ }
1453
+
1454
+ void scr_net_server_timeout_option_set(ScrNetServer *s, double field, const ScrDyn *value) {
1455
+ int i = (int)field;
1456
+ if (i < 0 || i >= 5) abort(); /* compiler/runtime ABI violation */
1457
+ if (value->kind == SCR_DYN_UNDEF) return; /* optional field is absent */
1458
+ if (value->kind != SCR_DYN_NUM) {
1459
+ static const char *const names[] = {
1460
+ "timeout", "keepAliveTimeout", "headersTimeout", "requestTimeout",
1461
+ "keepAliveTimeoutBuffer",
1462
+ };
1463
+ scr_dyn_arg_type_fail(names[i], "of type number", value);
1464
+ return;
1465
+ }
1466
+ if (!scr_net_server_timeout_option_check(field, value->v.num)) return;
1467
+ scr_net_server_timeout_set(s, field, value->v.num);
1468
+ }
1469
+
1381
1470
  /* address()'s other two fields — the bound host ('::'/'0.0.0.0' for the
1382
1471
  * host-less any, the normalized explicit host otherwise) and the family
1383
1472
  * string. Answer the any-form defaults before listen (Node answers null
@@ -3299,13 +3388,28 @@ static ScrDyn *scr_net_dynh_srv_invoke(void *h, ScrDyn *self, const char *method
3299
3388
  return NULL;
3300
3389
  }
3301
3390
 
3391
+ static int scr_net_dynh_srv_timeout_field(const char *key) {
3392
+ if (strcmp(key, "timeout") == 0) return 0;
3393
+ if (strcmp(key, "keepAliveTimeout") == 0) return 1;
3394
+ if (strcmp(key, "headersTimeout") == 0) return 2;
3395
+ if (strcmp(key, "requestTimeout") == 0) return 3;
3396
+ if (strcmp(key, "keepAliveTimeoutBuffer") == 0) return 4;
3397
+ return -1;
3398
+ }
3399
+
3302
3400
  static ScrDyn *scr_net_dynh_srv_get(void *h, const char *key, size_t key_len) {
3303
3401
  ScrNetServer *s = (ScrNetServer *)h;
3304
3402
  (void)key_len;
3305
3403
  if (strcmp(key, "listening") == 0) return scr_dyn_new_bool(s->listening);
3404
+ int timeout_field = scr_net_dynh_srv_timeout_field(key);
3405
+ if (timeout_field >= 0 && s->http_timeout_surface) {
3406
+ if (s->http_timeout_dyn[timeout_field] != NULL) {
3407
+ return scr_dyn_retain(s->http_timeout_dyn[timeout_field]);
3408
+ }
3409
+ return scr_dyn_new_num(scr_net_server_timeout_get(s, (double)timeout_field));
3410
+ }
3306
3411
  {
3307
- static const char *const known[] = { "maxConnections", "connections", "maxHeadersCount",
3308
- "timeout", "keepAliveTimeout", "headersTimeout", "requestTimeout", NULL };
3412
+ static const char *const known[] = { "maxConnections", "connections", "maxHeadersCount", NULL };
3309
3413
  for (size_t i = 0; known[i]; i++) {
3310
3414
  if (strcmp(key, known[i]) == 0) {
3311
3415
  scr_net_dynh_srv_unsupported(key, NULL);
@@ -3317,7 +3421,18 @@ static ScrDyn *scr_net_dynh_srv_get(void *h, const char *key, size_t key_len) {
3317
3421
  }
3318
3422
 
3319
3423
  static bool scr_net_dynh_srv_set(void *h, const char *key, size_t key_len, const ScrDyn *value) {
3320
- (void)h; (void)key; (void)key_len; (void)value;
3424
+ ScrNetServer *s = (ScrNetServer *)h;
3425
+ (void)key_len;
3426
+ int timeout_field = scr_net_dynh_srv_timeout_field(key);
3427
+ if (timeout_field >= 0 && s->http_timeout_surface) {
3428
+ if (value->kind == SCR_DYN_NUM) {
3429
+ scr_net_server_timeout_set(s, (double)timeout_field, value->v.num);
3430
+ } else {
3431
+ scr_dyn_release(s->http_timeout_dyn[timeout_field]);
3432
+ s->http_timeout_dyn[timeout_field] = scr_dyn_retain((ScrDyn *)value);
3433
+ }
3434
+ return true;
3435
+ }
3321
3436
  return false;
3322
3437
  }
3323
3438