xbintsc 0.3.81 → 0.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. package/AGENTS.md +8 -4
  2. package/README.md +16 -5
  3. package/README.zh-CN.md +14 -6
  4. package/dist/src/ast/common.d.ts +23 -0
  5. package/dist/src/cli/main.js +68 -24
  6. package/dist/src/cli/main.js.map +1 -1
  7. package/dist/src/codegen/generator/calls/access.js +6 -2
  8. package/dist/src/codegen/generator/calls/access.js.map +1 -1
  9. package/dist/src/codegen/generator/calls/closures.js +4 -0
  10. package/dist/src/codegen/generator/calls/closures.js.map +1 -1
  11. package/dist/src/codegen/generator/calls/invocation.js +9 -4
  12. package/dist/src/codegen/generator/calls/invocation.js.map +1 -1
  13. package/dist/src/codegen/generator/context.d.ts +50 -0
  14. package/dist/src/codegen/generator/context.js +124 -13
  15. package/dist/src/codegen/generator/context.js.map +1 -1
  16. package/dist/src/codegen/generator/debug.d.ts +33 -0
  17. package/dist/src/codegen/generator/debug.js +97 -0
  18. package/dist/src/codegen/generator/debug.js.map +1 -0
  19. package/dist/src/codegen/generator/expressions/assignment.js +9 -5
  20. package/dist/src/codegen/generator/expressions/assignment.js.map +1 -1
  21. package/dist/src/codegen/generator/expressions/operators.js +35 -9
  22. package/dist/src/codegen/generator/expressions/operators.js.map +1 -1
  23. package/dist/src/codegen/generator/expressions/primary.js +11 -3
  24. package/dist/src/codegen/generator/expressions/primary.js.map +1 -1
  25. package/dist/src/codegen/generator/generator.d.ts +3 -1
  26. package/dist/src/codegen/generator/generator.js +3 -1
  27. package/dist/src/codegen/generator/generator.js.map +1 -1
  28. package/dist/src/codegen/generator/module.d.ts +1 -1
  29. package/dist/src/codegen/generator/module.js +82 -12
  30. package/dist/src/codegen/generator/module.js.map +1 -1
  31. package/dist/src/codegen/generator/numeric.d.ts +26 -0
  32. package/dist/src/codegen/generator/numeric.js +127 -0
  33. package/dist/src/codegen/generator/numeric.js.map +1 -0
  34. package/dist/src/codegen/generator/state.d.ts +17 -0
  35. package/dist/src/codegen/generator/state.js +9 -0
  36. package/dist/src/codegen/generator/state.js.map +1 -1
  37. package/dist/src/codegen/generator/statements/control-flow.js +2 -4
  38. package/dist/src/codegen/generator/statements/control-flow.js.map +1 -1
  39. package/dist/src/codegen/generator/statements/dispatch.js +3 -0
  40. package/dist/src/codegen/generator/statements/dispatch.js.map +1 -1
  41. package/dist/src/codegen/generator/statements/variables.js +8 -2
  42. package/dist/src/codegen/generator/statements/variables.js.map +1 -1
  43. package/dist/src/codegen/generator/tables.d.ts +16 -0
  44. package/dist/src/codegen/generator/tables.js +99 -2
  45. package/dist/src/codegen/generator/tables.js.map +1 -1
  46. package/dist/src/driver/bundler/merge.js +41 -5
  47. package/dist/src/driver/bundler/merge.js.map +1 -1
  48. package/dist/src/driver/compiler.d.ts +23 -4
  49. package/dist/src/driver/compiler.js +46 -57
  50. package/dist/src/driver/compiler.js.map +1 -1
  51. package/dist/src/driver/fingerprint.d.ts +27 -0
  52. package/dist/src/driver/fingerprint.js +88 -0
  53. package/dist/src/driver/fingerprint.js.map +1 -0
  54. package/dist/src/driver/toolchain-provider.d.ts +9 -2
  55. package/dist/src/driver/toolchain-provider.js +15 -15
  56. package/dist/src/driver/toolchain-provider.js.map +1 -1
  57. package/dist/src/driver/toolchain.d.ts +58 -1
  58. package/dist/src/driver/toolchain.js +169 -22
  59. package/dist/src/driver/toolchain.js.map +1 -1
  60. package/dist/src/parser/expressions.js +20 -1
  61. package/dist/src/parser/expressions.js.map +1 -1
  62. package/dist/tests/cli/main.test.js +41 -1
  63. package/dist/tests/cli/main.test.js.map +1 -1
  64. package/dist/tests/codegen/closures.test.js +8 -0
  65. package/dist/tests/codegen/closures.test.js.map +1 -1
  66. package/dist/tests/codegen/expressions.test.js +14 -3
  67. package/dist/tests/codegen/expressions.test.js.map +1 -1
  68. package/dist/tests/codegen/llvm.test.js +23 -0
  69. package/dist/tests/codegen/llvm.test.js.map +1 -1
  70. package/dist/tests/driver/toolchain-provider.test.js +49 -2
  71. package/dist/tests/driver/toolchain-provider.test.js.map +1 -1
  72. package/dist/tests/driver/toolchain.test.js +62 -3
  73. package/dist/tests/driver/toolchain.test.js.map +1 -1
  74. package/dist/tests/e2e/async-await.test.d.ts +10 -0
  75. package/dist/tests/e2e/async-await.test.js +247 -0
  76. package/dist/tests/e2e/async-await.test.js.map +1 -0
  77. package/dist/tests/e2e/child-process.test.d.ts +10 -0
  78. package/dist/tests/e2e/child-process.test.js +59 -0
  79. package/dist/tests/e2e/child-process.test.js.map +1 -0
  80. package/dist/tests/e2e/compile-objects-classes.test.js +47 -0
  81. package/dist/tests/e2e/compile-objects-classes.test.js.map +1 -1
  82. package/dist/tests/e2e/error-stack.test.d.ts +9 -0
  83. package/dist/tests/e2e/error-stack.test.js +94 -0
  84. package/dist/tests/e2e/error-stack.test.js.map +1 -0
  85. package/dist/tests/e2e/runtime-coverage-globals.test.js +46 -0
  86. package/dist/tests/e2e/runtime-coverage-globals.test.js.map +1 -1
  87. package/dist/tests/e2e/string-concat.test.d.ts +9 -0
  88. package/dist/tests/e2e/string-concat.test.js +102 -0
  89. package/dist/tests/e2e/string-concat.test.js.map +1 -0
  90. package/dist/tests/e2e/string-utf16.test.d.ts +6 -0
  91. package/dist/tests/e2e/string-utf16.test.js +133 -0
  92. package/dist/tests/e2e/string-utf16.test.js.map +1 -0
  93. package/dist/tests/helpers.d.ts +9 -0
  94. package/dist/tests/helpers.js +26 -3
  95. package/dist/tests/helpers.js.map +1 -1
  96. package/dist/tests/parser/expressions.test.js +7 -0
  97. package/dist/tests/parser/expressions.test.js.map +1 -1
  98. package/dist/tests/version.test.d.ts +1 -0
  99. package/dist/tests/version.test.js +24 -0
  100. package/dist/tests/version.test.js.map +1 -0
  101. package/doc/DESIGN.md +20 -12
  102. package/doc/ai/cli.md +2 -0
  103. package/doc/ai/contributing.md +16 -8
  104. package/doc/ai/extensions.md +3 -2
  105. package/doc/ai/language-support.md +15 -12
  106. package/doc/ai/troubleshooting.md +43 -5
  107. package/doc/ai/zh-CN/cli.md +2 -0
  108. package/doc/ai/zh-CN/contributing.md +11 -7
  109. package/doc/ai/zh-CN/extensions.md +2 -1
  110. package/doc/ai/zh-CN/language-support.md +15 -12
  111. package/doc/ai/zh-CN/troubleshooting.md +38 -4
  112. package/doc/implemented.md +16 -13
  113. package/doc/requirements.md +13 -4
  114. package/doc/unimplemented.md +20 -18
  115. package/doc/zh-CN/DESIGN.md +15 -9
  116. package/doc/zh-CN/implemented.md +15 -12
  117. package/doc/zh-CN/requirements.md +12 -3
  118. package/doc/zh-CN/unimplemented.md +19 -17
  119. package/package.json +3 -1
  120. package/runtime/ext_node/buffer/parts/construction.inc +2 -2
  121. package/runtime/ext_node/buffer/parts/methods.inc +1 -1
  122. package/runtime/ext_node/buffer/parts/search.inc +1 -1
  123. package/runtime/ext_node/child_process/child_process.c +25 -0
  124. package/runtime/ext_node/fs/fd_ops.c +1 -1
  125. package/runtime/rt.h +49 -1
  126. package/runtime/rt_internal.h +136 -4
  127. package/runtime/xt_alloc.c +20 -2
  128. package/runtime/xt_async.c +197 -0
  129. package/runtime/xt_bigint/conversions.inc +1 -1
  130. package/runtime/xt_builtins.c +169 -43
  131. package/runtime/xt_constructors.c +389 -0
  132. package/runtime/xt_containers/access.inc +61 -24
  133. package/runtime/xt_containers/arrays.inc +26 -2
  134. package/runtime/xt_containers/objects.inc +206 -8
  135. package/runtime/xt_coroutine.c +212 -0
  136. package/runtime/xt_generator.c +31 -162
  137. package/runtime/xt_io/exception.inc +171 -2
  138. package/runtime/xt_io/inspect.inc +48 -13
  139. package/runtime/xt_io.c +5 -0
  140. package/runtime/xt_promise.c +62 -17
  141. package/runtime/xt_stdlib/arrays.inc +2 -2
  142. package/runtime/xt_stdlib/dispatch.inc +20 -4
  143. package/runtime/xt_stdlib/strings.inc +98 -29
  144. package/runtime/xt_stdlib2/array-statics.inc +3 -2
  145. package/runtime/xt_stdlib2/arrays.inc +1 -1
  146. package/runtime/xt_stdlib2/error.inc +33 -6
  147. package/runtime/xt_stdlib2/json.inc +46 -16
  148. package/runtime/xt_stdlib2/number-string-statics.inc +24 -13
  149. package/runtime/xt_stdlib2/objects.inc +1 -1
  150. package/runtime/xt_stdlib2/regexp-match.inc +50 -35
  151. package/runtime/xt_stdlib2/set.inc +6 -0
  152. package/runtime/xt_stdlib2/strings.inc +81 -42
  153. package/runtime/xt_symbol.c +1 -1
  154. package/runtime/xt_typed_array/construction.inc +43 -2
  155. package/runtime/xt_typed_array.c +4 -0
  156. package/runtime/xt_values/comparison.inc +2 -2
  157. package/runtime/xt_values/constructors.inc +18 -5
  158. package/runtime/xt_values/conversions.inc +4 -3
  159. package/runtime/xt_values/strings.inc +94 -4
  160. package/runtime/xt_values/utf16.inc +340 -0
  161. package/runtime/xt_values.c +1 -0
  162. package/scripts/bench.ts +172 -0
  163. package/scripts/build-runtime.ts +2 -1
  164. package/scripts/check-version.ts +53 -0
  165. package/src/ast/common.ts +23 -0
  166. package/src/cli/main.ts +68 -26
  167. package/src/codegen/generator/calls/access.ts +7 -2
  168. package/src/codegen/generator/calls/closures.ts +3 -0
  169. package/src/codegen/generator/calls/invocation.ts +9 -3
  170. package/src/codegen/generator/context.ts +129 -18
  171. package/src/codegen/generator/debug.ts +132 -0
  172. package/src/codegen/generator/expressions/assignment.ts +9 -5
  173. package/src/codegen/generator/expressions/operators.ts +47 -8
  174. package/src/codegen/generator/expressions/primary.ts +11 -3
  175. package/src/codegen/generator/generator.ts +18 -2
  176. package/src/codegen/generator/module.ts +83 -13
  177. package/src/codegen/generator/numeric.ts +158 -0
  178. package/src/codegen/generator/state.ts +26 -0
  179. package/src/codegen/generator/statements/control-flow.ts +2 -3
  180. package/src/codegen/generator/statements/dispatch.ts +3 -0
  181. package/src/codegen/generator/statements/variables.ts +8 -2
  182. package/src/codegen/generator/tables.ts +110 -2
  183. package/src/driver/bundler/merge.ts +39 -5
  184. package/src/driver/compiler.ts +65 -55
  185. package/src/driver/fingerprint.ts +85 -0
  186. package/src/driver/toolchain-provider.ts +17 -16
  187. package/src/driver/toolchain.ts +210 -21
  188. package/src/parser/expressions.ts +20 -1
@@ -1,10 +1,12 @@
1
1
  /*
2
- * xbintsc runtime — promises and async support.
2
+ * xbintsc runtime — promises and the microtask queue.
3
3
  *
4
- * There is no event loop in the runtime, so promises use a microtask queue
5
- * that the compiler drains when the program body finishes (and that `await`
6
- * drains on demand). This gives synchronous-looking `async`/`await` semantics
7
- * for the common case where every awaited value is already settled.
4
+ * Promise reactions are queued as microtasks and run when the program body
5
+ * finishes, by the event loop between callbacks, and on demand. `await` lives
6
+ * in `xt_async.c`: an `async` function runs on a coroutine, so awaiting a
7
+ * promise that is still pending suspends it and registers a waiter here. When
8
+ * the promise settles, the waiter becomes a microtask that resumes the
9
+ * coroutine. Everything is single-threaded and non-blocking.
8
10
  */
9
11
 
10
12
  #include "rt_internal.h"
@@ -12,10 +14,6 @@
12
14
  #include <stdlib.h>
13
15
  #include <string.h>
14
16
 
15
- #define XT_PROMISE_PENDING 0
16
- #define XT_PROMISE_FULFILLED 1
17
- #define XT_PROMISE_REJECTED 2
18
-
19
17
  typedef struct xt_promise xt_promise;
20
18
 
21
19
  typedef struct {
@@ -23,6 +21,9 @@ typedef struct {
23
21
  xt_value on_fulfilled;
24
22
  xt_value on_rejected;
25
23
  xt_promise *result;
24
+ /* Non-NULL for an `async` coroutine waiting on this promise. Such a reaction
25
+ * has no handlers: running it resumes the coroutine instead. */
26
+ void *async;
26
27
  } xt_reaction;
27
28
 
28
29
  struct xt_promise {
@@ -87,6 +88,7 @@ static void xt_promise_gc_scan_roots(void) {
87
88
  xt_gc_mark_value(reaction->on_fulfilled);
88
89
  xt_gc_mark_value(reaction->on_rejected);
89
90
  xt_gc_mark_header((xt_header *)reaction->result);
91
+ if (reaction->async) xt_gc_mark_header((xt_header *)reaction->async);
90
92
  }
91
93
  }
92
94
 
@@ -107,6 +109,7 @@ void xt_promise_gc_trace(xt_header *header) {
107
109
  xt_gc_mark_value(reaction->on_fulfilled);
108
110
  xt_gc_mark_value(reaction->on_rejected);
109
111
  xt_gc_mark_header((xt_header *)reaction->result);
112
+ if (reaction->async) xt_gc_mark_header((xt_header *)reaction->async);
110
113
  }
111
114
  }
112
115
 
@@ -133,6 +136,7 @@ static void xt_promise_resolve_into(xt_promise *result, xt_value value) {
133
136
  reaction->on_fulfilled = XT_UNDEFINED;
134
137
  reaction->on_rejected = XT_UNDEFINED;
135
138
  reaction->result = result;
139
+ reaction->async = NULL;
136
140
  if (other->reaction_count >= other->reaction_capacity) {
137
141
  other->reaction_capacity = other->reaction_capacity == 0 ? 4 : other->reaction_capacity * 2;
138
142
  other->reactions = (xt_reaction **)realloc(other->reactions, sizeof(xt_reaction *) * other->reaction_capacity);
@@ -156,6 +160,16 @@ static xt_value xt_promise_run_reactions(void) {
156
160
  while (xt_microtask_head < xt_microtask_count) {
157
161
  xt_reaction *reaction = xt_microtasks[xt_microtask_head++];
158
162
  xt_promise *promise = reaction->promise;
163
+ if (reaction->async) {
164
+ /* A suspended `async` coroutine: resume it with the settled value. It
165
+ * runs until it completes or awaits another pending promise. */
166
+ void *async = reaction->async;
167
+ int rejected = promise->state == XT_PROMISE_REJECTED;
168
+ xt_value settled = promise->value;
169
+ free(reaction);
170
+ xt_async_wake(async, settled, rejected);
171
+ continue;
172
+ }
159
173
  xt_value handler = promise->state == XT_PROMISE_FULFILLED ? reaction->on_fulfilled : reaction->on_rejected;
160
174
  if (XT_IS_FUNCTION(handler)) {
161
175
  xt_value arg = promise->value;
@@ -181,16 +195,46 @@ void xt_drain_microtasks(void) {
181
195
  }
182
196
  }
183
197
 
184
- xt_value xt_await(xt_value value) {
198
+ /* -- helpers used by the async coroutine runtime -------------------------- */
199
+
200
+ xt_value xt_promise_create(void) { return xt_promise_value(xt_promise_new()); }
201
+
202
+ void xt_promise_fulfill(xt_value promise, xt_value value) {
203
+ xt_promise *target = xt_as_promise(promise);
204
+ if (target) xt_promise_resolve_into(target, value);
205
+ }
206
+
207
+ void xt_promise_reject_value(xt_value promise, xt_value value) {
208
+ xt_promise *target = xt_as_promise(promise);
209
+ if (target) xt_promise_settle(target, XT_PROMISE_REJECTED, value);
210
+ }
211
+
212
+ int xt_promise_state_of(xt_value value) {
185
213
  xt_promise *promise = xt_as_promise(value);
186
- if (!promise) return value;
187
- int guard = 0;
188
- while (promise->state == XT_PROMISE_PENDING && guard++ < 100000) xt_drain_microtasks();
189
- if (promise->state == XT_PROMISE_REJECTED) {
190
- xt_throw(promise->value);
191
- return XT_UNDEFINED;
214
+ return promise ? promise->state : -1;
215
+ }
216
+
217
+ xt_value xt_promise_value_of(xt_value value) {
218
+ xt_promise *promise = xt_as_promise(value);
219
+ return promise ? promise->value : value;
220
+ }
221
+
222
+ void xt_promise_add_async_waiter(xt_value promiseValue, void *async) {
223
+ xt_promise *promise = xt_as_promise(promiseValue);
224
+ if (!promise || promise->state != XT_PROMISE_PENDING) return;
225
+ xt_reaction *reaction = (xt_reaction *)malloc(sizeof(xt_reaction));
226
+ if (!reaction) abort();
227
+ reaction->promise = promise;
228
+ reaction->on_fulfilled = XT_UNDEFINED;
229
+ reaction->on_rejected = XT_UNDEFINED;
230
+ reaction->result = NULL;
231
+ reaction->async = async;
232
+ if (promise->reaction_count >= promise->reaction_capacity) {
233
+ promise->reaction_capacity = promise->reaction_capacity == 0 ? 4 : promise->reaction_capacity * 2;
234
+ promise->reactions = (xt_reaction **)realloc(promise->reactions, sizeof(xt_reaction *) * promise->reaction_capacity);
235
+ if (!promise->reactions) abort();
192
236
  }
193
- return promise->value;
237
+ promise->reactions[promise->reaction_count++] = reaction;
194
238
  }
195
239
 
196
240
  /* -- C-callable resolve / reject ------------------------------------------ */
@@ -316,6 +360,7 @@ xt_value xt_promise_method(xt_value target, const char *method, int32_t argc, xt
316
360
  reaction->on_fulfilled = xt_arg_at(argc, argv, 0);
317
361
  reaction->on_rejected = xt_arg_at(argc, argv, 1);
318
362
  reaction->result = result;
363
+ reaction->async = NULL;
319
364
  if (promise->state == XT_PROMISE_PENDING) {
320
365
  if (promise->reaction_count >= promise->reaction_capacity) {
321
366
  promise->reaction_capacity = promise->reaction_capacity == 0 ? 4 : promise->reaction_capacity * 2;
@@ -47,11 +47,11 @@ static xt_value xt_array_join(xt_value value, xt_value separator) {
47
47
  if (!out) abort();
48
48
  out[0] = '\0';
49
49
  for (uint32_t i = 0; i < array->length; i++) {
50
- if (i > 0) xt_buffer_append(&out, &length, &capacity, sep->data, sep->length);
50
+ if (i > 0) xt_buffer_append(&out, &length, &capacity, xt_string_bytes(sep), sep->length);
51
51
  xt_value item = array->items[i];
52
52
  if (item == XT_UNDEFINED || item == XT_NULL) continue;
53
53
  xt_string *str = xt_as_string(xt_to_string(item));
54
- xt_buffer_append(&out, &length, &capacity, str->data, str->length);
54
+ xt_buffer_append(&out, &length, &capacity, xt_string_bytes(str), str->length);
55
55
  }
56
56
  xt_value result = xt_string_new(out, length);
57
57
  free(out);
@@ -40,6 +40,7 @@ static xt_value xt_string_method(xt_value target, const char *method, int32_t ar
40
40
  *handled = 1;
41
41
  if (strcmp(method, "charAt") == 0) return xt_string_char_at(target, xt_arg_at(argc, argv, 0));
42
42
  if (strcmp(method, "charCodeAt") == 0) return xt_string_char_code_at(target, xt_arg_at(argc, argv, 0));
43
+ if (strcmp(method, "codePointAt") == 0) return xt_string_code_point_at_value(target, xt_arg_at(argc, argv, 0));
43
44
  if (strcmp(method, "indexOf") == 0) return xt_string_index_of(target, xt_arg_at(argc, argv, 0), xt_arg_at(argc, argv, 1));
44
45
  if (strcmp(method, "includes") == 0) return xt_bool(xt_truthy(xt_ne(xt_string_index_of(target, xt_arg_at(argc, argv, 0), xt_arg_at(argc, argv, 1)), xt_number(-1))));
45
46
  if (strcmp(method, "slice") == 0) return xt_string_slice(target, xt_arg_at(argc, argv, 0), xt_arg_at(argc, argv, 1));
@@ -51,8 +52,8 @@ static xt_value xt_string_method(xt_value target, const char *method, int32_t ar
51
52
  if (strcmp(method, "trim") == 0) return xt_string_trim(target);
52
53
  if (strcmp(method, "replace") == 0) return xt_string_replace(target, xt_arg_at(argc, argv, 0), xt_arg_at(argc, argv, 1));
53
54
  if (strcmp(method, "repeat") == 0) return xt_string_repeat(target, xt_arg_at(argc, argv, 0));
54
- if (strcmp(method, "startsWith") == 0) return xt_string_starts_with(target, xt_arg_at(argc, argv, 0));
55
- if (strcmp(method, "endsWith") == 0) return xt_string_ends_with(target, xt_arg_at(argc, argv, 0));
55
+ if (strcmp(method, "startsWith") == 0) return xt_string_starts_with(target, xt_arg_at(argc, argv, 0), xt_arg_at(argc, argv, 1));
56
+ if (strcmp(method, "endsWith") == 0) return xt_string_ends_with(target, xt_arg_at(argc, argv, 0), xt_arg_at(argc, argv, 1));
56
57
  if (strcmp(method, "concat") == 0) return xt_string_concat_args(target, argc, argv);
57
58
  *handled = 0;
58
59
  return XT_UNDEFINED;
@@ -286,11 +287,19 @@ xt_value xt_call_method(xt_value target, xt_value name, int32_t argc, xt_value *
286
287
  if (kind != XT_OBJECT_KIND_OBJECT && kind != XT_OBJECT_KIND_FUNCTION) {
287
288
  xt_value result = xt_ext_container_method(target, method, argc, argv, &handled);
288
289
  if (handled) return result;
290
+ /* Containers keep their methods in dedicated C dispatch tables, but
291
+ * still inherit `Object.prototype` (`new Map().toString()`). */
292
+ xt_value inherited = xt_get(target, name);
293
+ if (XT_IS_FUNCTION(inherited)) return xt_call_with_this(inherited, target, argc, argv);
289
294
  }
290
295
  xt_value fn = xt_object_get(target, name);
291
296
  if (XT_IS_FUNCTION(fn)) return xt_call_with_this(fn, target, argc, argv);
292
297
  xt_value result = xt_ext_object_method(target, method, argc, argv, &handled);
293
298
  if (handled) return result;
299
+ /* Inherited Object.prototype methods on containers without a property bag
300
+ * (`new Map().toString()`, ...). */
301
+ xt_value inherited = xt_get(target, name);
302
+ if (XT_IS_FUNCTION(inherited)) return xt_call_with_this(inherited, target, argc, argv);
294
303
  {
295
304
  char message[256];
296
305
  snprintf(message, sizeof(message), "TypeError: object has no callable method '%s'", method);
@@ -322,7 +331,7 @@ xt_value xt_call_method(xt_value target, xt_value name, int32_t argc, xt_value *
322
331
  /* Bound functions report `name` as "bound <target>" and reduce `length`
323
332
  * by the number of pre-bound arguments, like the specification. */
324
333
  xt_function *targetFn = (xt_function *)XT_GET_PTR(target);
325
- const char *base = targetFn->name ? targetFn->name->data : "";
334
+ const char *base = targetFn->name ? xt_string_bytes(targetFn->name) : "";
326
335
  char boundName[256];
327
336
  snprintf(boundName, sizeof(boundName), "bound %s", base);
328
337
  int32_t prebound = argc > 1 ? argc - 1 : 0;
@@ -333,10 +342,17 @@ xt_value xt_call_method(xt_value target, xt_value name, int32_t argc, xt_value *
333
342
  }
334
343
  xt_value fn = xt_object_get(target, name);
335
344
  if (XT_IS_FUNCTION(fn)) return xt_call_with_this(fn, target, argc, argv);
345
+ /* Statics of a first-class built-in constructor (`const A = Array;
346
+ * A.isArray(x)`). */
347
+ xt_value builtinStatic = xt_builtin_static_value(target, name);
348
+ if (builtinStatic != XT_UNDEFINED) {
349
+ if (XT_IS_FUNCTION(builtinStatic)) return xt_call_with_this(builtinStatic, target, argc, argv);
350
+ return builtinStatic;
351
+ }
336
352
  if (strcmp(method, "toString") == 0) {
337
353
  /* Source text is not retained; report a native-style representation. */
338
354
  xt_function *self = (xt_function *)XT_GET_PTR(target);
339
- const char *selfName = self->name ? self->name->data : "";
355
+ const char *selfName = self->name ? xt_string_bytes(self->name) : "";
340
356
  char buffer[256];
341
357
  snprintf(buffer, sizeof(buffer), "function %s() { [native code] }", selfName);
342
358
  return xt_string_from_cstr(buffer);
@@ -1,53 +1,79 @@
1
1
  /* -- string methods ------------------------------------------------------- */
2
2
 
3
+ /* Byte-level search. `from` is a byte offset; returns a byte offset or -1. */
3
4
  static int32_t xt_string_find(xt_string *haystack, xt_string *needle, int32_t from) {
4
5
  if (from < 0) from = 0;
5
6
  if ((uint32_t)from > haystack->length) return -1;
6
7
  if (needle->length == 0) return from;
8
+ const char *hay = xt_string_bytes(haystack);
9
+ const char *need = xt_string_bytes(needle);
7
10
  for (uint32_t i = (uint32_t)from; i + needle->length <= haystack->length; i++) {
8
- if (memcmp(haystack->data + i, needle->data, needle->length) == 0) return (int32_t)i;
11
+ if (memcmp(hay + i, need, needle->length) == 0) return (int32_t)i;
9
12
  }
10
13
  return -1;
11
14
  }
12
15
 
16
+ /* JavaScript indexes strings in UTF-16 code units; the byte search above is
17
+ * still correct because every JS index maps to a code-point boundary. */
13
18
  static xt_value xt_string_char_at(xt_value value, xt_value indexValue) {
14
19
  xt_string *s = xt_as_string(value);
15
20
  if (!s) return xt_string_from_cstr("");
16
21
  int32_t index = xt_to_int32(indexValue);
17
- if (index < 0 || (uint32_t)index >= s->length) return xt_string_from_cstr("");
18
- return xt_string_new(s->data + index, 1);
22
+ if (index < 0 || (uint32_t)index >= xt_string_utf16_length(s)) return xt_string_from_cstr("");
23
+ return xt_string_unit_value(s, index);
19
24
  }
20
25
 
21
26
  static xt_value xt_string_char_code_at(xt_value value, xt_value indexValue) {
22
27
  xt_string *s = xt_as_string(value);
23
28
  if (!s) return xt_number(NAN);
24
29
  int32_t index = xt_to_int32(indexValue);
25
- if (index < 0 || (uint32_t)index >= s->length) return xt_number(NAN);
26
- return xt_number((double)(unsigned char)s->data[index]);
30
+ int32_t unit = xt_string_code_unit(s, index);
31
+ if (unit < 0) return xt_number(NAN);
32
+ return xt_number((double)unit);
33
+ }
34
+
35
+ static xt_value xt_string_code_point_at_value(xt_value value, xt_value indexValue) {
36
+ xt_string *s = xt_as_string(value);
37
+ if (!s) return XT_UNDEFINED;
38
+ int32_t index = xt_to_int32(indexValue);
39
+ int32_t code = xt_string_code_point_at(s, index);
40
+ if (code < 0) return XT_UNDEFINED;
41
+ return xt_number((double)code);
27
42
  }
28
43
 
29
44
  static xt_value xt_string_index_of(xt_value value, xt_value needleValue, xt_value fromValue) {
30
45
  xt_string *s = xt_as_string(value);
31
46
  if (!s) return xt_number(-1);
32
47
  xt_string *needle = xt_as_string(xt_to_string(needleValue));
48
+ int32_t units = (int32_t)xt_string_utf16_length(s);
33
49
  int32_t from = fromValue == XT_UNDEFINED ? 0 : xt_to_int32(fromValue);
34
- return xt_number((double)xt_string_find(s, needle, from));
50
+ if (from < 0) from = 0;
51
+ if (from > units) from = units;
52
+ /* A start position inside a surrogate pair leaves a lone low half; slice the
53
+ * tail so a search for the whole pair correctly fails. */
54
+ if (!xt_string_is_boundary(s, from)) {
55
+ xt_string *tail = xt_as_string(xt_string_slice_units(s, from, units));
56
+ int32_t at = xt_string_find(tail, needle, 0);
57
+ return xt_number(at < 0 ? -1.0 : (double)(from + xt_string_unit_offset(tail, at)));
58
+ }
59
+ int32_t at = xt_string_find(s, needle, xt_string_byte_offset(s, from));
60
+ return xt_number(at < 0 ? -1.0 : (double)xt_string_unit_offset(s, at));
35
61
  }
36
62
 
37
63
  static xt_value xt_string_slice(xt_value value, xt_value startValue, xt_value endValue) {
38
64
  xt_string *s = xt_as_string(value);
39
65
  if (!s) return xt_string_from_cstr("");
40
- int32_t len = (int32_t)s->length;
66
+ int32_t len = (int32_t)xt_string_utf16_length(s);
41
67
  int32_t start = startValue == XT_UNDEFINED ? 0 : xt_to_int32(startValue);
42
68
  int32_t end = endValue == XT_UNDEFINED ? len : xt_to_int32(endValue);
43
69
  xt_normalize_slice(len, &start, &end);
44
- return xt_string_new(s->data + start, (size_t)(end - start));
70
+ return xt_string_slice_units(s, start, end);
45
71
  }
46
72
 
47
73
  static xt_value xt_string_substring(xt_value value, xt_value startValue, xt_value endValue) {
48
74
  xt_string *s = xt_as_string(value);
49
75
  if (!s) return xt_string_from_cstr("");
50
- int32_t len = (int32_t)s->length;
76
+ int32_t len = (int32_t)xt_string_utf16_length(s);
51
77
  int32_t start = startValue == XT_UNDEFINED ? 0 : xt_to_int32(startValue);
52
78
  int32_t end = endValue == XT_UNDEFINED ? len : xt_to_int32(endValue);
53
79
  if (start < 0) start = 0;
@@ -59,13 +85,13 @@ static xt_value xt_string_substring(xt_value value, xt_value startValue, xt_valu
59
85
  start = end;
60
86
  end = swap;
61
87
  }
62
- return xt_string_new(s->data + start, (size_t)(end - start));
88
+ return xt_string_slice_units(s, start, end);
63
89
  }
64
90
 
65
91
  static xt_value xt_string_substr(xt_value value, xt_value startValue, xt_value lengthValue) {
66
92
  xt_string *s = xt_as_string(value);
67
93
  if (!s) return xt_string_from_cstr("");
68
- int32_t len = (int32_t)s->length;
94
+ int32_t len = (int32_t)xt_string_utf16_length(s);
69
95
  int32_t start = startValue == XT_UNDEFINED ? 0 : xt_to_int32(startValue);
70
96
  if (start < 0) start += len;
71
97
  if (start < 0) start = 0;
@@ -73,7 +99,7 @@ static xt_value xt_string_substr(xt_value value, xt_value startValue, xt_value l
73
99
  int32_t length = lengthValue == XT_UNDEFINED ? len - start : xt_to_int32(lengthValue);
74
100
  if (length < 0) length = 0;
75
101
  if (start + length > len) length = len - start;
76
- return xt_string_new(s->data + start, (size_t)length);
102
+ return xt_string_slice_units(s, start, start + length);
77
103
  }
78
104
 
79
105
  static xt_value xt_string_split(xt_value value, xt_value separatorValue, xt_value limitValue) {
@@ -87,21 +113,26 @@ static xt_value xt_string_split(xt_value value, xt_value separatorValue, xt_valu
87
113
  return result;
88
114
  }
89
115
  xt_string *sep = xt_as_string(xt_to_string(separatorValue));
116
+ const char *bytes = xt_string_bytes(s);
117
+ const char *sepBytes = xt_string_bytes(sep);
90
118
  if (sep->length == 0) {
91
- for (uint32_t i = 0; i < s->length; i++) xt_array_push(result, xt_string_new(s->data + i, 1));
119
+ /* Splitting on the empty string yields one string per UTF-16 code unit,
120
+ * so an emoji becomes its two surrogate halves. */
121
+ int32_t units = (int32_t)xt_string_utf16_length(s);
122
+ for (int32_t i = 0; i < units; i++) xt_array_push(result, xt_string_unit_value(s, i));
92
123
  } else {
93
124
  uint32_t start = 0;
94
125
  uint32_t i = 0;
95
126
  while (i + sep->length <= s->length) {
96
- if (memcmp(s->data + i, sep->data, sep->length) == 0) {
97
- xt_array_push(result, xt_string_new(s->data + start, i - start));
127
+ if (memcmp(bytes + i, sepBytes, sep->length) == 0) {
128
+ xt_array_push(result, xt_string_new(bytes + start, i - start));
98
129
  i += sep->length;
99
130
  start = i;
100
131
  } else {
101
132
  i++;
102
133
  }
103
134
  }
104
- xt_array_push(result, xt_string_new(s->data + start, s->length - start));
135
+ xt_array_push(result, xt_string_new(bytes + start, s->length - start));
105
136
  }
106
137
  xt_array *array = xt_as_array(result);
107
138
  if ((int64_t)array->length > limit) array->length = (uint32_t)limit;
@@ -112,21 +143,25 @@ static xt_value xt_string_case(xt_value value, int upper) {
112
143
  xt_string *s = xt_as_string(value);
113
144
  if (!s) return value;
114
145
  xt_string *out = xt_string_alloc(s->length);
146
+ const char *bytes = xt_string_bytes(s);
147
+ char *mutable = xt_string_mutable(out);
115
148
  for (uint32_t i = 0; i < s->length; i++) {
116
- unsigned char c = (unsigned char)s->data[i];
117
- out->data[i] = (char)(upper ? toupper((int)c) : tolower((int)c));
149
+ unsigned char c = (unsigned char)bytes[i];
150
+ mutable[i] = (char)(upper ? toupper((int)c) : tolower((int)c));
118
151
  }
152
+ xt_string_finish(out);
119
153
  return XT_FROM_PTR(XT_TAG_STRING, out);
120
154
  }
121
155
 
122
156
  static xt_value xt_string_trim(xt_value value) {
123
157
  xt_string *s = xt_as_string(value);
124
158
  if (!s) return value;
159
+ const char *bytes = xt_string_bytes(s);
125
160
  uint32_t start = 0;
126
161
  uint32_t end = s->length;
127
- while (start < end && isspace((unsigned char)s->data[start])) start++;
128
- while (end > start && isspace((unsigned char)s->data[end - 1])) end--;
129
- return xt_string_new(s->data + start, end - start);
162
+ while (start < end && isspace((unsigned char)bytes[start])) start++;
163
+ while (end > start && isspace((unsigned char)bytes[end - 1])) end--;
164
+ return xt_string_new(bytes + start, end - start);
130
165
  }
131
166
 
132
167
  static xt_value xt_string_replace(xt_value value, xt_value searchValue, xt_value replacementValue) {
@@ -137,12 +172,16 @@ static xt_value xt_string_replace(xt_value value, xt_value searchValue, xt_value
137
172
  xt_string *replacement = xt_as_string(xt_to_string(replacementValue));
138
173
  int32_t at = xt_string_find(s, search, 0);
139
174
  if (at < 0) return value;
175
+ const char *text = xt_string_bytes(s);
176
+ const char *repl = xt_string_bytes(replacement);
140
177
  size_t total = s->length - search->length + replacement->length;
141
178
  xt_string *out = xt_string_alloc(total);
142
- memcpy(out->data, s->data, (size_t)at);
143
- memcpy(out->data + at, replacement->data, replacement->length);
144
- memcpy(out->data + at + replacement->length, s->data + at + search->length,
179
+ char *mutable = xt_string_mutable(out);
180
+ memcpy(mutable, text, (size_t)at);
181
+ memcpy(mutable + at, repl, replacement->length);
182
+ memcpy(mutable + at + replacement->length, text + at + search->length,
145
183
  s->length - (size_t)at - search->length);
184
+ xt_string_finish(out);
146
185
  return XT_FROM_PTR(XT_TAG_STRING, out);
147
186
  }
148
187
 
@@ -155,24 +194,54 @@ static xt_value xt_string_repeat(xt_value value, xt_value countValue) {
155
194
  return XT_UNDEFINED;
156
195
  }
157
196
  xt_string *out = xt_string_alloc((size_t)s->length * (size_t)count);
158
- for (int32_t i = 0; i < count; i++) memcpy(out->data + (size_t)i * s->length, s->data, s->length);
197
+ const char *bytes = xt_string_bytes(s);
198
+ char *mutable = xt_string_mutable(out);
199
+ for (int32_t i = 0; i < count; i++) memcpy(mutable + (size_t)i * s->length, bytes, s->length);
200
+ xt_string_finish(out);
159
201
  return XT_FROM_PTR(XT_TAG_STRING, out);
160
202
  }
161
203
 
162
- static xt_value xt_string_starts_with(xt_value value, xt_value searchValue) {
204
+ static xt_value xt_string_starts_with(xt_value value, xt_value searchValue, xt_value positionValue) {
163
205
  xt_string *s = xt_as_string(value);
164
206
  if (!s) return XT_FALSE;
165
207
  xt_string *search = xt_as_string(xt_to_string(searchValue));
208
+ const char *sBytes = xt_string_bytes(s);
209
+ const char *searchBytes = xt_string_bytes(search);
210
+ int32_t units = (int32_t)xt_string_utf16_length(s);
211
+ int32_t position = positionValue == XT_UNDEFINED ? 0 : xt_to_int32(positionValue);
212
+ if (position < 0) position = 0;
213
+ if (position > units) position = units;
166
214
  if (search->length > s->length) return XT_FALSE;
167
- return xt_bool(memcmp(s->data, search->data, search->length) == 0);
215
+ if (!xt_string_is_boundary(s, position)) {
216
+ xt_string *tail = xt_as_string(xt_string_slice_units(s, position, units));
217
+ if (search->length > tail->length) return XT_FALSE;
218
+ return xt_bool(memcmp(xt_string_bytes(tail), searchBytes, search->length) == 0);
219
+ }
220
+ int32_t at = xt_string_byte_offset(s, position);
221
+ if ((uint32_t)at + search->length > s->length) return XT_FALSE;
222
+ return xt_bool(memcmp(sBytes + at, searchBytes, search->length) == 0);
168
223
  }
169
224
 
170
- static xt_value xt_string_ends_with(xt_value value, xt_value searchValue) {
225
+ static xt_value xt_string_ends_with(xt_value value, xt_value searchValue, xt_value endValue) {
171
226
  xt_string *s = xt_as_string(value);
172
227
  if (!s) return XT_FALSE;
173
228
  xt_string *search = xt_as_string(xt_to_string(searchValue));
229
+ const char *sBytes = xt_string_bytes(s);
230
+ const char *searchBytes = xt_string_bytes(search);
231
+ int32_t units = (int32_t)xt_string_utf16_length(s);
232
+ int32_t end = endValue == XT_UNDEFINED ? units : xt_to_int32(endValue);
233
+ if (end < 0) end = 0;
234
+ if (end > units) end = units;
174
235
  if (search->length > s->length) return XT_FALSE;
175
- return xt_bool(memcmp(s->data + s->length - search->length, search->data, search->length) == 0);
236
+ if (!xt_string_is_boundary(s, end)) {
237
+ xt_string *prefix = xt_as_string(xt_string_slice_units(s, 0, end));
238
+ if (search->length > prefix->length) return XT_FALSE;
239
+ return xt_bool(memcmp(xt_string_bytes(prefix) + prefix->length - search->length,
240
+ searchBytes, search->length) == 0);
241
+ }
242
+ int32_t at = xt_string_byte_offset(s, end);
243
+ if ((uint32_t)at < search->length) return XT_FALSE;
244
+ return xt_bool(memcmp(sBytes + at - search->length, searchBytes, search->length) == 0);
176
245
  }
177
246
 
178
247
  static xt_value xt_string_concat_args(xt_value value, int32_t argc, xt_value *argv) {
@@ -18,8 +18,9 @@ xt_value xt_array_static(xt_value name, int32_t argc, xt_value *argv) {
18
18
  }
19
19
  } else if (XT_IS_STRING(source)) {
20
20
  xt_string *s = xt_as_string(source);
21
- for (uint32_t i = 0; i < s->length; i++) {
22
- xt_value item = xt_string_new(s->data + i, 1);
21
+ uint32_t points = xt_string_code_point_count(s);
22
+ for (uint32_t i = 0; i < points; i++) {
23
+ xt_value item = xt_string_code_point_value(s, (int32_t)i);
23
24
  if (XT_IS_FUNCTION(mapper)) { xt_value args[2] = {item, xt_number((double)i)}; item = xt_closure_call(mapper, 2, args); }
24
25
  xt_array_push(out, item);
25
26
  }
@@ -46,7 +46,7 @@ static int xt_array_default_compare(xt_value a, xt_value b) {
46
46
  xt_string *sa = xt_as_string(xt_to_string(a));
47
47
  xt_string *sb = xt_as_string(xt_to_string(b));
48
48
  uint32_t n = sa->length < sb->length ? sa->length : sb->length;
49
- int cmp = n ? memcmp(sa->data, sb->data, n) : 0;
49
+ int cmp = n ? memcmp(xt_string_bytes(sa), xt_string_bytes(sb), n) : 0;
50
50
  if (cmp != 0) return cmp < 0 ? -1 : 1;
51
51
  if (sa->length == sb->length) return 0;
52
52
  return sa->length < sb->length ? -1 : 1;
@@ -26,6 +26,8 @@ static xt_value xt_error_proto_for(const char *name) {
26
26
  xt_string_from_cstr("Error"));
27
27
  xt_object_set(xt_error_root_proto, xt_string_from_cstr("message"),
28
28
  xt_string_from_cstr(""));
29
+ xt_value constructor = xt_error_constructor(xt_string_from_cstr("Error"));
30
+ xt_object_set(xt_error_root_proto, xt_string_from_cstr("constructor"), constructor);
29
31
  }
30
32
  if (strcmp(name, "Error") == 0) return xt_error_root_proto;
31
33
  xt_value key = xt_string_from_cstr(name);
@@ -41,6 +43,8 @@ static xt_value xt_error_proto_for(const char *name) {
41
43
  xt_object_set(proto, xt_string_from_cstr("name"), key);
42
44
  xt_object_set(proto, xt_string_from_cstr("message"), xt_string_from_cstr(""));
43
45
  xt_object_set(registry, key, proto);
46
+ xt_value constructor = xt_error_constructor(key);
47
+ xt_object_set(proto, xt_string_from_cstr("constructor"), constructor);
44
48
  return proto;
45
49
  }
46
50
 
@@ -50,6 +54,15 @@ xt_value xt_error_prototype(xt_value nameValue) {
50
54
  return xt_error_proto_for(name);
51
55
  }
52
56
 
57
+ /* Capture `Error.stack` into the object's private field (not a property, so it
58
+ * stays out of Object.keys/JSON.stringify, like V8's own non-enumerable stack). */
59
+ static void xt_error_capture_stack(xt_value error, const char *name, xt_value message) {
60
+ if (!XT_IS_OBJECT(error)) return;
61
+ xt_string *text = xt_as_string(xt_to_string(message));
62
+ xt_value stack = xt_stack_capture(name, text ? xt_string_bytes(text) : "");
63
+ ((xt_object *)XT_GET_PTR(error))->error_stack = stack;
64
+ }
65
+
53
66
  static xt_value xt_make_error(const char *name, int32_t argc, xt_value *argv) {
54
67
  if (!xt_error_is_family(name)) name = "Error";
55
68
  xt_value error = xt_object_new();
@@ -64,9 +77,10 @@ static xt_value xt_make_error(const char *name, int32_t argc, xt_value *argv) {
64
77
  } else {
65
78
  message = xt_arg_at(argc, argv, 0);
66
79
  }
80
+ xt_value messageValue = message == XT_UNDEFINED ? xt_string_from_cstr("") : message;
67
81
  xt_object_set(error, xt_string_from_cstr("name"), xt_string_from_cstr(name));
68
- xt_object_set(error, xt_string_from_cstr("message"),
69
- message == XT_UNDEFINED ? xt_string_from_cstr("") : message);
82
+ xt_object_set(error, xt_string_from_cstr("message"), messageValue);
83
+ xt_error_capture_stack(error, name, messageValue);
70
84
  return error;
71
85
  }
72
86
 
@@ -110,10 +124,10 @@ xt_value xt_error_to_string(xt_value error) {
110
124
  if (ms->length == 0) return nameStr;
111
125
  size_t total = ns->length + 2 + ms->length;
112
126
  char *buffer = (char *)malloc(total + 1);
113
- memcpy(buffer, ns->data, ns->length);
127
+ memcpy(buffer, xt_string_bytes(ns), ns->length);
114
128
  buffer[ns->length] = ':';
115
129
  buffer[ns->length + 1] = ' ';
116
- memcpy(buffer + ns->length + 2, ms->data, ms->length);
130
+ memcpy(buffer + ns->length + 2, xt_string_bytes(ms), ms->length);
117
131
  buffer[total] = '\0';
118
132
  xt_value result = xt_string_new(buffer, total);
119
133
  free(buffer);
@@ -152,6 +166,13 @@ xt_value xt_aggregate_error_ctor(int32_t argc, xt_value *argv) {
152
166
  return xt_make_error("AggregateError", argc, argv);
153
167
  }
154
168
 
169
+ /* Dispatch used by the first-class constructor factory (`const E = TypeError`). */
170
+ xt_value xt_error_ctor_by_name(xt_value nameValue, int32_t argc, xt_value *argv) {
171
+ const char *name = XT_IS_STRING(nameValue) ? xt_string_data(nameValue) : "Error";
172
+ if (!xt_error_is_family(name)) name = "Error";
173
+ return xt_make_error(name, argc, argv);
174
+ }
175
+
155
176
  /* `super(message)` inside a subclass of the builtin Error family: initialize
156
177
  * the already-allocated instance instead of creating a fresh object. The
157
178
  * parent's name selects the default `name` (`TypeError`, ...). */
@@ -159,6 +180,11 @@ xt_value xt_error_init(xt_value thisValue, xt_value nameValue, int32_t argc,
159
180
  xt_value *argv) {
160
181
  const char *name = XT_IS_STRING(nameValue) ? xt_string_data(nameValue) : "Error";
161
182
  if (!xt_error_is_family(name)) name = "Error";
183
+ /* `class MyError extends Error` allocates a plain object first; mark it as an
184
+ * Error so `.stack` and the error-specific property access apply. */
185
+ if (XT_IS_OBJECT(thisValue)) {
186
+ ((xt_object *)XT_GET_PTR(thisValue))->header.kind = XT_OBJECT_KIND_ERROR;
187
+ }
162
188
  xt_object_set(thisValue, xt_string_from_cstr("name"), xt_string_from_cstr(name));
163
189
  xt_value message;
164
190
  if (strcmp(name, "AggregateError") == 0) {
@@ -167,7 +193,8 @@ xt_value xt_error_init(xt_value thisValue, xt_value nameValue, int32_t argc,
167
193
  } else {
168
194
  message = xt_arg_at(argc, argv, 0);
169
195
  }
170
- xt_object_set(thisValue, xt_string_from_cstr("message"),
171
- message == XT_UNDEFINED ? xt_string_from_cstr("") : message);
196
+ xt_value messageValue = message == XT_UNDEFINED ? xt_string_from_cstr("") : message;
197
+ xt_object_set(thisValue, xt_string_from_cstr("message"), messageValue);
198
+ xt_error_capture_stack(thisValue, name, messageValue);
172
199
  return thisValue;
173
200
  }