xbintsc 0.3.81 → 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 (157) hide show
  1. package/README.md +12 -2
  2. package/README.zh-CN.md +12 -4
  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 +43 -57
  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/parser/expressions.js +20 -1
  56. package/dist/src/parser/expressions.js.map +1 -1
  57. package/dist/tests/cli/main.test.js +41 -1
  58. package/dist/tests/cli/main.test.js.map +1 -1
  59. package/dist/tests/codegen/expressions.test.js +14 -3
  60. package/dist/tests/codegen/expressions.test.js.map +1 -1
  61. package/dist/tests/codegen/llvm.test.js +23 -0
  62. package/dist/tests/codegen/llvm.test.js.map +1 -1
  63. package/dist/tests/driver/toolchain-provider.test.js +49 -2
  64. package/dist/tests/driver/toolchain-provider.test.js.map +1 -1
  65. package/dist/tests/driver/toolchain.test.js +62 -3
  66. package/dist/tests/driver/toolchain.test.js.map +1 -1
  67. package/dist/tests/e2e/child-process.test.d.ts +10 -0
  68. package/dist/tests/e2e/child-process.test.js +59 -0
  69. package/dist/tests/e2e/child-process.test.js.map +1 -0
  70. package/dist/tests/e2e/compile-objects-classes.test.js +47 -0
  71. package/dist/tests/e2e/compile-objects-classes.test.js.map +1 -1
  72. package/dist/tests/e2e/error-stack.test.d.ts +9 -0
  73. package/dist/tests/e2e/error-stack.test.js +94 -0
  74. package/dist/tests/e2e/error-stack.test.js.map +1 -0
  75. package/dist/tests/e2e/string-concat.test.d.ts +9 -0
  76. package/dist/tests/e2e/string-concat.test.js +102 -0
  77. package/dist/tests/e2e/string-concat.test.js.map +1 -0
  78. package/dist/tests/helpers.d.ts +9 -0
  79. package/dist/tests/helpers.js +26 -3
  80. package/dist/tests/helpers.js.map +1 -1
  81. package/dist/tests/parser/expressions.test.js +7 -0
  82. package/dist/tests/parser/expressions.test.js.map +1 -1
  83. package/dist/tests/version.test.d.ts +1 -0
  84. package/dist/tests/version.test.js +24 -0
  85. package/dist/tests/version.test.js.map +1 -0
  86. package/doc/DESIGN.md +20 -12
  87. package/doc/ai/cli.md +2 -0
  88. package/doc/ai/contributing.md +7 -1
  89. package/doc/ai/language-support.md +4 -4
  90. package/doc/ai/troubleshooting.md +39 -2
  91. package/doc/ai/zh-CN/cli.md +2 -0
  92. package/doc/ai/zh-CN/contributing.md +6 -2
  93. package/doc/ai/zh-CN/language-support.md +4 -4
  94. package/doc/ai/zh-CN/troubleshooting.md +35 -2
  95. package/doc/implemented.md +3 -3
  96. package/doc/requirements.md +13 -4
  97. package/doc/unimplemented.md +11 -10
  98. package/doc/zh-CN/DESIGN.md +15 -9
  99. package/doc/zh-CN/implemented.md +3 -3
  100. package/doc/zh-CN/requirements.md +12 -3
  101. package/doc/zh-CN/unimplemented.md +10 -9
  102. package/package.json +3 -1
  103. package/runtime/ext_node/buffer/parts/construction.inc +2 -2
  104. package/runtime/ext_node/buffer/parts/methods.inc +1 -1
  105. package/runtime/ext_node/buffer/parts/search.inc +1 -1
  106. package/runtime/ext_node/child_process/child_process.c +25 -0
  107. package/runtime/ext_node/fs/fd_ops.c +1 -1
  108. package/runtime/rt.h +17 -1
  109. package/runtime/rt_internal.h +47 -4
  110. package/runtime/xt_alloc.c +14 -2
  111. package/runtime/xt_bigint/conversions.inc +1 -1
  112. package/runtime/xt_builtins.c +136 -14
  113. package/runtime/xt_containers/access.inc +17 -16
  114. package/runtime/xt_containers/objects.inc +4 -3
  115. package/runtime/xt_io/exception.inc +171 -2
  116. package/runtime/xt_io/inspect.inc +15 -13
  117. package/runtime/xt_io.c +5 -0
  118. package/runtime/xt_stdlib/arrays.inc +2 -2
  119. package/runtime/xt_stdlib/dispatch.inc +2 -2
  120. package/runtime/xt_stdlib/strings.inc +33 -21
  121. package/runtime/xt_stdlib2/array-statics.inc +2 -1
  122. package/runtime/xt_stdlib2/arrays.inc +1 -1
  123. package/runtime/xt_stdlib2/error.inc +22 -6
  124. package/runtime/xt_stdlib2/json.inc +3 -2
  125. package/runtime/xt_stdlib2/number-string-statics.inc +3 -1
  126. package/runtime/xt_stdlib2/regexp-match.inc +48 -33
  127. package/runtime/xt_stdlib2/strings.inc +23 -14
  128. package/runtime/xt_symbol.c +1 -1
  129. package/runtime/xt_values/comparison.inc +2 -2
  130. package/runtime/xt_values/constructors.inc +10 -5
  131. package/runtime/xt_values/conversions.inc +4 -3
  132. package/runtime/xt_values/strings.inc +78 -4
  133. package/scripts/bench.ts +172 -0
  134. package/scripts/build-runtime.ts +2 -1
  135. package/scripts/check-version.ts +53 -0
  136. package/src/ast/common.ts +23 -0
  137. package/src/cli/main.ts +68 -26
  138. package/src/codegen/generator/calls/invocation.ts +5 -0
  139. package/src/codegen/generator/context.ts +129 -18
  140. package/src/codegen/generator/debug.ts +132 -0
  141. package/src/codegen/generator/expressions/assignment.ts +9 -5
  142. package/src/codegen/generator/expressions/operators.ts +47 -8
  143. package/src/codegen/generator/expressions/primary.ts +5 -2
  144. package/src/codegen/generator/generator.ts +18 -2
  145. package/src/codegen/generator/module.ts +78 -6
  146. package/src/codegen/generator/numeric.ts +158 -0
  147. package/src/codegen/generator/state.ts +22 -0
  148. package/src/codegen/generator/statements/control-flow.ts +1 -1
  149. package/src/codegen/generator/statements/dispatch.ts +3 -0
  150. package/src/codegen/generator/statements/variables.ts +8 -2
  151. package/src/codegen/generator/tables.ts +26 -0
  152. package/src/driver/bundler/merge.ts +39 -5
  153. package/src/driver/compiler.ts +62 -55
  154. package/src/driver/fingerprint.ts +85 -0
  155. package/src/driver/toolchain-provider.ts +17 -16
  156. package/src/driver/toolchain.ts +210 -21
  157. package/src/parser/expressions.ts +20 -1
@@ -322,7 +322,7 @@ xt_value xt_call_method(xt_value target, xt_value name, int32_t argc, xt_value *
322
322
  /* Bound functions report `name` as "bound <target>" and reduce `length`
323
323
  * by the number of pre-bound arguments, like the specification. */
324
324
  xt_function *targetFn = (xt_function *)XT_GET_PTR(target);
325
- const char *base = targetFn->name ? targetFn->name->data : "";
325
+ const char *base = targetFn->name ? xt_string_bytes(targetFn->name) : "";
326
326
  char boundName[256];
327
327
  snprintf(boundName, sizeof(boundName), "bound %s", base);
328
328
  int32_t prebound = argc > 1 ? argc - 1 : 0;
@@ -336,7 +336,7 @@ xt_value xt_call_method(xt_value target, xt_value name, int32_t argc, xt_value *
336
336
  if (strcmp(method, "toString") == 0) {
337
337
  /* Source text is not retained; report a native-style representation. */
338
338
  xt_function *self = (xt_function *)XT_GET_PTR(target);
339
- const char *selfName = self->name ? self->name->data : "";
339
+ const char *selfName = self->name ? xt_string_bytes(self->name) : "";
340
340
  char buffer[256];
341
341
  snprintf(buffer, sizeof(buffer), "function %s() { [native code] }", selfName);
342
342
  return xt_string_from_cstr(buffer);
@@ -4,8 +4,10 @@ static int32_t xt_string_find(xt_string *haystack, xt_string *needle, int32_t fr
4
4
  if (from < 0) from = 0;
5
5
  if ((uint32_t)from > haystack->length) return -1;
6
6
  if (needle->length == 0) return from;
7
+ const char *hay = xt_string_bytes(haystack);
8
+ const char *need = xt_string_bytes(needle);
7
9
  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;
10
+ if (memcmp(hay + i, need, needle->length) == 0) return (int32_t)i;
9
11
  }
10
12
  return -1;
11
13
  }
@@ -15,7 +17,7 @@ static xt_value xt_string_char_at(xt_value value, xt_value indexValue) {
15
17
  if (!s) return xt_string_from_cstr("");
16
18
  int32_t index = xt_to_int32(indexValue);
17
19
  if (index < 0 || (uint32_t)index >= s->length) return xt_string_from_cstr("");
18
- return xt_string_new(s->data + index, 1);
20
+ return xt_string_new(xt_string_bytes(s) + index, 1);
19
21
  }
20
22
 
21
23
  static xt_value xt_string_char_code_at(xt_value value, xt_value indexValue) {
@@ -23,7 +25,7 @@ static xt_value xt_string_char_code_at(xt_value value, xt_value indexValue) {
23
25
  if (!s) return xt_number(NAN);
24
26
  int32_t index = xt_to_int32(indexValue);
25
27
  if (index < 0 || (uint32_t)index >= s->length) return xt_number(NAN);
26
- return xt_number((double)(unsigned char)s->data[index]);
28
+ return xt_number((double)(unsigned char)xt_string_bytes(s)[index]);
27
29
  }
28
30
 
29
31
  static xt_value xt_string_index_of(xt_value value, xt_value needleValue, xt_value fromValue) {
@@ -41,7 +43,7 @@ static xt_value xt_string_slice(xt_value value, xt_value startValue, xt_value en
41
43
  int32_t start = startValue == XT_UNDEFINED ? 0 : xt_to_int32(startValue);
42
44
  int32_t end = endValue == XT_UNDEFINED ? len : xt_to_int32(endValue);
43
45
  xt_normalize_slice(len, &start, &end);
44
- return xt_string_new(s->data + start, (size_t)(end - start));
46
+ return xt_string_new(xt_string_bytes(s) + start, (size_t)(end - start));
45
47
  }
46
48
 
47
49
  static xt_value xt_string_substring(xt_value value, xt_value startValue, xt_value endValue) {
@@ -59,7 +61,7 @@ static xt_value xt_string_substring(xt_value value, xt_value startValue, xt_valu
59
61
  start = end;
60
62
  end = swap;
61
63
  }
62
- return xt_string_new(s->data + start, (size_t)(end - start));
64
+ return xt_string_new(xt_string_bytes(s) + start, (size_t)(end - start));
63
65
  }
64
66
 
65
67
  static xt_value xt_string_substr(xt_value value, xt_value startValue, xt_value lengthValue) {
@@ -73,7 +75,7 @@ static xt_value xt_string_substr(xt_value value, xt_value startValue, xt_value l
73
75
  int32_t length = lengthValue == XT_UNDEFINED ? len - start : xt_to_int32(lengthValue);
74
76
  if (length < 0) length = 0;
75
77
  if (start + length > len) length = len - start;
76
- return xt_string_new(s->data + start, (size_t)length);
78
+ return xt_string_new(xt_string_bytes(s) + start, (size_t)length);
77
79
  }
78
80
 
79
81
  static xt_value xt_string_split(xt_value value, xt_value separatorValue, xt_value limitValue) {
@@ -87,21 +89,23 @@ static xt_value xt_string_split(xt_value value, xt_value separatorValue, xt_valu
87
89
  return result;
88
90
  }
89
91
  xt_string *sep = xt_as_string(xt_to_string(separatorValue));
92
+ const char *bytes = xt_string_bytes(s);
93
+ const char *sepBytes = xt_string_bytes(sep);
90
94
  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));
95
+ for (uint32_t i = 0; i < s->length; i++) xt_array_push(result, xt_string_new(bytes + i, 1));
92
96
  } else {
93
97
  uint32_t start = 0;
94
98
  uint32_t i = 0;
95
99
  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));
100
+ if (memcmp(bytes + i, sepBytes, sep->length) == 0) {
101
+ xt_array_push(result, xt_string_new(bytes + start, i - start));
98
102
  i += sep->length;
99
103
  start = i;
100
104
  } else {
101
105
  i++;
102
106
  }
103
107
  }
104
- xt_array_push(result, xt_string_new(s->data + start, s->length - start));
108
+ xt_array_push(result, xt_string_new(bytes + start, s->length - start));
105
109
  }
106
110
  xt_array *array = xt_as_array(result);
107
111
  if ((int64_t)array->length > limit) array->length = (uint32_t)limit;
@@ -112,9 +116,11 @@ static xt_value xt_string_case(xt_value value, int upper) {
112
116
  xt_string *s = xt_as_string(value);
113
117
  if (!s) return value;
114
118
  xt_string *out = xt_string_alloc(s->length);
119
+ const char *bytes = xt_string_bytes(s);
120
+ char *mutable = xt_string_mutable(out);
115
121
  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));
122
+ unsigned char c = (unsigned char)bytes[i];
123
+ mutable[i] = (char)(upper ? toupper((int)c) : tolower((int)c));
118
124
  }
119
125
  return XT_FROM_PTR(XT_TAG_STRING, out);
120
126
  }
@@ -122,11 +128,12 @@ static xt_value xt_string_case(xt_value value, int upper) {
122
128
  static xt_value xt_string_trim(xt_value value) {
123
129
  xt_string *s = xt_as_string(value);
124
130
  if (!s) return value;
131
+ const char *bytes = xt_string_bytes(s);
125
132
  uint32_t start = 0;
126
133
  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);
134
+ while (start < end && isspace((unsigned char)bytes[start])) start++;
135
+ while (end > start && isspace((unsigned char)bytes[end - 1])) end--;
136
+ return xt_string_new(bytes + start, end - start);
130
137
  }
131
138
 
132
139
  static xt_value xt_string_replace(xt_value value, xt_value searchValue, xt_value replacementValue) {
@@ -137,11 +144,14 @@ static xt_value xt_string_replace(xt_value value, xt_value searchValue, xt_value
137
144
  xt_string *replacement = xt_as_string(xt_to_string(replacementValue));
138
145
  int32_t at = xt_string_find(s, search, 0);
139
146
  if (at < 0) return value;
147
+ const char *text = xt_string_bytes(s);
148
+ const char *repl = xt_string_bytes(replacement);
140
149
  size_t total = s->length - search->length + replacement->length;
141
150
  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,
151
+ char *mutable = xt_string_mutable(out);
152
+ memcpy(mutable, text, (size_t)at);
153
+ memcpy(mutable + at, repl, replacement->length);
154
+ memcpy(mutable + at + replacement->length, text + at + search->length,
145
155
  s->length - (size_t)at - search->length);
146
156
  return XT_FROM_PTR(XT_TAG_STRING, out);
147
157
  }
@@ -155,7 +165,9 @@ static xt_value xt_string_repeat(xt_value value, xt_value countValue) {
155
165
  return XT_UNDEFINED;
156
166
  }
157
167
  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);
168
+ const char *bytes = xt_string_bytes(s);
169
+ char *mutable = xt_string_mutable(out);
170
+ for (int32_t i = 0; i < count; i++) memcpy(mutable + (size_t)i * s->length, bytes, s->length);
159
171
  return XT_FROM_PTR(XT_TAG_STRING, out);
160
172
  }
161
173
 
@@ -164,7 +176,7 @@ static xt_value xt_string_starts_with(xt_value value, xt_value searchValue) {
164
176
  if (!s) return XT_FALSE;
165
177
  xt_string *search = xt_as_string(xt_to_string(searchValue));
166
178
  if (search->length > s->length) return XT_FALSE;
167
- return xt_bool(memcmp(s->data, search->data, search->length) == 0);
179
+ return xt_bool(memcmp(xt_string_bytes(s), xt_string_bytes(search), search->length) == 0);
168
180
  }
169
181
 
170
182
  static xt_value xt_string_ends_with(xt_value value, xt_value searchValue) {
@@ -172,7 +184,7 @@ static xt_value xt_string_ends_with(xt_value value, xt_value searchValue) {
172
184
  if (!s) return XT_FALSE;
173
185
  xt_string *search = xt_as_string(xt_to_string(searchValue));
174
186
  if (search->length > s->length) return XT_FALSE;
175
- return xt_bool(memcmp(s->data + s->length - search->length, search->data, search->length) == 0);
187
+ return xt_bool(memcmp(xt_string_bytes(s) + s->length - search->length, xt_string_bytes(search), search->length) == 0);
176
188
  }
177
189
 
178
190
  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
+ const char *bytes = xt_string_bytes(s);
21
22
  for (uint32_t i = 0; i < s->length; i++) {
22
- xt_value item = xt_string_new(s->data + i, 1);
23
+ xt_value item = xt_string_new(bytes + i, 1);
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;
@@ -50,6 +50,15 @@ xt_value xt_error_prototype(xt_value nameValue) {
50
50
  return xt_error_proto_for(name);
51
51
  }
52
52
 
53
+ /* Capture `Error.stack` into the object's private field (not a property, so it
54
+ * stays out of Object.keys/JSON.stringify, like V8's own non-enumerable stack). */
55
+ static void xt_error_capture_stack(xt_value error, const char *name, xt_value message) {
56
+ if (!XT_IS_OBJECT(error)) return;
57
+ xt_string *text = xt_as_string(xt_to_string(message));
58
+ xt_value stack = xt_stack_capture(name, text ? xt_string_bytes(text) : "");
59
+ ((xt_object *)XT_GET_PTR(error))->error_stack = stack;
60
+ }
61
+
53
62
  static xt_value xt_make_error(const char *name, int32_t argc, xt_value *argv) {
54
63
  if (!xt_error_is_family(name)) name = "Error";
55
64
  xt_value error = xt_object_new();
@@ -64,9 +73,10 @@ static xt_value xt_make_error(const char *name, int32_t argc, xt_value *argv) {
64
73
  } else {
65
74
  message = xt_arg_at(argc, argv, 0);
66
75
  }
76
+ xt_value messageValue = message == XT_UNDEFINED ? xt_string_from_cstr("") : message;
67
77
  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);
78
+ xt_object_set(error, xt_string_from_cstr("message"), messageValue);
79
+ xt_error_capture_stack(error, name, messageValue);
70
80
  return error;
71
81
  }
72
82
 
@@ -110,10 +120,10 @@ xt_value xt_error_to_string(xt_value error) {
110
120
  if (ms->length == 0) return nameStr;
111
121
  size_t total = ns->length + 2 + ms->length;
112
122
  char *buffer = (char *)malloc(total + 1);
113
- memcpy(buffer, ns->data, ns->length);
123
+ memcpy(buffer, xt_string_bytes(ns), ns->length);
114
124
  buffer[ns->length] = ':';
115
125
  buffer[ns->length + 1] = ' ';
116
- memcpy(buffer + ns->length + 2, ms->data, ms->length);
126
+ memcpy(buffer + ns->length + 2, xt_string_bytes(ms), ms->length);
117
127
  buffer[total] = '\0';
118
128
  xt_value result = xt_string_new(buffer, total);
119
129
  free(buffer);
@@ -159,6 +169,11 @@ xt_value xt_error_init(xt_value thisValue, xt_value nameValue, int32_t argc,
159
169
  xt_value *argv) {
160
170
  const char *name = XT_IS_STRING(nameValue) ? xt_string_data(nameValue) : "Error";
161
171
  if (!xt_error_is_family(name)) name = "Error";
172
+ /* `class MyError extends Error` allocates a plain object first; mark it as an
173
+ * Error so `.stack` and the error-specific property access apply. */
174
+ if (XT_IS_OBJECT(thisValue)) {
175
+ ((xt_object *)XT_GET_PTR(thisValue))->header.kind = XT_OBJECT_KIND_ERROR;
176
+ }
162
177
  xt_object_set(thisValue, xt_string_from_cstr("name"), xt_string_from_cstr(name));
163
178
  xt_value message;
164
179
  if (strcmp(name, "AggregateError") == 0) {
@@ -167,7 +182,8 @@ xt_value xt_error_init(xt_value thisValue, xt_value nameValue, int32_t argc,
167
182
  } else {
168
183
  message = xt_arg_at(argc, argv, 0);
169
184
  }
170
- xt_object_set(thisValue, xt_string_from_cstr("message"),
171
- message == XT_UNDEFINED ? xt_string_from_cstr("") : message);
185
+ xt_value messageValue = message == XT_UNDEFINED ? xt_string_from_cstr("") : message;
186
+ xt_object_set(thisValue, xt_string_from_cstr("message"), messageValue);
187
+ xt_error_capture_stack(thisValue, name, messageValue);
172
188
  return thisValue;
173
189
  }
@@ -120,7 +120,7 @@ xt_value xt_json_parse(int32_t argc, xt_value *argv) {
120
120
  xt_value input = xt_to_string(xt_arg_at(argc, argv, 0));
121
121
  xt_string *s = xt_as_string(input);
122
122
  if (!s) return XT_UNDEFINED;
123
- xt_json_cursor cursor = {s->data, s->length, 0};
123
+ xt_json_cursor cursor = {xt_string_bytes(s), s->length, 0};
124
124
  return xt_json_parse_value(&cursor);
125
125
  }
126
126
 
@@ -193,9 +193,10 @@ static void xt_json_stringify_value(xt_value value, int indent, int depth, xt_va
193
193
  }
194
194
  if (XT_IS_STRING(value)) {
195
195
  xt_string *s = xt_as_string(value);
196
+ const char *bytes = xt_string_bytes(s);
196
197
  *out = xt_add(*out, xt_string_from_cstr("\""));
197
198
  for (uint32_t i = 0; i < s->length; i++) {
198
- char ch = s->data[i];
199
+ char ch = bytes[i];
199
200
  if (ch == '"') *out = xt_add(*out, xt_string_from_cstr("\\\""));
200
201
  else if (ch == '\\') *out = xt_add(*out, xt_string_from_cstr("\\\\"));
201
202
  else if (ch == '\n') *out = xt_add(*out, xt_string_from_cstr("\\n"));
@@ -43,6 +43,7 @@ xt_value xt_string_static(xt_value name, int32_t argc, xt_value *argv) {
43
43
  if (!fn) return XT_UNDEFINED;
44
44
  if (strcmp(fn, "fromCharCode") == 0 || strcmp(fn, "fromCodePoint") == 0) {
45
45
  xt_string *out = xt_string_alloc((size_t)argc * 4 + 1);
46
+ char *mutable = xt_string_mutable(out);
46
47
  size_t length = 0;
47
48
  for (int32_t i = 0; i < argc; i++) {
48
49
  unsigned code = (unsigned)xt_to_int32(argv[i]);
@@ -52,9 +53,10 @@ xt_value xt_string_static(xt_value name, int32_t argc, xt_value *argv) {
52
53
  else if (code < 0x800) { buffer[bytes++] = (char)(0xC0 | (code >> 6)); buffer[bytes++] = (char)(0x80 | (code & 0x3F)); }
53
54
  else if (code < 0x10000) { buffer[bytes++] = (char)(0xE0 | (code >> 12)); buffer[bytes++] = (char)(0x80 | ((code >> 6) & 0x3F)); buffer[bytes++] = (char)(0x80 | (code & 0x3F)); }
54
55
  else { buffer[bytes++] = (char)(0xF0 | (code >> 18)); buffer[bytes++] = (char)(0x80 | ((code >> 12) & 0x3F)); buffer[bytes++] = (char)(0x80 | ((code >> 6) & 0x3F)); buffer[bytes++] = (char)(0x80 | (code & 0x3F)); }
55
- memcpy(out->data + length, buffer, (size_t)bytes);
56
+ memcpy(mutable + length, buffer, (size_t)bytes);
56
57
  length += (size_t)bytes;
57
58
  }
59
+ mutable[length] = '\0';
58
60
  out->length = (uint32_t)length;
59
61
  return XT_FROM_PTR(XT_TAG_STRING, out);
60
62
  }
@@ -109,8 +109,11 @@ static int xt_regexp_run(xt_value regexpValue, xt_value input, int32_t start,
109
109
  if ((uint32_t)start > text->length) return 0;
110
110
  int flags = REG_EXTENDED;
111
111
  xt_string *f = xt_as_string(regexp->flags);
112
- if (f) for (uint32_t i = 0; i < f->length; i++) if (f->data[i] == 'i') flags |= REG_ICASE;
113
- char *translated = xt_regexp_translate_js(pattern->data, jsGroups, groupCount);
112
+ if (f) {
113
+ const char *flagBytes = xt_string_bytes(f);
114
+ for (uint32_t i = 0; i < f->length; i++) if (flagBytes[i] == 'i') flags |= REG_ICASE;
115
+ }
116
+ char *translated = xt_regexp_translate_js(xt_string_bytes(pattern), jsGroups, groupCount);
114
117
  if (!translated) return 0;
115
118
  regex_t compiled;
116
119
  if (regcomp(&compiled, translated, flags) != 0) {
@@ -118,7 +121,7 @@ static int xt_regexp_run(xt_value regexpValue, xt_value input, int32_t start,
118
121
  return 0;
119
122
  }
120
123
  free(translated);
121
- int ok = regexec(&compiled, text->data + start, nmatch, matches, 0) == 0;
124
+ int ok = regexec(&compiled, xt_string_bytes(text) + start, nmatch, matches, 0) == 0;
122
125
  regfree(&compiled);
123
126
  if (!ok) return 0;
124
127
  for (size_t i = 0; i < nmatch; i++) {
@@ -142,7 +145,8 @@ static int xt_regexp_is_global(xt_value regexpValue) {
142
145
  xt_regexp *regexp = (xt_regexp *)XT_GET_PTR(regexpValue);
143
146
  xt_string *f = xt_as_string(regexp->flags);
144
147
  if (!f) return 0;
145
- for (uint32_t i = 0; i < f->length; i++) if (f->data[i] == 'g') return 1;
148
+ const char *flags = xt_string_bytes(f);
149
+ for (uint32_t i = 0; i < f->length; i++) if (flags[i] == 'g') return 1;
146
150
  return 0;
147
151
  }
148
152
 
@@ -154,7 +158,7 @@ static int xt_regexp_js_group_count(xt_value regexpValue) {
154
158
  int count = (int)pattern->length + 1;
155
159
  int *groups = (int *)malloc(sizeof(int) * (size_t)count);
156
160
  int groupCount = 0;
157
- char *translated = xt_regexp_translate_js(pattern->data, groups, &groupCount);
161
+ char *translated = xt_regexp_translate_js(xt_string_bytes(pattern), groups, &groupCount);
158
162
  free(translated);
159
163
  int total = 0;
160
164
  for (int i = 0; i < groupCount; i++) if (groups[i] > total) total = groups[i];
@@ -177,6 +181,7 @@ xt_value xt_regexp_exec(xt_value target, xt_value input) {
177
181
  if (!matches || !groups) { free(matches); free(groups); return XT_NULL; }
178
182
  xt_value textValue = xt_to_string(input);
179
183
  xt_string *text = xt_as_string(textValue);
184
+ const char *textBytes = xt_string_bytes(text);
180
185
  int groupCount = 0;
181
186
  if (!xt_regexp_run(target, input, 0, matches, cap, groups, &groupCount)) {
182
187
  free(matches);
@@ -186,13 +191,13 @@ xt_value xt_regexp_exec(xt_value target, xt_value input) {
186
191
  int jsTotal = 0;
187
192
  for (int i = 0; i < groupCount; i++) if (groups[i] > jsTotal) jsTotal = groups[i];
188
193
  xt_value out = xt_array_new(0, NULL);
189
- xt_array_push(out, xt_string_new(text->data + matches[0].rm_so, (int64_t)(matches[0].rm_eo - matches[0].rm_so)));
194
+ xt_array_push(out, xt_string_new(textBytes + matches[0].rm_so, (int64_t)(matches[0].rm_eo - matches[0].rm_so)));
190
195
  for (int g = 1; g <= jsTotal; g++) {
191
196
  xt_value groupValue = XT_UNDEFINED;
192
197
  for (int i = 0; i < groupCount; i++) {
193
198
  if (groups[i] != g) continue;
194
199
  if (matches[i + 1].rm_so >= 0) {
195
- groupValue = xt_string_new(text->data + matches[i + 1].rm_so, (int64_t)(matches[i + 1].rm_eo - matches[i + 1].rm_so));
200
+ groupValue = xt_string_new(textBytes + matches[i + 1].rm_so, (int64_t)(matches[i + 1].rm_eo - matches[i + 1].rm_so));
196
201
  }
197
202
  break;
198
203
  }
@@ -212,6 +217,7 @@ xt_value xt_regexp_match(xt_value input, xt_value regexp) {
212
217
  if (!xt_regexp_is_global(regexp)) return xt_regexp_exec(regexp, input);
213
218
  xt_value textValue = xt_to_string(input);
214
219
  xt_string *text = xt_as_string(textValue);
220
+ const char *textBytes = xt_string_bytes(text);
215
221
  xt_value results = xt_array_new(0, NULL);
216
222
  int32_t cursor = 0;
217
223
  int found = 0;
@@ -220,7 +226,7 @@ xt_value xt_regexp_match(xt_value input, xt_value regexp) {
220
226
  int32_t end = 0;
221
227
  if (!xt_regexp_find(regexp, input, cursor, &start, &end)) break;
222
228
  found = 1;
223
- xt_array_push(results, xt_string_new(text->data + start, (int64_t)(end - start)));
229
+ xt_array_push(results, xt_string_new(textBytes + start, (int64_t)(end - start)));
224
230
  cursor = end > start ? end : end + 1;
225
231
  }
226
232
  return found ? results : XT_NULL;
@@ -241,10 +247,11 @@ static void xt_regexp_append(char **out, size_t *length, size_t *capacity, const
241
247
  static void xt_regexp_append_group(char **out, size_t *length, size_t *capacity,
242
248
  xt_string *text, regmatch_t *matches, int *groups,
243
249
  int groupCount, int group) {
250
+ const char *textBytes = xt_string_bytes(text);
244
251
  for (int i = 0; i < groupCount; i++) {
245
252
  if (groups[i] != group) continue;
246
253
  if (matches[i + 1].rm_so >= 0) {
247
- xt_regexp_append(out, length, capacity, text->data + matches[i + 1].rm_so,
254
+ xt_regexp_append(out, length, capacity, textBytes + matches[i + 1].rm_so,
248
255
  (size_t)(matches[i + 1].rm_eo - matches[i + 1].rm_so));
249
256
  }
250
257
  return;
@@ -255,32 +262,34 @@ static void xt_regexp_expand_replacement(char **out, size_t *length, size_t *cap
255
262
  xt_string *replacement, xt_string *text,
256
263
  regmatch_t *matches, int *groups, int groupCount,
257
264
  int jsTotal) {
265
+ const char *repl = xt_string_bytes(replacement);
266
+ const char *textBytes = xt_string_bytes(text);
258
267
  size_t start = (size_t)matches[0].rm_so;
259
268
  size_t end = (size_t)matches[0].rm_eo;
260
269
  for (size_t i = 0; i < replacement->length; i++) {
261
- char c = replacement->data[i];
270
+ char c = repl[i];
262
271
  if (c != '$' || i + 1 >= replacement->length) {
263
272
  xt_regexp_append(out, length, capacity, &c, 1);
264
273
  continue;
265
274
  }
266
- char next = replacement->data[i + 1];
275
+ char next = repl[i + 1];
267
276
  if (next == '$') {
268
277
  xt_regexp_append(out, length, capacity, "$", 1);
269
278
  i++;
270
279
  } else if (next == '&') {
271
- xt_regexp_append(out, length, capacity, text->data + start, end - start);
280
+ xt_regexp_append(out, length, capacity, textBytes + start, end - start);
272
281
  i++;
273
282
  } else if (next == '`') {
274
- xt_regexp_append(out, length, capacity, text->data, start);
283
+ xt_regexp_append(out, length, capacity, textBytes, start);
275
284
  i++;
276
285
  } else if (next == '\'') {
277
- xt_regexp_append(out, length, capacity, text->data + end, text->length - end);
286
+ xt_regexp_append(out, length, capacity, textBytes + end, text->length - end);
278
287
  i++;
279
288
  } else if (next >= '0' && next <= '9') {
280
289
  int value = next - '0';
281
290
  int digits = 1;
282
- if (i + 2 < replacement->length && replacement->data[i + 2] >= '0' && replacement->data[i + 2] <= '9') {
283
- int two = value * 10 + (replacement->data[i + 2] - '0');
291
+ if (i + 2 < replacement->length && repl[i + 2] >= '0' && repl[i + 2] <= '9') {
292
+ int two = value * 10 + (repl[i + 2] - '0');
284
293
  if (two <= jsTotal) { value = two; digits = 2; }
285
294
  }
286
295
  if (value >= 1 && value <= jsTotal) {
@@ -299,6 +308,7 @@ static void xt_regexp_expand_replacement(char **out, size_t *length, size_t *cap
299
308
  xt_value xt_regexp_replace(xt_value value, xt_value regexpValue, xt_value replacementValue) {
300
309
  xt_string *text = xt_as_string(xt_to_string(value));
301
310
  if (!text) return value;
311
+ const char *textBytes = xt_string_bytes(text);
302
312
  int global = xt_regexp_is_global(regexpValue);
303
313
  int useFunction = XT_IS_FUNCTION(replacementValue);
304
314
  xt_string *replacement = NULL;
@@ -324,18 +334,18 @@ xt_value xt_regexp_replace(xt_value value, xt_value regexpValue, xt_value replac
324
334
  if (!xt_regexp_run(regexpValue, value, cursor, matches, cap, groups, &groupCount)) break;
325
335
  int32_t start = (int32_t)matches[0].rm_so;
326
336
  int32_t end = (int32_t)matches[0].rm_eo;
327
- xt_regexp_append(&out, &length, &capacity, text->data + cursor, (size_t)(start - cursor));
337
+ xt_regexp_append(&out, &length, &capacity, textBytes + cursor, (size_t)(start - cursor));
328
338
  if (useFunction) {
329
339
  /* Accumulate in an `xt_array` so the earlier match/group strings stay
330
340
  reachable while later groups (and the callback itself) allocate. */
331
341
  xt_value argArray = xt_array_new(0, NULL);
332
- xt_array_push(argArray, xt_string_new(text->data + start, (int64_t)(end - start)));
342
+ xt_array_push(argArray, xt_string_new(textBytes + start, (int64_t)(end - start)));
333
343
  for (int g = 1; g <= jsTotal; g++) {
334
344
  xt_value groupValue = XT_UNDEFINED;
335
345
  for (int i = 0; i < groupCount; i++) {
336
346
  if (groups[i] != g) continue;
337
347
  if (matches[i + 1].rm_so >= 0) {
338
- groupValue = xt_string_new(text->data + matches[i + 1].rm_so, (int64_t)(matches[i + 1].rm_eo - matches[i + 1].rm_so));
348
+ groupValue = xt_string_new(textBytes + matches[i + 1].rm_so, (int64_t)(matches[i + 1].rm_eo - matches[i + 1].rm_so));
339
349
  }
340
350
  break;
341
351
  }
@@ -345,7 +355,7 @@ xt_value xt_regexp_replace(xt_value value, xt_value regexpValue, xt_value replac
345
355
  xt_array_push(argArray, xt_to_string(value));
346
356
  xt_value produced = xt_closure_call(replacementValue, jsTotal + 3, xt_as_array(argArray)->items);
347
357
  xt_string *producedText = xt_as_string(xt_to_string(produced));
348
- if (producedText) xt_regexp_append(&out, &length, &capacity, producedText->data, producedText->length);
358
+ if (producedText) xt_regexp_append(&out, &length, &capacity, xt_string_bytes(producedText), producedText->length);
349
359
  } else {
350
360
  xt_regexp_expand_replacement(&out, &length, &capacity, replacement, text, matches, groups, groupCount, jsTotal);
351
361
  }
@@ -355,7 +365,7 @@ xt_value xt_regexp_replace(xt_value value, xt_value regexpValue, xt_value replac
355
365
  } else {
356
366
  /* Zero-width match: copy one byte so the scan always progresses. */
357
367
  if (cursor >= (int32_t)text->length) break;
358
- xt_regexp_append(&out, &length, &capacity, text->data + cursor, 1);
368
+ xt_regexp_append(&out, &length, &capacity, textBytes + cursor, 1);
359
369
  cursor++;
360
370
  }
361
371
  if (!global) break;
@@ -367,7 +377,7 @@ xt_value xt_regexp_replace(xt_value value, xt_value regexpValue, xt_value replac
367
377
  return value;
368
378
  }
369
379
  size_t tail = text->length - (size_t)cursor;
370
- xt_regexp_append(&out, &length, &capacity, text->data + cursor, tail);
380
+ xt_regexp_append(&out, &length, &capacity, textBytes + cursor, tail);
371
381
  xt_value result = xt_string_new(out, (int64_t)length);
372
382
  free(out);
373
383
  free(matches);
@@ -381,6 +391,7 @@ xt_value xt_regexp_split(xt_value value, xt_value regexpValue, xt_value limitVal
381
391
  xt_value result = xt_array_new(0, NULL);
382
392
  xt_string *text = xt_as_string(xt_to_string(value));
383
393
  if (!text) return result;
394
+ const char *textBytes = xt_string_bytes(text);
384
395
  int64_t limit = limitValue == XT_UNDEFINED ? 0x7fffffff : (int64_t)xt_to_int32(limitValue);
385
396
  if (limit <= 0) return result;
386
397
  xt_regexp *regexp = (xt_regexp *)XT_GET_PTR(regexpValue);
@@ -400,14 +411,14 @@ xt_value xt_regexp_split(xt_value value, xt_value regexpValue, xt_value limitVal
400
411
  int32_t end = (int32_t)matches[0].rm_eo;
401
412
  if (start != q) { q++; continue; }
402
413
  if (end == p) { q++; continue; }
403
- xt_array_push(result, xt_string_new(text->data + p, (int64_t)(q - p)));
414
+ xt_array_push(result, xt_string_new(textBytes + p, (int64_t)(q - p)));
404
415
  if ((int64_t)xt_as_array(result)->length >= limit) { free(matches); free(groups); return result; }
405
416
  for (int g = 1; g <= jsTotal; g++) {
406
417
  xt_value groupValue = XT_UNDEFINED;
407
418
  for (int i = 0; i < groupCount; i++) {
408
419
  if (groups[i] != g) continue;
409
420
  if (matches[i + 1].rm_so >= 0) {
410
- groupValue = xt_string_new(text->data + matches[i + 1].rm_so, (int64_t)(matches[i + 1].rm_eo - matches[i + 1].rm_so));
421
+ groupValue = xt_string_new(textBytes + matches[i + 1].rm_so, (int64_t)(matches[i + 1].rm_eo - matches[i + 1].rm_so));
411
422
  }
412
423
  break;
413
424
  }
@@ -418,7 +429,7 @@ xt_value xt_regexp_split(xt_value value, xt_value regexpValue, xt_value limitVal
418
429
  q = p;
419
430
  }
420
431
  if ((int64_t)xt_as_array(result)->length < limit) {
421
- xt_array_push(result, xt_string_new(text->data + p, (int64_t)(len - p)));
432
+ xt_array_push(result, xt_string_new(textBytes + p, (int64_t)(len - p)));
422
433
  }
423
434
  free(matches);
424
435
  free(groups);
@@ -431,16 +442,20 @@ xt_value xt_regexp_get_property(xt_value target, xt_value key) {
431
442
  if (!XT_IS_STRING(key)) return XT_UNDEFINED;
432
443
  xt_string *k = xt_as_string(key);
433
444
  xt_string *f = xt_as_string(regexp->flags);
445
+ const char *keyBytes = xt_string_bytes(k);
434
446
  int flag = 0;
435
- if (k->length == 6 && memcmp(k->data, "source", 6) == 0) return regexp->source;
436
- if (k->length == 5 && memcmp(k->data, "flags", 5) == 0) return regexp->flags;
437
- if (k->length == 9 && memcmp(k->data, "lastIndex", 9) == 0) return xt_number(0);
438
- if (k->length == 6 && memcmp(k->data, "global", 6) == 0) flag = 'g';
439
- else if (k->length == 10 && memcmp(k->data, "ignoreCase", 10) == 0) flag = 'i';
440
- else if (k->length == 9 && memcmp(k->data, "multiline", 9) == 0) flag = 'm';
441
- else if (k->length == 6 && memcmp(k->data, "sticky", 6) == 0) flag = 'y';
447
+ if (k->length == 6 && memcmp(keyBytes, "source", 6) == 0) return regexp->source;
448
+ if (k->length == 5 && memcmp(keyBytes, "flags", 5) == 0) return regexp->flags;
449
+ if (k->length == 9 && memcmp(keyBytes, "lastIndex", 9) == 0) return xt_number(0);
450
+ if (k->length == 6 && memcmp(keyBytes, "global", 6) == 0) flag = 'g';
451
+ else if (k->length == 10 && memcmp(keyBytes, "ignoreCase", 10) == 0) flag = 'i';
452
+ else if (k->length == 9 && memcmp(keyBytes, "multiline", 9) == 0) flag = 'm';
453
+ else if (k->length == 6 && memcmp(keyBytes, "sticky", 6) == 0) flag = 'y';
442
454
  if (flag) {
443
- if (f) for (uint32_t i = 0; i < f->length; i++) if (f->data[i] == flag) return XT_TRUE;
455
+ if (f) {
456
+ const char *flags = xt_string_bytes(f);
457
+ for (uint32_t i = 0; i < f->length; i++) if (flags[i] == flag) return XT_TRUE;
458
+ }
444
459
  return XT_FALSE;
445
460
  }
446
461
  return XT_UNDEFINED;