xbintsc 0.3.3 → 0.3.5

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.
Files changed (215) hide show
  1. package/dist/src/ast/expressions.d.ts +8 -0
  2. package/dist/src/binder/binder.js +1 -0
  3. package/dist/src/binder/binder.js.map +1 -1
  4. package/dist/src/binder/references.d.ts +1 -1
  5. package/dist/src/binder/references.js +34 -5
  6. package/dist/src/binder/references.js.map +1 -1
  7. package/dist/src/binder/types.d.ts +4 -0
  8. package/dist/src/binder/types.js.map +1 -1
  9. package/dist/src/cli/main.js.map +1 -1
  10. package/dist/src/codegen/generator/calls/access.d.ts +9 -1
  11. package/dist/src/codegen/generator/calls/access.js +146 -13
  12. package/dist/src/codegen/generator/calls/access.js.map +1 -1
  13. package/dist/src/codegen/generator/calls/closures.d.ts +1 -0
  14. package/dist/src/codegen/generator/calls/closures.js +25 -0
  15. package/dist/src/codegen/generator/calls/closures.js.map +1 -1
  16. package/dist/src/codegen/generator/calls/invocation.js +12 -1
  17. package/dist/src/codegen/generator/calls/invocation.js.map +1 -1
  18. package/dist/src/codegen/generator/calls/literals.js +12 -4
  19. package/dist/src/codegen/generator/calls/literals.js.map +1 -1
  20. package/dist/src/codegen/generator/calls/super.js +4 -7
  21. package/dist/src/codegen/generator/calls/super.js.map +1 -1
  22. package/dist/src/codegen/generator/context.d.ts +21 -2
  23. package/dist/src/codegen/generator/context.js +42 -5
  24. package/dist/src/codegen/generator/context.js.map +1 -1
  25. package/dist/src/codegen/generator/expressions/assignment.js +1 -3
  26. package/dist/src/codegen/generator/expressions/assignment.js.map +1 -1
  27. package/dist/src/codegen/generator/expressions/primary.d.ts +3 -1
  28. package/dist/src/codegen/generator/expressions/primary.js +99 -1
  29. package/dist/src/codegen/generator/expressions/primary.js.map +1 -1
  30. package/dist/src/codegen/generator/generator.js.map +1 -1
  31. package/dist/src/codegen/generator/module.js +25 -15
  32. package/dist/src/codegen/generator/module.js.map +1 -1
  33. package/dist/src/codegen/generator/state.d.ts +15 -0
  34. package/dist/src/codegen/generator/state.js +28 -1
  35. package/dist/src/codegen/generator/state.js.map +1 -1
  36. package/dist/src/codegen/generator/statements/control-flow.d.ts +3 -1
  37. package/dist/src/codegen/generator/statements/control-flow.js +85 -0
  38. package/dist/src/codegen/generator/statements/control-flow.js.map +1 -1
  39. package/dist/src/codegen/generator/statements/dispatch.js +15 -6
  40. package/dist/src/codegen/generator/statements/dispatch.js.map +1 -1
  41. package/dist/src/codegen/generator/statements/loops.d.ts +3 -0
  42. package/dist/src/codegen/generator/statements/loops.js +25 -11
  43. package/dist/src/codegen/generator/statements/loops.js.map +1 -1
  44. package/dist/src/codegen/generator/tables.d.ts +7 -0
  45. package/dist/src/codegen/generator/tables.js +41 -1
  46. package/dist/src/codegen/generator/tables.js.map +1 -1
  47. package/dist/src/driver/compiler.d.ts +1 -1
  48. package/dist/src/driver/compiler.js +33 -1
  49. package/dist/src/driver/compiler.js.map +1 -1
  50. package/dist/src/driver/modules.d.ts +4 -3
  51. package/dist/src/driver/modules.js +61 -4
  52. package/dist/src/driver/modules.js.map +1 -1
  53. package/dist/src/driver/toolchain.js +1 -1
  54. package/dist/src/driver/toolchain.js.map +1 -1
  55. package/dist/src/lexer/scanner.js.map +1 -1
  56. package/dist/src/parser/declarations/classes.js +4 -0
  57. package/dist/src/parser/declarations/classes.js.map +1 -1
  58. package/dist/src/parser/declarations/type-members.js.map +1 -1
  59. package/dist/src/parser/expressions.d.ts +2 -1
  60. package/dist/src/parser/expressions.js +10 -1
  61. package/dist/src/parser/expressions.js.map +1 -1
  62. package/dist/src/parser/helpers.js +2 -2
  63. package/dist/src/parser/helpers.js.map +1 -1
  64. package/dist/src/parser/literals.js +79 -6
  65. package/dist/src/parser/literals.js.map +1 -1
  66. package/dist/src/parser/modules.js.map +1 -1
  67. package/dist/src/parser/parser.js +0 -1
  68. package/dist/src/parser/parser.js.map +1 -1
  69. package/dist/src/parser/statements.d.ts +1 -0
  70. package/dist/src/parser/statements.js +13 -1
  71. package/dist/src/parser/statements.js.map +1 -1
  72. package/dist/src/parser/types.js +2 -2
  73. package/dist/src/parser/types.js.map +1 -1
  74. package/dist/tests/ast/factory.test.d.ts +1 -0
  75. package/dist/tests/ast/factory.test.js +35 -0
  76. package/dist/tests/ast/factory.test.js.map +1 -0
  77. package/dist/tests/ast/visitor.test.d.ts +1 -0
  78. package/dist/tests/ast/visitor.test.js +53 -0
  79. package/dist/tests/ast/visitor.test.js.map +1 -0
  80. package/dist/tests/cli/main.test.js +109 -9
  81. package/dist/tests/cli/main.test.js.map +1 -1
  82. package/dist/tests/codegen/closures.test.d.ts +1 -0
  83. package/dist/tests/codegen/closures.test.js +32 -0
  84. package/dist/tests/codegen/closures.test.js.map +1 -0
  85. package/dist/tests/codegen/expressions.test.d.ts +1 -0
  86. package/dist/tests/codegen/expressions.test.js +149 -0
  87. package/dist/tests/codegen/expressions.test.js.map +1 -0
  88. package/dist/tests/codegen/llvm.test.js +14 -0
  89. package/dist/tests/codegen/llvm.test.js.map +1 -1
  90. package/dist/tests/codegen/values.test.d.ts +1 -0
  91. package/dist/tests/codegen/values.test.js +48 -0
  92. package/dist/tests/codegen/values.test.js.map +1 -0
  93. package/dist/tests/diagnostics/diagnostic.test.d.ts +1 -0
  94. package/dist/tests/diagnostics/diagnostic.test.js +117 -0
  95. package/dist/tests/diagnostics/diagnostic.test.js.map +1 -0
  96. package/dist/tests/diagnostics/source.test.d.ts +1 -0
  97. package/dist/tests/diagnostics/source.test.js +58 -0
  98. package/dist/tests/diagnostics/source.test.js.map +1 -0
  99. package/dist/tests/driver/modules.test.d.ts +1 -0
  100. package/dist/tests/driver/modules.test.js +140 -0
  101. package/dist/tests/driver/modules.test.js.map +1 -0
  102. package/dist/tests/driver/paths.test.js +17 -1
  103. package/dist/tests/driver/paths.test.js.map +1 -1
  104. package/dist/tests/driver/runtime-lib.test.d.ts +1 -0
  105. package/dist/tests/driver/runtime-lib.test.js +19 -0
  106. package/dist/tests/driver/runtime-lib.test.js.map +1 -0
  107. package/dist/tests/driver/toolchain-provider.test.js +91 -0
  108. package/dist/tests/driver/toolchain-provider.test.js.map +1 -1
  109. package/dist/tests/driver/toolchain.test.js +11 -1
  110. package/dist/tests/driver/toolchain.test.js.map +1 -1
  111. package/dist/tests/e2e/compile-node.test.js +14 -1
  112. package/dist/tests/e2e/compile-node.test.js.map +1 -1
  113. package/dist/tests/e2e/compile.test.js +465 -2
  114. package/dist/tests/e2e/compile.test.js.map +1 -1
  115. package/dist/tests/e2e/differential.test.d.ts +7 -0
  116. package/dist/tests/e2e/differential.test.js +581 -0
  117. package/dist/tests/e2e/differential.test.js.map +1 -0
  118. package/dist/tests/e2e/harness.d.ts +21 -0
  119. package/dist/tests/e2e/harness.js +26 -0
  120. package/dist/tests/e2e/harness.js.map +1 -1
  121. package/dist/tests/extensions/node.test.d.ts +1 -0
  122. package/dist/tests/extensions/node.test.js +32 -0
  123. package/dist/tests/extensions/node.test.js.map +1 -0
  124. package/dist/tests/lexer/identifiers.test.d.ts +1 -0
  125. package/dist/tests/lexer/identifiers.test.js +13 -0
  126. package/dist/tests/lexer/identifiers.test.js.map +1 -0
  127. package/dist/tests/lexer/numbers.test.d.ts +1 -0
  128. package/dist/tests/lexer/numbers.test.js +23 -0
  129. package/dist/tests/lexer/numbers.test.js.map +1 -0
  130. package/dist/tests/lexer/strings.test.d.ts +1 -0
  131. package/dist/tests/lexer/strings.test.js +72 -0
  132. package/dist/tests/lexer/strings.test.js.map +1 -0
  133. package/dist/tests/parser/declarations.test.d.ts +1 -0
  134. package/dist/tests/parser/declarations.test.js +70 -0
  135. package/dist/tests/parser/declarations.test.js.map +1 -0
  136. package/dist/tests/parser/expressions.test.d.ts +1 -0
  137. package/dist/tests/parser/expressions.test.js +47 -0
  138. package/dist/tests/parser/expressions.test.js.map +1 -0
  139. package/dist/tests/parser/helpers.test.d.ts +1 -0
  140. package/dist/tests/parser/helpers.test.js +58 -0
  141. package/dist/tests/parser/helpers.test.js.map +1 -0
  142. package/dist/tests/parser/literals.test.d.ts +1 -0
  143. package/dist/tests/parser/literals.test.js +70 -0
  144. package/dist/tests/parser/literals.test.js.map +1 -0
  145. package/dist/tests/parser/module-syntax.test.d.ts +1 -0
  146. package/dist/tests/parser/module-syntax.test.js +91 -0
  147. package/dist/tests/parser/module-syntax.test.js.map +1 -0
  148. package/dist/tests/parser/statements.test.d.ts +1 -0
  149. package/dist/tests/parser/statements.test.js +85 -0
  150. package/dist/tests/parser/statements.test.js.map +1 -0
  151. package/dist/tests/parser/type-members.test.d.ts +1 -0
  152. package/dist/tests/parser/type-members.test.js +86 -0
  153. package/dist/tests/parser/type-members.test.js.map +1 -0
  154. package/dist/tests/parser/types.test.d.ts +1 -0
  155. package/dist/tests/parser/types.test.js +143 -0
  156. package/dist/tests/parser/types.test.js.map +1 -0
  157. package/package.json +6 -1
  158. package/runtime/ext_node/fs/fs_ops.c +36 -1
  159. package/runtime/rt.h +17 -0
  160. package/runtime/rt_internal.h +88 -2
  161. package/runtime/xt_builtins.c +174 -6
  162. package/runtime/xt_containers.c +234 -42
  163. package/runtime/xt_generator.c +304 -0
  164. package/runtime/xt_io.c +170 -30
  165. package/runtime/xt_promise.c +6 -1
  166. package/runtime/xt_regex.h +105 -35
  167. package/runtime/xt_stdlib/dispatch.inc +255 -4
  168. package/runtime/xt_stdlib/strings.inc +17 -13
  169. package/runtime/xt_stdlib2/arrays.inc +126 -19
  170. package/runtime/xt_stdlib2/date.inc +9 -2
  171. package/runtime/xt_stdlib2/error.inc +162 -12
  172. package/runtime/xt_stdlib2/helpers.inc +4 -0
  173. package/runtime/xt_stdlib2/json.inc +19 -6
  174. package/runtime/xt_stdlib2/number-string-statics.inc +1 -1
  175. package/runtime/xt_stdlib2/numbers.inc +4 -5
  176. package/runtime/xt_stdlib2/objects.inc +29 -0
  177. package/runtime/xt_stdlib2/regexp-match.inc +293 -51
  178. package/runtime/xt_stdlib2/set.inc +58 -2
  179. package/runtime/xt_stdlib2/strings.inc +38 -0
  180. package/runtime/xt_symbol.c +149 -0
  181. package/runtime/xt_values.c +221 -15
  182. package/scripts/build-runtime.ts +1 -1
  183. package/src/ast/expressions.ts +8 -0
  184. package/src/binder/binder.ts +1 -1
  185. package/src/binder/references.ts +47 -4
  186. package/src/binder/types.ts +4 -0
  187. package/src/cli/main.ts +1 -2
  188. package/src/codegen/generator/calls/access.ts +158 -13
  189. package/src/codegen/generator/calls/closures.ts +24 -0
  190. package/src/codegen/generator/calls/invocation.ts +11 -1
  191. package/src/codegen/generator/calls/literals.ts +10 -4
  192. package/src/codegen/generator/calls/super.ts +6 -9
  193. package/src/codegen/generator/context.ts +44 -7
  194. package/src/codegen/generator/expressions/assignment.ts +1 -3
  195. package/src/codegen/generator/expressions/primary.ts +108 -2
  196. package/src/codegen/generator/generator.ts +0 -1
  197. package/src/codegen/generator/module.ts +24 -18
  198. package/src/codegen/generator/state.ts +44 -1
  199. package/src/codegen/generator/statements/control-flow.ts +86 -0
  200. package/src/codegen/generator/statements/dispatch.ts +18 -6
  201. package/src/codegen/generator/statements/loops.ts +29 -11
  202. package/src/codegen/generator/tables.ts +44 -1
  203. package/src/driver/compiler.ts +32 -2
  204. package/src/driver/modules.ts +64 -4
  205. package/src/driver/toolchain.ts +1 -1
  206. package/src/lexer/scanner.ts +0 -1
  207. package/src/parser/declarations/classes.ts +4 -0
  208. package/src/parser/declarations/type-members.ts +0 -2
  209. package/src/parser/expressions.ts +13 -1
  210. package/src/parser/helpers.ts +2 -2
  211. package/src/parser/literals.ts +82 -7
  212. package/src/parser/modules.ts +0 -1
  213. package/src/parser/parser.ts +0 -1
  214. package/src/parser/statements.ts +14 -0
  215. package/src/parser/types.ts +2 -2
@@ -0,0 +1,304 @@
1
+ /*
2
+ * xbintsc runtime — stackful generators.
3
+ *
4
+ * A generator function object carries `is_generator`. Calling it does not run
5
+ * the body; it creates an `xt_generator` that owns a private stack. `.next()`
6
+ * switches onto that stack, where the body runs until it calls `xt_yield`,
7
+ * which switches back. Because the body keeps a real call stack, `try`,
8
+ * `finally` and nested loops work across suspension points without a state
9
+ * machine transform.
10
+ *
11
+ * POSIX uses `ucontext` (`swapcontext`); Windows uses fibers. The generator's
12
+ * exception stack is swapped alongside it so a `throw` inside the body still
13
+ * targets the body's own `try` frames.
14
+ */
15
+
16
+ #define _XOPEN_SOURCE 700
17
+
18
+ #include "rt_internal.h"
19
+
20
+ #include <stdlib.h>
21
+ #include <string.h>
22
+
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
+ typedef struct xt_generator {
34
+ xt_header header;
35
+ xt_value function;
36
+ xt_value this_value;
37
+ xt_value *args;
38
+ int32_t argc;
39
+ xt_value sent; /* value passed by the most recent resume */
40
+ xt_value result; /* yielded / returned value, or a thrown exception */
41
+ /* Value pulled by `xt_iter_has` and consumed by `xt_iter_value`. */
42
+ xt_value iter_value;
43
+ int resume_kind; /* 0 = next, 1 = throw, 2 = return */
44
+ int started;
45
+ int done;
46
+ int threw;
47
+ int booted;
48
+ void *saved_try_top;
49
+ #ifdef _WIN32
50
+ void *fiber;
51
+ void *caller_fiber;
52
+ #else
53
+ ucontext_t ctx;
54
+ ucontext_t caller;
55
+ char *stack;
56
+ #endif
57
+ } xt_generator;
58
+
59
+ /* The generator whose coroutine is running, if any. */
60
+ static xt_generator *g_active_generator = NULL;
61
+ /* Filled in just before a coroutine is first entered (POSIX has no portable
62
+ * way to pass a pointer to `makecontext`). */
63
+ static xt_generator *g_boot_generator = NULL;
64
+ #ifdef _WIN32
65
+ static void *g_main_fiber = NULL;
66
+ #endif
67
+
68
+ int xt_is_generator(xt_value value) {
69
+ if (!XT_IS_OBJECT(value)) return 0;
70
+ return ((xt_header *)XT_GET_PTR(value))->kind == XT_OBJECT_KIND_GENERATOR;
71
+ }
72
+
73
+ static xt_generator *xt_as_generator(xt_value value) {
74
+ if (!xt_is_generator(value)) return NULL;
75
+ return (xt_generator *)XT_GET_PTR(value);
76
+ }
77
+
78
+ static xt_value xt_generator_result_object(xt_value value, int done) {
79
+ xt_value result = xt_object_new();
80
+ xt_object_set(result, xt_string_from_cstr("value"), value);
81
+ xt_object_set(result, xt_string_from_cstr("done"), xt_bool(done));
82
+ return result;
83
+ }
84
+
85
+ static void xt_generator_suspend(xt_generator *gen) {
86
+ gen->saved_try_top = xt_try_mark();
87
+ #ifdef _WIN32
88
+ SwitchToFiber(gen->caller_fiber);
89
+ #else
90
+ swapcontext(&gen->ctx, &gen->caller);
91
+ #endif
92
+ }
93
+
94
+ /* Runs on the generator's private stack. */
95
+ static void xt_generator_run(xt_generator *gen) {
96
+ void *boundary = xt_try_enter();
97
+ if (xt_try_setjmp(boundary) == 0) {
98
+ xt_function *function = (xt_function *)XT_GET_PTR(gen->function);
99
+ gen->result = function->code(gen->this_value, gen->function, gen->argc, gen->args);
100
+ gen->threw = 0;
101
+ } else {
102
+ gen->result = xt_try_exception(boundary);
103
+ gen->threw = 1;
104
+ }
105
+ gen->done = 1;
106
+ xt_generator_suspend(gen);
107
+ }
108
+
109
+ #ifndef _WIN32
110
+ static void xt_generator_entry(void) {
111
+ xt_generator *gen = g_boot_generator;
112
+ g_boot_generator = NULL;
113
+ xt_generator_run(gen);
114
+ }
115
+ #else
116
+ static void CALLBACK xt_generator_entry(void *param) {
117
+ xt_generator_run((xt_generator *)param);
118
+ }
119
+ #endif
120
+
121
+ static void xt_generator_boot(xt_generator *gen) {
122
+ #ifdef _WIN32
123
+ if (!g_main_fiber) g_main_fiber = ConvertThreadToFiber(NULL);
124
+ gen->fiber = CreateFiber(1 << 20, xt_generator_entry, gen);
125
+ if (!gen->fiber) abort();
126
+ #else
127
+ size_t size = 1 << 20;
128
+ gen->stack = (char *)malloc(size);
129
+ if (!gen->stack) abort();
130
+ if (getcontext(&gen->ctx) != 0) abort();
131
+ gen->ctx.uc_stack.ss_sp = gen->stack;
132
+ gen->ctx.uc_stack.ss_size = size;
133
+ gen->ctx.uc_link = NULL;
134
+ g_boot_generator = gen;
135
+ makecontext(&gen->ctx, xt_generator_entry, 0);
136
+ #endif
137
+ gen->booted = 1;
138
+ }
139
+
140
+ /* Release the private stack once the coroutine has finished. The generator
141
+ * object itself stays valid; later `.next()` calls just report `done`. */
142
+ static void xt_generator_dispose(xt_generator *gen) {
143
+ #ifdef _WIN32
144
+ if (gen->fiber) {
145
+ DeleteFiber(gen->fiber);
146
+ gen->fiber = NULL;
147
+ }
148
+ gen->caller_fiber = NULL;
149
+ #else
150
+ if (gen->stack) {
151
+ free(gen->stack);
152
+ gen->stack = NULL;
153
+ }
154
+ #endif
155
+ }
156
+
157
+ static void xt_generator_resume(xt_generator *gen) {
158
+ if (!gen->booted) xt_generator_boot(gen);
159
+ xt_generator *previous = g_active_generator;
160
+ g_active_generator = gen;
161
+ void *caller_try = xt_try_mark();
162
+ xt_try_restore(gen->saved_try_top);
163
+ #ifdef _WIN32
164
+ gen->caller_fiber = GetCurrentFiber();
165
+ SwitchToFiber(gen->fiber);
166
+ #else
167
+ swapcontext(&gen->caller, &gen->ctx);
168
+ #endif
169
+ gen->saved_try_top = xt_try_mark();
170
+ xt_try_restore(caller_try);
171
+ if (gen->done) xt_generator_dispose(gen);
172
+ g_active_generator = previous;
173
+ }
174
+
175
+ xt_value xt_generator_new(xt_value function, xt_value thisValue, int32_t argc, xt_value *argv) {
176
+ xt_generator *gen = (xt_generator *)xt_alloc(sizeof(xt_generator), XT_OBJECT_KIND_GENERATOR);
177
+ gen->function = function;
178
+ gen->this_value = thisValue;
179
+ gen->argc = argc;
180
+ gen->result = XT_UNDEFINED;
181
+ gen->sent = XT_UNDEFINED;
182
+ gen->iter_value = XT_UNDEFINED;
183
+ if (argc > 0 && argv) {
184
+ gen->args = (xt_value *)malloc(sizeof(xt_value) * (size_t)argc);
185
+ if (!gen->args) abort();
186
+ memcpy(gen->args, argv, sizeof(xt_value) * (size_t)argc);
187
+ }
188
+ return XT_FROM_PTR(XT_TAG_OBJECT, gen);
189
+ }
190
+
191
+ xt_value xt_generator_next(xt_value generator, xt_value sent) {
192
+ xt_generator *gen = xt_as_generator(generator);
193
+ if (!gen) {
194
+ xt_throw(xt_string_from_cstr("TypeError: not a generator"));
195
+ return XT_UNDEFINED;
196
+ }
197
+ if (gen->done) return xt_generator_result_object(XT_UNDEFINED, 1);
198
+ gen->sent = sent;
199
+ gen->resume_kind = 0;
200
+ xt_generator_resume(gen);
201
+ if (gen->done) {
202
+ if (gen->threw) xt_throw(gen->result);
203
+ return xt_generator_result_object(gen->result, 1);
204
+ }
205
+ return xt_generator_result_object(gen->result, 0);
206
+ }
207
+
208
+ xt_value xt_generator_throw(xt_value generator, xt_value error) {
209
+ xt_generator *gen = xt_as_generator(generator);
210
+ if (!gen) {
211
+ xt_throw(xt_string_from_cstr("TypeError: not a generator"));
212
+ return XT_UNDEFINED;
213
+ }
214
+ if (gen->done) {
215
+ xt_throw(error);
216
+ return XT_UNDEFINED;
217
+ }
218
+ gen->sent = error;
219
+ gen->resume_kind = 1;
220
+ xt_generator_resume(gen);
221
+ if (gen->done) {
222
+ if (gen->threw) xt_throw(gen->result);
223
+ return xt_generator_result_object(gen->result, 1);
224
+ }
225
+ return xt_generator_result_object(gen->result, 0);
226
+ }
227
+
228
+ xt_value xt_generator_return(xt_value generator, xt_value value) {
229
+ xt_generator *gen = xt_as_generator(generator);
230
+ if (!gen) {
231
+ xt_throw(xt_string_from_cstr("TypeError: not a generator"));
232
+ return XT_UNDEFINED;
233
+ }
234
+ /* Abandon the coroutine without resuming: finally blocks are not run. */
235
+ gen->done = 1;
236
+ gen->threw = 0;
237
+ gen->result = value;
238
+ xt_generator_dispose(gen);
239
+ return xt_generator_result_object(value, 1);
240
+ }
241
+
242
+ /* Suspend the running generator with `value`; returns the value the caller
243
+ * passes to the next resume (`next(v)` / `throw(e)` / `return(v)`). */
244
+ xt_value xt_yield(xt_value value) {
245
+ xt_generator *gen = g_active_generator;
246
+ if (!gen || gen->done) return XT_UNDEFINED;
247
+ gen->result = value;
248
+ xt_generator_suspend(gen);
249
+ if (gen->resume_kind == 1) {
250
+ xt_throw(gen->sent);
251
+ return XT_UNDEFINED;
252
+ }
253
+ if (gen->resume_kind == 2) {
254
+ gen->done = 1;
255
+ gen->threw = 0;
256
+ gen->result = gen->sent;
257
+ xt_generator_suspend(gen);
258
+ return XT_UNDEFINED;
259
+ }
260
+ return gen->sent;
261
+ }
262
+
263
+ /* `yield* delegate`: relay every value the delegate produces. */
264
+ xt_value xt_yield_star(xt_value delegate) {
265
+ xt_value nextName = xt_string_from_cstr("next");
266
+ int usesNext = XT_IS_FUNCTION(xt_get(delegate, nextName));
267
+ /* A plain iterable (`[Symbol.iterator]` but no `next`) resolves through the
268
+ * iteration protocol; arrays/strings/Map/Set keep their index protocol. */
269
+ if (!usesNext) delegate = xt_iter_open(delegate);
270
+ xt_value sent = XT_UNDEFINED;
271
+ int32_t index = 0;
272
+ for (;;) {
273
+ xt_value value;
274
+ int done;
275
+ if (usesNext) {
276
+ xt_value step = xt_call_method(delegate, nextName, 1, &sent);
277
+ done = xt_truthy(xt_get(step, xt_string_from_cstr("done")));
278
+ value = xt_get(step, xt_string_from_cstr("value"));
279
+ } else {
280
+ xt_value position = xt_number((double)index);
281
+ if (!xt_truthy(xt_iter_has(delegate, position))) return XT_UNDEFINED;
282
+ value = xt_iter_value(delegate, position);
283
+ index++;
284
+ done = 0;
285
+ }
286
+ if (done) return value;
287
+ sent = xt_yield(value);
288
+ }
289
+ }
290
+
291
+ /* `for...of` step for generators: pull and cache the next value. */
292
+ int xt_generator_has_next(xt_value generator) {
293
+ xt_generator *gen = xt_as_generator(generator);
294
+ if (!gen || gen->done) return 0;
295
+ xt_value step = xt_generator_next(generator, XT_UNDEFINED);
296
+ if (xt_truthy(xt_get(step, xt_string_from_cstr("done")))) return 0;
297
+ gen->iter_value = xt_get(step, xt_string_from_cstr("value"));
298
+ return 1;
299
+ }
300
+
301
+ xt_value xt_generator_iter_value(xt_value generator) {
302
+ xt_generator *gen = xt_as_generator(generator);
303
+ return gen ? gen->iter_value : XT_UNDEFINED;
304
+ }
package/runtime/xt_io.c CHANGED
@@ -7,15 +7,17 @@
7
7
  *
8
8
  * The generated IR calls `_setjmp` with two arguments — the jmp_buf and the
9
9
  * caller's frame address (`@llvm.frameaddress(0)`) — paired with `longjmp`
10
- * here. This is exactly what clang lowers a C `_setjmp(buf)` call to on MSVC.
11
- * On Windows the UCRT `_setjmp` takes the frame as a second argument and
12
- * stores it in `_JUMP_BUFFER.Frame`; `longjmp` passes that frame to
13
- * `RtlUnwind` to run the unwind. Calling `_setjmp` with only one argument left
14
- * `Frame` as garbage, so `longjmp` unwound to a bogus target
15
- * (`STATUS_BAD_FUNCTION_TABLE`, 0xC00000FF). `_setjmp` is used rather than the
16
- * exported `setjmp` symbol, which is a legacy two-argument routine with an
17
- * incompatible ABI; on Linux/macOS `_setjmp` simply ignores the extra
18
- * argument and pairs with `longjmp` (the XSI `_longjmp` does not exist on
10
+ * here. This is exactly what clang lowers a C `_setjmp(buf)` call to on MSVC:
11
+ * on Windows the UCRT `_setjmp` stores that frame in `_JUMP_BUFFER.Frame` and
12
+ * `longjmp` passes it to `RtlUnwind` to run the unwind, so omitting it makes
13
+ * `longjmp` unwind to a bogus target (`STATUS_BAD_FUNCTION_TABLE`,
14
+ * 0xC00000FF). Because the generated call is IR it must pass the frame
15
+ * explicitly; the C runtime (see `xt_try_setjmp`) instead follows whatever
16
+ * the active `<setjmp.h>` declares -- one argument on MSVC (clang injects the
17
+ * frame) and two on MinGW-w64. `_setjmp` is used
18
+ * rather than the exported `setjmp` symbol, whose Windows ABI is an
19
+ * incompatible two-argument routine; on Linux/macOS `_setjmp` takes only the
20
+ * buffer and pairs with `longjmp` (the XSI `_longjmp` does not exist on
19
21
  * Windows).
20
22
  */
21
23
 
@@ -43,12 +45,6 @@
43
45
  * with `xt_try_leave`. `xt_throw` longjmps into the innermost frame when one
44
46
  * is active, and only prints + exits when the exception is uncaught.
45
47
  */
46
- typedef struct xt_try_frame {
47
- jmp_buf buf;
48
- struct xt_try_frame *prev;
49
- xt_value exception;
50
- } xt_try_frame;
51
-
52
48
  static xt_try_frame *g_try_top = NULL;
53
49
 
54
50
  /* Program arguments, captured once by `xt_set_program_args` from `main`. */
@@ -138,9 +134,18 @@ xt_value xt_try_exception(void *framePtr) {
138
134
 
139
135
  void xt_try_leave(void *framePtr) {
140
136
  xt_try_frame *frame = (xt_try_frame *)framePtr;
141
- if (frame && g_try_top == frame) g_try_top = frame->prev;
137
+ if (frame && g_try_top == frame) {
138
+ g_try_top = frame->prev;
139
+ free(frame);
140
+ }
142
141
  }
143
142
 
143
+ /* Save/restore the innermost try frame. Generators use this to swap their own
144
+ * exception stack in while they run on their private coroutine stack. */
145
+ void *xt_try_mark(void) { return (void *)g_try_top; }
146
+
147
+ void xt_try_restore(void *mark) { g_try_top = (xt_try_frame *)mark; }
148
+
144
149
  void xt_throw(xt_value v) {
145
150
  if (g_try_top) {
146
151
  g_try_top->exception = v;
@@ -172,8 +177,77 @@ void xt_println(xt_value v) {
172
177
  /*
173
178
  * console.log applies a Node-like inspection format: strings unquoted at the
174
179
  * top level, arrays as `[ a, b ]` and objects as `{ key: value }`.
180
+ *
181
+ * Recursion tracks the ancestor chain so self-referential containers print
182
+ * Node's `[Circular *N]` marker instead of recursing forever. `N` is the
183
+ * 1-based position of the repeated container in the current path.
175
184
  */
176
- static void xt_inspect(xt_value v, FILE *out) {
185
+ #define XT_INSPECT_MAX_DEPTH 512
186
+ static const void *xt_inspect_stack[XT_INSPECT_MAX_DEPTH];
187
+ static int xt_inspect_depth = 0;
188
+
189
+ static int xt_inspect_seen(const void *ptr) {
190
+ for (int i = 0; i < xt_inspect_depth; i++) {
191
+ if (xt_inspect_stack[i] == ptr) return i + 1;
192
+ }
193
+ return 0;
194
+ }
195
+
196
+ static void xt_inspect_push(const void *ptr) {
197
+ if (xt_inspect_depth < XT_INSPECT_MAX_DEPTH) xt_inspect_stack[xt_inspect_depth++] = ptr;
198
+ }
199
+
200
+ static void xt_inspect_pop(void) {
201
+ if (xt_inspect_depth > 0) xt_inspect_depth--;
202
+ }
203
+
204
+ static int xt_inspect_identifier_key(xt_string *key) {
205
+ if (key->length == 0) return 0;
206
+ char c = key->data[0];
207
+ if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '$')) return 0;
208
+ for (uint32_t i = 1; i < key->length; i++) {
209
+ c = key->data[i];
210
+ if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c == '$')) return 0;
211
+ }
212
+ return 1;
213
+ }
214
+
215
+ /* Render a string the way `util.inspect` does inside a container: quoted,
216
+ * preferring whichever quote character needs fewer escapes. */
217
+ static void xt_inspect_string(xt_string *s, FILE *out) {
218
+ int hasSingle = 0;
219
+ int hasDouble = 0;
220
+ for (uint32_t i = 0; i < s->length; i++) {
221
+ if (s->data[i] == '\'') hasSingle = 1;
222
+ else if (s->data[i] == '"') hasDouble = 1;
223
+ }
224
+ char quote = (hasSingle && !hasDouble) ? '"' : '\'';
225
+ fputc(quote, out);
226
+ for (uint32_t i = 0; i < s->length; i++) {
227
+ unsigned char c = (unsigned char)s->data[i];
228
+ switch (c) {
229
+ case '\\': fputs("\\\\", out); break;
230
+ case '\n': fputs("\\n", out); break;
231
+ case '\r': fputs("\\r", out); break;
232
+ case '\t': fputs("\\t", out); break;
233
+ case '\f': fputs("\\f", out); break;
234
+ case '\b': fputs("\\b", out); break;
235
+ case '\v': fputs("\\v", out); break;
236
+ default:
237
+ if (c == (unsigned char)quote) { fputc('\\', out); fputc(c, out); }
238
+ else if (c < 0x20) fprintf(out, "\\x%02x", c);
239
+ else fputc(c, out);
240
+ }
241
+ }
242
+ fputc(quote, out);
243
+ }
244
+
245
+ static void xt_inspect(xt_value v, FILE *out, int nested) {
246
+ if (xt_is_symbol(v)) {
247
+ xt_string *text = xt_as_string(xt_symbol_to_string(v));
248
+ fwrite(text->data, 1, text->length, out);
249
+ return;
250
+ }
177
251
  if (XT_IS_BIGINT(v)) {
178
252
  xt_value text = xt_bigint_to_decimal(v);
179
253
  xt_string *s = xt_as_string(text);
@@ -181,39 +255,105 @@ static void xt_inspect(xt_value v, FILE *out) {
181
255
  fputc('n', out);
182
256
  return;
183
257
  }
258
+ if (nested && XT_IS_STRING(v)) {
259
+ xt_inspect_string(xt_as_string(v), out);
260
+ return;
261
+ }
184
262
  if (XT_IS_ARRAY(v)) {
185
263
  xt_array *array = (xt_array *)XT_GET_PTR(v);
264
+ int seen = xt_inspect_seen(array);
265
+ if (seen > 0) {
266
+ fprintf(out, "[Circular *%d]", seen);
267
+ return;
268
+ }
269
+ if (xt_inspect_depth >= XT_INSPECT_MAX_DEPTH) {
270
+ fputs("[Array]", out);
271
+ return;
272
+ }
273
+ xt_inspect_push(array);
186
274
  fputc('[', out);
187
275
  for (uint32_t i = 0; i < array->length; i++) {
188
276
  if (i > 0) fputs(", ", out);
189
277
  else fputc(' ', out);
190
- xt_inspect(array->items[i], out);
278
+ xt_inspect(array->items[i], out, 1);
279
+ }
280
+ if (XT_IS_OBJECT(array->extra)) {
281
+ xt_object *extra = (xt_object *)XT_GET_PTR(array->extra);
282
+ for (uint32_t i = 0; i < extra->count; i++) {
283
+ if (array->length > 0 || i > 0) fputs(", ", out);
284
+ else fputc(' ', out);
285
+ xt_value key = extra->properties[i].key;
286
+ if (xt_is_symbol(key)) {
287
+ xt_string *text = xt_as_string(xt_symbol_to_string(key));
288
+ fwrite(text->data, 1, text->length, out);
289
+ } else {
290
+ xt_string *keyString = xt_as_string(key);
291
+ fwrite(keyString->data, 1, keyString->length, out);
292
+ }
293
+ fputs(": ", out);
294
+ xt_inspect(extra->properties[i].value, out, 1);
295
+ }
191
296
  }
192
- if (array->length > 0) fputc(' ', out);
297
+ if (array->length > 0 || (XT_IS_OBJECT(array->extra) && ((xt_object *)XT_GET_PTR(array->extra))->count > 0)) fputc(' ', out);
193
298
  fputc(']', out);
299
+ xt_inspect_pop();
194
300
  return;
195
301
  }
196
302
  if (XT_IS_OBJECT(v)) {
197
303
  xt_object *obj = (xt_object *)XT_GET_PTR(v);
304
+ int seen = xt_inspect_seen(obj);
305
+ if (seen > 0) {
306
+ fprintf(out, "[Circular *%d]", seen);
307
+ return;
308
+ }
309
+ if (xt_inspect_depth >= XT_INSPECT_MAX_DEPTH) {
310
+ fputs("[Object]", out);
311
+ return;
312
+ }
313
+ xt_inspect_push(obj);
198
314
  fputc('{', out);
199
- for (uint32_t i = 0; i < obj->count; i++) {
200
- if (i > 0) fputs(", ", out);
201
- else fputc(' ', out);
202
- fwrite(obj->properties[i].key->data, 1, obj->properties[i].key->length, out);
203
- fputs(": ", out);
204
- xt_inspect(obj->properties[i].value, out);
315
+ /* `util.inspect` lists string keys first, then symbol keys, in insertion
316
+ * order; symbol keys are printed without brackets. */
317
+ int written = 0;
318
+ for (int pass = 0; pass < 2; pass++) {
319
+ for (uint32_t i = 0; i < obj->count; i++) {
320
+ xt_value key = obj->properties[i].key;
321
+ int isSymbol = xt_is_symbol(key);
322
+ if ((pass == 0) == isSymbol) continue;
323
+ if (written > 0) fputs(", ", out);
324
+ else fputc(' ', out);
325
+ written++;
326
+ if (isSymbol) {
327
+ xt_string *text = xt_as_string(xt_symbol_to_string(key));
328
+ fwrite(text->data, 1, text->length, out);
329
+ } else {
330
+ xt_string *keyString = xt_as_string(key);
331
+ if (xt_inspect_identifier_key(keyString)) fwrite(keyString->data, 1, keyString->length, out);
332
+ else xt_inspect_string(keyString, out);
333
+ }
334
+ fputs(": ", out);
335
+ xt_inspect(obj->properties[i].value, out, 1);
336
+ }
205
337
  }
206
- if (obj->count > 0) fputc(' ', out);
338
+ if (written > 0) fputc(' ', out);
207
339
  fputc('}', out);
340
+ xt_inspect_pop();
208
341
  return;
209
342
  }
210
343
  xt_print_value(v, out);
211
344
  }
212
345
 
346
+ /* Inspect one top-level value; reset the ancestor chain so nested console
347
+ * calls (or a `longjmp` out of a throw) never leave stale state behind. */
348
+ static void xt_inspect_top(xt_value v, FILE *out) {
349
+ xt_inspect_depth = 0;
350
+ xt_inspect(v, out, 0);
351
+ }
352
+
213
353
  void xt_console_log(int32_t argc, xt_value *argv) {
214
354
  for (int32_t i = 0; i < argc; i++) {
215
355
  if (i > 0) fputc(' ', stdout);
216
- xt_inspect(argv[i], stdout);
356
+ xt_inspect_top(argv[i], stdout);
217
357
  }
218
358
  fputc('\n', stdout);
219
359
  }
@@ -221,7 +361,7 @@ void xt_console_log(int32_t argc, xt_value *argv) {
221
361
  static void xt_console_write(int32_t argc, xt_value *argv, FILE *out) {
222
362
  for (int32_t i = 0; i < argc; i++) {
223
363
  if (i > 0) fputc(' ', out);
224
- xt_inspect(argv[i], out);
364
+ xt_inspect_top(argv[i], out);
225
365
  }
226
366
  fputc('\n', out);
227
367
  }
@@ -239,7 +379,7 @@ xt_value xt_console_trace(int32_t argc, xt_value *argv) {
239
379
  fputs("Trace", stderr);
240
380
  for (int32_t i = 0; i < argc; i++) {
241
381
  fputc(' ', stderr);
242
- xt_inspect(argv[i], stderr);
382
+ xt_inspect_top(argv[i], stderr);
243
383
  }
244
384
  fputc('\n', stderr);
245
385
  return XT_UNDEFINED;
@@ -251,7 +391,7 @@ xt_value xt_console_assert(int32_t argc, xt_value *argv) {
251
391
  if (argc > 1) fputs(":", stderr);
252
392
  for (int32_t i = 1; i < argc; i++) {
253
393
  fputc(' ', stderr);
254
- xt_inspect(argv[i], stderr);
394
+ xt_inspect_top(argv[i], stderr);
255
395
  }
256
396
  fputc('\n', stderr);
257
397
  }
@@ -210,6 +210,7 @@ xt_value xt_promise_static(xt_value name, int32_t argc, xt_value *argv) {
210
210
  if (XT_IS_ARRAY(iterable)) {
211
211
  xt_array *array = xt_as_array(iterable);
212
212
  int anyRejected = 0;
213
+ xt_value reasons = xt_array_new(0, NULL);
213
214
  for (uint32_t i = 0; i < array->length; i++) {
214
215
  xt_value item = array->items[i];
215
216
  xt_promise *inner = xt_is_promise(item) ? xt_as_promise(item) : NULL;
@@ -224,6 +225,7 @@ xt_value xt_promise_static(xt_value name, int32_t argc, xt_value *argv) {
224
225
  if (rejected) {
225
226
  if (strcmp(fn, "any") == 0) {
226
227
  anyRejected++;
228
+ xt_array_push(reasons, inner->value);
227
229
  continue;
228
230
  }
229
231
  xt_promise_settle(result, XT_PROMISE_REJECTED, inner->value);
@@ -237,7 +239,10 @@ xt_value xt_promise_static(xt_value name, int32_t argc, xt_value *argv) {
237
239
  xt_array_push(values, settled);
238
240
  }
239
241
  if (strcmp(fn, "any") == 0 && anyRejected > 0) {
240
- xt_promise_settle(result, XT_PROMISE_REJECTED, xt_string_from_cstr("AggregateError: all promises rejected"));
242
+ xt_value ctorArgv[2];
243
+ ctorArgv[0] = reasons;
244
+ ctorArgv[1] = xt_string_from_cstr("All promises were rejected");
245
+ xt_promise_settle(result, XT_PROMISE_REJECTED, xt_aggregate_error_ctor(2, ctorArgv));
241
246
  return xt_promise_value(result);
242
247
  }
243
248
  }