xbintsc 0.3.79 → 0.3.87

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 (201) hide show
  1. package/README.md +13 -5
  2. package/README.zh-CN.md +13 -6
  3. package/dist/src/ast/common.d.ts +23 -0
  4. package/dist/src/cli/main.js +68 -24
  5. package/dist/src/cli/main.js.map +1 -1
  6. package/dist/src/codegen/generator/calls/invocation.js +5 -1
  7. package/dist/src/codegen/generator/calls/invocation.js.map +1 -1
  8. package/dist/src/codegen/generator/context.d.ts +50 -0
  9. package/dist/src/codegen/generator/context.js +124 -13
  10. package/dist/src/codegen/generator/context.js.map +1 -1
  11. package/dist/src/codegen/generator/debug.d.ts +33 -0
  12. package/dist/src/codegen/generator/debug.js +97 -0
  13. package/dist/src/codegen/generator/debug.js.map +1 -0
  14. package/dist/src/codegen/generator/expressions/assignment.js +9 -5
  15. package/dist/src/codegen/generator/expressions/assignment.js.map +1 -1
  16. package/dist/src/codegen/generator/expressions/operators.js +35 -9
  17. package/dist/src/codegen/generator/expressions/operators.js.map +1 -1
  18. package/dist/src/codegen/generator/expressions/primary.js +5 -2
  19. package/dist/src/codegen/generator/expressions/primary.js.map +1 -1
  20. package/dist/src/codegen/generator/generator.d.ts +3 -1
  21. package/dist/src/codegen/generator/generator.js +3 -1
  22. package/dist/src/codegen/generator/generator.js.map +1 -1
  23. package/dist/src/codegen/generator/module.d.ts +1 -0
  24. package/dist/src/codegen/generator/module.js +76 -6
  25. package/dist/src/codegen/generator/module.js.map +1 -1
  26. package/dist/src/codegen/generator/numeric.d.ts +26 -0
  27. package/dist/src/codegen/generator/numeric.js +127 -0
  28. package/dist/src/codegen/generator/numeric.js.map +1 -0
  29. package/dist/src/codegen/generator/state.d.ts +17 -0
  30. package/dist/src/codegen/generator/state.js +5 -0
  31. package/dist/src/codegen/generator/state.js.map +1 -1
  32. package/dist/src/codegen/generator/statements/control-flow.js +1 -1
  33. package/dist/src/codegen/generator/statements/control-flow.js.map +1 -1
  34. package/dist/src/codegen/generator/statements/dispatch.js +3 -0
  35. package/dist/src/codegen/generator/statements/dispatch.js.map +1 -1
  36. package/dist/src/codegen/generator/statements/variables.js +8 -2
  37. package/dist/src/codegen/generator/statements/variables.js.map +1 -1
  38. package/dist/src/codegen/generator/tables.d.ts +7 -0
  39. package/dist/src/codegen/generator/tables.js +25 -0
  40. package/dist/src/codegen/generator/tables.js.map +1 -1
  41. package/dist/src/driver/bundler/merge.js +41 -5
  42. package/dist/src/driver/bundler/merge.js.map +1 -1
  43. package/dist/src/driver/compiler.d.ts +22 -3
  44. package/dist/src/driver/compiler.js +49 -58
  45. package/dist/src/driver/compiler.js.map +1 -1
  46. package/dist/src/driver/fingerprint.d.ts +27 -0
  47. package/dist/src/driver/fingerprint.js +88 -0
  48. package/dist/src/driver/fingerprint.js.map +1 -0
  49. package/dist/src/driver/toolchain-provider.d.ts +9 -2
  50. package/dist/src/driver/toolchain-provider.js +15 -15
  51. package/dist/src/driver/toolchain-provider.js.map +1 -1
  52. package/dist/src/driver/toolchain.d.ts +58 -1
  53. package/dist/src/driver/toolchain.js +169 -22
  54. package/dist/src/driver/toolchain.js.map +1 -1
  55. package/dist/src/extensions/gui/index.js +10 -2
  56. package/dist/src/extensions/gui/index.js.map +1 -1
  57. package/dist/src/parser/expressions.js +20 -1
  58. package/dist/src/parser/expressions.js.map +1 -1
  59. package/dist/tests/cli/main.test.js +41 -1
  60. package/dist/tests/cli/main.test.js.map +1 -1
  61. package/dist/tests/codegen/expressions.test.js +14 -3
  62. package/dist/tests/codegen/expressions.test.js.map +1 -1
  63. package/dist/tests/codegen/llvm.test.js +23 -0
  64. package/dist/tests/codegen/llvm.test.js.map +1 -1
  65. package/dist/tests/driver/toolchain-provider.test.js +49 -2
  66. package/dist/tests/driver/toolchain-provider.test.js.map +1 -1
  67. package/dist/tests/driver/toolchain.test.js +62 -3
  68. package/dist/tests/driver/toolchain.test.js.map +1 -1
  69. package/dist/tests/e2e/child-process.test.d.ts +10 -0
  70. package/dist/tests/e2e/child-process.test.js +59 -0
  71. package/dist/tests/e2e/child-process.test.js.map +1 -0
  72. package/dist/tests/e2e/compile-objects-classes.test.js +47 -0
  73. package/dist/tests/e2e/compile-objects-classes.test.js.map +1 -1
  74. package/dist/tests/e2e/error-stack.test.d.ts +9 -0
  75. package/dist/tests/e2e/error-stack.test.js +94 -0
  76. package/dist/tests/e2e/error-stack.test.js.map +1 -0
  77. package/dist/tests/e2e/gui-example-svg.test.d.ts +21 -0
  78. package/dist/tests/e2e/gui-example-svg.test.js +105 -0
  79. package/dist/tests/e2e/gui-example-svg.test.js.map +1 -0
  80. package/dist/tests/e2e/gui-transform.test.d.ts +7 -0
  81. package/dist/tests/e2e/gui-transform.test.js +198 -0
  82. package/dist/tests/e2e/gui-transform.test.js.map +1 -0
  83. package/dist/tests/e2e/string-concat.test.d.ts +9 -0
  84. package/dist/tests/e2e/string-concat.test.js +102 -0
  85. package/dist/tests/e2e/string-concat.test.js.map +1 -0
  86. package/dist/tests/helpers.d.ts +9 -0
  87. package/dist/tests/helpers.js +26 -3
  88. package/dist/tests/helpers.js.map +1 -1
  89. package/dist/tests/parser/expressions.test.js +7 -0
  90. package/dist/tests/parser/expressions.test.js.map +1 -1
  91. package/dist/tests/version.test.d.ts +1 -0
  92. package/dist/tests/version.test.js +24 -0
  93. package/dist/tests/version.test.js.map +1 -0
  94. package/doc/DESIGN.md +20 -12
  95. package/doc/ai/cli.md +7 -0
  96. package/doc/ai/contributing.md +7 -1
  97. package/doc/ai/extensions.md +5 -1
  98. package/doc/ai/language-support.md +4 -4
  99. package/doc/ai/troubleshooting.md +39 -2
  100. package/doc/ai/zh-CN/cli.md +2 -0
  101. package/doc/ai/zh-CN/contributing.md +6 -2
  102. package/doc/ai/zh-CN/extensions.md +5 -1
  103. package/doc/ai/zh-CN/language-support.md +4 -4
  104. package/doc/ai/zh-CN/troubleshooting.md +35 -2
  105. package/doc/gui/animation.md +53 -2
  106. package/doc/gui/api.md +1 -1
  107. package/doc/gui/dom.md +6 -1
  108. package/doc/gui/input.md +4 -1
  109. package/doc/gui/known-issues.md +16 -4
  110. package/doc/gui/layout.md +36 -0
  111. package/doc/gui/rendering.md +15 -0
  112. package/doc/gui.md +32 -3
  113. package/doc/implemented.md +4 -4
  114. package/doc/requirements.md +13 -4
  115. package/doc/unimplemented.md +11 -10
  116. package/doc/zh-CN/DESIGN.md +15 -9
  117. package/doc/zh-CN/gui/animation.md +49 -2
  118. package/doc/zh-CN/gui/api.md +1 -1
  119. package/doc/zh-CN/gui/dom.md +6 -1
  120. package/doc/zh-CN/gui/input.md +2 -0
  121. package/doc/zh-CN/gui/known-issues.md +12 -2
  122. package/doc/zh-CN/gui/layout.md +31 -0
  123. package/doc/zh-CN/gui/rendering.md +11 -0
  124. package/doc/zh-CN/gui.md +28 -3
  125. package/doc/zh-CN/implemented.md +4 -4
  126. package/doc/zh-CN/requirements.md +12 -3
  127. package/doc/zh-CN/unimplemented.md +10 -9
  128. package/package.json +3 -1
  129. package/runtime/ext_gui/document.cpp +21 -3
  130. package/runtime/ext_gui/document.h +4 -1
  131. package/runtime/ext_gui/dom_api_node.cpp +6 -4
  132. package/runtime/ext_gui/layout.cpp +78 -1
  133. package/runtime/ext_gui/layout.h +21 -0
  134. package/runtime/ext_gui/paint.cpp +48 -15
  135. package/runtime/ext_gui/paint.h +8 -2
  136. package/runtime/ext_gui/renderer.cpp +4 -2
  137. package/runtime/ext_gui/renderer_image.cpp +2 -1
  138. package/runtime/ext_gui/renderer_internal.h +6 -1
  139. package/runtime/ext_gui/renderer_text.cpp +37 -15
  140. package/runtime/ext_gui/style.h +10 -0
  141. package/runtime/ext_gui/style_print.cpp +6 -0
  142. package/runtime/ext_gui/style_values.cpp +43 -0
  143. package/runtime/ext_gui/transform.cpp +412 -0
  144. package/runtime/ext_gui/transform.h +112 -0
  145. package/runtime/ext_gui/window.cpp +6 -4
  146. package/runtime/ext_node/buffer/parts/construction.inc +2 -2
  147. package/runtime/ext_node/buffer/parts/methods.inc +1 -1
  148. package/runtime/ext_node/buffer/parts/search.inc +1 -1
  149. package/runtime/ext_node/child_process/child_process.c +25 -0
  150. package/runtime/ext_node/fs/fd_ops.c +1 -1
  151. package/runtime/rt.h +17 -1
  152. package/runtime/rt_internal.h +47 -4
  153. package/runtime/xt_alloc.c +14 -2
  154. package/runtime/xt_bigint/conversions.inc +1 -1
  155. package/runtime/xt_builtins.c +136 -14
  156. package/runtime/xt_containers/access.inc +17 -16
  157. package/runtime/xt_containers/objects.inc +4 -3
  158. package/runtime/xt_io/exception.inc +171 -2
  159. package/runtime/xt_io/inspect.inc +15 -13
  160. package/runtime/xt_io.c +5 -0
  161. package/runtime/xt_stdlib/arrays.inc +2 -2
  162. package/runtime/xt_stdlib/dispatch.inc +2 -2
  163. package/runtime/xt_stdlib/strings.inc +33 -21
  164. package/runtime/xt_stdlib2/array-statics.inc +2 -1
  165. package/runtime/xt_stdlib2/arrays.inc +1 -1
  166. package/runtime/xt_stdlib2/error.inc +22 -6
  167. package/runtime/xt_stdlib2/json.inc +3 -2
  168. package/runtime/xt_stdlib2/number-string-statics.inc +3 -1
  169. package/runtime/xt_stdlib2/regexp-match.inc +48 -33
  170. package/runtime/xt_stdlib2/strings.inc +23 -14
  171. package/runtime/xt_symbol.c +1 -1
  172. package/runtime/xt_values/comparison.inc +2 -2
  173. package/runtime/xt_values/constructors.inc +10 -5
  174. package/runtime/xt_values/conversions.inc +4 -3
  175. package/runtime/xt_values/strings.inc +78 -4
  176. package/scripts/bench.ts +172 -0
  177. package/scripts/build-runtime.ts +2 -1
  178. package/scripts/check-version.ts +53 -0
  179. package/src/ast/common.ts +23 -0
  180. package/src/cli/main.ts +68 -26
  181. package/src/codegen/generator/calls/invocation.ts +5 -0
  182. package/src/codegen/generator/context.ts +129 -18
  183. package/src/codegen/generator/debug.ts +132 -0
  184. package/src/codegen/generator/expressions/assignment.ts +9 -5
  185. package/src/codegen/generator/expressions/operators.ts +47 -8
  186. package/src/codegen/generator/expressions/primary.ts +5 -2
  187. package/src/codegen/generator/generator.ts +18 -2
  188. package/src/codegen/generator/module.ts +78 -6
  189. package/src/codegen/generator/numeric.ts +158 -0
  190. package/src/codegen/generator/state.ts +22 -0
  191. package/src/codegen/generator/statements/control-flow.ts +1 -1
  192. package/src/codegen/generator/statements/dispatch.ts +3 -0
  193. package/src/codegen/generator/statements/variables.ts +8 -2
  194. package/src/codegen/generator/tables.ts +26 -0
  195. package/src/driver/bundler/merge.ts +39 -5
  196. package/src/driver/compiler.ts +68 -56
  197. package/src/driver/fingerprint.ts +85 -0
  198. package/src/driver/toolchain-provider.ts +17 -16
  199. package/src/driver/toolchain.ts +210 -21
  200. package/src/extensions/gui/index.ts +10 -2
  201. package/src/parser/expressions.ts +20 -1
@@ -76,7 +76,7 @@ static unsigned char *xt_fs_value_bytes(xt_value value, size_t *outLength) {
76
76
  *outLength = 0;
77
77
  return NULL;
78
78
  }
79
- if (length > 0) memcpy(bytes, string->data, length);
79
+ if (length > 0) memcpy(bytes, xt_string_bytes(string), length);
80
80
  *outLength = length;
81
81
  return bytes;
82
82
  }
package/runtime/rt.h CHANGED
@@ -92,7 +92,9 @@ xt_value xt_bool(int b);
92
92
  xt_value xt_number(double d);
93
93
  xt_value xt_string_new(const char *data, size_t len);
94
94
  xt_value xt_string_from_cstr(const char *data);
95
- /** Raw UTF-8 bytes of a string value (NULL for non-strings); not NUL-safe. */
95
+ /** Raw UTF-8 bytes of a string value (NULL for non-strings); not NUL-safe.
96
+ * Materialises a lazily concatenated string, so the returned pointer is stable
97
+ * until the string is collected. */
96
98
  const char *xt_string_data(xt_value value);
97
99
  int32_t xt_string_length_value(xt_value value);
98
100
 
@@ -119,6 +121,8 @@ xt_value xt_function_set_metadata(xt_value fn, xt_value name, int32_t arity);
119
121
  xt_value xt_this(void);
120
122
  xt_value xt_new(xt_value ctor, int32_t argc, xt_value *argv);
121
123
  xt_value xt_instance_of(xt_value value, xt_value ctor);
124
+ /** `value instanceof <built-in constructor>`; id from `BUILTIN_INSTANCEOF`. */
125
+ xt_value xt_instance_of_builtin(xt_value value, int32_t id);
122
126
  xt_value xt_function_set_prototype(xt_value fn, xt_value proto);
123
127
  xt_value xt_function_get_prototype(xt_value fn);
124
128
 
@@ -177,6 +181,8 @@ xt_value xt_object_values(xt_value obj);
177
181
  xt_value xt_object_entries(xt_value obj);
178
182
  xt_value xt_object_assign(int32_t argc, xt_value *argv);
179
183
  xt_value xt_object_spread(xt_value target, xt_value source);
184
+ /** Own enumerable properties of `source` except the keys in `argv` (object rest). */
185
+ xt_value xt_object_rest(xt_value source, int32_t argc, xt_value *argv);
180
186
 
181
187
  /* -- symbols -------------------------------------------------------------- */
182
188
  /** Implements `Symbol(description)`. */
@@ -340,6 +346,16 @@ void xt_throw(xt_value v);
340
346
  void *xt_try_enter(void);
341
347
  xt_value xt_try_exception(void *frame);
342
348
  void xt_try_leave(void *frame);
349
+ /**
350
+ * JavaScript-level shadow stack. Generated code pushes `(name, file, line)` on
351
+ * function entry and pops before returning; `Error` construction walks it to
352
+ * build `.stack`, and a fatal native signal dumps it. The strings are static
353
+ * module data, so the runtime never owns them.
354
+ */
355
+ void xt_stack_push(const char *name, const char *file, int32_t line);
356
+ void xt_stack_pop(void);
357
+ /** Set the innermost frame's line (called just before `new Error(...)`). */
358
+ void xt_stack_set_line(int32_t line);
343
359
  void xt_console_log(int32_t argc, xt_value *argv);
344
360
  void xt_console_info(int32_t argc, xt_value *argv);
345
361
  void xt_console_warn(int32_t argc, xt_value *argv);
@@ -21,8 +21,12 @@
21
21
  * code free of write barriers and makes the first implementation small and
22
22
  * predictable. Replacing it with a precise/conservative collector is an
23
23
  * isolated change behind `xt_alloc` plus root registration.
24
- * * Strings are UTF-8. JavaScript measures string length in UTF-16 code
25
- * units; `xt_string_length` currently reports code points (see README).
24
+ * * Strings are UTF-8. Concatenation is lazy: a `+` between two non-empty
25
+ * strings builds an immutable cons node (`flat == NULL`) and the bytes are
26
+ * materialised once, on first observation, by `xt_string_bytes`. This keeps
27
+ * `s += x` in a loop linear instead of copying the accumulated prefix every
28
+ * iteration. JavaScript measures string length in UTF-16 code units;
29
+ * `xt_string_length` currently reports code points (see README).
26
30
  * * Objects use a small linear property list. Fine for the language subset
27
31
  * xbintsc targets today; swap for a hash map when the profile demands it.
28
32
  */
@@ -64,11 +68,19 @@ typedef struct xt_header {
64
68
 
65
69
  #define XT_GC_MARK 0x01u
66
70
 
67
- typedef struct {
71
+ /* A string is either *flat* (`flat` points at `capacity` bytes, the last one
72
+ * NUL) or a *cons* node (`flat == NULL`, `left`/`right` name the operands).
73
+ * `length` is always the total byte length, so `.length`, comparisons and the
74
+ * GC never need to force a cons node. `xt_string_bytes` turns a cons node into
75
+ * a flat one in place; treat the result as immutable and never write through a
76
+ * pointer obtained from it. */
77
+ typedef struct xt_string {
68
78
  xt_header header;
69
79
  uint32_t length;
70
80
  uint32_t capacity;
71
- char *data;
81
+ char *flat;
82
+ struct xt_string *left;
83
+ struct xt_string *right;
72
84
  } xt_string;
73
85
 
74
86
  /* Sign-magnitude arbitrary-precision integer; see xt_bigint.c. */
@@ -99,6 +111,10 @@ typedef struct {
99
111
  uint8_t frozen;
100
112
  uint8_t is_array_base;
101
113
  uint16_t reserved2;
114
+ /* Captured `Error.stack` for XT_OBJECT_KIND_ERROR, XT_UNDEFINED otherwise.
115
+ * Deliberately not a property: `Object.keys`, `for...in` and JSON.stringify
116
+ * must not see it, while `error.stack` returns it through `xt_get`. */
117
+ xt_value error_stack;
102
118
  } xt_object;
103
119
 
104
120
  typedef struct {
@@ -191,6 +207,12 @@ xt_string *xt_string_alloc(size_t length);
191
207
  xt_string *xt_as_string(xt_value value);
192
208
  int32_t xt_string_length(xt_string *s);
193
209
  int xt_string_equals(xt_string *a, xt_string *b);
210
+ /* Contiguous UTF-8 bytes of a string, flattening a cons node in place on first
211
+ * use. The result is NUL-terminated at `s->length`; never write through it. */
212
+ const char *xt_string_bytes(xt_string *s);
213
+ /* Mutable bytes of a string just created with `xt_string_alloc` (never a cons
214
+ * node). Used by the constructors that fill a buffer before publishing it. */
215
+ char *xt_string_mutable(xt_string *s);
194
216
  int32_t xt_to_int32(xt_value v);
195
217
 
196
218
  /* ECMAScript number formatting (xt_values.c). */
@@ -284,6 +306,10 @@ typedef struct xt_try_frame {
284
306
  jmp_buf buf;
285
307
  struct xt_try_frame *prev;
286
308
  xt_value exception;
309
+ /* Shadow-stack depth when the protected region was entered; restored when
310
+ * the region is left (normally or through a throw) so frames from unwound
311
+ * calls do not leak into later stack traces. */
312
+ int32_t stack_depth;
287
313
  } xt_try_frame;
288
314
  /* Save a try frame with clang's `_setjmp` built-in.
289
315
  *
@@ -300,6 +326,23 @@ typedef struct xt_try_frame {
300
326
  void *xt_try_mark(void);
301
327
  void xt_try_restore(void *mark);
302
328
 
329
+ /* -- shadow call stack and diagnostics (xt_io/exception.inc) --------------- */
330
+ /* Generated code pushes a frame on entry and pops it before returning, so an
331
+ * Error can capture a JavaScript-level stack without any debug metadata. Frame
332
+ * strings live in the module's static data and are never freed. */
333
+ void xt_stack_push(const char *name, const char *file, int32_t line);
334
+ void xt_stack_pop(void);
335
+ int32_t xt_stack_depth(void);
336
+ void xt_stack_set_line(int32_t line);
337
+ /* Restore the depth saved by a try frame after unwinding inner calls. */
338
+ void xt_stack_trim(int32_t depth);
339
+ /* Format `Name: message` plus the current frames as an `Error.stack` string. */
340
+ xt_value xt_stack_capture(const char *name, const char *message);
341
+ /* The captured stack of an error object, or XT_UNDEFINED. */
342
+ xt_value xt_error_stack(xt_value error);
343
+ /* Best-effort stack dump on a fatal native signal / unhandled exception. */
344
+ void xt_install_crash_handler(void);
345
+
303
346
  /* -- collection / date / regexp representations --------------------------- */
304
347
  typedef struct {
305
348
  xt_header header;
@@ -210,6 +210,9 @@ void xt_gc_init(void) {
210
210
  g_gc_threshold_pinned = 1;
211
211
  }
212
212
  }
213
+ /* A crash is the one case where the JavaScript-level shadow stack is the
214
+ * most useful thing to print; install the handler once per process. */
215
+ xt_install_crash_handler();
213
216
  }
214
217
 
215
218
  void xt_gc_arm(void) { g_gc_armed = 1; }
@@ -328,7 +331,16 @@ static void xt_gc_process(xt_header *header) {
328
331
  const void *base = (const char *)header + sizeof(xt_header);
329
332
 
330
333
  switch (header->kind) {
331
- case XT_OBJECT_KIND_STRING:
334
+ case XT_OBJECT_KIND_STRING: {
335
+ /* A flat string holds bytes only. A cons node is an immutable rope whose
336
+ * children must stay alive until `xt_string_bytes` materialises it. */
337
+ xt_string *string = (xt_string *)header;
338
+ if (string->flat == NULL) {
339
+ if (string->left) xt_gc_gray_push(&string->left->header);
340
+ if (string->right) xt_gc_gray_push(&string->right->header);
341
+ }
342
+ return;
343
+ }
332
344
  case XT_OBJECT_KIND_BIGINT:
333
345
  case XT_OBJECT_KIND_DATE:
334
346
  /* Numbers/bytes only: no references to follow. */
@@ -392,7 +404,7 @@ static void xt_gc_process(xt_header *header) {
392
404
  static void xt_gc_release(xt_header *header) {
393
405
  switch (header->kind) {
394
406
  case XT_OBJECT_KIND_STRING:
395
- free(((xt_string *)header)->data);
407
+ free(((xt_string *)header)->flat);
396
408
  break;
397
409
  case XT_OBJECT_KIND_BIGINT:
398
410
  free(((xt_bigint *)header)->limbs);
@@ -41,7 +41,7 @@ static xt_value bi_from_xt_value(xt_value value) {
41
41
  if (XT_IS_NUMBER(value)) return xt_bigint_from_double(xt_to_double(value));
42
42
  if (XT_IS_STRING(value)) {
43
43
  xt_string *s = xt_as_string(value);
44
- return xt_bigint_from_string(s->data, s->length);
44
+ return xt_bigint_from_string(xt_string_bytes(s), s->length);
45
45
  }
46
46
  if (value == XT_TRUE) return xt_bigint_from_i64(1);
47
47
  if (value == XT_FALSE) return xt_bigint_from_i64(0);
@@ -62,9 +62,77 @@ xt_value xt_object_spread(xt_value target, xt_value source) {
62
62
  return xt_object_assign(2, args);
63
63
  }
64
64
 
65
+ /* True when `key` is one of the explicitly destructured properties. */
66
+ static int xt_object_rest_excludes(xt_value key, int32_t argc, xt_value *argv) {
67
+ for (int32_t i = 0; i < argc; i++) {
68
+ if (xt_property_key_equals(key, argv[i])) return 1;
69
+ }
70
+ return 0;
71
+ }
72
+
73
+ static void xt_object_rest_copy(xt_value target, xt_value key, xt_value value, int32_t argc, xt_value *argv) {
74
+ if (xt_object_rest_excludes(key, argc, argv)) return;
75
+ xt_object_set(target, key, value);
76
+ }
77
+
78
+ /*
79
+ * `const { a, ...rest } = source` (and the assignment / parameter forms).
80
+ *
81
+ * Copies the *own enumerable* properties that the pattern did not name, in
82
+ * insertion order. Arrays and strings contribute their index properties, like
83
+ * ECMAScript; accessor properties are read through their getter, with the
84
+ * source as the receiver.
85
+ */
86
+ xt_value xt_object_rest(xt_value source, int32_t argc, xt_value *argv) {
87
+ if (source == XT_UNDEFINED || source == XT_NULL) {
88
+ xt_throw(xt_string_from_cstr("TypeError: Cannot destructure null or undefined"));
89
+ return XT_UNDEFINED;
90
+ }
91
+ xt_value result = xt_object_new();
92
+
93
+ if (XT_IS_ARRAY(source)) {
94
+ xt_array *array = (xt_array *)XT_GET_PTR(source);
95
+ for (uint32_t i = 0; i < array->length; i++) {
96
+ char buffer[16];
97
+ snprintf(buffer, sizeof(buffer), "%u", i);
98
+ xt_object_rest_copy(result, xt_string_from_cstr(buffer), array->items[i], argc, argv);
99
+ }
100
+ return result;
101
+ }
102
+
103
+ if (XT_IS_STRING(source)) {
104
+ xt_string *string = (xt_string *)XT_GET_PTR(source);
105
+ const char *bytes = xt_string_bytes(string);
106
+ for (uint32_t i = 0; i < string->length; i++) {
107
+ char buffer[16];
108
+ snprintf(buffer, sizeof(buffer), "%u", i);
109
+ xt_object_rest_copy(result, xt_string_from_cstr(buffer), xt_string_new(bytes + i, 1), argc, argv);
110
+ }
111
+ return result;
112
+ }
113
+
114
+ if (!XT_IS_OBJECT(source) && !XT_IS_FUNCTION(source)) return result;
115
+ xt_object *object = xt_as_object(source);
116
+ if (!object) return result;
117
+ if (object->header.kind != XT_OBJECT_KIND_OBJECT && object->header.kind != XT_OBJECT_KIND_ERROR) {
118
+ /* Map/Set/Date/RegExp/Promise have no own enumerable string properties. */
119
+ return result;
120
+ }
121
+ for (uint32_t i = 0; i < object->count; i++) {
122
+ xt_property *property = &object->properties[i];
123
+ if (xt_object_rest_excludes(property->key, argc, argv)) continue;
124
+ xt_value value = property->value;
125
+ if (property->getter != XT_UNDEFINED && XT_IS_FUNCTION(property->getter)) {
126
+ value = xt_call_with_this(property->getter, source, 0, NULL);
127
+ }
128
+ xt_object_set(result, property->key, value);
129
+ }
130
+ return result;
131
+ }
132
+
65
133
  xt_value xt_parse_int(int32_t argc, xt_value *argv) {
66
134
  xt_string *s = xt_as_string(xt_to_string(xt_arg_at(argc, argv, 0)));
67
- const char *p = s->data;
135
+ const char *p = xt_string_bytes(s);
68
136
  /* JavaScript `parseInt` semantics: skip leading whitespace, accept an
69
137
  optional sign, pick the radix (auto-detecting a `0x` prefix), read the
70
138
  longest run of valid digits and accumulate into a double. Accumulating as
@@ -107,9 +175,10 @@ xt_value xt_parse_int(int32_t argc, xt_value *argv) {
107
175
 
108
176
  xt_value xt_parse_float(int32_t argc, xt_value *argv) {
109
177
  xt_string *s = xt_as_string(xt_to_string(xt_arg_at(argc, argv, 0)));
178
+ const char *bytes = xt_string_bytes(s);
110
179
  char *end = NULL;
111
- double value = strtod(s->data, &end);
112
- if (end == s->data) return xt_number(NAN);
180
+ double value = strtod(bytes, &end);
181
+ if (end == bytes) return xt_number(NAN);
113
182
  return xt_number(value);
114
183
  }
115
184
 
@@ -140,6 +209,7 @@ static int xt_uri_is_kept(const char *allowed, unsigned char c) {
140
209
 
141
210
  static xt_value xt_uri_encode(xt_value input, const char *allowed) {
142
211
  xt_string *s = xt_as_string(xt_to_string(input));
212
+ const char *bytes = xt_string_bytes(s);
143
213
  static const char hex[] = "0123456789ABCDEF";
144
214
  char *out = (char *)malloc((size_t)s->length * 3 + 1);
145
215
  if (!out) {
@@ -148,7 +218,7 @@ static xt_value xt_uri_encode(xt_value input, const char *allowed) {
148
218
  }
149
219
  size_t written = 0;
150
220
  for (uint32_t i = 0; i < s->length; i++) {
151
- unsigned char c = (unsigned char)s->data[i];
221
+ unsigned char c = (unsigned char)bytes[i];
152
222
  if (xt_uri_is_kept(allowed, c)) {
153
223
  out[written++] = (char)c;
154
224
  } else {
@@ -173,6 +243,7 @@ static int xt_hex_value(int c) {
173
243
  * escapes for reserved characters are copied through unchanged. */
174
244
  static xt_value xt_uri_decode(xt_value input, int component) {
175
245
  xt_string *s = xt_as_string(xt_to_string(input));
246
+ const char *bytes = xt_string_bytes(s);
176
247
  char *out = (char *)malloc((size_t)s->length + 1);
177
248
  if (!out) {
178
249
  fprintf(stderr, "xbintsc: out of memory\n");
@@ -181,22 +252,22 @@ static xt_value xt_uri_decode(xt_value input, int component) {
181
252
  size_t written = 0;
182
253
  uint32_t i = 0;
183
254
  while (i < s->length) {
184
- unsigned char c = (unsigned char)s->data[i];
255
+ unsigned char c = (unsigned char)bytes[i];
185
256
  if (c != '%') {
186
257
  out[written++] = (char)c;
187
258
  i++;
188
259
  continue;
189
260
  }
190
261
  if (i + 2 >= s->length) xt_throw_uri_error("URI malformed");
191
- int h1 = xt_hex_value((unsigned char)s->data[i + 1]);
192
- int h2 = xt_hex_value((unsigned char)s->data[i + 2]);
262
+ int h1 = xt_hex_value((unsigned char)bytes[i + 1]);
263
+ int h2 = xt_hex_value((unsigned char)bytes[i + 2]);
193
264
  if (h1 < 0 || h2 < 0) xt_throw_uri_error("URI malformed");
194
265
  unsigned char b = (unsigned char)((h1 << 4) | h2);
195
266
  if (b < 0x80) {
196
267
  if (!component && xt_uri_is_kept(";/?:@&=+$,#", b)) {
197
- out[written++] = s->data[i];
198
- out[written++] = s->data[i + 1];
199
- out[written++] = s->data[i + 2];
268
+ out[written++] = bytes[i];
269
+ out[written++] = bytes[i + 1];
270
+ out[written++] = bytes[i + 2];
200
271
  } else {
201
272
  out[written++] = (char)b;
202
273
  }
@@ -213,9 +284,9 @@ static xt_value xt_uri_decode(xt_value input, int component) {
213
284
  for (int k = 1; k <= extra; k++) {
214
285
  uint32_t pos = i + (uint32_t)k * 3;
215
286
  if (pos + 2 >= s->length) xt_throw_uri_error("URI malformed");
216
- if (s->data[pos] != '%') xt_throw_uri_error("URI malformed");
217
- int c1 = xt_hex_value((unsigned char)s->data[pos + 1]);
218
- int c2 = xt_hex_value((unsigned char)s->data[pos + 2]);
287
+ if (bytes[pos] != '%') xt_throw_uri_error("URI malformed");
288
+ int c1 = xt_hex_value((unsigned char)bytes[pos + 1]);
289
+ int c2 = xt_hex_value((unsigned char)bytes[pos + 2]);
219
290
  if (c1 < 0 || c2 < 0) xt_throw_uri_error("URI malformed");
220
291
  unsigned char cb = (unsigned char)((c1 << 4) | c2);
221
292
  if ((cb & 0xC0) != 0x80) xt_throw_uri_error("URI malformed");
@@ -322,7 +393,7 @@ xt_value xt_in(xt_value key, xt_value value) {
322
393
  if (xt_is_symbol(key)) return XT_FALSE;
323
394
  xt_string *s = xt_as_string(value);
324
395
  xt_string *k = xt_as_string(xt_to_string(key));
325
- if (k->length == 6 && memcmp(k->data, "length", 6) == 0) return XT_TRUE;
396
+ if (k->length == 6 && memcmp(xt_string_bytes(k), "length", 6) == 0) return XT_TRUE;
326
397
  int32_t index = xt_to_int32(key);
327
398
  return xt_bool(index >= 0 && (uint32_t)index < s->length);
328
399
  }
@@ -446,6 +517,57 @@ xt_value xt_instance_of(xt_value value, xt_value ctor) {
446
517
  return XT_FALSE;
447
518
  }
448
519
 
520
+ /*
521
+ * `value instanceof Array` (and the rest of the built-in constructors).
522
+ *
523
+ * Built-in constructors are not first-class values: the codegen recognises the
524
+ * name and asks this helper instead. Ids match `BUILTIN_INSTANCEOF` in
525
+ * `src/codegen/generator/tables.ts`; 16..24 map onto the runtime's `XT_TA_*`
526
+ * order (Uint8Array first).
527
+ */
528
+ enum {
529
+ XT_INSTANCE_ARRAY = 0,
530
+ XT_INSTANCE_OBJECT = 1,
531
+ XT_INSTANCE_FUNCTION = 2,
532
+ XT_INSTANCE_MAP = 3,
533
+ XT_INSTANCE_SET = 4,
534
+ XT_INSTANCE_DATE = 5,
535
+ XT_INSTANCE_REGEXP = 6,
536
+ XT_INSTANCE_PROMISE = 7,
537
+ XT_INSTANCE_TYPED_ARRAY_FIRST = 16,
538
+ XT_INSTANCE_TYPED_ARRAY_LAST = 24
539
+ };
540
+
541
+ static int xt_value_has_kind(xt_value value, uint8_t kind) {
542
+ return XT_IS_OBJECT(value) && ((xt_object *)XT_GET_PTR(value))->header.kind == kind;
543
+ }
544
+
545
+ xt_value xt_instance_of_builtin(xt_value value, int32_t id) {
546
+ switch (id) {
547
+ case XT_INSTANCE_ARRAY:
548
+ return xt_bool(XT_IS_ARRAY(value));
549
+ case XT_INSTANCE_OBJECT:
550
+ return xt_bool(XT_IS_OBJECT(value) || XT_IS_ARRAY(value) || XT_IS_FUNCTION(value));
551
+ case XT_INSTANCE_FUNCTION:
552
+ return xt_bool(XT_IS_FUNCTION(value));
553
+ case XT_INSTANCE_MAP:
554
+ return xt_bool(xt_value_has_kind(value, XT_OBJECT_KIND_MAP));
555
+ case XT_INSTANCE_SET:
556
+ return xt_bool(xt_value_has_kind(value, XT_OBJECT_KIND_SET));
557
+ case XT_INSTANCE_DATE:
558
+ return xt_bool(xt_value_has_kind(value, XT_OBJECT_KIND_DATE));
559
+ case XT_INSTANCE_REGEXP:
560
+ return xt_bool(xt_value_has_kind(value, XT_OBJECT_KIND_REGEXP));
561
+ case XT_INSTANCE_PROMISE:
562
+ return xt_bool(xt_value_has_kind(value, XT_OBJECT_KIND_PROMISE));
563
+ default:
564
+ if (id >= XT_INSTANCE_TYPED_ARRAY_FIRST && id <= XT_INSTANCE_TYPED_ARRAY_LAST) {
565
+ return xt_bool(xt_typed_array_kind_of(value) == id - XT_INSTANCE_TYPED_ARRAY_FIRST);
566
+ }
567
+ return XT_FALSE;
568
+ }
569
+ }
570
+
449
571
  xt_value xt_closure_env(xt_value value, int32_t index) {
450
572
  if (!XT_IS_FUNCTION(value)) return XT_UNDEFINED;
451
573
  xt_function *function = (xt_function *)XT_GET_PTR(value);
@@ -13,7 +13,7 @@ static void xt_throw_read_of_nullish(xt_value target, xt_value key) {
13
13
  char keybuf[128];
14
14
  xt_string *k = XT_IS_STRING(key) ? xt_as_string(key) : xt_as_string(xt_to_string(key));
15
15
  uint32_t n = k->length < sizeof(keybuf) - 1 ? k->length : (uint32_t)(sizeof(keybuf) - 1);
16
- memcpy(keybuf, k->data, n);
16
+ memcpy(keybuf, xt_string_bytes(k), n);
17
17
  keybuf[n] = 0;
18
18
  char message[256];
19
19
  snprintf(message, sizeof(message),
@@ -27,7 +27,7 @@ xt_value xt_get(xt_value target, xt_value key) {
27
27
  /* `arr.length` must read the array's length, never an element. */
28
28
  if (XT_IS_STRING(key)) {
29
29
  xt_string *k = xt_as_string(key);
30
- if (k->length == 6 && memcmp(k->data, "length", 6) == 0)
30
+ if (k->length == 6 && memcmp(xt_string_bytes(k), "length", 6) == 0)
31
31
  return xt_number((double)((xt_array *)XT_GET_PTR(target))->length);
32
32
  /* Named (non-index) properties such as `raw`/`index`/`input`. */
33
33
  uint32_t ignored = 0;
@@ -57,11 +57,11 @@ xt_value xt_get(xt_value target, xt_value key) {
57
57
  /* Functions expose own `name` / `length` (arity) properties. */
58
58
  if (XT_IS_STRING(key)) {
59
59
  xt_string *k = xt_as_string(key);
60
- if (k->length == 6 && memcmp(k->data, "length", 6) == 0) {
60
+ if (k->length == 6 && memcmp(xt_string_bytes(k), "length", 6) == 0) {
61
61
  int32_t arity = ((xt_function *)XT_GET_PTR(target))->arity;
62
62
  return xt_number(arity >= 0 ? (double)arity : 0.0);
63
63
  }
64
- if (k->length == 4 && memcmp(k->data, "name", 4) == 0) {
64
+ if (k->length == 4 && memcmp(xt_string_bytes(k), "name", 4) == 0) {
65
65
  xt_string *name = ((xt_function *)XT_GET_PTR(target))->name;
66
66
  return name ? XT_FROM_PTR(XT_TAG_STRING, name) : xt_string_from_cstr("");
67
67
  }
@@ -79,7 +79,7 @@ xt_value xt_get(xt_value target, xt_value key) {
79
79
  if (obj->header.kind == XT_OBJECT_KIND_SYMBOL) {
80
80
  if (XT_IS_STRING(key)) {
81
81
  xt_string *k = xt_as_string(key);
82
- if (k->length == 11 && memcmp(k->data, "description", 11) == 0) {
82
+ if (k->length == 11 && memcmp(xt_string_bytes(k), "description", 11) == 0) {
83
83
  return xt_symbol_description(target);
84
84
  }
85
85
  }
@@ -88,7 +88,7 @@ xt_value xt_get(xt_value target, xt_value key) {
88
88
  if (obj->header.kind == XT_OBJECT_KIND_MAP || obj->header.kind == XT_OBJECT_KIND_SET) {
89
89
  if (XT_IS_STRING(key)) {
90
90
  xt_string *k = xt_as_string(key);
91
- if (k->length == 4 && memcmp(k->data, "size", 4) == 0) {
91
+ if (k->length == 4 && memcmp(xt_string_bytes(k), "size", 4) == 0) {
92
92
  int32_t size = obj->header.kind == XT_OBJECT_KIND_MAP ? xt_map_size(target) : xt_set_size(target);
93
93
  return xt_number((double)size);
94
94
  }
@@ -100,9 +100,10 @@ xt_value xt_get(xt_value target, xt_value key) {
100
100
  }
101
101
  if (obj->header.kind == XT_OBJECT_KIND_ERROR && XT_IS_STRING(key)) {
102
102
  xt_string *k = xt_as_string(key);
103
- if (k->length == 5 && memcmp(k->data, "stack", 5) == 0) {
104
- xt_value own = xt_object_get(target, key);
105
- return own != XT_UNDEFINED ? own : xt_error_to_string(target);
103
+ if (k->length == 5 && memcmp(xt_string_bytes(k), "stack", 5) == 0) {
104
+ xt_value captured = xt_error_stack(target);
105
+ if (captured != XT_UNDEFINED) return captured;
106
+ return xt_error_to_string(target);
106
107
  }
107
108
  }
108
109
  {
@@ -117,16 +118,16 @@ xt_value xt_get(xt_value target, xt_value key) {
117
118
  xt_string *s = xt_as_string(target);
118
119
  if (XT_IS_STRING(key)) {
119
120
  xt_string *k = xt_as_string(key);
120
- if (k->length == 6 && memcmp(k->data, "length", 6) == 0) return xt_number((double)s->length);
121
+ if (k->length == 6 && memcmp(xt_string_bytes(k), "length", 6) == 0) return xt_number((double)s->length);
121
122
  uint32_t index = 0;
122
123
  if (xt_key_is_array_index(k, &index) && index < s->length) {
123
- return xt_string_new(s->data + index, 1);
124
+ return xt_string_new(xt_string_bytes(s) + index, 1);
124
125
  }
125
126
  return xt_method_value(target, key);
126
127
  }
127
128
  if (XT_IS_NUMBER(key)) {
128
129
  int32_t index = xt_to_int32(key);
129
- if (index >= 0 && (uint32_t)index < s->length) return xt_string_new(s->data + index, 1);
130
+ if (index >= 0 && (uint32_t)index < s->length) return xt_string_new(xt_string_bytes(s) + index, 1);
130
131
  }
131
132
  return XT_UNDEFINED;
132
133
  }
@@ -142,7 +143,7 @@ xt_value xt_set(xt_value target, xt_value key, xt_value value) {
142
143
  /* Assigning `arr.length` truncates or extends the array. */
143
144
  if (XT_IS_STRING(key)) {
144
145
  xt_string *k = xt_as_string(key);
145
- if (k->length == 6 && memcmp(k->data, "length", 6) == 0) {
146
+ if (k->length == 6 && memcmp(xt_string_bytes(k), "length", 6) == 0) {
146
147
  xt_array *array = (xt_array *)XT_GET_PTR(target);
147
148
  int32_t n = xt_to_int32(value);
148
149
  if (n < 0) n = 0;
@@ -169,9 +170,9 @@ xt_value xt_set(xt_value target, xt_value key, xt_value value) {
169
170
  if (typed >= 0) {
170
171
  if (XT_IS_STRING(key)) {
171
172
  xt_string *k = xt_as_string(key);
172
- if ((k->length == 6 && memcmp(k->data, "length", 6) == 0) ||
173
- (k->length == 10 && memcmp(k->data, "byteLength", 10) == 0) ||
174
- (k->length == 10 && memcmp(k->data, "byteOffset", 10) == 0)) {
173
+ if ((k->length == 6 && memcmp(xt_string_bytes(k), "length", 6) == 0) ||
174
+ (k->length == 10 && memcmp(xt_string_bytes(k), "byteLength", 10) == 0) ||
175
+ (k->length == 10 && memcmp(xt_string_bytes(k), "byteOffset", 10) == 0)) {
175
176
  xt_throw(xt_string_from_cstr("TypeError: Cannot assign to read only property"));
176
177
  return value;
177
178
  }
@@ -54,10 +54,11 @@ static xt_property *xt_object_find(xt_object *obj, xt_value key) {
54
54
  */
55
55
  static int xt_key_is_array_index(xt_string *key, uint32_t *out) {
56
56
  if (key->length == 0 || key->length > 10) return 0;
57
- if (key->data[0] == '0' && key->length != 1) return 0; /* "0" is canonical, "01" is not */
57
+ const char *bytes = xt_string_bytes(key);
58
+ if (bytes[0] == '0' && key->length != 1) return 0; /* "0" is canonical, "01" is not */
58
59
  uint64_t value = 0;
59
60
  for (uint32_t i = 0; i < key->length; i++) {
60
- char ch = key->data[i];
61
+ char ch = bytes[i];
61
62
  if (ch < '0' || ch > '9') return 0;
62
63
  value = value * 10 + (uint64_t)(ch - '0');
63
64
  }
@@ -117,7 +118,7 @@ int xt_property_key_equals(xt_value a, xt_value b) {
117
118
  static int xt_key_is_prototype(xt_value key) {
118
119
  if (!XT_IS_STRING(key)) return 0;
119
120
  xt_string *k = xt_as_string(key);
120
- return k && k->length == 9 && memcmp(k->data, "prototype", 9) == 0;
121
+ return k && k->length == 9 && memcmp(xt_string_bytes(k), "prototype", 9) == 0;
121
122
  }
122
123
 
123
124
  xt_value xt_object_get(xt_value value, xt_value key) {