xbintsc 0.3.35 → 0.3.49

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 (130) hide show
  1. package/AGENTS.md +95 -0
  2. package/README.md +31 -3
  3. package/README.zh-CN.md +23 -0
  4. package/dist/src/cli/hints.d.ts +54 -0
  5. package/dist/src/cli/hints.js +165 -0
  6. package/dist/src/cli/hints.js.map +1 -0
  7. package/dist/src/cli/main.js +73 -9
  8. package/dist/src/cli/main.js.map +1 -1
  9. package/dist/src/codegen/generator/module.js +10 -0
  10. package/dist/src/codegen/generator/module.js.map +1 -1
  11. package/dist/src/codegen/generator/state.js +4 -0
  12. package/dist/src/codegen/generator/state.js.map +1 -1
  13. package/dist/src/codegen/generator/tables.d.ts +26 -0
  14. package/dist/src/codegen/generator/tables.js +64 -12
  15. package/dist/src/codegen/generator/tables.js.map +1 -1
  16. package/dist/src/diagnostics/source-text.d.ts +22 -0
  17. package/dist/src/diagnostics/source-text.js +76 -0
  18. package/dist/src/diagnostics/source-text.js.map +1 -0
  19. package/dist/src/driver/bundler/graph.js +2 -1
  20. package/dist/src/driver/bundler/graph.js.map +1 -1
  21. package/dist/src/driver/compiler.js +3 -2
  22. package/dist/src/driver/compiler.js.map +1 -1
  23. package/dist/src/lexer/scanner/strings.js +16 -3
  24. package/dist/src/lexer/scanner/strings.js.map +1 -1
  25. package/dist/tests/cli/hints.test.d.ts +9 -0
  26. package/dist/tests/cli/hints.test.js +143 -0
  27. package/dist/tests/cli/hints.test.js.map +1 -0
  28. package/dist/tests/cli/main.test.js +6 -4
  29. package/dist/tests/cli/main.test.js.map +1 -1
  30. package/dist/tests/codegen/llvm.test.js +17 -2
  31. package/dist/tests/codegen/llvm.test.js.map +1 -1
  32. package/dist/tests/e2e/gc.test.d.ts +1 -0
  33. package/dist/tests/e2e/gc.test.js +168 -0
  34. package/dist/tests/e2e/gc.test.js.map +1 -0
  35. package/dist/tests/e2e/harness.d.ts +2 -0
  36. package/dist/tests/e2e/harness.js +1 -0
  37. package/dist/tests/e2e/harness.js.map +1 -1
  38. package/dist/tests/helpers.js +3 -2
  39. package/dist/tests/helpers.js.map +1 -1
  40. package/dist/tests/lexer/strings.test.js +14 -2
  41. package/dist/tests/lexer/strings.test.js.map +1 -1
  42. package/doc/DESIGN.md +117 -0
  43. package/doc/ai/README.md +63 -0
  44. package/doc/ai/build-recipe.md +137 -0
  45. package/doc/ai/cli.md +142 -0
  46. package/doc/ai/contributing.md +196 -0
  47. package/doc/ai/extensions.md +148 -0
  48. package/doc/ai/language-support.md +152 -0
  49. package/doc/ai/troubleshooting.md +163 -0
  50. package/doc/ai/zh-CN/README.md +56 -0
  51. package/doc/ai/zh-CN/build-recipe.md +132 -0
  52. package/doc/ai/zh-CN/cli.md +127 -0
  53. package/doc/ai/zh-CN/contributing.md +173 -0
  54. package/doc/ai/zh-CN/extensions.md +139 -0
  55. package/doc/ai/zh-CN/language-support.md +147 -0
  56. package/doc/ai/zh-CN/troubleshooting.md +150 -0
  57. package/doc/gui-scripts.md +350 -0
  58. package/doc/gui.md +646 -0
  59. package/doc/icon.md +265 -0
  60. package/doc/implemented.md +373 -0
  61. package/doc/node-implemented.md +588 -0
  62. package/doc/node-unimplemented.md +167 -0
  63. package/doc/post/announce.md +43 -0
  64. package/doc/requirements.md +145 -0
  65. package/doc/unimplemented.md +286 -0
  66. package/doc/xbintsc.config.schema.json +67 -0
  67. package/doc/zh-CN/DESIGN.md +104 -0
  68. package/doc/zh-CN/gui-scripts.md +329 -0
  69. package/doc/zh-CN/gui.md +588 -0
  70. package/doc/zh-CN/icon.md +241 -0
  71. package/doc/zh-CN/implemented.md +365 -0
  72. package/doc/zh-CN/node-implemented.md +533 -0
  73. package/doc/zh-CN/node-unimplemented.md +141 -0
  74. package/doc/zh-CN/plan-require-node-modules.md +284 -0
  75. package/doc/zh-CN/post/announce.md +47 -0
  76. package/doc/zh-CN/requirements.md +134 -0
  77. package/doc/zh-CN/unimplemented.md +247 -0
  78. package/llms.txt +45 -0
  79. package/package.json +6 -2
  80. package/runtime/ext_gui/dom_api_proto.cpp +5 -0
  81. package/runtime/ext_gui/gui.cpp +3 -1
  82. package/runtime/ext_gui/renderer.cpp +13 -11
  83. package/runtime/ext_gui/renderer_image.cpp +12 -8
  84. package/runtime/ext_gui/renderer_shaders.h +131 -4
  85. package/runtime/ext_gui/renderer_shaders_data.h +1809 -0
  86. package/runtime/ext_gui/renderer_text.cpp +12 -8
  87. package/runtime/ext_gui/shaders.hlsl +98 -0
  88. package/runtime/ext_gui/spirv/fill.frag +19 -0
  89. package/runtime/ext_gui/spirv/fill.vert +42 -0
  90. package/runtime/ext_gui/spirv/image.frag +16 -0
  91. package/runtime/ext_gui/spirv/quad.vert +30 -0
  92. package/runtime/ext_gui/spirv/text.frag +16 -0
  93. package/runtime/ext_gui/window.cpp +1 -0
  94. package/runtime/ext_node/buffer/parts/prototype.inc +1 -0
  95. package/runtime/ext_node/dgram/dgram.c +1 -0
  96. package/runtime/ext_node/events/events.c +1 -0
  97. package/runtime/ext_node/fs/fs_ops.c +10 -25
  98. package/runtime/ext_node/fs/glob.c +13 -30
  99. package/runtime/ext_node/fs/promises.c +1 -0
  100. package/runtime/ext_node/http/parts/prototypes.inc +5 -0
  101. package/runtime/ext_node/net/parts/prototypes.inc +2 -0
  102. package/runtime/ext_node/process/process.c +9 -7
  103. package/runtime/ext_node/stream/stream.c +1 -0
  104. package/runtime/ext_node/util/util.c +6 -6
  105. package/runtime/rt.h +10 -0
  106. package/runtime/rt_internal.h +63 -2
  107. package/runtime/xt_alloc.c +442 -5
  108. package/runtime/xt_generator.c +95 -1
  109. package/runtime/xt_loop.c +35 -1
  110. package/runtime/xt_promise.c +46 -0
  111. package/runtime/xt_stdlib2/error.inc +1 -0
  112. package/runtime/xt_stdlib2/regexp-match.inc +8 -7
  113. package/runtime/xt_symbol.c +2 -0
  114. package/runtime/xt_typed_array/construction.inc +142 -0
  115. package/runtime/xt_typed_array/elements.inc +92 -0
  116. package/runtime/xt_typed_array/methods.inc +329 -0
  117. package/runtime/xt_typed_array.c +6 -548
  118. package/runtime/xt_values/number-format.inc +26 -0
  119. package/scripts/build-gui-shaders.mjs +204 -0
  120. package/scripts/build-gui.ts +35 -0
  121. package/scripts/check-file-length.ts +89 -0
  122. package/src/cli/hints.ts +194 -0
  123. package/src/cli/main.ts +82 -9
  124. package/src/codegen/generator/module.ts +10 -0
  125. package/src/codegen/generator/state.ts +4 -0
  126. package/src/codegen/generator/tables.ts +60 -14
  127. package/src/diagnostics/source-text.ts +78 -0
  128. package/src/driver/bundler/graph.ts +2 -1
  129. package/src/driver/compiler.ts +3 -2
  130. package/src/lexer/scanner/strings.ts +16 -3
@@ -45,14 +45,23 @@ typedef struct xt_generator {
45
45
  int done;
46
46
  int threw;
47
47
  int booted;
48
+ /* Private stack bounds, registered as an active stack region while the
49
+ * generator runs and scanned conservatively while it is suspended. */
50
+ char *stack;
51
+ size_t stack_size;
52
+ int stack_pushed;
48
53
  void *saved_try_top;
49
54
  #ifdef _WIN32
50
55
  void *fiber;
51
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;
52
62
  #else
53
63
  ucontext_t ctx;
54
64
  ucontext_t caller;
55
- char *stack;
56
65
  #endif
57
66
  } xt_generator;
58
67
 
@@ -93,6 +102,27 @@ static void xt_generator_suspend(xt_generator *gen) {
93
102
 
94
103
  /* Runs on the generator's private stack. */
95
104
  static void xt_generator_run(xt_generator *gen) {
105
+ #ifdef _WIN32
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
+ }
96
126
  void *boundary = xt_try_enter();
97
127
  if (xt_try_setjmp(boundary) == 0) {
98
128
  xt_function *function = (xt_function *)XT_GET_PTR(gen->function);
@@ -127,6 +157,7 @@ static void xt_generator_boot(xt_generator *gen) {
127
157
  size_t size = 1 << 20;
128
158
  gen->stack = (char *)malloc(size);
129
159
  if (!gen->stack) abort();
160
+ gen->stack_size = size;
130
161
  if (getcontext(&gen->ctx) != 0) abort();
131
162
  gen->ctx.uc_stack.ss_sp = gen->stack;
132
163
  gen->ctx.uc_stack.ss_size = size;
@@ -146,6 +177,9 @@ static void xt_generator_dispose(xt_generator *gen) {
146
177
  gen->fiber = NULL;
147
178
  }
148
179
  gen->caller_fiber = NULL;
180
+ /* `DeleteFiber` releases the stack: never scan it again. */
181
+ gen->stack = NULL;
182
+ gen->stack_size = 0;
149
183
  #else
150
184
  if (gen->stack) {
151
185
  free(gen->stack);
@@ -158,9 +192,20 @@ static void xt_generator_resume(xt_generator *gen) {
158
192
  if (!gen->booted) xt_generator_boot(gen);
159
193
  xt_generator *previous = g_active_generator;
160
194
  g_active_generator = gen;
195
+ /* Record where the current (calling) stack suspends before switching away.
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
+ }
161
204
  void *caller_try = xt_try_mark();
162
205
  xt_try_restore(gen->saved_try_top);
163
206
  #ifdef _WIN32
207
+ _setjmp(gen->caller_regs);
208
+ gen->has_caller_regs = 1;
164
209
  gen->caller_fiber = GetCurrentFiber();
165
210
  SwitchToFiber(gen->fiber);
166
211
  #else
@@ -168,15 +213,63 @@ static void xt_generator_resume(xt_generator *gen) {
168
213
  #endif
169
214
  gen->saved_try_top = xt_try_mark();
170
215
  xt_try_restore(caller_try);
216
+ if (gen->stack_pushed) {
217
+ xt_gc_pop_stack();
218
+ gen->stack_pushed = 0;
219
+ }
171
220
  if (gen->done) xt_generator_dispose(gen);
172
221
  g_active_generator = previous;
173
222
  }
174
223
 
224
+ /* The active generator is only reachable through this static pointer, and its
225
+ * private stack holds values that are invisible to a scan of the main stack. */
226
+ static void xt_generator_gc_scan_roots(void) {
227
+ if (!g_active_generator) return;
228
+ xt_gc_mark_header((xt_header *)g_active_generator);
229
+ }
230
+
231
+ static void xt_generator_gc_register(void) {
232
+ static int registered = 0;
233
+ if (registered) return;
234
+ registered = 1;
235
+ xt_gc_register_root_provider(xt_generator_gc_scan_roots);
236
+ }
237
+
238
+ void xt_generator_gc_trace(xt_header *header) {
239
+ xt_generator *gen = (xt_generator *)header;
240
+ size_t payload = header->size > sizeof(xt_header) ? header->size - sizeof(xt_header) : 0;
241
+ xt_gc_scan_region((const char *)header + sizeof(xt_header), payload);
242
+ if (gen->args && gen->argc > 0) {
243
+ xt_gc_scan_region(gen->args, sizeof(xt_value) * (size_t)gen->argc);
244
+ }
245
+ if (gen->stack && gen->stack_size) xt_gc_scan_region(gen->stack, gen->stack_size);
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
252
+ }
253
+
254
+ void xt_generator_gc_release(xt_header *header) {
255
+ xt_generator *gen = (xt_generator *)header;
256
+ free(gen->args);
257
+ #ifdef _WIN32
258
+ if (gen->fiber) DeleteFiber(gen->fiber);
259
+ #else
260
+ free(gen->stack);
261
+ #endif
262
+ }
263
+
175
264
  xt_value xt_generator_new(xt_value function, xt_value thisValue, int32_t argc, xt_value *argv) {
176
265
  xt_generator *gen = (xt_generator *)xt_alloc(sizeof(xt_generator), XT_OBJECT_KIND_GENERATOR);
177
266
  gen->function = function;
178
267
  gen->this_value = thisValue;
179
268
  gen->argc = argc;
269
+ gen->args = NULL;
270
+ gen->stack = NULL;
271
+ gen->stack_size = 0;
272
+ gen->stack_pushed = 0;
180
273
  gen->result = XT_UNDEFINED;
181
274
  gen->sent = XT_UNDEFINED;
182
275
  gen->iter_value = XT_UNDEFINED;
@@ -185,6 +278,7 @@ xt_value xt_generator_new(xt_value function, xt_value thisValue, int32_t argc, x
185
278
  if (!gen->args) abort();
186
279
  memcpy(gen->args, argv, sizeof(xt_value) * (size_t)argc);
187
280
  }
281
+ xt_generator_gc_register();
188
282
  return XT_FROM_PTR(XT_TAG_OBJECT, gen);
189
283
  }
190
284
 
package/runtime/xt_loop.c CHANGED
@@ -130,14 +130,22 @@ static int64_t xt_timer_next_due(void) {
130
130
  static void xt_timer_compact(void) {
131
131
  uint32_t write = 0;
132
132
  for (uint32_t read = 0; read < xt_timer_count; read++) {
133
- if (!xt_timers[read].active) continue;
133
+ if (!xt_timers[read].active) {
134
+ free(xt_timers[read].args);
135
+ xt_timers[read].args = NULL;
136
+ xt_timers[read].argc = 0;
137
+ continue;
138
+ }
134
139
  if (write != read) xt_timers[write] = xt_timers[read];
135
140
  write++;
136
141
  }
137
142
  xt_timer_count = write;
138
143
  }
139
144
 
145
+ static void xt_loop_gc_register(void);
146
+
140
147
  static uint64_t xt_timer_add(xt_value callback, int64_t delay_ms, int64_t interval_ms, int32_t argc, xt_value *argv) {
148
+ xt_loop_gc_register();
141
149
  if (xt_timer_count >= xt_timer_capacity) {
142
150
  xt_timer_capacity = xt_timer_capacity == 0 ? 16 : xt_timer_capacity * 2;
143
151
  xt_timer *grown = (xt_timer *)realloc(xt_timers, sizeof(xt_timer) * xt_timer_capacity);
@@ -170,6 +178,31 @@ static int xt_timer_cancel(uint64_t id) {
170
178
  return 0;
171
179
  }
172
180
 
181
+ /* Watchers and pending timers hold values outside the value graph: the timer
182
+ * callbacks/arguments live in the flat array, and extension watchers keep an
183
+ * `xt_value *` box in their opaque userdata (the `xt_loop_add` convention). */
184
+ static void xt_loop_gc_scan_roots(void) {
185
+ for (int i = 0; i < xt_loop_watcher_count; i++) {
186
+ if (!xt_loop_watchers[i].active) continue;
187
+ xt_value *boxed = (xt_value *)xt_loop_watchers[i].userdata;
188
+ if (boxed) xt_gc_mark_value(*boxed);
189
+ }
190
+ for (uint32_t i = 0; i < xt_timer_count; i++) {
191
+ if (!xt_timers[i].active) continue;
192
+ xt_gc_mark_value(xt_timers[i].callback);
193
+ if (xt_timers[i].args && xt_timers[i].argc > 0) {
194
+ xt_gc_scan_region(xt_timers[i].args, sizeof(xt_value) * (size_t)xt_timers[i].argc);
195
+ }
196
+ }
197
+ }
198
+
199
+ static void xt_loop_gc_register(void) {
200
+ static int registered = 0;
201
+ if (registered) return;
202
+ registered = 1;
203
+ xt_gc_register_root_provider(xt_loop_gc_scan_roots);
204
+ }
205
+
173
206
  /* Invoke every timer whose deadline has passed. Timers scheduled by a callback
174
207
  * (or by a microtask) are left for the next reactor iteration, so a recursive
175
208
  * `setTimeout(fn, 0)` cannot spin forever inside a single call. */
@@ -232,6 +265,7 @@ xt_value xt_clear_interval(int32_t argc, xt_value *argv) {
232
265
 
233
266
  int xt_loop_add(int fd, int events, xt_io_handler handler, void *userdata) {
234
267
  if (fd < 0 || !handler) return -1;
268
+ xt_loop_gc_register();
235
269
  xt_loop_watcher *existing = xt_loop_find(fd);
236
270
  if (existing) {
237
271
  existing->events = events;
@@ -63,6 +63,8 @@ static xt_value xt_promise_value(xt_promise *promise) {
63
63
  return XT_FROM_PTR(XT_TAG_OBJECT, promise);
64
64
  }
65
65
 
66
+ static void xt_promise_gc_register(void);
67
+
66
68
  static xt_promise *xt_promise_new(void) {
67
69
  xt_promise *promise = (xt_promise *)xt_alloc(sizeof(xt_promise), XT_OBJECT_KIND_PROMISE);
68
70
  promise->state = XT_PROMISE_PENDING;
@@ -70,9 +72,52 @@ static xt_promise *xt_promise_new(void) {
70
72
  promise->reactions = NULL;
71
73
  promise->reaction_count = 0;
72
74
  promise->reaction_capacity = 0;
75
+ xt_promise_gc_register();
73
76
  return promise;
74
77
  }
75
78
 
79
+ /* Reactions are plain `malloc` blocks referenced from promises and from the
80
+ * microtask queue, so neither can be found by scanning the heap: the queue is
81
+ * a root provider and a promise traces the reactions it still owns. */
82
+ static void xt_promise_gc_scan_roots(void) {
83
+ for (uint32_t i = xt_microtask_head; i < xt_microtask_count; i++) {
84
+ xt_reaction *reaction = xt_microtasks[i];
85
+ if (!reaction) continue;
86
+ xt_gc_mark_header((xt_header *)reaction->promise);
87
+ xt_gc_mark_value(reaction->on_fulfilled);
88
+ xt_gc_mark_value(reaction->on_rejected);
89
+ xt_gc_mark_header((xt_header *)reaction->result);
90
+ }
91
+ }
92
+
93
+ static void xt_promise_gc_register(void) {
94
+ static int registered = 0;
95
+ if (registered) return;
96
+ registered = 1;
97
+ xt_gc_register_root_provider(xt_promise_gc_scan_roots);
98
+ }
99
+
100
+ void xt_promise_gc_trace(xt_header *header) {
101
+ xt_promise *promise = (xt_promise *)header;
102
+ xt_gc_mark_value(promise->value);
103
+ for (uint32_t i = 0; i < promise->reaction_count; i++) {
104
+ xt_reaction *reaction = promise->reactions[i];
105
+ if (!reaction) continue;
106
+ xt_gc_mark_header((xt_header *)reaction->promise);
107
+ xt_gc_mark_value(reaction->on_fulfilled);
108
+ xt_gc_mark_value(reaction->on_rejected);
109
+ xt_gc_mark_header((xt_header *)reaction->result);
110
+ }
111
+ }
112
+
113
+ void xt_promise_gc_release(xt_header *header) {
114
+ xt_promise *promise = (xt_promise *)header;
115
+ /* Pending reactions are owned solely by the promise; queued ones live in
116
+ * `xt_microtasks` and are freed as they run. */
117
+ for (uint32_t i = 0; i < promise->reaction_count; i++) free(promise->reactions[i]);
118
+ free(promise->reactions);
119
+ }
120
+
76
121
  static void xt_promise_settle(xt_promise *promise, int state, xt_value value);
77
122
 
78
123
  static void xt_promise_resolve_into(xt_promise *result, xt_value value) {
@@ -122,6 +167,7 @@ static xt_value xt_promise_run_reactions(void) {
122
167
  } else {
123
168
  xt_promise_settle(reaction->result, XT_PROMISE_REJECTED, promise->value);
124
169
  }
170
+ free(reaction);
125
171
  }
126
172
  xt_microtask_head = 0;
127
173
  xt_microtask_count = 0;
@@ -19,6 +19,7 @@ static int xt_error_is_family(const char *name) {
19
19
  static xt_value xt_error_root_proto = XT_UNDEFINED;
20
20
 
21
21
  static xt_value xt_error_proto_for(const char *name) {
22
+ xt_gc_add_root(&xt_error_root_proto);
22
23
  if (!XT_IS_OBJECT(xt_error_root_proto)) {
23
24
  xt_error_root_proto = xt_object_new();
24
25
  xt_object_set(xt_error_root_proto, xt_string_from_cstr("name"),
@@ -326,8 +326,10 @@ xt_value xt_regexp_replace(xt_value value, xt_value regexpValue, xt_value replac
326
326
  int32_t end = (int32_t)matches[0].rm_eo;
327
327
  xt_regexp_append(&out, &length, &capacity, text->data + cursor, (size_t)(start - cursor));
328
328
  if (useFunction) {
329
- xt_value *args = (xt_value *)malloc(sizeof(xt_value) * (size_t)(jsTotal + 3));
330
- args[0] = xt_string_new(text->data + start, (int64_t)(end - start));
329
+ /* Accumulate in an `xt_array` so the earlier match/group strings stay
330
+ reachable while later groups (and the callback itself) allocate. */
331
+ xt_value argArray = xt_array_new(0, NULL);
332
+ xt_array_push(argArray, xt_string_new(text->data + start, (int64_t)(end - start)));
331
333
  for (int g = 1; g <= jsTotal; g++) {
332
334
  xt_value groupValue = XT_UNDEFINED;
333
335
  for (int i = 0; i < groupCount; i++) {
@@ -337,12 +339,11 @@ xt_value xt_regexp_replace(xt_value value, xt_value regexpValue, xt_value replac
337
339
  }
338
340
  break;
339
341
  }
340
- args[g] = groupValue;
342
+ xt_array_push(argArray, groupValue);
341
343
  }
342
- args[jsTotal + 1] = xt_number((double)start);
343
- args[jsTotal + 2] = xt_to_string(value);
344
- xt_value produced = xt_closure_call(replacementValue, jsTotal + 3, args);
345
- free(args);
344
+ xt_array_push(argArray, xt_number((double)start));
345
+ xt_array_push(argArray, xt_to_string(value));
346
+ xt_value produced = xt_closure_call(replacementValue, jsTotal + 3, xt_as_array(argArray)->items);
346
347
  xt_string *producedText = xt_as_string(xt_to_string(produced));
347
348
  if (producedText) xt_regexp_append(&out, &length, &capacity, producedText->data, producedText->length);
348
349
  } else {
@@ -57,6 +57,7 @@ static int g_well_known_ready = 0;
57
57
  static void xt_symbol_init_well_known(void) {
58
58
  if (g_well_known_ready) return;
59
59
  g_well_known_ready = 1;
60
+ xt_gc_add_root_range(g_well_known, XT_WELL_KNOWN_COUNT);
60
61
  for (int i = 0; i < XT_WELL_KNOWN_COUNT; i++) {
61
62
  char buffer[64];
62
63
  snprintf(buffer, sizeof(buffer), "Symbol.%s", XT_WELL_KNOWN_NAMES[i]);
@@ -116,6 +117,7 @@ xt_value xt_symbol(int32_t argc, xt_value *argv) {
116
117
  static xt_value g_symbol_registry = XT_UNDEFINED;
117
118
 
118
119
  xt_value xt_symbol_for(xt_value key) {
120
+ xt_gc_add_root(&g_symbol_registry);
119
121
  xt_value keyString = xt_to_string(key);
120
122
  if (g_symbol_registry == XT_UNDEFINED) g_symbol_registry = xt_object_new();
121
123
  xt_value existing = xt_object_get(g_symbol_registry, keyString);
@@ -0,0 +1,142 @@
1
+ /* xbintsc runtime - typed array constructors, prototypes and statics.
2
+ * Included by `xt_typed_array.c` as part of a single translation unit. */
3
+
4
+ /* -- construction --------------------------------------------------------- */
5
+
6
+ static void xt_ta_define(xt_value proto, const char *name, void *fn) {
7
+ xt_object_set(proto, xt_string_from_cstr(name), xt_closure_new(fn, 0, NULL));
8
+ }
9
+
10
+ static xt_value xt_ta_proto(int kind) {
11
+ static int registered = 0;
12
+ if (!registered) {
13
+ registered = 1;
14
+ xt_gc_add_root_range(xt_ta_protos, XT_TA_KIND_COUNT);
15
+ }
16
+ if (kind < 0 || kind >= XT_TA_KIND_COUNT) return XT_UNDEFINED;
17
+ if (xt_ta_protos[kind]) return xt_ta_protos[kind];
18
+ xt_value proto = xt_object_new();
19
+ xt_ta_protos[kind] = proto;
20
+ xt_object_set(proto, xt_string_from_cstr("BYTES_PER_ELEMENT"), xt_number((double)xt_ta_bytes[kind]));
21
+ xt_ta_define(proto, "fill", (void *)xt_ta_method_fill);
22
+ xt_ta_define(proto, "set", (void *)xt_ta_method_set);
23
+ xt_ta_define(proto, "slice", (void *)xt_ta_method_slice);
24
+ xt_ta_define(proto, "subarray", (void *)xt_ta_method_slice);
25
+ xt_ta_define(proto, "join", (void *)xt_ta_method_join);
26
+ xt_ta_define(proto, "toString", (void *)xt_ta_method_to_string);
27
+ xt_ta_define(proto, "toLocaleString", (void *)xt_ta_method_to_string);
28
+ xt_ta_define(proto, "indexOf", (void *)xt_ta_method_index_of);
29
+ xt_ta_define(proto, "lastIndexOf", (void *)xt_ta_method_last_index_of);
30
+ xt_ta_define(proto, "includes", (void *)xt_ta_method_includes);
31
+ xt_ta_define(proto, "forEach", (void *)xt_ta_method_for_each);
32
+ xt_ta_define(proto, "map", (void *)xt_ta_method_map);
33
+ xt_ta_define(proto, "filter", (void *)xt_ta_method_filter);
34
+ xt_ta_define(proto, "every", (void *)xt_ta_method_every);
35
+ xt_ta_define(proto, "some", (void *)xt_ta_method_some);
36
+ xt_ta_define(proto, "find", (void *)xt_ta_method_find);
37
+ xt_ta_define(proto, "findIndex", (void *)xt_ta_method_find_index);
38
+ xt_ta_define(proto, "reduce", (void *)xt_ta_method_reduce);
39
+ xt_ta_define(proto, "reverse", (void *)xt_ta_method_reverse);
40
+ xt_ta_define(proto, "sort", (void *)xt_ta_method_sort);
41
+ xt_ta_define(proto, "copyWithin", (void *)xt_ta_method_copy_within);
42
+ xt_ta_define(proto, "at", (void *)xt_ta_method_at);
43
+ xt_ta_define(proto, "keys", (void *)xt_ta_method_keys);
44
+ xt_ta_define(proto, "values", (void *)xt_ta_method_values);
45
+ xt_ta_define(proto, "entries", (void *)xt_ta_method_entries);
46
+ return proto;
47
+ }
48
+
49
+ static xt_value xt_ta_new(int kind, uint32_t length) {
50
+ xt_value array = xt_object_new_with_proto(xt_ta_proto(kind));
51
+ xt_object_set(array, xt_string_from_cstr("length"), xt_number((double)length));
52
+ xt_object_set(array, xt_string_from_cstr("byteLength"), xt_number((double)length * xt_ta_bytes[kind]));
53
+ xt_object_set(array, xt_string_from_cstr("byteOffset"), xt_number(0));
54
+ for (uint32_t i = 0; i < length; i++) xt_ta_store(array, kind, i, 0);
55
+ return array;
56
+ }
57
+
58
+ /* Length of any value a typed array can be built from, or 0 when it is not
59
+ * array-like (strings/iterables are intentionally not treated as element
60
+ * sources yet). */
61
+ static int xt_ta_length_of(xt_value value, uint32_t *out) {
62
+ if (XT_IS_ARRAY(value)) {
63
+ *out = (uint32_t)xt_to_number(xt_array_length(value));
64
+ return 1;
65
+ }
66
+ if (XT_IS_STRING(value)) {
67
+ *out = xt_as_string(value)->length;
68
+ return 1;
69
+ }
70
+ if (xt_typed_array_kind_of(value) >= 0) {
71
+ *out = xt_ta_length(value);
72
+ return 1;
73
+ }
74
+ if (XT_IS_OBJECT(value)) {
75
+ xt_value length = xt_object_get_cstr(value, "length");
76
+ if (length != XT_UNDEFINED) {
77
+ double size = xt_to_number(length);
78
+ *out = size > 0 ? (uint32_t)size : 0;
79
+ return 1;
80
+ }
81
+ }
82
+ return 0;
83
+ }
84
+
85
+ static xt_value xt_ta_from_value(int kind, xt_value value, xt_value mapFn, xt_value thisArg, int haveMap) {
86
+ uint32_t length = 0;
87
+ if (!xt_ta_length_of(value, &length)) length = 0;
88
+ xt_value array = xt_ta_new(kind, length);
89
+ for (uint32_t i = 0; i < length; i++) {
90
+ xt_value item = xt_get(value, xt_number((double)i));
91
+ if (haveMap) {
92
+ xt_value args[3] = {item, xt_number((double)i), array};
93
+ item = xt_call_with_this(mapFn, thisArg, 3, args);
94
+ }
95
+ xt_ta_store(array, kind, i, xt_to_number(item));
96
+ }
97
+ return array;
98
+ }
99
+
100
+ static xt_value xt_ta_ctor_impl(int kind, int32_t argc, xt_value *argv) {
101
+ if (argc == 0) return xt_ta_new(kind, 0);
102
+ xt_value argument = argv[0];
103
+ /* Objects (arrays / typed arrays / array-likes / iterables) are copied
104
+ * element-wise; every other argument is a length via `ToIndex`. */
105
+ if (XT_IS_ARRAY(argument) || XT_IS_OBJECT(argument)) {
106
+ return xt_ta_from_value(kind, argument, XT_UNDEFINED, XT_UNDEFINED, 0);
107
+ }
108
+ double size = xt_to_number(argument);
109
+ return xt_ta_new(kind, size > 0 ? (uint32_t)trunc(size) : 0);
110
+ }
111
+
112
+ static xt_value xt_ta_static_impl(int kind, xt_value name, int32_t argc, xt_value *argv) {
113
+ const char *method = xt_string_data(name);
114
+ if (!method) return XT_UNDEFINED;
115
+ if (strcmp(method, "from") == 0) {
116
+ int haveMap = argc > 1 && XT_IS_FUNCTION(argv[1]);
117
+ return xt_ta_from_value(kind, xt_arg_at(argc, argv, 0), xt_arg_at(argc, argv, 1), xt_arg_at(argc, argv, 2), haveMap);
118
+ }
119
+ if (strcmp(method, "of") == 0) {
120
+ xt_value array = xt_ta_new(kind, (uint32_t)argc);
121
+ for (int32_t i = 0; i < argc; i++) xt_ta_store(array, kind, (uint32_t)i, xt_to_number(argv[i]));
122
+ return array;
123
+ }
124
+ return XT_UNDEFINED;
125
+ }
126
+
127
+ #define XT_TA_CTOR(suffix, kind) \
128
+ xt_value xt_ta_##suffix##_ctor(int32_t argc, xt_value *argv) { return xt_ta_ctor_impl(kind, argc, argv); } \
129
+ xt_value xt_ta_##suffix##_static(xt_value name, int32_t argc, xt_value *argv) { \
130
+ return xt_ta_static_impl(kind, name, argc, argv); \
131
+ }
132
+
133
+ XT_TA_CTOR(u8, XT_TA_U8)
134
+ XT_TA_CTOR(i8, XT_TA_I8)
135
+ XT_TA_CTOR(u8c, XT_TA_U8C)
136
+ XT_TA_CTOR(u16, XT_TA_U16)
137
+ XT_TA_CTOR(i16, XT_TA_I16)
138
+ XT_TA_CTOR(u32, XT_TA_U32)
139
+ XT_TA_CTOR(i32, XT_TA_I32)
140
+ XT_TA_CTOR(f32, XT_TA_F32)
141
+ XT_TA_CTOR(f64, XT_TA_F64)
142
+
@@ -0,0 +1,92 @@
1
+ /* xbintsc runtime - typed array element conversion, storage and helpers.
2
+ * Included by `xt_typed_array.c` as part of a single translation unit. */
3
+
4
+ /* -- element conversion --------------------------------------------------- */
5
+
6
+ /* ECMAScript `ToUintN` / `ToIntN`: truncate, then wrap modulo 2^N. */
7
+ static double xt_ta_wrap(double value, double modulus) {
8
+ if (isnan(value) || isinf(value)) return 0;
9
+ double result = fmod(trunc(value), modulus);
10
+ if (result < 0) result += modulus;
11
+ return result;
12
+ }
13
+
14
+ double xt_typed_array_coerce(int kind, double value) {
15
+ switch (kind) {
16
+ case XT_TA_U8:
17
+ return xt_ta_wrap(value, 256.0);
18
+ case XT_TA_I8: {
19
+ double wrapped = xt_ta_wrap(value, 256.0);
20
+ return wrapped >= 128.0 ? wrapped - 256.0 : wrapped;
21
+ }
22
+ case XT_TA_U8C:
23
+ if (isnan(value) || value <= 0) return 0;
24
+ if (value >= 255) return 255;
25
+ return rint(value);
26
+ case XT_TA_U16:
27
+ return xt_ta_wrap(value, 65536.0);
28
+ case XT_TA_I16: {
29
+ double wrapped = xt_ta_wrap(value, 65536.0);
30
+ return wrapped >= 32768.0 ? wrapped - 65536.0 : wrapped;
31
+ }
32
+ case XT_TA_U32:
33
+ return xt_ta_wrap(value, 4294967296.0);
34
+ case XT_TA_I32: {
35
+ double wrapped = xt_ta_wrap(value, 4294967296.0);
36
+ return wrapped >= 2147483648.0 ? wrapped - 4294967296.0 : wrapped;
37
+ }
38
+ case XT_TA_F32:
39
+ return (double)(float)value;
40
+ case XT_TA_F64:
41
+ default:
42
+ return value;
43
+ }
44
+ }
45
+
46
+ int xt_typed_array_kind_of(xt_value value) {
47
+ if (!XT_IS_OBJECT(value)) return -1;
48
+ xt_object *object = (xt_object *)XT_GET_PTR(value);
49
+ if (object->header.kind != XT_OBJECT_KIND_OBJECT) return -1;
50
+ for (int kind = 0; kind < XT_TA_KIND_COUNT; kind++) {
51
+ if (xt_ta_protos[kind] && object->prototype == xt_ta_protos[kind]) return kind;
52
+ }
53
+ return -1;
54
+ }
55
+
56
+ /* -- element storage ------------------------------------------------------ */
57
+
58
+ static uint32_t xt_ta_length(xt_value array) {
59
+ double length = xt_to_number(xt_object_get_cstr(array, "length"));
60
+ if (!(length > 0)) return 0;
61
+ return (uint32_t)length;
62
+ }
63
+
64
+ static xt_value xt_ta_element(xt_value array, uint32_t index) {
65
+ char key[24];
66
+ snprintf(key, sizeof(key), "%u", index);
67
+ return xt_object_get_cstr(array, key);
68
+ }
69
+
70
+ static void xt_ta_store(xt_value array, int kind, uint32_t index, double value) {
71
+ char key[24];
72
+ snprintf(key, sizeof(key), "%u", index);
73
+ xt_object_set(array, xt_string_from_cstr(key), xt_number(xt_typed_array_coerce(kind, value)));
74
+ }
75
+
76
+ /* -- helpers -------------------------------------------------------------- */
77
+
78
+ static int32_t xt_ta_relative(int32_t index, int32_t length) {
79
+ if (index < 0) index += length;
80
+ if (index < 0) index = 0;
81
+ if (index > length) index = length;
82
+ return index;
83
+ }
84
+
85
+ static xt_value xt_ta_append(xt_value array, int kind, xt_value value) {
86
+ uint32_t length = xt_ta_length(array);
87
+ xt_object_set(array, xt_string_from_cstr("length"), xt_number((double)(length + 1)));
88
+ xt_object_set(array, xt_string_from_cstr("byteLength"), xt_number((double)(length + 1) * xt_ta_bytes[kind]));
89
+ xt_ta_store(array, kind, length, xt_to_number(value));
90
+ return array;
91
+ }
92
+