xbintsc 0.3.81 → 0.5.6
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 +8 -4
- package/README.md +16 -5
- package/README.zh-CN.md +14 -6
- package/dist/src/ast/common.d.ts +23 -0
- package/dist/src/cli/main.js +68 -24
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/codegen/generator/calls/access.js +6 -2
- package/dist/src/codegen/generator/calls/access.js.map +1 -1
- package/dist/src/codegen/generator/calls/closures.js +4 -0
- package/dist/src/codegen/generator/calls/closures.js.map +1 -1
- package/dist/src/codegen/generator/calls/invocation.js +9 -4
- package/dist/src/codegen/generator/calls/invocation.js.map +1 -1
- package/dist/src/codegen/generator/context.d.ts +50 -0
- package/dist/src/codegen/generator/context.js +124 -13
- package/dist/src/codegen/generator/context.js.map +1 -1
- package/dist/src/codegen/generator/debug.d.ts +33 -0
- package/dist/src/codegen/generator/debug.js +97 -0
- package/dist/src/codegen/generator/debug.js.map +1 -0
- package/dist/src/codegen/generator/expressions/assignment.js +9 -5
- package/dist/src/codegen/generator/expressions/assignment.js.map +1 -1
- package/dist/src/codegen/generator/expressions/operators.js +35 -9
- package/dist/src/codegen/generator/expressions/operators.js.map +1 -1
- package/dist/src/codegen/generator/expressions/primary.js +11 -3
- package/dist/src/codegen/generator/expressions/primary.js.map +1 -1
- package/dist/src/codegen/generator/generator.d.ts +3 -1
- package/dist/src/codegen/generator/generator.js +3 -1
- package/dist/src/codegen/generator/generator.js.map +1 -1
- package/dist/src/codegen/generator/module.d.ts +1 -1
- package/dist/src/codegen/generator/module.js +82 -12
- package/dist/src/codegen/generator/module.js.map +1 -1
- package/dist/src/codegen/generator/numeric.d.ts +26 -0
- package/dist/src/codegen/generator/numeric.js +127 -0
- package/dist/src/codegen/generator/numeric.js.map +1 -0
- package/dist/src/codegen/generator/state.d.ts +17 -0
- package/dist/src/codegen/generator/state.js +9 -0
- package/dist/src/codegen/generator/state.js.map +1 -1
- package/dist/src/codegen/generator/statements/control-flow.js +2 -4
- package/dist/src/codegen/generator/statements/control-flow.js.map +1 -1
- package/dist/src/codegen/generator/statements/dispatch.js +3 -0
- package/dist/src/codegen/generator/statements/dispatch.js.map +1 -1
- package/dist/src/codegen/generator/statements/variables.js +8 -2
- package/dist/src/codegen/generator/statements/variables.js.map +1 -1
- package/dist/src/codegen/generator/tables.d.ts +16 -0
- package/dist/src/codegen/generator/tables.js +99 -2
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/driver/bundler/merge.js +41 -5
- package/dist/src/driver/bundler/merge.js.map +1 -1
- package/dist/src/driver/compiler.d.ts +23 -4
- package/dist/src/driver/compiler.js +46 -57
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/driver/fingerprint.d.ts +27 -0
- package/dist/src/driver/fingerprint.js +88 -0
- package/dist/src/driver/fingerprint.js.map +1 -0
- package/dist/src/driver/toolchain-provider.d.ts +9 -2
- package/dist/src/driver/toolchain-provider.js +15 -15
- package/dist/src/driver/toolchain-provider.js.map +1 -1
- package/dist/src/driver/toolchain.d.ts +58 -1
- package/dist/src/driver/toolchain.js +169 -22
- package/dist/src/driver/toolchain.js.map +1 -1
- package/dist/src/parser/expressions.js +20 -1
- package/dist/src/parser/expressions.js.map +1 -1
- package/dist/tests/cli/main.test.js +41 -1
- package/dist/tests/cli/main.test.js.map +1 -1
- package/dist/tests/codegen/closures.test.js +8 -0
- package/dist/tests/codegen/closures.test.js.map +1 -1
- package/dist/tests/codegen/expressions.test.js +14 -3
- package/dist/tests/codegen/expressions.test.js.map +1 -1
- package/dist/tests/codegen/llvm.test.js +23 -0
- package/dist/tests/codegen/llvm.test.js.map +1 -1
- package/dist/tests/driver/toolchain-provider.test.js +49 -2
- package/dist/tests/driver/toolchain-provider.test.js.map +1 -1
- package/dist/tests/driver/toolchain.test.js +62 -3
- package/dist/tests/driver/toolchain.test.js.map +1 -1
- package/dist/tests/e2e/async-await.test.d.ts +10 -0
- package/dist/tests/e2e/async-await.test.js +247 -0
- package/dist/tests/e2e/async-await.test.js.map +1 -0
- package/dist/tests/e2e/child-process.test.d.ts +10 -0
- package/dist/tests/e2e/child-process.test.js +59 -0
- package/dist/tests/e2e/child-process.test.js.map +1 -0
- package/dist/tests/e2e/compile-objects-classes.test.js +47 -0
- package/dist/tests/e2e/compile-objects-classes.test.js.map +1 -1
- package/dist/tests/e2e/error-stack.test.d.ts +9 -0
- package/dist/tests/e2e/error-stack.test.js +94 -0
- package/dist/tests/e2e/error-stack.test.js.map +1 -0
- package/dist/tests/e2e/runtime-coverage-globals.test.js +46 -0
- package/dist/tests/e2e/runtime-coverage-globals.test.js.map +1 -1
- package/dist/tests/e2e/string-concat.test.d.ts +9 -0
- package/dist/tests/e2e/string-concat.test.js +102 -0
- package/dist/tests/e2e/string-concat.test.js.map +1 -0
- package/dist/tests/e2e/string-utf16.test.d.ts +6 -0
- package/dist/tests/e2e/string-utf16.test.js +133 -0
- package/dist/tests/e2e/string-utf16.test.js.map +1 -0
- package/dist/tests/helpers.d.ts +9 -0
- package/dist/tests/helpers.js +26 -3
- package/dist/tests/helpers.js.map +1 -1
- package/dist/tests/parser/expressions.test.js +7 -0
- package/dist/tests/parser/expressions.test.js.map +1 -1
- package/dist/tests/version.test.d.ts +1 -0
- package/dist/tests/version.test.js +24 -0
- package/dist/tests/version.test.js.map +1 -0
- package/doc/DESIGN.md +20 -12
- package/doc/ai/cli.md +2 -0
- package/doc/ai/contributing.md +16 -8
- package/doc/ai/extensions.md +3 -2
- package/doc/ai/language-support.md +15 -12
- package/doc/ai/troubleshooting.md +43 -5
- package/doc/ai/zh-CN/cli.md +2 -0
- package/doc/ai/zh-CN/contributing.md +11 -7
- package/doc/ai/zh-CN/extensions.md +2 -1
- package/doc/ai/zh-CN/language-support.md +15 -12
- package/doc/ai/zh-CN/troubleshooting.md +38 -4
- package/doc/implemented.md +16 -13
- package/doc/requirements.md +13 -4
- package/doc/unimplemented.md +20 -18
- package/doc/zh-CN/DESIGN.md +15 -9
- package/doc/zh-CN/implemented.md +15 -12
- package/doc/zh-CN/requirements.md +12 -3
- package/doc/zh-CN/unimplemented.md +19 -17
- package/package.json +3 -1
- package/runtime/ext_node/buffer/parts/construction.inc +2 -2
- package/runtime/ext_node/buffer/parts/methods.inc +1 -1
- package/runtime/ext_node/buffer/parts/search.inc +1 -1
- package/runtime/ext_node/child_process/child_process.c +25 -0
- package/runtime/ext_node/fs/fd_ops.c +1 -1
- package/runtime/rt.h +49 -1
- package/runtime/rt_internal.h +136 -4
- package/runtime/xt_alloc.c +20 -2
- package/runtime/xt_async.c +197 -0
- package/runtime/xt_bigint/conversions.inc +1 -1
- package/runtime/xt_builtins.c +169 -43
- package/runtime/xt_constructors.c +389 -0
- package/runtime/xt_containers/access.inc +61 -24
- package/runtime/xt_containers/arrays.inc +26 -2
- package/runtime/xt_containers/objects.inc +206 -8
- package/runtime/xt_coroutine.c +212 -0
- package/runtime/xt_generator.c +31 -162
- package/runtime/xt_io/exception.inc +171 -2
- package/runtime/xt_io/inspect.inc +48 -13
- package/runtime/xt_io.c +5 -0
- package/runtime/xt_promise.c +62 -17
- package/runtime/xt_stdlib/arrays.inc +2 -2
- package/runtime/xt_stdlib/dispatch.inc +20 -4
- package/runtime/xt_stdlib/strings.inc +98 -29
- package/runtime/xt_stdlib2/array-statics.inc +3 -2
- package/runtime/xt_stdlib2/arrays.inc +1 -1
- package/runtime/xt_stdlib2/error.inc +33 -6
- package/runtime/xt_stdlib2/json.inc +46 -16
- package/runtime/xt_stdlib2/number-string-statics.inc +24 -13
- package/runtime/xt_stdlib2/objects.inc +1 -1
- package/runtime/xt_stdlib2/regexp-match.inc +50 -35
- package/runtime/xt_stdlib2/set.inc +6 -0
- package/runtime/xt_stdlib2/strings.inc +81 -42
- package/runtime/xt_symbol.c +1 -1
- package/runtime/xt_typed_array/construction.inc +43 -2
- package/runtime/xt_typed_array.c +4 -0
- package/runtime/xt_values/comparison.inc +2 -2
- package/runtime/xt_values/constructors.inc +18 -5
- package/runtime/xt_values/conversions.inc +4 -3
- package/runtime/xt_values/strings.inc +94 -4
- package/runtime/xt_values/utf16.inc +340 -0
- package/runtime/xt_values.c +1 -0
- package/scripts/bench.ts +172 -0
- package/scripts/build-runtime.ts +2 -1
- package/scripts/check-version.ts +53 -0
- package/src/ast/common.ts +23 -0
- package/src/cli/main.ts +68 -26
- package/src/codegen/generator/calls/access.ts +7 -2
- package/src/codegen/generator/calls/closures.ts +3 -0
- package/src/codegen/generator/calls/invocation.ts +9 -3
- package/src/codegen/generator/context.ts +129 -18
- package/src/codegen/generator/debug.ts +132 -0
- package/src/codegen/generator/expressions/assignment.ts +9 -5
- package/src/codegen/generator/expressions/operators.ts +47 -8
- package/src/codegen/generator/expressions/primary.ts +11 -3
- package/src/codegen/generator/generator.ts +18 -2
- package/src/codegen/generator/module.ts +83 -13
- package/src/codegen/generator/numeric.ts +158 -0
- package/src/codegen/generator/state.ts +26 -0
- package/src/codegen/generator/statements/control-flow.ts +2 -3
- package/src/codegen/generator/statements/dispatch.ts +3 -0
- package/src/codegen/generator/statements/variables.ts +8 -2
- package/src/codegen/generator/tables.ts +110 -2
- package/src/driver/bundler/merge.ts +39 -5
- package/src/driver/compiler.ts +65 -55
- package/src/driver/fingerprint.ts +85 -0
- package/src/driver/toolchain-provider.ts +17 -16
- package/src/driver/toolchain.ts +210 -21
- package/src/parser/expressions.ts +20 -1
package/runtime/xt_generator.c
CHANGED
|
@@ -8,28 +8,17 @@
|
|
|
8
8
|
* `finally` and nested loops work across suspension points without a state
|
|
9
9
|
* machine transform.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* targets the body's own
|
|
11
|
+
* The private stack and the context switch come from `xt_coroutine.c`, which
|
|
12
|
+
* `async` functions use as well. The generator's exception stack is swapped
|
|
13
|
+
* alongside it so a `throw` inside the body still targets the body's own
|
|
14
|
+
* `try` frames.
|
|
14
15
|
*/
|
|
15
16
|
|
|
16
|
-
#define _XOPEN_SOURCE 700
|
|
17
|
-
|
|
18
17
|
#include "rt_internal.h"
|
|
19
18
|
|
|
20
19
|
#include <stdlib.h>
|
|
21
20
|
#include <string.h>
|
|
22
21
|
|
|
23
|
-
#if defined(__APPLE__)
|
|
24
|
-
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
25
|
-
#endif
|
|
26
|
-
|
|
27
|
-
#ifdef _WIN32
|
|
28
|
-
#include <windows.h>
|
|
29
|
-
#else
|
|
30
|
-
#include <ucontext.h>
|
|
31
|
-
#endif
|
|
32
|
-
|
|
33
22
|
typedef struct xt_generator {
|
|
34
23
|
xt_header header;
|
|
35
24
|
xt_value function;
|
|
@@ -44,35 +33,13 @@ typedef struct xt_generator {
|
|
|
44
33
|
int started;
|
|
45
34
|
int done;
|
|
46
35
|
int threw;
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
char *stack;
|
|
51
|
-
size_t stack_size;
|
|
52
|
-
int stack_pushed;
|
|
53
|
-
void *saved_try_top;
|
|
54
|
-
#ifdef _WIN32
|
|
55
|
-
void *fiber;
|
|
56
|
-
void *caller_fiber;
|
|
57
|
-
/* `SwitchToFiber` keeps the resumer's callee-saved registers in an opaque
|
|
58
|
-
* fiber context (POSIX stores them in `caller`), so snapshot them for the
|
|
59
|
-
* collector before switching away. */
|
|
60
|
-
jmp_buf caller_regs;
|
|
61
|
-
int has_caller_regs;
|
|
62
|
-
#else
|
|
63
|
-
ucontext_t ctx;
|
|
64
|
-
ucontext_t caller;
|
|
65
|
-
#endif
|
|
36
|
+
/* Private stack, context switch and GC region bookkeeping live in
|
|
37
|
+
* xt_coroutine.c; `NULL` once the body has finished. */
|
|
38
|
+
xt_coroutine *coroutine;
|
|
66
39
|
} xt_generator;
|
|
67
40
|
|
|
68
41
|
/* The generator whose coroutine is running, if any. */
|
|
69
42
|
static xt_generator *g_active_generator = NULL;
|
|
70
|
-
/* Filled in just before a coroutine is first entered (POSIX has no portable
|
|
71
|
-
* way to pass a pointer to `makecontext`). */
|
|
72
|
-
static xt_generator *g_boot_generator = NULL;
|
|
73
|
-
#ifdef _WIN32
|
|
74
|
-
static void *g_main_fiber = NULL;
|
|
75
|
-
#endif
|
|
76
43
|
|
|
77
44
|
int xt_is_generator(xt_value value) {
|
|
78
45
|
if (!XT_IS_OBJECT(value)) return 0;
|
|
@@ -91,38 +58,9 @@ static xt_value xt_generator_result_object(xt_value value, int done) {
|
|
|
91
58
|
return result;
|
|
92
59
|
}
|
|
93
60
|
|
|
94
|
-
static void xt_generator_suspend(xt_generator *gen) {
|
|
95
|
-
gen->saved_try_top = xt_try_mark();
|
|
96
|
-
#ifdef _WIN32
|
|
97
|
-
SwitchToFiber(gen->caller_fiber);
|
|
98
|
-
#else
|
|
99
|
-
swapcontext(&gen->ctx, &gen->caller);
|
|
100
|
-
#endif
|
|
101
|
-
}
|
|
102
|
-
|
|
103
61
|
/* Runs on the generator's private stack. */
|
|
104
|
-
static void xt_generator_run(
|
|
105
|
-
|
|
106
|
-
/* The fiber stack is not observable from the outside; capture its bounds
|
|
107
|
-
* here (before the body allocates anything) so a collection during the body
|
|
108
|
-
* has a valid scan region. */
|
|
109
|
-
if (!gen->stack) {
|
|
110
|
-
MEMORY_BASIC_INFORMATION info;
|
|
111
|
-
uintptr_t here = (uintptr_t)&info;
|
|
112
|
-
if (VirtualQuery((LPCVOID)here, &info, sizeof(info))) {
|
|
113
|
-
/* Use the *committed* region containing the current frame: a Windows
|
|
114
|
-
* stack reserves more than it commits, and `AllocationBase + RegionSize`
|
|
115
|
-
* can fall in uncommitted memory. `BaseAddress + RegionSize` is the
|
|
116
|
-
* stack top. */
|
|
117
|
-
gen->stack = (char *)info.BaseAddress;
|
|
118
|
-
gen->stack_size = info.RegionSize;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
#endif
|
|
122
|
-
if (gen->stack && gen->stack_size && !gen->stack_pushed) {
|
|
123
|
-
xt_gc_push_stack((uintptr_t)gen->stack, (uintptr_t)gen->stack + gen->stack_size);
|
|
124
|
-
gen->stack_pushed = 1;
|
|
125
|
-
}
|
|
62
|
+
static void xt_generator_run(void *arg) {
|
|
63
|
+
xt_generator *gen = (xt_generator *)arg;
|
|
126
64
|
void *boundary = xt_try_enter();
|
|
127
65
|
if (xt_try_setjmp(boundary) == 0) {
|
|
128
66
|
xt_function *function = (xt_function *)XT_GET_PTR(gen->function);
|
|
@@ -133,90 +71,23 @@ static void xt_generator_run(xt_generator *gen) {
|
|
|
133
71
|
gen->threw = 1;
|
|
134
72
|
}
|
|
135
73
|
gen->done = 1;
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
#ifndef _WIN32
|
|
140
|
-
static void xt_generator_entry(void) {
|
|
141
|
-
xt_generator *gen = g_boot_generator;
|
|
142
|
-
g_boot_generator = NULL;
|
|
143
|
-
xt_generator_run(gen);
|
|
144
|
-
}
|
|
145
|
-
#else
|
|
146
|
-
static void CALLBACK xt_generator_entry(void *param) {
|
|
147
|
-
xt_generator_run((xt_generator *)param);
|
|
148
|
-
}
|
|
149
|
-
#endif
|
|
150
|
-
|
|
151
|
-
static void xt_generator_boot(xt_generator *gen) {
|
|
152
|
-
#ifdef _WIN32
|
|
153
|
-
if (!g_main_fiber) g_main_fiber = ConvertThreadToFiber(NULL);
|
|
154
|
-
gen->fiber = CreateFiber(1 << 20, xt_generator_entry, gen);
|
|
155
|
-
if (!gen->fiber) abort();
|
|
156
|
-
#else
|
|
157
|
-
size_t size = 1 << 20;
|
|
158
|
-
gen->stack = (char *)malloc(size);
|
|
159
|
-
if (!gen->stack) abort();
|
|
160
|
-
gen->stack_size = size;
|
|
161
|
-
if (getcontext(&gen->ctx) != 0) abort();
|
|
162
|
-
gen->ctx.uc_stack.ss_sp = gen->stack;
|
|
163
|
-
gen->ctx.uc_stack.ss_size = size;
|
|
164
|
-
gen->ctx.uc_link = NULL;
|
|
165
|
-
g_boot_generator = gen;
|
|
166
|
-
makecontext(&gen->ctx, xt_generator_entry, 0);
|
|
167
|
-
#endif
|
|
168
|
-
gen->booted = 1;
|
|
74
|
+
xt_coroutine_leave(gen->coroutine);
|
|
169
75
|
}
|
|
170
76
|
|
|
171
77
|
/* Release the private stack once the coroutine has finished. The generator
|
|
172
78
|
* object itself stays valid; later `.next()` calls just report `done`. */
|
|
173
79
|
static void xt_generator_dispose(xt_generator *gen) {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
DeleteFiber(gen->fiber);
|
|
177
|
-
gen->fiber = NULL;
|
|
178
|
-
}
|
|
179
|
-
gen->caller_fiber = NULL;
|
|
180
|
-
/* `DeleteFiber` releases the stack: never scan it again. */
|
|
181
|
-
gen->stack = NULL;
|
|
182
|
-
gen->stack_size = 0;
|
|
183
|
-
#else
|
|
184
|
-
if (gen->stack) {
|
|
185
|
-
free(gen->stack);
|
|
186
|
-
gen->stack = NULL;
|
|
187
|
-
}
|
|
188
|
-
#endif
|
|
80
|
+
xt_coroutine_release(gen->coroutine);
|
|
81
|
+
gen->coroutine = NULL;
|
|
189
82
|
}
|
|
190
83
|
|
|
84
|
+
static void xt_generator_boot(xt_generator *gen);
|
|
85
|
+
|
|
191
86
|
static void xt_generator_resume(xt_generator *gen) {
|
|
192
|
-
if (!gen->
|
|
87
|
+
if (!gen->coroutine) xt_generator_boot(gen);
|
|
193
88
|
xt_generator *previous = g_active_generator;
|
|
194
89
|
g_active_generator = gen;
|
|
195
|
-
|
|
196
|
-
* On Windows the first push happens inside the fiber once its stack bounds
|
|
197
|
-
* are known, so the caller's region must be marked suspended here, on the
|
|
198
|
-
* caller's own stack. */
|
|
199
|
-
xt_gc_suspend((uintptr_t)&gen);
|
|
200
|
-
if (gen->stack && gen->stack_size && !gen->stack_pushed) {
|
|
201
|
-
xt_gc_push_stack((uintptr_t)gen->stack, (uintptr_t)gen->stack + gen->stack_size);
|
|
202
|
-
gen->stack_pushed = 1;
|
|
203
|
-
}
|
|
204
|
-
void *caller_try = xt_try_mark();
|
|
205
|
-
xt_try_restore(gen->saved_try_top);
|
|
206
|
-
#ifdef _WIN32
|
|
207
|
-
_setjmp(gen->caller_regs);
|
|
208
|
-
gen->has_caller_regs = 1;
|
|
209
|
-
gen->caller_fiber = GetCurrentFiber();
|
|
210
|
-
SwitchToFiber(gen->fiber);
|
|
211
|
-
#else
|
|
212
|
-
swapcontext(&gen->caller, &gen->ctx);
|
|
213
|
-
#endif
|
|
214
|
-
gen->saved_try_top = xt_try_mark();
|
|
215
|
-
xt_try_restore(caller_try);
|
|
216
|
-
if (gen->stack_pushed) {
|
|
217
|
-
xt_gc_pop_stack();
|
|
218
|
-
gen->stack_pushed = 0;
|
|
219
|
-
}
|
|
90
|
+
xt_coroutine_enter(gen->coroutine);
|
|
220
91
|
if (gen->done) xt_generator_dispose(gen);
|
|
221
92
|
g_active_generator = previous;
|
|
222
93
|
}
|
|
@@ -242,23 +113,18 @@ void xt_generator_gc_trace(xt_header *header) {
|
|
|
242
113
|
if (gen->args && gen->argc > 0) {
|
|
243
114
|
xt_gc_scan_region(gen->args, sizeof(xt_value) * (size_t)gen->argc);
|
|
244
115
|
}
|
|
245
|
-
|
|
246
|
-
#ifndef _WIN32
|
|
247
|
-
/* Registers saved by `swapcontext` at the last yield are not on the stack. */
|
|
248
|
-
xt_gc_scan_region(&gen->ctx, sizeof(gen->ctx));
|
|
249
|
-
#else
|
|
250
|
-
if (gen->has_caller_regs) xt_gc_scan_region(&gen->caller_regs, sizeof(gen->caller_regs));
|
|
251
|
-
#endif
|
|
116
|
+
xt_coroutine_gc_trace(gen->coroutine);
|
|
252
117
|
}
|
|
253
118
|
|
|
254
119
|
void xt_generator_gc_release(xt_header *header) {
|
|
255
120
|
xt_generator *gen = (xt_generator *)header;
|
|
256
121
|
free(gen->args);
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
122
|
+
xt_coroutine_release(gen->coroutine);
|
|
123
|
+
gen->coroutine = NULL;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
static void xt_generator_boot(xt_generator *gen) {
|
|
127
|
+
gen->coroutine = xt_coroutine_create(xt_generator_run, gen);
|
|
262
128
|
}
|
|
263
129
|
|
|
264
130
|
xt_value xt_generator_new(xt_value function, xt_value thisValue, int32_t argc, xt_value *argv) {
|
|
@@ -267,9 +133,7 @@ xt_value xt_generator_new(xt_value function, xt_value thisValue, int32_t argc, x
|
|
|
267
133
|
gen->this_value = thisValue;
|
|
268
134
|
gen->argc = argc;
|
|
269
135
|
gen->args = NULL;
|
|
270
|
-
gen->
|
|
271
|
-
gen->stack_size = 0;
|
|
272
|
-
gen->stack_pushed = 0;
|
|
136
|
+
gen->coroutine = NULL;
|
|
273
137
|
gen->result = XT_UNDEFINED;
|
|
274
138
|
gen->sent = XT_UNDEFINED;
|
|
275
139
|
gen->iter_value = XT_UNDEFINED;
|
|
@@ -339,7 +203,7 @@ xt_value xt_yield(xt_value value) {
|
|
|
339
203
|
xt_generator *gen = g_active_generator;
|
|
340
204
|
if (!gen || gen->done) return XT_UNDEFINED;
|
|
341
205
|
gen->result = value;
|
|
342
|
-
|
|
206
|
+
xt_coroutine_leave(gen->coroutine);
|
|
343
207
|
if (gen->resume_kind == 1) {
|
|
344
208
|
xt_throw(gen->sent);
|
|
345
209
|
return XT_UNDEFINED;
|
|
@@ -348,7 +212,7 @@ xt_value xt_yield(xt_value value) {
|
|
|
348
212
|
gen->done = 1;
|
|
349
213
|
gen->threw = 0;
|
|
350
214
|
gen->result = gen->sent;
|
|
351
|
-
|
|
215
|
+
xt_coroutine_leave(gen->coroutine);
|
|
352
216
|
return XT_UNDEFINED;
|
|
353
217
|
}
|
|
354
218
|
return gen->sent;
|
|
@@ -396,3 +260,8 @@ xt_value xt_generator_iter_value(xt_value generator) {
|
|
|
396
260
|
xt_generator *gen = xt_as_generator(generator);
|
|
397
261
|
return gen ? gen->iter_value : XT_UNDEFINED;
|
|
398
262
|
}
|
|
263
|
+
|
|
264
|
+
xt_value xt_function_set_generator(xt_value value) {
|
|
265
|
+
if (XT_IS_FUNCTION(value)) ((xt_function *)XT_GET_PTR(value))->is_generator = 1;
|
|
266
|
+
return value;
|
|
267
|
+
}
|
|
@@ -14,6 +14,162 @@ static xt_try_frame *g_try_top = NULL;
|
|
|
14
14
|
int32_t xt_program_argc = 0;
|
|
15
15
|
char **xt_program_argv = NULL;
|
|
16
16
|
|
|
17
|
+
/* ------------------------------------------------------------------------- */
|
|
18
|
+
/* Shadow call stack */
|
|
19
|
+
/* ------------------------------------------------------------------------- */
|
|
20
|
+
|
|
21
|
+
/*
|
|
22
|
+
* Compiled functions push one frame on entry and pop it before returning, so
|
|
23
|
+
* `Error` can capture a JavaScript-level stack without any debug metadata.
|
|
24
|
+
* Generator bodies are not tracked (they switch stacks and can be suspended
|
|
25
|
+
* indefinitely); a normal function called from a generator is tracked normally.
|
|
26
|
+
*
|
|
27
|
+
* `name`/`file` point into the module's static string constants; the runtime
|
|
28
|
+
* never frees them. The depth is trimmed when a `try` frame is left, so frames
|
|
29
|
+
* from calls unwound by a throw cannot leak into a later stack.
|
|
30
|
+
*/
|
|
31
|
+
#define XT_STACK_MAX_DEPTH 256
|
|
32
|
+
|
|
33
|
+
typedef struct {
|
|
34
|
+
const char *name;
|
|
35
|
+
const char *file;
|
|
36
|
+
int32_t line;
|
|
37
|
+
} xt_stack_entry;
|
|
38
|
+
|
|
39
|
+
static xt_stack_entry g_stack[XT_STACK_MAX_DEPTH];
|
|
40
|
+
static int32_t g_stack_depth = 0;
|
|
41
|
+
|
|
42
|
+
void xt_stack_push(const char *name, const char *file, int32_t line) {
|
|
43
|
+
if (g_stack_depth >= XT_STACK_MAX_DEPTH) return;
|
|
44
|
+
xt_stack_entry *entry = &g_stack[g_stack_depth++];
|
|
45
|
+
entry->name = name;
|
|
46
|
+
entry->file = file;
|
|
47
|
+
entry->line = line;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
void xt_stack_pop(void) {
|
|
51
|
+
if (g_stack_depth > 0) g_stack_depth--;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
int32_t xt_stack_depth(void) { return g_stack_depth; }
|
|
55
|
+
|
|
56
|
+
void xt_stack_set_line(int32_t line) {
|
|
57
|
+
/* Called just before an `Error` is constructed, so the innermost frame
|
|
58
|
+
* reports the construction line rather than the function's declaration. */
|
|
59
|
+
if (g_stack_depth > 0) g_stack[g_stack_depth - 1].line = line;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
void xt_stack_trim(int32_t depth) {
|
|
63
|
+
if (depth < 0) depth = 0;
|
|
64
|
+
if (depth < g_stack_depth) g_stack_depth = depth;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
static void xt_stack_append(char **buffer, size_t *length, size_t *capacity, const char *text) {
|
|
68
|
+
size_t count = strlen(text);
|
|
69
|
+
if (*length + count + 1 > *capacity) {
|
|
70
|
+
size_t next = *capacity ? *capacity : 256;
|
|
71
|
+
while (next < *length + count + 1) next *= 2;
|
|
72
|
+
char *grown = (char *)realloc(*buffer, next);
|
|
73
|
+
if (!grown) return;
|
|
74
|
+
*buffer = grown;
|
|
75
|
+
*capacity = next;
|
|
76
|
+
}
|
|
77
|
+
memcpy(*buffer + *length, text, count);
|
|
78
|
+
*length += count;
|
|
79
|
+
(*buffer)[*length] = 0;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
xt_value xt_stack_capture(const char *name, const char *message) {
|
|
83
|
+
const char *header = name && name[0] ? name : "Error";
|
|
84
|
+
char *buffer = NULL;
|
|
85
|
+
size_t length = 0;
|
|
86
|
+
size_t capacity = 0;
|
|
87
|
+
xt_stack_append(&buffer, &length, &capacity, header);
|
|
88
|
+
if (message && message[0]) {
|
|
89
|
+
xt_stack_append(&buffer, &length, &capacity, ": ");
|
|
90
|
+
xt_stack_append(&buffer, &length, &capacity, message);
|
|
91
|
+
}
|
|
92
|
+
xt_stack_append(&buffer, &length, &capacity, "\n");
|
|
93
|
+
for (int32_t index = g_stack_depth - 1; index >= 0; index--) {
|
|
94
|
+
const xt_stack_entry *entry = &g_stack[index];
|
|
95
|
+
char line[24];
|
|
96
|
+
xt_stack_append(&buffer, &length, &capacity, " at ");
|
|
97
|
+
xt_stack_append(&buffer, &length, &capacity, entry->name && entry->name[0] ? entry->name : "anonymous");
|
|
98
|
+
xt_stack_append(&buffer, &length, &capacity, " (");
|
|
99
|
+
xt_stack_append(&buffer, &length, &capacity, entry->file && entry->file[0] ? entry->file : "<anonymous>");
|
|
100
|
+
if (entry->line > 0) {
|
|
101
|
+
snprintf(line, sizeof(line), ":%d", (int)entry->line);
|
|
102
|
+
xt_stack_append(&buffer, &length, &capacity, line);
|
|
103
|
+
}
|
|
104
|
+
xt_stack_append(&buffer, &length, &capacity, ")\n");
|
|
105
|
+
}
|
|
106
|
+
xt_value result = xt_string_new(buffer ? buffer : "", length);
|
|
107
|
+
free(buffer);
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
xt_value xt_error_stack(xt_value error) {
|
|
112
|
+
if (!XT_IS_OBJECT(error)) return XT_UNDEFINED;
|
|
113
|
+
xt_object *object = (xt_object *)XT_GET_PTR(error);
|
|
114
|
+
if (object->header.kind != XT_OBJECT_KIND_ERROR) return XT_UNDEFINED;
|
|
115
|
+
return object->error_stack;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* ------------------------------------------------------------------------- */
|
|
119
|
+
/* Fatal-signal diagnostics */
|
|
120
|
+
/* ------------------------------------------------------------------------- */
|
|
121
|
+
|
|
122
|
+
static char g_crash_report[16384];
|
|
123
|
+
|
|
124
|
+
static int xt_format_stack(char *out, size_t size) {
|
|
125
|
+
int written = 0;
|
|
126
|
+
for (int32_t index = g_stack_depth - 1; index >= 0 && written < (int)size; index--) {
|
|
127
|
+
const xt_stack_entry *entry = &g_stack[index];
|
|
128
|
+
int count = snprintf(out + written, size - (size_t)written, " at %s (%s:%d)\n",
|
|
129
|
+
entry->name && entry->name[0] ? entry->name : "anonymous",
|
|
130
|
+
entry->file && entry->file[0] ? entry->file : "<anonymous>", (int)entry->line);
|
|
131
|
+
if (count < 0) break;
|
|
132
|
+
written += count;
|
|
133
|
+
}
|
|
134
|
+
return written < (int)size ? written : (int)size - 1;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
#if defined(_WIN32)
|
|
138
|
+
|
|
139
|
+
static LONG WINAPI xt_fatal_exception(EXCEPTION_POINTERS *info) {
|
|
140
|
+
unsigned long code =
|
|
141
|
+
info && info->ExceptionRecord ? (unsigned long)info->ExceptionRecord->ExceptionCode : 0UL;
|
|
142
|
+
int written = snprintf(g_crash_report, sizeof(g_crash_report), "\nxbintsc: fatal exception 0x%08lx\n", code);
|
|
143
|
+
written += xt_format_stack(g_crash_report + written, sizeof(g_crash_report) - (size_t)written);
|
|
144
|
+
_write(2, g_crash_report, (unsigned int)written);
|
|
145
|
+
_exit(1);
|
|
146
|
+
return EXCEPTION_EXECUTE_HANDLER;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
void xt_install_crash_handler(void) { SetUnhandledExceptionFilter(xt_fatal_exception); }
|
|
150
|
+
|
|
151
|
+
#else
|
|
152
|
+
|
|
153
|
+
static void xt_fatal_signal(int sig) {
|
|
154
|
+
int written = snprintf(g_crash_report, sizeof(g_crash_report), "\nxbintsc: fatal signal %d\n", sig);
|
|
155
|
+
written += xt_format_stack(g_crash_report + written, sizeof(g_crash_report) - (size_t)written);
|
|
156
|
+
ssize_t ignored = write(STDERR_FILENO, g_crash_report, (size_t)written);
|
|
157
|
+
(void)ignored;
|
|
158
|
+
_exit(128 + sig);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
void xt_install_crash_handler(void) {
|
|
162
|
+
signal(SIGSEGV, xt_fatal_signal);
|
|
163
|
+
signal(SIGABRT, xt_fatal_signal);
|
|
164
|
+
signal(SIGFPE, xt_fatal_signal);
|
|
165
|
+
signal(SIGBUS, xt_fatal_signal);
|
|
166
|
+
#ifdef SIGILL
|
|
167
|
+
signal(SIGILL, xt_fatal_signal);
|
|
168
|
+
#endif
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
#endif
|
|
172
|
+
|
|
17
173
|
#if defined(_WIN32)
|
|
18
174
|
/*
|
|
19
175
|
* The UCRT validates its low-level I/O arguments and, when one is out of
|
|
@@ -107,18 +263,23 @@ void *xt_try_enter(void) {
|
|
|
107
263
|
if (!frame) abort();
|
|
108
264
|
frame->prev = g_try_top;
|
|
109
265
|
frame->exception = XT_UNDEFINED;
|
|
266
|
+
frame->stack_depth = g_stack_depth;
|
|
110
267
|
g_try_top = frame;
|
|
111
268
|
return (void *)frame;
|
|
112
269
|
}
|
|
113
270
|
|
|
114
271
|
xt_value xt_try_exception(void *framePtr) {
|
|
115
272
|
xt_try_frame *frame = (xt_try_frame *)framePtr;
|
|
116
|
-
|
|
273
|
+
if (!frame) return XT_UNDEFINED;
|
|
274
|
+
/* A throw longjmps past the calls it unwinds; drop their shadow frames. */
|
|
275
|
+
xt_stack_trim(frame->stack_depth);
|
|
276
|
+
return frame->exception;
|
|
117
277
|
}
|
|
118
278
|
|
|
119
279
|
void xt_try_leave(void *framePtr) {
|
|
120
280
|
xt_try_frame *frame = (xt_try_frame *)framePtr;
|
|
121
281
|
if (frame && g_try_top == frame) {
|
|
282
|
+
xt_stack_trim(frame->stack_depth);
|
|
122
283
|
g_try_top = frame->prev;
|
|
123
284
|
free(frame);
|
|
124
285
|
}
|
|
@@ -135,8 +296,16 @@ void xt_throw(xt_value v) {
|
|
|
135
296
|
g_try_top->exception = v;
|
|
136
297
|
longjmp(g_try_top->buf, 1);
|
|
137
298
|
}
|
|
299
|
+
xt_value stack = xt_error_stack(v);
|
|
300
|
+
if (stack != XT_UNDEFINED) {
|
|
301
|
+
xt_string *text = xt_as_string(stack);
|
|
302
|
+
if (text) {
|
|
303
|
+
fprintf(stderr, "Uncaught %s\n", xt_string_bytes(text));
|
|
304
|
+
exit(1);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
138
307
|
xt_value message = xt_to_string(v);
|
|
139
308
|
xt_string *s = xt_as_string(message);
|
|
140
|
-
fprintf(stderr, "Uncaught %s\n", s
|
|
309
|
+
fprintf(stderr, "Uncaught %s\n", xt_string_bytes(s));
|
|
141
310
|
exit(1);
|
|
142
311
|
}
|
|
@@ -2,15 +2,36 @@
|
|
|
2
2
|
/* Output and inspection */
|
|
3
3
|
/* ------------------------------------------------------------------------- */
|
|
4
4
|
|
|
5
|
+
/* Write UTF-8 bytes, turning a lone surrogate half (WTF-8) into U+FFFD. Node's
|
|
6
|
+
* terminal encoding does the same, so `console.log("😀"[0])` prints one
|
|
7
|
+
* replacement character instead of emitting invalid UTF-8. */
|
|
8
|
+
static void xt_print_utf8(FILE *out, const char *bytes, size_t length) {
|
|
9
|
+
size_t offset = 0;
|
|
10
|
+
while (offset < length) {
|
|
11
|
+
unsigned char c = (unsigned char)bytes[offset];
|
|
12
|
+
if (c == 0xED && offset + 2 < length) {
|
|
13
|
+
unsigned char second = (unsigned char)bytes[offset + 1];
|
|
14
|
+
unsigned char third = (unsigned char)bytes[offset + 2];
|
|
15
|
+
if (second >= 0xA0 && second <= 0xBF && third >= 0x80 && third <= 0xBF) {
|
|
16
|
+
fwrite("\xEF\xBF\xBD", 1, 3, out);
|
|
17
|
+
offset += 3;
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
fputc((int)c, out);
|
|
22
|
+
offset++;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
5
26
|
static void xt_print_value(xt_value v, FILE *out) {
|
|
6
27
|
if (XT_IS_STRING(v)) {
|
|
7
28
|
xt_string *s = xt_as_string(v);
|
|
8
|
-
|
|
29
|
+
xt_print_utf8(out, xt_string_bytes(s), s->length);
|
|
9
30
|
return;
|
|
10
31
|
}
|
|
11
32
|
xt_value text = xt_to_string(v);
|
|
12
33
|
xt_string *s = xt_as_string(text);
|
|
13
|
-
|
|
34
|
+
xt_print_utf8(out, xt_string_bytes(s), s->length);
|
|
14
35
|
}
|
|
15
36
|
|
|
16
37
|
void xt_print(xt_value v) { xt_print_value(v, stdout); }
|
|
@@ -48,10 +69,11 @@ static void xt_inspect_pop(void) {
|
|
|
48
69
|
|
|
49
70
|
static int xt_inspect_identifier_key(xt_string *key) {
|
|
50
71
|
if (key->length == 0) return 0;
|
|
51
|
-
char
|
|
72
|
+
const char *bytes = xt_string_bytes(key);
|
|
73
|
+
char c = bytes[0];
|
|
52
74
|
if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '$')) return 0;
|
|
53
75
|
for (uint32_t i = 1; i < key->length; i++) {
|
|
54
|
-
c =
|
|
76
|
+
c = bytes[i];
|
|
55
77
|
if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c == '$')) return 0;
|
|
56
78
|
}
|
|
57
79
|
return 1;
|
|
@@ -60,16 +82,17 @@ static int xt_inspect_identifier_key(xt_string *key) {
|
|
|
60
82
|
/* Render a string the way `util.inspect` does inside a container: quoted,
|
|
61
83
|
* preferring whichever quote character needs fewer escapes. */
|
|
62
84
|
static void xt_inspect_string(xt_string *s, FILE *out) {
|
|
85
|
+
const char *bytes = xt_string_bytes(s);
|
|
63
86
|
int hasSingle = 0;
|
|
64
87
|
int hasDouble = 0;
|
|
65
88
|
for (uint32_t i = 0; i < s->length; i++) {
|
|
66
|
-
if (
|
|
67
|
-
else if (
|
|
89
|
+
if (bytes[i] == '\'') hasSingle = 1;
|
|
90
|
+
else if (bytes[i] == '"') hasDouble = 1;
|
|
68
91
|
}
|
|
69
92
|
char quote = (hasSingle && !hasDouble) ? '"' : '\'';
|
|
70
93
|
fputc(quote, out);
|
|
71
94
|
for (uint32_t i = 0; i < s->length; i++) {
|
|
72
|
-
unsigned char c = (unsigned char)
|
|
95
|
+
unsigned char c = (unsigned char)bytes[i];
|
|
73
96
|
switch (c) {
|
|
74
97
|
case '\\': fputs("\\\\", out); break;
|
|
75
98
|
case '\n': fputs("\\n", out); break;
|
|
@@ -81,6 +104,18 @@ static void xt_inspect_string(xt_string *s, FILE *out) {
|
|
|
81
104
|
default:
|
|
82
105
|
if (c == (unsigned char)quote) { fputc('\\', out); fputc(c, out); }
|
|
83
106
|
else if (c < 0x20) fprintf(out, "\\x%02x", c);
|
|
107
|
+
else if (c == 0xED && i + 2 < s->length) {
|
|
108
|
+
unsigned char second = (unsigned char)bytes[i + 1];
|
|
109
|
+
unsigned char third = (unsigned char)bytes[i + 2];
|
|
110
|
+
if (second >= 0xA0 && second <= 0xBF && third >= 0x80 && third <= 0xBF) {
|
|
111
|
+
unsigned code = ((unsigned)(c & 0x0F) << 12) | ((unsigned)(second & 0x3F) << 6) |
|
|
112
|
+
(unsigned)(third & 0x3F);
|
|
113
|
+
fprintf(out, "\\u%04x", code);
|
|
114
|
+
i += 2;
|
|
115
|
+
} else {
|
|
116
|
+
fputc(c, out);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
84
119
|
else fputc(c, out);
|
|
85
120
|
}
|
|
86
121
|
}
|
|
@@ -90,13 +125,13 @@ static void xt_inspect_string(xt_string *s, FILE *out) {
|
|
|
90
125
|
static void xt_inspect(xt_value v, FILE *out, int nested) {
|
|
91
126
|
if (xt_is_symbol(v)) {
|
|
92
127
|
xt_string *text = xt_as_string(xt_symbol_to_string(v));
|
|
93
|
-
fwrite(text
|
|
128
|
+
fwrite(xt_string_bytes(text), 1, text->length, out);
|
|
94
129
|
return;
|
|
95
130
|
}
|
|
96
131
|
if (XT_IS_BIGINT(v)) {
|
|
97
132
|
xt_value text = xt_bigint_to_decimal(v);
|
|
98
133
|
xt_string *s = xt_as_string(text);
|
|
99
|
-
fwrite(s
|
|
134
|
+
fwrite(xt_string_bytes(s), 1, s->length, out);
|
|
100
135
|
fputc('n', out);
|
|
101
136
|
return;
|
|
102
137
|
}
|
|
@@ -130,10 +165,10 @@ static void xt_inspect(xt_value v, FILE *out, int nested) {
|
|
|
130
165
|
xt_value key = extra->properties[i].key;
|
|
131
166
|
if (xt_is_symbol(key)) {
|
|
132
167
|
xt_string *text = xt_as_string(xt_symbol_to_string(key));
|
|
133
|
-
fwrite(text
|
|
168
|
+
fwrite(xt_string_bytes(text), 1, text->length, out);
|
|
134
169
|
} else {
|
|
135
170
|
xt_string *keyString = xt_as_string(key);
|
|
136
|
-
fwrite(keyString
|
|
171
|
+
fwrite(xt_string_bytes(keyString), 1, keyString->length, out);
|
|
137
172
|
}
|
|
138
173
|
fputs(": ", out);
|
|
139
174
|
xt_inspect(extra->properties[i].value, out, 1);
|
|
@@ -170,10 +205,10 @@ static void xt_inspect(xt_value v, FILE *out, int nested) {
|
|
|
170
205
|
written++;
|
|
171
206
|
if (isSymbol) {
|
|
172
207
|
xt_string *text = xt_as_string(xt_symbol_to_string(key));
|
|
173
|
-
fwrite(text
|
|
208
|
+
fwrite(xt_string_bytes(text), 1, text->length, out);
|
|
174
209
|
} else {
|
|
175
210
|
xt_string *keyString = xt_as_string(key);
|
|
176
|
-
if (xt_inspect_identifier_key(keyString)) fwrite(keyString
|
|
211
|
+
if (xt_inspect_identifier_key(keyString)) fwrite(xt_string_bytes(keyString), 1, keyString->length, out);
|
|
177
212
|
else xt_inspect_string(keyString, out);
|
|
178
213
|
}
|
|
179
214
|
fputs(": ", out);
|
package/runtime/xt_io.c
CHANGED