xbintsc 0.5.20 → 0.5.21
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/AGENTS.md +5 -3
- package/dist/src/driver/compiler.js +1 -1
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/driver/runtime-objects.d.ts +1 -1
- package/dist/src/driver/runtime-objects.js +1 -0
- package/dist/src/driver/runtime-objects.js.map +1 -1
- package/dist/src/driver/version.d.ts +1 -1
- package/dist/src/driver/version.js +1 -1
- package/dist/src/extensions/node/fs/index.d.ts +4 -3
- package/dist/src/extensions/node/fs/index.js +12 -3
- package/dist/src/extensions/node/fs/index.js.map +1 -1
- package/dist/src/extensions/node/fs-promises/index.d.ts +5 -5
- package/dist/src/extensions/node/fs-promises/index.js +15 -6
- package/dist/src/extensions/node/fs-promises/index.js.map +1 -1
- package/dist/tests/e2e/async-await.test.js +1 -1
- package/dist/tests/e2e/async-fs.test.d.ts +9 -0
- package/dist/tests/e2e/async-fs.test.js +119 -0
- package/dist/tests/e2e/async-fs.test.js.map +1 -0
- package/dist/tests/e2e/fetch.test.js +24 -0
- package/dist/tests/e2e/fetch.test.js.map +1 -1
- package/dist/tests/e2e/gui-inline-svg.test.d.ts +11 -0
- package/dist/tests/e2e/gui-inline-svg.test.js +160 -0
- package/dist/tests/e2e/gui-inline-svg.test.js.map +1 -0
- package/doc/ai/contributing.md +2 -1
- package/doc/ai/extensions.md +4 -3
- package/doc/ai/language-support.md +2 -1
- package/doc/ai/zh-CN/contributing.md +1 -1
- package/doc/ai/zh-CN/extensions.md +4 -3
- package/doc/ai/zh-CN/language-support.md +2 -1
- package/doc/gui/api.md +8 -0
- package/doc/gui/known-issues.md +10 -9
- package/doc/gui/media.md +32 -3
- package/doc/gui.md +24 -2
- package/doc/implemented/async.md +16 -3
- package/doc/implemented/fetch.md +38 -6
- package/doc/implemented/node/fs-promises.md +19 -8
- package/doc/implemented/node.md +1 -1
- package/doc/implemented.md +1 -1
- package/doc/unimplemented/deviations.md +1 -1
- package/doc/unimplemented/node/async.md +6 -3
- package/doc/unimplemented/node/fs-promises.md +10 -6
- package/doc/unimplemented/node/fs.md +7 -8
- package/doc/unimplemented/node.md +2 -2
- package/doc/unimplemented/toolchain.md +1 -1
- package/doc/zh-CN/gui/api.md +7 -0
- package/doc/zh-CN/gui/known-issues.md +8 -7
- package/doc/zh-CN/gui/media.md +26 -2
- package/doc/zh-CN/gui.md +18 -1
- package/doc/zh-CN/implemented/async.md +13 -2
- package/doc/zh-CN/implemented/fetch.md +23 -4
- package/doc/zh-CN/implemented/node/fs-promises.md +6 -3
- package/doc/zh-CN/implemented/node.md +1 -1
- package/doc/zh-CN/implemented.md +1 -1
- package/doc/zh-CN/unimplemented/deviations.md +1 -1
- package/doc/zh-CN/unimplemented/node/async.md +6 -3
- package/doc/zh-CN/unimplemented/node/fs-promises.md +4 -4
- package/doc/zh-CN/unimplemented/node/fs.md +2 -2
- package/doc/zh-CN/unimplemented/node.md +2 -2
- package/doc/zh-CN/unimplemented/toolchain.md +1 -1
- package/package.json +1 -1
- package/runtime/ext_gui/inline_svg.cpp +219 -0
- package/runtime/ext_gui/inline_svg.h +41 -0
- package/runtime/ext_gui/layout.cpp +18 -0
- package/runtime/ext_gui/layout.h +7 -0
- package/runtime/ext_gui/layout_flow.cpp +4 -3
- package/runtime/ext_gui/paint.cpp +14 -0
- package/runtime/ext_gui/paint.h +5 -1
- package/runtime/ext_gui/renderer_image.cpp +53 -25
- package/runtime/ext_gui/style_ua.cpp +3 -0
- package/runtime/ext_gui/svg.cpp +122 -39
- package/runtime/ext_gui/svg.h +14 -3
- package/runtime/ext_gui/window.cpp +28 -0
- package/runtime/ext_node/fs/async_api.c +314 -0
- package/runtime/ext_node/fs/async_complete.c +99 -0
- package/runtime/ext_node/fs/async_fh.c +206 -0
- package/runtime/ext_node/fs/async_job.c +81 -0
- package/runtime/ext_node/fs/async_job.h +132 -0
- package/runtime/ext_node/fs/async_worker.c +534 -0
- package/runtime/ext_node/fs/async_worker_dir.c +439 -0
- package/runtime/ext_node/fs/async_worker_dir.h +33 -0
- package/runtime/ext_node/fs/async_worker_fd.c +74 -0
- package/runtime/ext_node/fs/dir.c +60 -15
- package/runtime/ext_node/fs/fs_common.h +9 -0
- package/runtime/ext_node/fs/fs_ops.c +18 -29
- package/runtime/ext_node/fs/parts/builtins.inc +34 -0
- package/runtime/ext_node/fs/parts/platform.inc +66 -0
- package/runtime/ext_node/fs/promises.c +44 -234
- package/runtime/rt_internal.h +2 -0
- package/runtime/xt_async_io.c +333 -0
- package/runtime/xt_async_io.h +47 -0
- package/runtime/xt_fetch/async.inc +295 -0
- package/runtime/xt_fetch/request.inc +6 -183
- package/runtime/xt_fetch/socket.inc +5 -4
- package/runtime/xt_fetch.c +6 -4
- package/runtime/xt_loop.c +44 -3
- package/src/driver/compiler.ts +1 -1
- package/src/driver/runtime-objects.ts +1 -0
- package/src/driver/version.ts +1 -1
- package/src/extensions/node/fs/index.ts +12 -3
- package/src/extensions/node/fs-promises/index.ts +15 -6
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Main-thread half of `fs/promises`: turn worker results into values.
|
|
3
|
+
*
|
|
4
|
+
* Runs on the reactor thread, so `xt_value` construction, promise settlement
|
|
5
|
+
* and the collector are all safe here. Everything the job owns is freed by
|
|
6
|
+
* `xt_fs_job_release` after this returns (except the entry table handed over to
|
|
7
|
+
* a `Dir` object).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
#include "async_job.h"
|
|
11
|
+
|
|
12
|
+
static void xt_fs_job_resolve(xt_fs_job *job, xt_value value) {
|
|
13
|
+
xt_promise_fulfill(job->base.roots[0], value);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
static void xt_fs_job_reject(xt_fs_job *job) {
|
|
17
|
+
xt_value error = xt_fs_error_object(job->err, job->syscall, job->error_path);
|
|
18
|
+
xt_promise_reject_value(job->base.roots[0], error);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
static xt_value xt_fs_statfs_value(const long long *numbers) {
|
|
22
|
+
xt_value object = xt_object_new();
|
|
23
|
+
xt_object_set(object, xt_string_from_cstr("type"), xt_number((double)numbers[0]));
|
|
24
|
+
xt_object_set(object, xt_string_from_cstr("bsize"), xt_number((double)numbers[1]));
|
|
25
|
+
xt_object_set(object, xt_string_from_cstr("blocks"), xt_number((double)numbers[2]));
|
|
26
|
+
xt_object_set(object, xt_string_from_cstr("bfree"), xt_number((double)numbers[3]));
|
|
27
|
+
xt_object_set(object, xt_string_from_cstr("bavail"), xt_number((double)numbers[4]));
|
|
28
|
+
xt_object_set(object, xt_string_from_cstr("files"), xt_number((double)numbers[5]));
|
|
29
|
+
xt_object_set(object, xt_string_from_cstr("ffree"), xt_number((double)numbers[6]));
|
|
30
|
+
return object;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static xt_value xt_fs_readdir_value(xt_fs_job *job) {
|
|
34
|
+
xt_value list = xt_array_new(0, NULL);
|
|
35
|
+
for (size_t i = 0; i < job->entry_count; i++) {
|
|
36
|
+
xt_fs_entry *entry = &job->entries[i];
|
|
37
|
+
if (job->with_types) {
|
|
38
|
+
xt_array_push(list, xt_node_dirent_named(entry->bare ? entry->bare : entry->name, entry->mode));
|
|
39
|
+
} else {
|
|
40
|
+
xt_array_push(list, xt_string_from_cstr(entry->name));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return list;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static xt_value xt_fs_glob_value(xt_fs_job *job) {
|
|
47
|
+
xt_value list = xt_array_new(0, NULL);
|
|
48
|
+
for (size_t i = 0; i < job->entry_count; i++) {
|
|
49
|
+
xt_array_push(list, xt_string_from_cstr(job->entries[i].name));
|
|
50
|
+
}
|
|
51
|
+
return list;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
static xt_value xt_fs_read_bytes_value(xt_fs_job *job) {
|
|
55
|
+
return xt_fs_encode_bytes(job->bytes, job->bytes_length, job->text);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
static void xt_fs_fd_read_complete(xt_fs_job *job) {
|
|
59
|
+
xt_value buffer = job->base.roots[1];
|
|
60
|
+
uint32_t offset = (uint32_t)job->offset;
|
|
61
|
+
for (size_t i = 0; i < job->bytes_length; i++) {
|
|
62
|
+
xt_node_buffer_set(buffer, offset + (uint32_t)i, job->bytes[i]);
|
|
63
|
+
}
|
|
64
|
+
xt_fs_job_resolve(job, xt_number((double)job->bytes_length));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
void xt_fs_job_complete(xt_async_job *base) {
|
|
68
|
+
xt_fs_job *job = (xt_fs_job *)base;
|
|
69
|
+
if (job->err) {
|
|
70
|
+
xt_fs_job_reject(job);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
switch (job->kind) {
|
|
74
|
+
case XT_FS_JOB_READ_FILE:
|
|
75
|
+
case XT_FS_JOB_FD_READ_FILE: xt_fs_job_resolve(job, xt_fs_read_bytes_value(job)); break;
|
|
76
|
+
case XT_FS_JOB_READDIR: xt_fs_job_resolve(job, xt_fs_readdir_value(job)); break;
|
|
77
|
+
case XT_FS_JOB_GLOB: xt_fs_job_resolve(job, xt_fs_glob_value(job)); break;
|
|
78
|
+
case XT_FS_JOB_OPENDIR: {
|
|
79
|
+
xt_value dir = xt_node_dir_from_entries(job->path, job->entries, job->entry_count);
|
|
80
|
+
job->entries = NULL;
|
|
81
|
+
job->entry_count = 0;
|
|
82
|
+
xt_fs_job_resolve(job, dir);
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
case XT_FS_JOB_REALPATH:
|
|
86
|
+
case XT_FS_JOB_READLINK:
|
|
87
|
+
case XT_FS_JOB_MKDTEMP:
|
|
88
|
+
xt_fs_job_resolve(job, job->string ? xt_string_from_cstr(job->string) : XT_UNDEFINED);
|
|
89
|
+
break;
|
|
90
|
+
case XT_FS_JOB_STAT:
|
|
91
|
+
case XT_FS_JOB_LSTAT:
|
|
92
|
+
case XT_FS_JOB_FD_STAT: xt_fs_job_resolve(job, xt_node_stat_result(&job->stat)); break;
|
|
93
|
+
case XT_FS_JOB_STATFS: xt_fs_job_resolve(job, xt_fs_statfs_value(job->numbers)); break;
|
|
94
|
+
case XT_FS_JOB_OPEN: xt_fs_job_resolve(job, xt_fh_new(xt_number((double)job->result_fd))); break;
|
|
95
|
+
case XT_FS_JOB_FD_READ: xt_fs_fd_read_complete(job); break;
|
|
96
|
+
case XT_FS_JOB_FD_WRITE: xt_fs_job_resolve(job, xt_number((double)job->written)); break;
|
|
97
|
+
default: xt_fs_job_resolve(job, XT_UNDEFINED); break;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* `FileHandle` for `fs/promises`, backed by the async I/O pool.
|
|
3
|
+
*
|
|
4
|
+
* The handle is a plain object with an `fd` property and the usual methods;
|
|
5
|
+
* each method marshals its arguments and submits an `XT_FS_JOB_FD_*` job, so a
|
|
6
|
+
* read, write, fsync or close never blocks the reactor. `read` keeps the target
|
|
7
|
+
* `Buffer` rooted in `roots[1]` while the worker fills a temporary byte array.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
#include "async_job.h"
|
|
11
|
+
|
|
12
|
+
#include <errno.h>
|
|
13
|
+
|
|
14
|
+
static int32_t xt_fh_fd(xt_value thisValue) { return (int32_t)xt_to_number(xt_object_get_cstr(thisValue, "fd")); }
|
|
15
|
+
|
|
16
|
+
static xt_value xt_fh_invalid_buffer(void) {
|
|
17
|
+
return xt_promise_reject(xt_fs_error_object(EINVAL, "read", NULL));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
static xt_value xt_fh_submit(xt_fs_job *job) { return xt_fs_job_submit(job); }
|
|
21
|
+
|
|
22
|
+
static xt_value xt_fh_read(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
23
|
+
(void)env;
|
|
24
|
+
if (argc < 1 || !xt_node_is_buffer(argv[0])) return xt_fh_invalid_buffer();
|
|
25
|
+
xt_value buffer = argv[0];
|
|
26
|
+
uint32_t bufferLength = xt_node_buffer_length(buffer);
|
|
27
|
+
uint32_t offset = argc > 1 ? (uint32_t)xt_to_number(argv[1]) : 0;
|
|
28
|
+
uint32_t length = argc > 2 ? (uint32_t)xt_to_number(argv[2]) : (bufferLength > offset ? bufferLength - offset : 0);
|
|
29
|
+
int64_t position =
|
|
30
|
+
argc > 3 && !XT_IS_UNDEFINED(argv[3]) && !XT_IS_NULL(argv[3]) ? (int64_t)xt_to_number(argv[3]) : -1;
|
|
31
|
+
if (offset > bufferLength) offset = bufferLength;
|
|
32
|
+
if (length > bufferLength - offset) length = bufferLength - offset;
|
|
33
|
+
|
|
34
|
+
xt_fs_job *job = xt_fs_job_new(XT_FS_JOB_FD_READ);
|
|
35
|
+
job->fd = xt_fh_fd(thisValue);
|
|
36
|
+
job->offset = (long long)offset;
|
|
37
|
+
job->length = (long long)length;
|
|
38
|
+
job->position = (long long)position;
|
|
39
|
+
job->base.roots[1] = buffer;
|
|
40
|
+
return xt_fh_submit(job);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/* Copy `data` into a flat byte array the worker can read. */
|
|
44
|
+
static unsigned char *xt_fh_bytes(xt_value data, xt_value options, size_t *outLength, int *isBuffer) {
|
|
45
|
+
if (xt_node_is_buffer(data)) {
|
|
46
|
+
*isBuffer = 1;
|
|
47
|
+
return xt_node_buffer_bytes(data, outLength);
|
|
48
|
+
}
|
|
49
|
+
*isBuffer = 0;
|
|
50
|
+
xt_value text = xt_to_string(data);
|
|
51
|
+
const char *bytes = xt_string_data(text);
|
|
52
|
+
int32_t length = xt_string_length_value(text);
|
|
53
|
+
if (!bytes || length < 0) {
|
|
54
|
+
*outLength = 0;
|
|
55
|
+
return NULL;
|
|
56
|
+
}
|
|
57
|
+
const char *encoding = xt_fs_encoding(options);
|
|
58
|
+
return xt_fs_decode_text(bytes, (size_t)length, encoding, outLength);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
static xt_value xt_fh_write(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
62
|
+
(void)env;
|
|
63
|
+
if (argc < 1) return xt_promise_resolve(XT_UNDEFINED);
|
|
64
|
+
size_t length = 0;
|
|
65
|
+
int isBuffer = 0;
|
|
66
|
+
unsigned char *bytes = xt_fh_bytes(argv[0], argc > 2 ? argv[2] : XT_UNDEFINED, &length, &isBuffer);
|
|
67
|
+
uint32_t offset = 0;
|
|
68
|
+
uint32_t writeLength = (uint32_t)length;
|
|
69
|
+
int64_t position = -1;
|
|
70
|
+
if (isBuffer) {
|
|
71
|
+
if (argc > 1 && !XT_IS_UNDEFINED(argv[1]) && !XT_IS_NULL(argv[1])) offset = (uint32_t)xt_to_number(argv[1]);
|
|
72
|
+
if (argc > 2 && !XT_IS_UNDEFINED(argv[2]) && !XT_IS_NULL(argv[2])) writeLength = (uint32_t)xt_to_number(argv[2]);
|
|
73
|
+
if (argc > 3 && !XT_IS_UNDEFINED(argv[3]) && !XT_IS_NULL(argv[3])) position = (int64_t)xt_to_number(argv[3]);
|
|
74
|
+
} else if (argc > 1 && !XT_IS_UNDEFINED(argv[1]) && !XT_IS_NULL(argv[1])) {
|
|
75
|
+
position = (int64_t)xt_to_number(argv[1]);
|
|
76
|
+
}
|
|
77
|
+
if (offset > length) offset = (uint32_t)length;
|
|
78
|
+
if (writeLength > length - offset) writeLength = (uint32_t)length - offset;
|
|
79
|
+
|
|
80
|
+
xt_fs_job *job = xt_fs_job_new(XT_FS_JOB_FD_WRITE);
|
|
81
|
+
job->fd = xt_fh_fd(thisValue);
|
|
82
|
+
job->position = (long long)position;
|
|
83
|
+
if (writeLength > 0 && bytes) {
|
|
84
|
+
job->data = (unsigned char *)malloc(writeLength);
|
|
85
|
+
if (!job->data) abort();
|
|
86
|
+
memcpy(job->data, bytes + offset, writeLength);
|
|
87
|
+
job->data_length = writeLength;
|
|
88
|
+
}
|
|
89
|
+
free(bytes);
|
|
90
|
+
return xt_fh_submit(job);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
static xt_value xt_fh_write_file(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
94
|
+
(void)env;
|
|
95
|
+
if (argc < 1) return xt_promise_resolve(XT_UNDEFINED);
|
|
96
|
+
size_t length = 0;
|
|
97
|
+
int isBuffer = 0;
|
|
98
|
+
unsigned char *bytes = xt_fh_bytes(argv[0], argc > 1 ? argv[1] : XT_UNDEFINED, &length, &isBuffer);
|
|
99
|
+
(void)isBuffer;
|
|
100
|
+
xt_fs_job *job = xt_fs_job_new(XT_FS_JOB_FD_WRITE_FILE);
|
|
101
|
+
job->fd = xt_fh_fd(thisValue);
|
|
102
|
+
job->data = bytes;
|
|
103
|
+
job->data_length = length;
|
|
104
|
+
return xt_fh_submit(job);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
static xt_value xt_fh_read_file(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
108
|
+
(void)env;
|
|
109
|
+
xt_fs_job *job = xt_fs_job_new(XT_FS_JOB_FD_READ_FILE);
|
|
110
|
+
job->fd = xt_fh_fd(thisValue);
|
|
111
|
+
job->text = xt_fs_copy_cstr(argc > 0 ? xt_fs_encoding(argv[0]) : NULL);
|
|
112
|
+
return xt_fh_submit(job);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
static xt_value xt_fh_close(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
116
|
+
(void)env;
|
|
117
|
+
(void)argc;
|
|
118
|
+
(void)argv;
|
|
119
|
+
xt_fs_job *job = xt_fs_job_new(XT_FS_JOB_FD_CLOSE);
|
|
120
|
+
job->fd = xt_fh_fd(thisValue);
|
|
121
|
+
return xt_fh_submit(job);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
static xt_value xt_fh_stat(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
125
|
+
(void)env;
|
|
126
|
+
(void)argc;
|
|
127
|
+
(void)argv;
|
|
128
|
+
xt_fs_job *job = xt_fs_job_new(XT_FS_JOB_FD_STAT);
|
|
129
|
+
job->fd = xt_fh_fd(thisValue);
|
|
130
|
+
return xt_fh_submit(job);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
static xt_value xt_fh_truncate(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
134
|
+
(void)env;
|
|
135
|
+
if (argc < 1) return xt_promise_resolve(XT_UNDEFINED);
|
|
136
|
+
xt_fs_job *job = xt_fs_job_new(XT_FS_JOB_FD_TRUNCATE);
|
|
137
|
+
job->fd = xt_fh_fd(thisValue);
|
|
138
|
+
job->length = (long long)xt_to_number(argv[0]);
|
|
139
|
+
return xt_fh_submit(job);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
static xt_value xt_fh_chmod(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
143
|
+
(void)env;
|
|
144
|
+
if (argc < 1) return xt_promise_resolve(XT_UNDEFINED);
|
|
145
|
+
xt_fs_job *job = xt_fs_job_new(XT_FS_JOB_FD_CHMOD);
|
|
146
|
+
job->fd = xt_fh_fd(thisValue);
|
|
147
|
+
job->mode = (int)xt_to_number(argv[0]);
|
|
148
|
+
return xt_fh_submit(job);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
static xt_value xt_fh_chown(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
152
|
+
(void)env;
|
|
153
|
+
if (argc < 2) return xt_promise_resolve(XT_UNDEFINED);
|
|
154
|
+
xt_fs_job *job = xt_fs_job_new(XT_FS_JOB_FD_CHOWN);
|
|
155
|
+
job->fd = xt_fh_fd(thisValue);
|
|
156
|
+
job->uid = (int)xt_to_number(argv[0]);
|
|
157
|
+
job->gid = (int)xt_to_number(argv[1]);
|
|
158
|
+
return xt_fh_submit(job);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
static xt_value xt_fh_utimes(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
162
|
+
(void)env;
|
|
163
|
+
if (argc < 2) return xt_promise_resolve(XT_UNDEFINED);
|
|
164
|
+
xt_fs_job *job = xt_fs_job_new(XT_FS_JOB_FD_UTIMES);
|
|
165
|
+
job->fd = xt_fh_fd(thisValue);
|
|
166
|
+
job->atime = xt_fs_time_seconds(argv[0]);
|
|
167
|
+
job->mtime = xt_fs_time_seconds(argv[1]);
|
|
168
|
+
return xt_fh_submit(job);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
static xt_value xt_fh_sync(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
172
|
+
(void)env;
|
|
173
|
+
(void)argc;
|
|
174
|
+
(void)argv;
|
|
175
|
+
xt_fs_job *job = xt_fs_job_new(XT_FS_JOB_FD_SYNC);
|
|
176
|
+
job->fd = xt_fh_fd(thisValue);
|
|
177
|
+
return xt_fh_submit(job);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
static xt_value xt_fh_datasync(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
181
|
+
(void)env;
|
|
182
|
+
(void)argc;
|
|
183
|
+
(void)argv;
|
|
184
|
+
xt_fs_job *job = xt_fs_job_new(XT_FS_JOB_FD_DATASYNC);
|
|
185
|
+
job->fd = xt_fh_fd(thisValue);
|
|
186
|
+
return xt_fh_submit(job);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
xt_value xt_fh_new(xt_value fdValue) {
|
|
190
|
+
xt_value handle = xt_object_new();
|
|
191
|
+
xt_node_set(handle, "fd", fdValue);
|
|
192
|
+
xt_node_define_method(handle, "read", (void *)xt_fh_read);
|
|
193
|
+
xt_node_define_method(handle, "write", (void *)xt_fh_write);
|
|
194
|
+
xt_node_define_method(handle, "readFile", (void *)xt_fh_read_file);
|
|
195
|
+
xt_node_define_method(handle, "writeFile", (void *)xt_fh_write_file);
|
|
196
|
+
xt_node_define_method(handle, "appendFile", (void *)xt_fh_write_file);
|
|
197
|
+
xt_node_define_method(handle, "close", (void *)xt_fh_close);
|
|
198
|
+
xt_node_define_method(handle, "stat", (void *)xt_fh_stat);
|
|
199
|
+
xt_node_define_method(handle, "truncate", (void *)xt_fh_truncate);
|
|
200
|
+
xt_node_define_method(handle, "chmod", (void *)xt_fh_chmod);
|
|
201
|
+
xt_node_define_method(handle, "chown", (void *)xt_fh_chown);
|
|
202
|
+
xt_node_define_method(handle, "utimes", (void *)xt_fh_utimes);
|
|
203
|
+
xt_node_define_method(handle, "sync", (void *)xt_fh_sync);
|
|
204
|
+
xt_node_define_method(handle, "datasync", (void *)xt_fh_datasync);
|
|
205
|
+
return handle;
|
|
206
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Lifecycle helpers for asynchronous `fs/promises` jobs.
|
|
3
|
+
*
|
|
4
|
+
* Allocation, submission, failure bookkeeping and release are shared by the
|
|
5
|
+
* argument marshalling (async_api.c), the FileHandle surface (async_fh.c), the
|
|
6
|
+
* worker (async_worker.c) and the completion handler (async_complete.c).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
#include "async_job.h"
|
|
10
|
+
|
|
11
|
+
#include <errno.h>
|
|
12
|
+
|
|
13
|
+
char *xt_fs_copy_cstr(const char *text) {
|
|
14
|
+
if (!text) return NULL;
|
|
15
|
+
size_t length = strlen(text);
|
|
16
|
+
char *copy = (char *)malloc(length + 1);
|
|
17
|
+
if (!copy) return NULL;
|
|
18
|
+
memcpy(copy, text, length + 1);
|
|
19
|
+
return copy;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
char *xt_fs_take_string(xt_value value) {
|
|
23
|
+
if (XT_IS_UNDEFINED(value) || XT_IS_NULL(value)) return NULL;
|
|
24
|
+
xt_value text = xt_to_string(value);
|
|
25
|
+
const char *data = xt_string_data(text);
|
|
26
|
+
if (!data) return NULL;
|
|
27
|
+
size_t length = (size_t)xt_string_length_value(text);
|
|
28
|
+
char *copy = (char *)malloc(length + 1);
|
|
29
|
+
if (!copy) return NULL;
|
|
30
|
+
memcpy(copy, data, length);
|
|
31
|
+
copy[length] = '\0';
|
|
32
|
+
return copy;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
xt_fs_job *xt_fs_job_new(xt_fs_job_kind kind) {
|
|
36
|
+
xt_fs_job *job = (xt_fs_job *)calloc(1, sizeof(xt_fs_job));
|
|
37
|
+
if (!job) abort();
|
|
38
|
+
job->base.run = xt_fs_job_run;
|
|
39
|
+
job->base.complete = xt_fs_job_complete;
|
|
40
|
+
job->base.release = xt_fs_job_release;
|
|
41
|
+
job->kind = kind;
|
|
42
|
+
job->fd = -1;
|
|
43
|
+
return job;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
xt_value xt_fs_job_submit(xt_fs_job *job) {
|
|
47
|
+
xt_value promise = xt_promise_create();
|
|
48
|
+
job->base.roots[0] = promise;
|
|
49
|
+
xt_async_submit(&job->base);
|
|
50
|
+
return promise;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/* Record the errno of a just-failed worker syscall. `error_path` aliases a
|
|
54
|
+
* job-owned string, so release() must not free it separately. */
|
|
55
|
+
void xt_fs_job_fail(xt_fs_job *job, int err, const char *syscall, const char *path) {
|
|
56
|
+
job->err = err ? err : EIO;
|
|
57
|
+
job->syscall = syscall;
|
|
58
|
+
job->error_path = (char *)path;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
void xt_fs_job_release(xt_async_job *base) {
|
|
62
|
+
xt_fs_job *job = (xt_fs_job *)base;
|
|
63
|
+
free(job->path);
|
|
64
|
+
free(job->path2);
|
|
65
|
+
free(job->text);
|
|
66
|
+
if (job->patterns) {
|
|
67
|
+
for (size_t i = 0; i < job->pattern_count; i++) free(job->patterns[i]);
|
|
68
|
+
free(job->patterns);
|
|
69
|
+
}
|
|
70
|
+
free(job->data);
|
|
71
|
+
free(job->bytes);
|
|
72
|
+
free(job->string);
|
|
73
|
+
if (job->entries) {
|
|
74
|
+
for (size_t i = 0; i < job->entry_count; i++) {
|
|
75
|
+
free(job->entries[i].name);
|
|
76
|
+
if (job->entries[i].bare != job->entries[i].name) free(job->entries[i].bare);
|
|
77
|
+
}
|
|
78
|
+
free(job->entries);
|
|
79
|
+
}
|
|
80
|
+
free(job);
|
|
81
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Shared job model for the asynchronous `fs/promises` implementation.
|
|
3
|
+
*
|
|
4
|
+
* The promise API deliberately splits work in two halves:
|
|
5
|
+
*
|
|
6
|
+
* - the worker half (async_worker.c) performs blocking syscalls and writes
|
|
7
|
+
* plain C results into `xt_fs_job`;
|
|
8
|
+
* - the main-thread half (async_complete.c) turns those results into
|
|
9
|
+
* `xt_value`s, settles the promise and frees the job.
|
|
10
|
+
*
|
|
11
|
+
* Argument marshalling lives in async_api.c and the FileHandle surface in
|
|
12
|
+
* async_fh.c. Keeping the split explicit is what lets `await` yield while a
|
|
13
|
+
* large read or a slow network filesystem is in flight.
|
|
14
|
+
*/
|
|
15
|
+
#ifndef XT_NODE_FS_ASYNC_JOB_H
|
|
16
|
+
#define XT_NODE_FS_ASYNC_JOB_H
|
|
17
|
+
|
|
18
|
+
#include "../node_common.h"
|
|
19
|
+
#include "fs_common.h"
|
|
20
|
+
|
|
21
|
+
typedef enum {
|
|
22
|
+
XT_FS_JOB_READ_FILE,
|
|
23
|
+
XT_FS_JOB_WRITE_FILE,
|
|
24
|
+
XT_FS_JOB_MKDIR,
|
|
25
|
+
XT_FS_JOB_READDIR,
|
|
26
|
+
XT_FS_JOB_RM,
|
|
27
|
+
XT_FS_JOB_UNLINK,
|
|
28
|
+
XT_FS_JOB_RMDIR,
|
|
29
|
+
XT_FS_JOB_RENAME,
|
|
30
|
+
XT_FS_JOB_COPY_FILE,
|
|
31
|
+
XT_FS_JOB_CP,
|
|
32
|
+
XT_FS_JOB_REALPATH,
|
|
33
|
+
XT_FS_JOB_STAT,
|
|
34
|
+
XT_FS_JOB_LSTAT,
|
|
35
|
+
XT_FS_JOB_STATFS,
|
|
36
|
+
XT_FS_JOB_ACCESS,
|
|
37
|
+
XT_FS_JOB_OPEN,
|
|
38
|
+
XT_FS_JOB_CHMOD,
|
|
39
|
+
XT_FS_JOB_LCHMOD,
|
|
40
|
+
XT_FS_JOB_CHOWN,
|
|
41
|
+
XT_FS_JOB_LCHOWN,
|
|
42
|
+
XT_FS_JOB_TRUNCATE,
|
|
43
|
+
XT_FS_JOB_UTIMES,
|
|
44
|
+
XT_FS_JOB_LUTIMES,
|
|
45
|
+
XT_FS_JOB_LINK,
|
|
46
|
+
XT_FS_JOB_SYMLINK,
|
|
47
|
+
XT_FS_JOB_READLINK,
|
|
48
|
+
XT_FS_JOB_MKDTEMP,
|
|
49
|
+
XT_FS_JOB_OPENDIR,
|
|
50
|
+
XT_FS_JOB_GLOB,
|
|
51
|
+
XT_FS_JOB_FD_READ,
|
|
52
|
+
XT_FS_JOB_FD_WRITE,
|
|
53
|
+
XT_FS_JOB_FD_READ_FILE,
|
|
54
|
+
XT_FS_JOB_FD_WRITE_FILE,
|
|
55
|
+
XT_FS_JOB_FD_CLOSE,
|
|
56
|
+
XT_FS_JOB_FD_STAT,
|
|
57
|
+
XT_FS_JOB_FD_TRUNCATE,
|
|
58
|
+
XT_FS_JOB_FD_CHMOD,
|
|
59
|
+
XT_FS_JOB_FD_CHOWN,
|
|
60
|
+
XT_FS_JOB_FD_UTIMES,
|
|
61
|
+
XT_FS_JOB_FD_SYNC,
|
|
62
|
+
XT_FS_JOB_FD_DATASYNC,
|
|
63
|
+
} xt_fs_job_kind;
|
|
64
|
+
|
|
65
|
+
typedef struct {
|
|
66
|
+
xt_async_job base;
|
|
67
|
+
xt_fs_job_kind kind;
|
|
68
|
+
|
|
69
|
+
/* input */
|
|
70
|
+
char *path;
|
|
71
|
+
char *path2;
|
|
72
|
+
char *text; /* encoding, symlink type, or a single glob pattern */
|
|
73
|
+
char **patterns;
|
|
74
|
+
size_t pattern_count;
|
|
75
|
+
unsigned char *data;
|
|
76
|
+
size_t data_length;
|
|
77
|
+
int flags;
|
|
78
|
+
int mode;
|
|
79
|
+
int fd;
|
|
80
|
+
long long offset;
|
|
81
|
+
long long length;
|
|
82
|
+
long long position;
|
|
83
|
+
int uid;
|
|
84
|
+
int gid;
|
|
85
|
+
double atime;
|
|
86
|
+
double mtime;
|
|
87
|
+
int recursive;
|
|
88
|
+
int force;
|
|
89
|
+
int error_on_exist;
|
|
90
|
+
int dereference;
|
|
91
|
+
int preserve;
|
|
92
|
+
int with_types;
|
|
93
|
+
|
|
94
|
+
/* worker results */
|
|
95
|
+
int err;
|
|
96
|
+
const char *syscall;
|
|
97
|
+
char *error_path;
|
|
98
|
+
xt_fs_stat_t stat;
|
|
99
|
+
unsigned char *bytes;
|
|
100
|
+
size_t bytes_length;
|
|
101
|
+
char *string;
|
|
102
|
+
xt_fs_entry *entries;
|
|
103
|
+
size_t entry_count;
|
|
104
|
+
long long numbers[7];
|
|
105
|
+
int result_fd;
|
|
106
|
+
long long written;
|
|
107
|
+
} xt_fs_job;
|
|
108
|
+
|
|
109
|
+
/* Value builders shared with the synchronous implementation (fs_ops.c). */
|
|
110
|
+
xt_value xt_node_stat_result(const xt_fs_stat_t *info);
|
|
111
|
+
xt_value xt_node_dirent_named(const char *name, unsigned int mode);
|
|
112
|
+
xt_value xt_node_dir_from_entries(const char *path, xt_fs_entry *entries, size_t count);
|
|
113
|
+
|
|
114
|
+
/* FileHandle factory (async_fh.c). */
|
|
115
|
+
xt_value xt_fh_new(xt_value fdValue);
|
|
116
|
+
|
|
117
|
+
/* Job lifecycle (async_job.c). */
|
|
118
|
+
xt_fs_job *xt_fs_job_new(xt_fs_job_kind kind);
|
|
119
|
+
/** Create the promise and submit; returns it. */
|
|
120
|
+
xt_value xt_fs_job_submit(xt_fs_job *job);
|
|
121
|
+
void xt_fs_job_fail(xt_fs_job *job, int err, const char *syscall, const char *path);
|
|
122
|
+
|
|
123
|
+
/* Implemented by async_worker.c / async_complete.c. */
|
|
124
|
+
void xt_fs_job_run(xt_async_job *job);
|
|
125
|
+
void xt_fs_job_complete(xt_async_job *job);
|
|
126
|
+
void xt_fs_job_release(xt_async_job *job);
|
|
127
|
+
|
|
128
|
+
/* Argument helpers (async_job.c). */
|
|
129
|
+
char *xt_fs_copy_cstr(const char *text);
|
|
130
|
+
char *xt_fs_take_string(xt_value value);
|
|
131
|
+
|
|
132
|
+
#endif /* XT_NODE_FS_ASYNC_JOB_H */
|