@scriptc/runtime 0.0.23 → 0.0.24
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 +65 -0
- package/src/scr_async.c +286 -5
- package/src/scr_bytes.c +634 -4
- package/src/scr_bytes_io.c +4 -2
- package/src/scr_file_handle.c +491 -0
- package/src/scr_island.c +10 -0
- package/src/scr_json.c +51 -19
- package/src/scr_lib.c +680 -30
- package/src/scr_number.c +9 -0
- package/src/scr_regex.c +12 -1
- package/src/scr_runtime.h +101 -8
- package/src/scr_string.c +13 -2
- package/src/scr_text_decoder_data.h +7389 -0
- package/src/scr_util.c +984 -0
- package/src/scr_win_stats.h +23 -0
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
/* The fs/promises FileHandle slice. Kept in its own gated translation unit
|
|
2
|
+
* so programs that never open a handle retain the base runtime's size class.
|
|
3
|
+
* Operations settle through scr_async.c's ordinary promise helpers: a pending
|
|
4
|
+
* synchronous exception becomes a rejection with the same Error payload. */
|
|
5
|
+
#include "scr_runtime.h"
|
|
6
|
+
|
|
7
|
+
#include <errno.h>
|
|
8
|
+
#include <fcntl.h>
|
|
9
|
+
#include <math.h>
|
|
10
|
+
#include <stdio.h>
|
|
11
|
+
#include <stdlib.h>
|
|
12
|
+
#include <string.h>
|
|
13
|
+
#include <sys/stat.h>
|
|
14
|
+
|
|
15
|
+
#ifdef _WIN32
|
|
16
|
+
#include <io.h>
|
|
17
|
+
#include <windows.h>
|
|
18
|
+
#else
|
|
19
|
+
#include <unistd.h>
|
|
20
|
+
#endif
|
|
21
|
+
|
|
22
|
+
#ifndef O_BINARY
|
|
23
|
+
#define O_BINARY 0
|
|
24
|
+
#endif
|
|
25
|
+
#ifndef O_SYNC
|
|
26
|
+
#define O_SYNC 0
|
|
27
|
+
#endif
|
|
28
|
+
|
|
29
|
+
/* One shared mutable descriptor slot gives aliases Node's close/fd behavior.
|
|
30
|
+
* The last native reference closes a still-open descriptor; explicit close
|
|
31
|
+
* reports errors through its promise wrapper instead. */
|
|
32
|
+
struct ScrFileHandle {
|
|
33
|
+
size_t rc;
|
|
34
|
+
int fd;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/* ScrStats is opaque at the public runtime boundary. FileHandle's fstat
|
|
38
|
+
* snapshot completes the same layout privately in this translation unit. */
|
|
39
|
+
struct ScrStats {
|
|
40
|
+
size_t rc;
|
|
41
|
+
bool is_file;
|
|
42
|
+
bool is_dir;
|
|
43
|
+
bool is_symlink;
|
|
44
|
+
double size;
|
|
45
|
+
double blocks;
|
|
46
|
+
double nlink;
|
|
47
|
+
double atime_ms;
|
|
48
|
+
double mtime_ms;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
#ifdef _WIN32
|
|
52
|
+
/* Keep the FILETIME conversion byte-for-byte with scr_lib.c's path-stat arm:
|
|
53
|
+
* libuv splits the 100ns count into Unix seconds and nanoseconds before Node
|
|
54
|
+
* combines it into the public millisecond value. */
|
|
55
|
+
static double scr_file_handle_filetime_ms(FILETIME ft) {
|
|
56
|
+
ULARGE_INTEGER raw;
|
|
57
|
+
raw.LowPart = ft.dwLowDateTime;
|
|
58
|
+
raw.HighPart = ft.dwHighDateTime;
|
|
59
|
+
int64_t ticks = (int64_t)raw.QuadPart - INT64_C(116444736000000000);
|
|
60
|
+
int64_t sec = ticks / INT64_C(10000000);
|
|
61
|
+
int64_t rem = ticks % INT64_C(10000000);
|
|
62
|
+
if (rem < 0) {
|
|
63
|
+
sec--;
|
|
64
|
+
rem += INT64_C(10000000);
|
|
65
|
+
}
|
|
66
|
+
return (double)sec * 1000.0 + (double)rem / 10000.0;
|
|
67
|
+
}
|
|
68
|
+
#endif
|
|
69
|
+
|
|
70
|
+
typedef struct {
|
|
71
|
+
char *data;
|
|
72
|
+
size_t len;
|
|
73
|
+
size_t cap;
|
|
74
|
+
} ScrFileHandleBuf;
|
|
75
|
+
|
|
76
|
+
static void scr_file_handle_buf_grow(ScrFileHandleBuf *b, size_t need) {
|
|
77
|
+
if (need <= b->cap - b->len) return;
|
|
78
|
+
if (need > SIZE_MAX - b->len) scr_trap("scriptc: out of memory\n");
|
|
79
|
+
size_t want = b->len + need;
|
|
80
|
+
size_t cap = b->cap ? b->cap : 64;
|
|
81
|
+
while (cap < want) {
|
|
82
|
+
if (cap > SIZE_MAX / 2) {
|
|
83
|
+
cap = want;
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
cap *= 2;
|
|
87
|
+
}
|
|
88
|
+
char *data = realloc(b->data, cap);
|
|
89
|
+
if (!data) scr_trap("scriptc: out of memory\n");
|
|
90
|
+
b->data = data;
|
|
91
|
+
b->cap = cap;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
static void scr_file_handle_buf_bytes(ScrFileHandleBuf *b, const char *data,
|
|
95
|
+
size_t len) {
|
|
96
|
+
scr_file_handle_buf_grow(b, len);
|
|
97
|
+
memcpy(b->data + b->len, data, len);
|
|
98
|
+
b->len += len;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
static void scr_file_handle_buf_cstr(ScrFileHandleBuf *b, const char *data) {
|
|
102
|
+
scr_file_handle_buf_bytes(b, data, strlen(data));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
static void scr_file_handle_buf_char(ScrFileHandleBuf *b, char c) {
|
|
106
|
+
scr_file_handle_buf_bytes(b, &c, 1);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
static bool scr_file_handle_has_dollar_brace(const ScrStr *value) {
|
|
110
|
+
for (size_t i = 0; i + 1 < value->len; i++) {
|
|
111
|
+
if (value->data[i] == '$' && value->data[i + 1] == '{') return true;
|
|
112
|
+
}
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
static char scr_file_handle_inspect_quote(const ScrStr *value) {
|
|
117
|
+
if (!memchr(value->data, '\'', value->len)) return '\'';
|
|
118
|
+
if (!memchr(value->data, '"', value->len)) return '"';
|
|
119
|
+
if (!memchr(value->data, '`', value->len) &&
|
|
120
|
+
!scr_file_handle_has_dollar_brace(value)) {
|
|
121
|
+
return '`';
|
|
122
|
+
}
|
|
123
|
+
return '\'';
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/* Node's ERR_INVALID_ARG_VALUE renderer runs util.inspect on the value and
|
|
127
|
+
* truncates that rendered text to 128 UTF-16 units before appending "...".
|
|
128
|
+
* FileHandle flags and paths use the static ScrStr representation, so this
|
|
129
|
+
* scalar slice is all this translation unit needs from the optional inspect
|
|
130
|
+
* runtime. */
|
|
131
|
+
static ScrStr *scr_file_handle_inspect(const ScrStr *value) {
|
|
132
|
+
ScrFileHandleBuf b = {0};
|
|
133
|
+
char quote = scr_file_handle_inspect_quote(value);
|
|
134
|
+
scr_file_handle_buf_char(&b, quote);
|
|
135
|
+
for (size_t i = 0; i < value->len; i++) {
|
|
136
|
+
unsigned char c = (unsigned char)value->data[i];
|
|
137
|
+
if (c == '\\' || (c == '\'' && quote == '\'')) {
|
|
138
|
+
scr_file_handle_buf_char(&b, '\\');
|
|
139
|
+
scr_file_handle_buf_char(&b, (char)c);
|
|
140
|
+
} else if (c == '\b') {
|
|
141
|
+
scr_file_handle_buf_cstr(&b, "\\b");
|
|
142
|
+
} else if (c == '\t') {
|
|
143
|
+
scr_file_handle_buf_cstr(&b, "\\t");
|
|
144
|
+
} else if (c == '\n') {
|
|
145
|
+
scr_file_handle_buf_cstr(&b, "\\n");
|
|
146
|
+
} else if (c == '\f') {
|
|
147
|
+
scr_file_handle_buf_cstr(&b, "\\f");
|
|
148
|
+
} else if (c == '\r') {
|
|
149
|
+
scr_file_handle_buf_cstr(&b, "\\r");
|
|
150
|
+
} else if (c < 0x20 || c == 0x7f) {
|
|
151
|
+
char escaped[5];
|
|
152
|
+
int len = snprintf(escaped, sizeof escaped, "\\x%02X", c);
|
|
153
|
+
scr_file_handle_buf_bytes(&b, escaped, (size_t)len);
|
|
154
|
+
} else if (c == 0xc2 && i + 1 < value->len &&
|
|
155
|
+
(unsigned char)value->data[i + 1] >= 0x80 &&
|
|
156
|
+
(unsigned char)value->data[i + 1] <= 0x9f) {
|
|
157
|
+
char escaped[5];
|
|
158
|
+
int len = snprintf(escaped, sizeof escaped, "\\x%02X",
|
|
159
|
+
(unsigned char)value->data[++i]);
|
|
160
|
+
scr_file_handle_buf_bytes(&b, escaped, (size_t)len);
|
|
161
|
+
} else {
|
|
162
|
+
scr_file_handle_buf_char(&b, (char)c);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
scr_file_handle_buf_char(&b, quote);
|
|
166
|
+
ScrStr *full = scr_str_new(b.data ? b.data : "", b.len);
|
|
167
|
+
free(b.data);
|
|
168
|
+
if (scr_str_utf16_len(full) <= 128) return full;
|
|
169
|
+
ScrStr *head = scr_str_slice(full, 0, 128);
|
|
170
|
+
scr_str_release(full);
|
|
171
|
+
ScrFileHandleBuf truncated = {0};
|
|
172
|
+
scr_file_handle_buf_bytes(&truncated, head->data, head->len);
|
|
173
|
+
scr_file_handle_buf_cstr(&truncated, "...");
|
|
174
|
+
scr_str_release(head);
|
|
175
|
+
ScrStr *out = scr_str_new(truncated.data, truncated.len);
|
|
176
|
+
free(truncated.data);
|
|
177
|
+
return out;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
static void scr_file_handle_arg_value_error(const char *prefix,
|
|
181
|
+
const ScrStr *value) {
|
|
182
|
+
ScrStr *inspected = scr_file_handle_inspect(value);
|
|
183
|
+
ScrFileHandleBuf msg = {0};
|
|
184
|
+
scr_file_handle_buf_cstr(&msg, prefix);
|
|
185
|
+
scr_file_handle_buf_bytes(&msg, inspected->data, inspected->len);
|
|
186
|
+
scr_throw_error_msg_code(SCR_ERR_TYPE, msg.data, msg.len,
|
|
187
|
+
"ERR_INVALID_ARG_VALUE");
|
|
188
|
+
free(msg.data);
|
|
189
|
+
scr_str_release(inspected);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
static bool scr_file_handle_flag_eq(const ScrStr *flags, const char *want) {
|
|
193
|
+
size_t len = strlen(want);
|
|
194
|
+
return flags->len == len && memcmp(flags->data, want, len) == 0;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
static void scr_file_handle_invalid_flags(const ScrStr *flags) {
|
|
198
|
+
scr_file_handle_arg_value_error(
|
|
199
|
+
"The argument 'flags' is invalid. Received ", flags);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
static bool scr_file_handle_path_valid(const ScrStr *path) {
|
|
203
|
+
if (!memchr(path->data, 0, path->len)) return true;
|
|
204
|
+
scr_file_handle_arg_value_error(
|
|
205
|
+
"The argument 'path' must be a string, Uint8Array, or URL without null bytes. Received ",
|
|
206
|
+
path);
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
static int scr_file_handle_open_flags(ScrStr *flags) {
|
|
211
|
+
int of;
|
|
212
|
+
if (scr_file_handle_flag_eq(flags, "r")) of = O_RDONLY;
|
|
213
|
+
else if (scr_file_handle_flag_eq(flags, "rs") || scr_file_handle_flag_eq(flags, "sr")) of = O_RDONLY | O_SYNC;
|
|
214
|
+
else if (scr_file_handle_flag_eq(flags, "r+")) of = O_RDWR;
|
|
215
|
+
else if (scr_file_handle_flag_eq(flags, "rs+") || scr_file_handle_flag_eq(flags, "sr+")) of = O_RDWR | O_SYNC;
|
|
216
|
+
else if (scr_file_handle_flag_eq(flags, "w")) of = O_TRUNC | O_CREAT | O_WRONLY;
|
|
217
|
+
else if (scr_file_handle_flag_eq(flags, "wx") || scr_file_handle_flag_eq(flags, "xw")) of = O_TRUNC | O_CREAT | O_WRONLY | O_EXCL;
|
|
218
|
+
else if (scr_file_handle_flag_eq(flags, "w+")) of = O_TRUNC | O_CREAT | O_RDWR;
|
|
219
|
+
else if (scr_file_handle_flag_eq(flags, "wx+") || scr_file_handle_flag_eq(flags, "xw+")) of = O_TRUNC | O_CREAT | O_RDWR | O_EXCL;
|
|
220
|
+
else if (scr_file_handle_flag_eq(flags, "a")) of = O_APPEND | O_CREAT | O_WRONLY;
|
|
221
|
+
else if (scr_file_handle_flag_eq(flags, "ax") || scr_file_handle_flag_eq(flags, "xa")) of = O_APPEND | O_CREAT | O_WRONLY | O_EXCL;
|
|
222
|
+
else if (scr_file_handle_flag_eq(flags, "as") || scr_file_handle_flag_eq(flags, "sa")) of = O_APPEND | O_CREAT | O_WRONLY | O_SYNC;
|
|
223
|
+
else if (scr_file_handle_flag_eq(flags, "a+")) of = O_APPEND | O_CREAT | O_RDWR;
|
|
224
|
+
else if (scr_file_handle_flag_eq(flags, "ax+") || scr_file_handle_flag_eq(flags, "xa+")) of = O_APPEND | O_CREAT | O_RDWR | O_EXCL;
|
|
225
|
+
else if (scr_file_handle_flag_eq(flags, "as+") || scr_file_handle_flag_eq(flags, "sa+")) of = O_APPEND | O_CREAT | O_RDWR | O_SYNC;
|
|
226
|
+
else {
|
|
227
|
+
scr_file_handle_invalid_flags(flags);
|
|
228
|
+
return -1;
|
|
229
|
+
}
|
|
230
|
+
return of;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
static bool scr_file_handle_mode_valid(double mode) {
|
|
234
|
+
char msg[160];
|
|
235
|
+
char recv[48];
|
|
236
|
+
scr_num_received(mode, recv);
|
|
237
|
+
if (!(isfinite(mode) && trunc(mode) == mode)) {
|
|
238
|
+
int len = snprintf(msg, sizeof msg,
|
|
239
|
+
"The value of \"mode\" is out of range. It must be an integer. Received %s",
|
|
240
|
+
recv);
|
|
241
|
+
scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)len, "ERR_OUT_OF_RANGE");
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
if (mode < 0 || mode > 4294967295.0) {
|
|
245
|
+
int len = snprintf(msg, sizeof msg,
|
|
246
|
+
"The value of \"mode\" is out of range. It must be >= 0 && <= 4294967295. Received %s",
|
|
247
|
+
recv);
|
|
248
|
+
scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)len, "ERR_OUT_OF_RANGE");
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
return true;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
ScrFileHandle *scr_file_handle_open(ScrStr *path, ScrStr *flags, double mode) {
|
|
255
|
+
if (!scr_file_handle_path_valid(path)) return NULL;
|
|
256
|
+
int of = scr_file_handle_open_flags(flags);
|
|
257
|
+
if (of < 0) return NULL;
|
|
258
|
+
if (!scr_file_handle_mode_valid(mode)) return NULL;
|
|
259
|
+
int fd = open(path->data, of | O_BINARY, (mode_t)mode);
|
|
260
|
+
if (fd < 0) {
|
|
261
|
+
scr_fs_throw(errno, "open", path);
|
|
262
|
+
return NULL;
|
|
263
|
+
}
|
|
264
|
+
ScrFileHandle *h = malloc(sizeof(ScrFileHandle));
|
|
265
|
+
if (!h) scr_trap("scriptc: out of memory\n");
|
|
266
|
+
h->rc = 1;
|
|
267
|
+
h->fd = fd;
|
|
268
|
+
return h;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
ScrFileHandle *scr_file_handle_retain(ScrFileHandle *h) {
|
|
272
|
+
if (h->rc != SIZE_MAX) h->rc++;
|
|
273
|
+
return h;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
void scr_file_handle_release(ScrFileHandle *h) {
|
|
277
|
+
if (!h || h->rc == SIZE_MAX) return;
|
|
278
|
+
if (--h->rc != 0) return;
|
|
279
|
+
if (h->fd >= 0) (void)close(h->fd);
|
|
280
|
+
free(h);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
void *scr_file_handle_retain_v(void *p) { return scr_file_handle_retain(p); }
|
|
284
|
+
void scr_file_handle_release_v(void *p) { scr_file_handle_release(p); }
|
|
285
|
+
|
|
286
|
+
double scr_file_handle_fd(ScrFileHandle *h) { return (double)h->fd; }
|
|
287
|
+
|
|
288
|
+
static bool scr_file_handle_require_open(ScrFileHandle *h) {
|
|
289
|
+
if (h->fd >= 0) return true;
|
|
290
|
+
static const char msg[] = "file closed";
|
|
291
|
+
scr_throw_error_msg_code(SCR_ERR_ERROR, msg, sizeof msg - 1, "EBADF");
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
void scr_file_handle_close(ScrFileHandle *h) {
|
|
296
|
+
if (h->fd < 0) return; /* Node's FileHandle.close() is idempotent. */
|
|
297
|
+
int fd = h->fd;
|
|
298
|
+
h->fd = -1;
|
|
299
|
+
scr_fs_close((double)fd);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
double scr_file_handle_read(ScrFileHandle *h, ScrBytes *buf, double offset,
|
|
303
|
+
double length, double position,
|
|
304
|
+
bool length_default) {
|
|
305
|
+
if (!scr_file_handle_require_open(h)) return 0;
|
|
306
|
+
if (buf->len == 0) {
|
|
307
|
+
/* FileHandle.read's empty-buffer ladder differs from readSync's generic
|
|
308
|
+
* window check: validate offset intrinsically first, then a zero request
|
|
309
|
+
* succeeds without consulting position/the descriptor; every other
|
|
310
|
+
* request rejects with Node's dedicated invalid-buffer error. Reusing a
|
|
311
|
+
* zero-length read performs exactly that offset-only validation. */
|
|
312
|
+
double checked = scr_fs_read_sync((double)h->fd, buf, offset, 0, position);
|
|
313
|
+
if (scr_exc_pending()) return 0;
|
|
314
|
+
if ((!length_default && length >= 0 && length < 1) ||
|
|
315
|
+
(length_default && offset == 0)) {
|
|
316
|
+
return checked;
|
|
317
|
+
}
|
|
318
|
+
static const char msg[] =
|
|
319
|
+
"The argument 'buffer' is empty and cannot be written. Received <Buffer >";
|
|
320
|
+
scr_throw_error_msg_code(SCR_ERR_TYPE, msg, sizeof msg - 1,
|
|
321
|
+
"ERR_INVALID_ARG_VALUE");
|
|
322
|
+
return 0;
|
|
323
|
+
}
|
|
324
|
+
if (length_default && isfinite(offset) && trunc(offset) == offset &&
|
|
325
|
+
offset >= 0 && offset <= (double)buf->len) {
|
|
326
|
+
length = (double)buf->len - offset;
|
|
327
|
+
}
|
|
328
|
+
return scr_fs_read_sync((double)h->fd, buf, offset, length, position);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
double scr_file_handle_write_bytes(ScrFileHandle *h, ScrBytes *buf,
|
|
332
|
+
double offset, double length,
|
|
333
|
+
double position, bool length_default) {
|
|
334
|
+
if (!scr_file_handle_require_open(h)) return 0;
|
|
335
|
+
/* Node completes an empty Buffer write before validating offset/length/
|
|
336
|
+
* position or testing whether the descriptor is writable. */
|
|
337
|
+
if (buf->len == 0) return 0;
|
|
338
|
+
if (length_default && isfinite(offset) && trunc(offset) == offset &&
|
|
339
|
+
offset >= 0 && offset <= (double)buf->len) {
|
|
340
|
+
length = (double)buf->len - offset;
|
|
341
|
+
}
|
|
342
|
+
return scr_fs_write_sync((double)h->fd, buf, offset, length, position);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
double scr_file_handle_write_str(ScrFileHandle *h, ScrStr *data,
|
|
346
|
+
double position, ScrStr *encoding) {
|
|
347
|
+
if (!scr_file_handle_require_open(h)) return 0;
|
|
348
|
+
return scr_fs_write_str_sync((double)h->fd, data, position, encoding);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
ScrStr *scr_file_handle_read_file(ScrFileHandle *h) {
|
|
352
|
+
if (!scr_file_handle_require_open(h)) return NULL;
|
|
353
|
+
return scr_fs_read_fd((double)h->fd);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
ScrBytes *scr_file_handle_read_file_bytes(ScrFileHandle *h) {
|
|
357
|
+
if (!scr_file_handle_require_open(h)) return NULL;
|
|
358
|
+
return scr_fs_read_fd_bytes((double)h->fd);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
static void scr_file_handle_write_all(ScrFileHandle *h, const void *data,
|
|
362
|
+
size_t length) {
|
|
363
|
+
if (!scr_file_handle_require_open(h)) return;
|
|
364
|
+
ScrBytes bytes = {SIZE_MAX, length, SCR_BYTES_U8, (uint8_t *)data, NULL};
|
|
365
|
+
size_t at = 0;
|
|
366
|
+
while (at < length) {
|
|
367
|
+
double count = scr_fs_write_sync((double)h->fd, &bytes, (double)at,
|
|
368
|
+
(double)(length - at), -1);
|
|
369
|
+
if (scr_exc_pending()) return;
|
|
370
|
+
if (count <= 0) {
|
|
371
|
+
static const char msg[] = "EIO: i/o error, write";
|
|
372
|
+
scr_throw_error_msg_code(SCR_ERR_ERROR, msg, sizeof msg - 1, "EIO");
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
at += (size_t)count;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
void scr_file_handle_write_file(ScrFileHandle *h, ScrStr *data) {
|
|
380
|
+
scr_file_handle_write_all(h, data->data, data->len);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
void scr_file_handle_write_file_bytes(ScrFileHandle *h, ScrBytes *data) {
|
|
384
|
+
scr_file_handle_write_all(h, data->data,
|
|
385
|
+
data->len * scr_bytes_elem_size(data->elem));
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
ScrStats *scr_file_handle_stat(ScrFileHandle *h) {
|
|
389
|
+
if (!scr_file_handle_require_open(h)) return NULL;
|
|
390
|
+
struct stat st;
|
|
391
|
+
if (fstat(h->fd, &st) != 0) {
|
|
392
|
+
int err = errno;
|
|
393
|
+
const char *name = err == EBADF ? "EBADF" : err == EIO ? "EIO" : "EUNKNOWN";
|
|
394
|
+
const char *text = err == EBADF ? "bad file descriptor" :
|
|
395
|
+
err == EIO ? "i/o error" : strerror(err);
|
|
396
|
+
char msg[256];
|
|
397
|
+
int len = snprintf(msg, sizeof msg, "%s: %s, fstat", name, text);
|
|
398
|
+
scr_throw_error_msg_code(SCR_ERR_ERROR, msg, (size_t)len, name);
|
|
399
|
+
return NULL;
|
|
400
|
+
}
|
|
401
|
+
ScrStats *out = malloc(sizeof(ScrStats));
|
|
402
|
+
if (!out) scr_trap("scriptc: out of memory\n");
|
|
403
|
+
out->rc = 1;
|
|
404
|
+
out->is_file = S_ISREG(st.st_mode);
|
|
405
|
+
out->is_dir = S_ISDIR(st.st_mode);
|
|
406
|
+
out->is_symlink = false; /* fstat follows the open descriptor. */
|
|
407
|
+
out->size = (double)st.st_size;
|
|
408
|
+
#if defined(_WIN32)
|
|
409
|
+
out->blocks = st.st_size <= 0 ? 0.0 : (double)(((uint64_t)st.st_size + 511) >> 9);
|
|
410
|
+
out->nlink = (double)st.st_nlink;
|
|
411
|
+
out->atime_ms = (double)st.st_atime * 1000.0;
|
|
412
|
+
out->mtime_ms = (double)st.st_mtime * 1000.0;
|
|
413
|
+
HANDLE os_handle = (HANDLE)_get_osfhandle(h->fd);
|
|
414
|
+
if (os_handle != INVALID_HANDLE_VALUE) {
|
|
415
|
+
BY_HANDLE_FILE_INFORMATION basic;
|
|
416
|
+
if (GetFileInformationByHandle(os_handle, &basic)) {
|
|
417
|
+
out->nlink = (double)basic.nNumberOfLinks;
|
|
418
|
+
out->atime_ms = scr_file_handle_filetime_ms(basic.ftLastAccessTime);
|
|
419
|
+
out->mtime_ms = scr_file_handle_filetime_ms(basic.ftLastWriteTime);
|
|
420
|
+
}
|
|
421
|
+
FILE_STANDARD_INFO standard;
|
|
422
|
+
if (GetFileInformationByHandleEx(
|
|
423
|
+
os_handle, FileStandardInfo, &standard, sizeof standard)) {
|
|
424
|
+
out->blocks = (double)((uint64_t)standard.AllocationSize.QuadPart >> 9);
|
|
425
|
+
out->nlink = (double)standard.NumberOfLinks;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
#elif defined(__APPLE__)
|
|
429
|
+
out->blocks = (double)st.st_blocks;
|
|
430
|
+
out->nlink = (double)st.st_nlink;
|
|
431
|
+
out->atime_ms = (double)st.st_atimespec.tv_sec * 1000.0 +
|
|
432
|
+
(double)st.st_atimespec.tv_nsec / 1e6;
|
|
433
|
+
out->mtime_ms = (double)st.st_mtimespec.tv_sec * 1000.0 +
|
|
434
|
+
(double)st.st_mtimespec.tv_nsec / 1e6;
|
|
435
|
+
#else
|
|
436
|
+
out->blocks = (double)st.st_blocks;
|
|
437
|
+
out->nlink = (double)st.st_nlink;
|
|
438
|
+
out->atime_ms = (double)st.st_atim.tv_sec * 1000.0 +
|
|
439
|
+
(double)st.st_atim.tv_nsec / 1e6;
|
|
440
|
+
out->mtime_ms = (double)st.st_mtim.tv_sec * 1000.0 +
|
|
441
|
+
(double)st.st_mtim.tv_nsec / 1e6;
|
|
442
|
+
#endif
|
|
443
|
+
return out;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
ScrPromise *scr_fsp_open(ScrStr *path, ScrStr *flags, double mode) {
|
|
447
|
+
ScrFileHandle *h = scr_file_handle_open(path, flags, mode);
|
|
448
|
+
return scr_promise_settled_ref(h, &scr_file_handle_retain_v,
|
|
449
|
+
&scr_file_handle_release_v, NULL);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
ScrPromise *scr_file_handle_close_promise(ScrFileHandle *h) {
|
|
453
|
+
scr_file_handle_close(h);
|
|
454
|
+
return scr_promise_settled_void();
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
ScrPromise *scr_file_handle_read_file_promise(ScrFileHandle *h,
|
|
458
|
+
ScrStr *encoding) {
|
|
459
|
+
(void)encoding;
|
|
460
|
+
return scr_promise_settled_str(scr_file_handle_read_file(h));
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
ScrPromise *scr_file_handle_read_file_bytes_promise(ScrFileHandle *h,
|
|
464
|
+
ScrStr *encoding) {
|
|
465
|
+
(void)encoding; /* evaluates the explicit undefined/null default in order */
|
|
466
|
+
ScrBytes *data = scr_file_handle_read_file_bytes(h);
|
|
467
|
+
return scr_promise_settled_ref(data, &scr_bytes_retain_v,
|
|
468
|
+
&scr_bytes_release_v, NULL);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
ScrPromise *scr_file_handle_write_file_promise(ScrFileHandle *h,
|
|
472
|
+
ScrStr *data,
|
|
473
|
+
ScrStr *encoding) {
|
|
474
|
+
(void)encoding; /* frontend admits utf8 only; evaluation is observable */
|
|
475
|
+
scr_file_handle_write_file(h, data);
|
|
476
|
+
return scr_promise_settled_void();
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
ScrPromise *scr_file_handle_write_file_bytes_promise(ScrFileHandle *h,
|
|
480
|
+
ScrBytes *data,
|
|
481
|
+
ScrStr *encoding) {
|
|
482
|
+
(void)encoding;
|
|
483
|
+
scr_file_handle_write_file_bytes(h, data);
|
|
484
|
+
return scr_promise_settled_void();
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
ScrPromise *scr_file_handle_stat_promise(ScrFileHandle *h) {
|
|
488
|
+
ScrStats *st = scr_file_handle_stat(h);
|
|
489
|
+
return scr_promise_settled_ref(st, &scr_stats_retain_v,
|
|
490
|
+
&scr_stats_release_v, NULL);
|
|
491
|
+
}
|
package/src/scr_island.c
CHANGED
|
@@ -2978,6 +2978,9 @@ static JSValue isl_host_fs(JSContext *ctx, JSValueConst this_val, int argc,
|
|
|
2978
2978
|
JS_SetPropertyUint32(ctx, arr, 2, JS_NewBool(ctx, scr_stats_is_symlink(st)));
|
|
2979
2979
|
JS_SetPropertyUint32(ctx, arr, 3, JS_NewFloat64(ctx, scr_stats_size(st)));
|
|
2980
2980
|
JS_SetPropertyUint32(ctx, arr, 4, JS_NewFloat64(ctx, scr_stats_mtime_ms(st)));
|
|
2981
|
+
JS_SetPropertyUint32(ctx, arr, 5, JS_NewFloat64(ctx, scr_stats_blocks(st)));
|
|
2982
|
+
JS_SetPropertyUint32(ctx, arr, 6, JS_NewFloat64(ctx, scr_stats_nlink(st)));
|
|
2983
|
+
JS_SetPropertyUint32(ctx, arr, 7, JS_NewFloat64(ctx, scr_stats_atime_ms(st)));
|
|
2981
2984
|
scr_stats_release(st);
|
|
2982
2985
|
ret = arr;
|
|
2983
2986
|
}
|
|
@@ -3996,6 +3999,10 @@ static const char isl_modules_bootstrap[] =
|
|
|
3996
3999
|
" this._l = row[2];\n"
|
|
3997
4000
|
" this.size = row[3];\n"
|
|
3998
4001
|
" this.mtimeMs = row[4];\n"
|
|
4002
|
+
" this.blocks = row[5];\n"
|
|
4003
|
+
" this.nlink = row[6];\n"
|
|
4004
|
+
" this.atimeMs = row[7];\n"
|
|
4005
|
+
" this.atime = new Date(row[7]);\n"
|
|
3999
4006
|
" this.mtime = new Date(row[4]);\n"
|
|
4000
4007
|
" this.mode = (this._f ? constants.S_IFREG : this._d ? constants.S_IFDIR : this._l ? (constants.S_IFLNK || 0) : 0);\n"
|
|
4001
4008
|
" }\n"
|
|
@@ -4194,6 +4201,9 @@ static const char isl_modules_bootstrap[] =
|
|
|
4194
4201
|
" readSync: () => {\n"
|
|
4195
4202
|
" throw new Error(\"fs.readSync is not available in the scriptc island (whole-file reads/writes only)\");\n"
|
|
4196
4203
|
" },\n"
|
|
4204
|
+
" writeSync: () => {\n"
|
|
4205
|
+
" throw new Error(\"fs.writeSync is not available in the scriptc island (whole-file reads/writes only)\");\n"
|
|
4206
|
+
" },\n"
|
|
4197
4207
|
" read: () => {\n"
|
|
4198
4208
|
" throw new Error(\"fs.read is not available in the scriptc island (whole-file reads/writes only)\");\n"
|
|
4199
4209
|
" },\n"
|
package/src/scr_json.c
CHANGED
|
@@ -94,6 +94,10 @@ static void scr_jb_write(ScrJsonBuf *b, const char *s, size_t n) {
|
|
|
94
94
|
b->len += n;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
void scr_jb_put_str(ScrJsonBuf *b, const ScrStr *s) {
|
|
98
|
+
scr_jb_write(b, s->data, s->len);
|
|
99
|
+
}
|
|
100
|
+
|
|
97
101
|
void scr_jb_puts(ScrJsonBuf *b, const char *s) { scr_jb_write(b, s, strlen(s)); }
|
|
98
102
|
|
|
99
103
|
void scr_jb_put_f64(ScrJsonBuf *b, double v) {
|
|
@@ -986,10 +990,18 @@ void scr_throw_arg_type(const ScrStr *argname, const ScrStr *expected, const Scr
|
|
|
986
990
|
void scr_dyn_arg_type_fail(const char *argname, const char *expected, const ScrDyn *got) {
|
|
987
991
|
char detail[64];
|
|
988
992
|
const char *d = scr_dyn_specific_type(got, detail, sizeof detail);
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
+
ScrJsonBuf b;
|
|
994
|
+
scr_jb_init(&b);
|
|
995
|
+
scr_jb_puts(&b, "The \"");
|
|
996
|
+
scr_jb_puts(&b, argname);
|
|
997
|
+
scr_jb_puts(&b, "\" argument must be ");
|
|
998
|
+
scr_jb_puts(&b, expected);
|
|
999
|
+
scr_jb_puts(&b, ". Received ");
|
|
1000
|
+
scr_jb_puts(&b, d);
|
|
1001
|
+
ScrStr *msg = scr_jb_finish(&b);
|
|
1002
|
+
scr_throw_error_msg_code(SCR_ERR_TYPE, msg->data, msg->len,
|
|
1003
|
+
"ERR_INVALID_ARG_TYPE");
|
|
1004
|
+
scr_str_release(msg);
|
|
993
1005
|
}
|
|
994
1006
|
|
|
995
1007
|
/* The property flavor of the same ladder — Node renders option-bag
|
|
@@ -999,10 +1011,18 @@ void scr_dyn_arg_type_fail(const char *argname, const char *expected, const ScrD
|
|
|
999
1011
|
void scr_dyn_prop_type_fail(const char *name, const char *expected, const ScrDyn *got) {
|
|
1000
1012
|
char detail[64];
|
|
1001
1013
|
const char *d = scr_dyn_specific_type(got, detail, sizeof detail);
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1014
|
+
ScrJsonBuf b;
|
|
1015
|
+
scr_jb_init(&b);
|
|
1016
|
+
scr_jb_puts(&b, "The \"");
|
|
1017
|
+
scr_jb_puts(&b, name);
|
|
1018
|
+
scr_jb_puts(&b, "\" property must be ");
|
|
1019
|
+
scr_jb_puts(&b, expected);
|
|
1020
|
+
scr_jb_puts(&b, ". Received ");
|
|
1021
|
+
scr_jb_puts(&b, d);
|
|
1022
|
+
ScrStr *msg = scr_jb_finish(&b);
|
|
1023
|
+
scr_throw_error_msg_code(SCR_ERR_TYPE, msg->data, msg->len,
|
|
1024
|
+
"ERR_INVALID_ARG_TYPE");
|
|
1025
|
+
scr_str_release(msg);
|
|
1006
1026
|
}
|
|
1007
1027
|
|
|
1008
1028
|
/* The compiler-resolved property-typed throw (error.propTypeThrow —
|
|
@@ -1051,11 +1071,19 @@ const char *scr_dyn_inspect_lite(const ScrDyn *v, char *buf, size_t cap) {
|
|
|
1051
1071
|
void scr_dyn_arg_value_fail(const char *name, const char *reason, const ScrDyn *got) {
|
|
1052
1072
|
char insp[64];
|
|
1053
1073
|
const char *d = scr_dyn_inspect_lite(got, insp, sizeof insp);
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1074
|
+
ScrJsonBuf b;
|
|
1075
|
+
scr_jb_init(&b);
|
|
1076
|
+
scr_jb_puts(&b, "The ");
|
|
1077
|
+
scr_jb_puts(&b, strchr(name, '.') != NULL ? "property '" : "argument '");
|
|
1078
|
+
scr_jb_puts(&b, name);
|
|
1079
|
+
scr_jb_puts(&b, "' ");
|
|
1080
|
+
scr_jb_puts(&b, reason != NULL ? reason : "is invalid");
|
|
1081
|
+
scr_jb_puts(&b, ". Received ");
|
|
1082
|
+
scr_jb_puts(&b, d);
|
|
1083
|
+
ScrStr *msg = scr_jb_finish(&b);
|
|
1084
|
+
scr_throw_error_msg_code(SCR_ERR_TYPE, msg->data, msg->len,
|
|
1085
|
+
"ERR_INVALID_ARG_VALUE");
|
|
1086
|
+
scr_str_release(msg);
|
|
1059
1087
|
}
|
|
1060
1088
|
|
|
1061
1089
|
/* The deferred JS lowering fence, thrown from a ladder's post-validation
|
|
@@ -1362,7 +1390,8 @@ ScrStr *scr_dyn_typeof(const ScrDyn *d) {
|
|
|
1362
1390
|
/* ── JSON.stringify over a dyn value (util.format's %j) ───────────────
|
|
1363
1391
|
* The RUNTIME walk the type-directed serializers deliberately avoid for
|
|
1364
1392
|
* static values — a dyn value has no static type, so the checked-dynamic tree's own kinds
|
|
1365
|
-
* drive it, JS-exactly:
|
|
1393
|
+
* drive it, JS-exactly: objects in OrdinaryOwnPropertyKeys order (array
|
|
1394
|
+
* indices ascending, then other strings in insertion order) with undefined/function
|
|
1366
1395
|
* members OMITTED, arrays rendering those as null, Buffer's toJSON shape
|
|
1367
1396
|
* ({"type":"Buffer","data":[...]}), shortest-roundtrip numbers, escaped
|
|
1368
1397
|
* strings. HANDLE values fence loudly (Node walks own enumerable props
|
|
@@ -1390,23 +1419,26 @@ static bool scr_dyn_json_write(ScrJsonBuf *b, const ScrDyn *d) {
|
|
|
1390
1419
|
case SCR_DYN_OBJ: {
|
|
1391
1420
|
scr_jb_putc(b, '{');
|
|
1392
1421
|
bool first = true;
|
|
1393
|
-
|
|
1422
|
+
ScrDyn *entries = scr_dyn_obj_entries(d);
|
|
1423
|
+
for (size_t i = 0; i < entries->v.arr.len; i++) {
|
|
1424
|
+
const ScrDyn *pair = entries->v.arr.items[i];
|
|
1425
|
+
const ScrDyn *key = pair->v.arr.items[0];
|
|
1426
|
+
const ScrDyn *value = pair->v.arr.items[1];
|
|
1394
1427
|
ScrJsonBuf probe;
|
|
1395
1428
|
scr_jb_init(&probe);
|
|
1396
|
-
if (!scr_dyn_json_write(&probe,
|
|
1429
|
+
if (!scr_dyn_json_write(&probe, value)) {
|
|
1397
1430
|
scr_jb_dispose(&probe);
|
|
1398
1431
|
continue; /* undefined/function members drop, like Node */
|
|
1399
1432
|
}
|
|
1400
1433
|
if (!first) scr_jb_putc(b, ',');
|
|
1401
1434
|
first = false;
|
|
1402
|
-
|
|
1403
|
-
scr_jb_put_json_str(b, k);
|
|
1404
|
-
scr_str_release(k);
|
|
1435
|
+
scr_jb_put_json_str(b, key->v.str);
|
|
1405
1436
|
scr_jb_putc(b, ':');
|
|
1406
1437
|
ScrStr *body = scr_jb_finish(&probe);
|
|
1407
1438
|
scr_jb_write(b, body->data, body->len);
|
|
1408
1439
|
scr_str_release(body);
|
|
1409
1440
|
}
|
|
1441
|
+
scr_dyn_release(entries);
|
|
1410
1442
|
scr_jb_putc(b, '}');
|
|
1411
1443
|
return true;
|
|
1412
1444
|
}
|