xbintsc 0.3.3 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. package/dist/src/ast/expressions.d.ts +8 -0
  2. package/dist/src/binder/binder.js +1 -0
  3. package/dist/src/binder/binder.js.map +1 -1
  4. package/dist/src/binder/references.d.ts +1 -1
  5. package/dist/src/binder/references.js +34 -5
  6. package/dist/src/binder/references.js.map +1 -1
  7. package/dist/src/binder/types.d.ts +4 -0
  8. package/dist/src/binder/types.js.map +1 -1
  9. package/dist/src/cli/main.js.map +1 -1
  10. package/dist/src/codegen/generator/calls/access.d.ts +9 -1
  11. package/dist/src/codegen/generator/calls/access.js +146 -13
  12. package/dist/src/codegen/generator/calls/access.js.map +1 -1
  13. package/dist/src/codegen/generator/calls/closures.d.ts +1 -0
  14. package/dist/src/codegen/generator/calls/closures.js +25 -0
  15. package/dist/src/codegen/generator/calls/closures.js.map +1 -1
  16. package/dist/src/codegen/generator/calls/invocation.js +12 -1
  17. package/dist/src/codegen/generator/calls/invocation.js.map +1 -1
  18. package/dist/src/codegen/generator/calls/literals.js +12 -4
  19. package/dist/src/codegen/generator/calls/literals.js.map +1 -1
  20. package/dist/src/codegen/generator/calls/super.js +4 -7
  21. package/dist/src/codegen/generator/calls/super.js.map +1 -1
  22. package/dist/src/codegen/generator/context.d.ts +21 -2
  23. package/dist/src/codegen/generator/context.js +42 -5
  24. package/dist/src/codegen/generator/context.js.map +1 -1
  25. package/dist/src/codegen/generator/expressions/assignment.js +1 -3
  26. package/dist/src/codegen/generator/expressions/assignment.js.map +1 -1
  27. package/dist/src/codegen/generator/expressions/primary.d.ts +3 -1
  28. package/dist/src/codegen/generator/expressions/primary.js +99 -1
  29. package/dist/src/codegen/generator/expressions/primary.js.map +1 -1
  30. package/dist/src/codegen/generator/generator.js.map +1 -1
  31. package/dist/src/codegen/generator/module.js +25 -15
  32. package/dist/src/codegen/generator/module.js.map +1 -1
  33. package/dist/src/codegen/generator/state.d.ts +15 -0
  34. package/dist/src/codegen/generator/state.js +28 -1
  35. package/dist/src/codegen/generator/state.js.map +1 -1
  36. package/dist/src/codegen/generator/statements/control-flow.d.ts +3 -1
  37. package/dist/src/codegen/generator/statements/control-flow.js +85 -0
  38. package/dist/src/codegen/generator/statements/control-flow.js.map +1 -1
  39. package/dist/src/codegen/generator/statements/dispatch.js +15 -6
  40. package/dist/src/codegen/generator/statements/dispatch.js.map +1 -1
  41. package/dist/src/codegen/generator/statements/loops.d.ts +3 -0
  42. package/dist/src/codegen/generator/statements/loops.js +25 -11
  43. package/dist/src/codegen/generator/statements/loops.js.map +1 -1
  44. package/dist/src/codegen/generator/tables.d.ts +7 -0
  45. package/dist/src/codegen/generator/tables.js +41 -1
  46. package/dist/src/codegen/generator/tables.js.map +1 -1
  47. package/dist/src/driver/compiler.d.ts +1 -1
  48. package/dist/src/driver/compiler.js +33 -1
  49. package/dist/src/driver/compiler.js.map +1 -1
  50. package/dist/src/driver/modules.d.ts +4 -3
  51. package/dist/src/driver/modules.js +61 -4
  52. package/dist/src/driver/modules.js.map +1 -1
  53. package/dist/src/driver/toolchain.js +1 -1
  54. package/dist/src/driver/toolchain.js.map +1 -1
  55. package/dist/src/lexer/scanner.js.map +1 -1
  56. package/dist/src/parser/declarations/classes.js +4 -0
  57. package/dist/src/parser/declarations/classes.js.map +1 -1
  58. package/dist/src/parser/declarations/type-members.js.map +1 -1
  59. package/dist/src/parser/expressions.d.ts +2 -1
  60. package/dist/src/parser/expressions.js +10 -1
  61. package/dist/src/parser/expressions.js.map +1 -1
  62. package/dist/src/parser/helpers.js +2 -2
  63. package/dist/src/parser/helpers.js.map +1 -1
  64. package/dist/src/parser/literals.js +79 -6
  65. package/dist/src/parser/literals.js.map +1 -1
  66. package/dist/src/parser/modules.js.map +1 -1
  67. package/dist/src/parser/parser.js +0 -1
  68. package/dist/src/parser/parser.js.map +1 -1
  69. package/dist/src/parser/statements.d.ts +1 -0
  70. package/dist/src/parser/statements.js +13 -1
  71. package/dist/src/parser/statements.js.map +1 -1
  72. package/dist/src/parser/types.js +2 -2
  73. package/dist/src/parser/types.js.map +1 -1
  74. package/dist/tests/ast/factory.test.d.ts +1 -0
  75. package/dist/tests/ast/factory.test.js +35 -0
  76. package/dist/tests/ast/factory.test.js.map +1 -0
  77. package/dist/tests/ast/visitor.test.d.ts +1 -0
  78. package/dist/tests/ast/visitor.test.js +53 -0
  79. package/dist/tests/ast/visitor.test.js.map +1 -0
  80. package/dist/tests/cli/main.test.js +109 -9
  81. package/dist/tests/cli/main.test.js.map +1 -1
  82. package/dist/tests/codegen/closures.test.d.ts +1 -0
  83. package/dist/tests/codegen/closures.test.js +32 -0
  84. package/dist/tests/codegen/closures.test.js.map +1 -0
  85. package/dist/tests/codegen/expressions.test.d.ts +1 -0
  86. package/dist/tests/codegen/expressions.test.js +149 -0
  87. package/dist/tests/codegen/expressions.test.js.map +1 -0
  88. package/dist/tests/codegen/llvm.test.js +14 -0
  89. package/dist/tests/codegen/llvm.test.js.map +1 -1
  90. package/dist/tests/codegen/values.test.d.ts +1 -0
  91. package/dist/tests/codegen/values.test.js +48 -0
  92. package/dist/tests/codegen/values.test.js.map +1 -0
  93. package/dist/tests/diagnostics/diagnostic.test.d.ts +1 -0
  94. package/dist/tests/diagnostics/diagnostic.test.js +117 -0
  95. package/dist/tests/diagnostics/diagnostic.test.js.map +1 -0
  96. package/dist/tests/diagnostics/source.test.d.ts +1 -0
  97. package/dist/tests/diagnostics/source.test.js +58 -0
  98. package/dist/tests/diagnostics/source.test.js.map +1 -0
  99. package/dist/tests/driver/modules.test.d.ts +1 -0
  100. package/dist/tests/driver/modules.test.js +140 -0
  101. package/dist/tests/driver/modules.test.js.map +1 -0
  102. package/dist/tests/driver/paths.test.js +17 -1
  103. package/dist/tests/driver/paths.test.js.map +1 -1
  104. package/dist/tests/driver/runtime-lib.test.d.ts +1 -0
  105. package/dist/tests/driver/runtime-lib.test.js +19 -0
  106. package/dist/tests/driver/runtime-lib.test.js.map +1 -0
  107. package/dist/tests/driver/toolchain-provider.test.js +91 -0
  108. package/dist/tests/driver/toolchain-provider.test.js.map +1 -1
  109. package/dist/tests/driver/toolchain.test.js +11 -1
  110. package/dist/tests/driver/toolchain.test.js.map +1 -1
  111. package/dist/tests/e2e/compile-node.test.js +14 -1
  112. package/dist/tests/e2e/compile-node.test.js.map +1 -1
  113. package/dist/tests/e2e/compile.test.js +465 -2
  114. package/dist/tests/e2e/compile.test.js.map +1 -1
  115. package/dist/tests/e2e/differential.test.d.ts +7 -0
  116. package/dist/tests/e2e/differential.test.js +581 -0
  117. package/dist/tests/e2e/differential.test.js.map +1 -0
  118. package/dist/tests/e2e/harness.d.ts +21 -0
  119. package/dist/tests/e2e/harness.js +26 -0
  120. package/dist/tests/e2e/harness.js.map +1 -1
  121. package/dist/tests/extensions/node.test.d.ts +1 -0
  122. package/dist/tests/extensions/node.test.js +32 -0
  123. package/dist/tests/extensions/node.test.js.map +1 -0
  124. package/dist/tests/lexer/identifiers.test.d.ts +1 -0
  125. package/dist/tests/lexer/identifiers.test.js +13 -0
  126. package/dist/tests/lexer/identifiers.test.js.map +1 -0
  127. package/dist/tests/lexer/numbers.test.d.ts +1 -0
  128. package/dist/tests/lexer/numbers.test.js +23 -0
  129. package/dist/tests/lexer/numbers.test.js.map +1 -0
  130. package/dist/tests/lexer/strings.test.d.ts +1 -0
  131. package/dist/tests/lexer/strings.test.js +72 -0
  132. package/dist/tests/lexer/strings.test.js.map +1 -0
  133. package/dist/tests/parser/declarations.test.d.ts +1 -0
  134. package/dist/tests/parser/declarations.test.js +70 -0
  135. package/dist/tests/parser/declarations.test.js.map +1 -0
  136. package/dist/tests/parser/expressions.test.d.ts +1 -0
  137. package/dist/tests/parser/expressions.test.js +47 -0
  138. package/dist/tests/parser/expressions.test.js.map +1 -0
  139. package/dist/tests/parser/helpers.test.d.ts +1 -0
  140. package/dist/tests/parser/helpers.test.js +58 -0
  141. package/dist/tests/parser/helpers.test.js.map +1 -0
  142. package/dist/tests/parser/literals.test.d.ts +1 -0
  143. package/dist/tests/parser/literals.test.js +70 -0
  144. package/dist/tests/parser/literals.test.js.map +1 -0
  145. package/dist/tests/parser/module-syntax.test.d.ts +1 -0
  146. package/dist/tests/parser/module-syntax.test.js +91 -0
  147. package/dist/tests/parser/module-syntax.test.js.map +1 -0
  148. package/dist/tests/parser/statements.test.d.ts +1 -0
  149. package/dist/tests/parser/statements.test.js +85 -0
  150. package/dist/tests/parser/statements.test.js.map +1 -0
  151. package/dist/tests/parser/type-members.test.d.ts +1 -0
  152. package/dist/tests/parser/type-members.test.js +86 -0
  153. package/dist/tests/parser/type-members.test.js.map +1 -0
  154. package/dist/tests/parser/types.test.d.ts +1 -0
  155. package/dist/tests/parser/types.test.js +143 -0
  156. package/dist/tests/parser/types.test.js.map +1 -0
  157. package/package.json +6 -1
  158. package/runtime/ext_node/fs/fs_ops.c +36 -1
  159. package/runtime/rt.h +17 -0
  160. package/runtime/rt_internal.h +88 -2
  161. package/runtime/xt_builtins.c +174 -6
  162. package/runtime/xt_containers.c +234 -42
  163. package/runtime/xt_generator.c +304 -0
  164. package/runtime/xt_io.c +170 -30
  165. package/runtime/xt_promise.c +6 -1
  166. package/runtime/xt_regex.h +105 -35
  167. package/runtime/xt_stdlib/dispatch.inc +255 -4
  168. package/runtime/xt_stdlib/strings.inc +17 -13
  169. package/runtime/xt_stdlib2/arrays.inc +126 -19
  170. package/runtime/xt_stdlib2/date.inc +9 -2
  171. package/runtime/xt_stdlib2/error.inc +162 -12
  172. package/runtime/xt_stdlib2/helpers.inc +4 -0
  173. package/runtime/xt_stdlib2/json.inc +19 -6
  174. package/runtime/xt_stdlib2/number-string-statics.inc +1 -1
  175. package/runtime/xt_stdlib2/numbers.inc +4 -5
  176. package/runtime/xt_stdlib2/objects.inc +29 -0
  177. package/runtime/xt_stdlib2/regexp-match.inc +293 -51
  178. package/runtime/xt_stdlib2/set.inc +58 -2
  179. package/runtime/xt_stdlib2/strings.inc +38 -0
  180. package/runtime/xt_symbol.c +149 -0
  181. package/runtime/xt_values.c +221 -15
  182. package/scripts/build-runtime.ts +1 -1
  183. package/src/ast/expressions.ts +8 -0
  184. package/src/binder/binder.ts +1 -1
  185. package/src/binder/references.ts +47 -4
  186. package/src/binder/types.ts +4 -0
  187. package/src/cli/main.ts +1 -2
  188. package/src/codegen/generator/calls/access.ts +158 -13
  189. package/src/codegen/generator/calls/closures.ts +24 -0
  190. package/src/codegen/generator/calls/invocation.ts +11 -1
  191. package/src/codegen/generator/calls/literals.ts +10 -4
  192. package/src/codegen/generator/calls/super.ts +6 -9
  193. package/src/codegen/generator/context.ts +44 -7
  194. package/src/codegen/generator/expressions/assignment.ts +1 -3
  195. package/src/codegen/generator/expressions/primary.ts +108 -2
  196. package/src/codegen/generator/generator.ts +0 -1
  197. package/src/codegen/generator/module.ts +24 -18
  198. package/src/codegen/generator/state.ts +44 -1
  199. package/src/codegen/generator/statements/control-flow.ts +86 -0
  200. package/src/codegen/generator/statements/dispatch.ts +18 -6
  201. package/src/codegen/generator/statements/loops.ts +29 -11
  202. package/src/codegen/generator/tables.ts +44 -1
  203. package/src/driver/compiler.ts +32 -2
  204. package/src/driver/modules.ts +64 -4
  205. package/src/driver/toolchain.ts +1 -1
  206. package/src/lexer/scanner.ts +0 -1
  207. package/src/parser/declarations/classes.ts +4 -0
  208. package/src/parser/declarations/type-members.ts +0 -2
  209. package/src/parser/expressions.ts +13 -1
  210. package/src/parser/helpers.ts +2 -2
  211. package/src/parser/literals.ts +82 -7
  212. package/src/parser/modules.ts +0 -1
  213. package/src/parser/parser.ts +0 -1
  214. package/src/parser/statements.ts +14 -0
  215. package/src/parser/types.ts +2 -2
@@ -33,33 +33,63 @@ static xt_value xt_array_quantifier(xt_value value, xt_value fn, int every) {
33
33
  return xt_bool(every ? 1 : 0);
34
34
  }
35
35
 
36
- static int xt_array_compare_values(xt_value a, xt_value b) {
37
- if (xt_truthy(xt_gt(a, b))) return 1;
38
- if (xt_truthy(xt_lt(a, b))) return -1;
36
+ /*
37
+ * Default `Array.prototype.sort` comparator. Per spec the elements are
38
+ * converted to strings and compared by UTF-16 code unit; `undefined` always
39
+ * sorts last and holes are handled by the caller. Because our strings are
40
+ * UTF-8, a byte-wise compare matches UTF-16 ordering for the BMP (and differs
41
+ * only for supplementary vs. U+E000..U+FFFF code points).
42
+ */
43
+ static int xt_array_default_compare(xt_value a, xt_value b) {
44
+ if (a == XT_UNDEFINED) return b == XT_UNDEFINED ? 0 : 1;
45
+ if (b == XT_UNDEFINED) return -1;
46
+ xt_string *sa = xt_as_string(xt_to_string(a));
47
+ xt_string *sb = xt_as_string(xt_to_string(b));
48
+ uint32_t n = sa->length < sb->length ? sa->length : sb->length;
49
+ int cmp = n ? memcmp(sa->data, sb->data, n) : 0;
50
+ if (cmp != 0) return cmp < 0 ? -1 : 1;
51
+ if (sa->length == sb->length) return 0;
52
+ return sa->length < sb->length ? -1 : 1;
53
+ }
54
+
55
+ static int xt_array_sort_compare(xt_value fn, int hasFn, xt_value a, xt_value b) {
56
+ if (!hasFn) return xt_array_default_compare(a, b);
57
+ xt_value args[2] = {a, b};
58
+ double result = xt_to_number(xt_closure_call(fn, 2, args));
59
+ if (result < 0) return -1;
60
+ if (result > 0) return 1;
39
61
  return 0;
40
62
  }
41
63
 
64
+ /* Stable bottom-up merge sort: O(n log n) and never reorders equal keys. */
65
+ static void xt_array_merge_sort(xt_value *items, xt_value *scratch, uint32_t length, xt_value fn, int hasFn) {
66
+ for (uint32_t width = 1; width < length; width *= 2) {
67
+ for (uint32_t lo = 0; lo < length; lo += 2 * width) {
68
+ uint32_t mid = lo + width < length ? lo + width : length;
69
+ uint32_t hi = lo + 2 * width < length ? lo + 2 * width : length;
70
+ uint32_t i = lo, j = mid, k = lo;
71
+ while (i < mid && j < hi) {
72
+ if (xt_array_sort_compare(fn, hasFn, items[j], items[i]) < 0) scratch[k++] = items[j++];
73
+ else scratch[k++] = items[i++];
74
+ }
75
+ while (i < mid) scratch[k++] = items[i++];
76
+ while (j < hi) scratch[k++] = items[j++];
77
+ for (k = lo; k < hi; k++) items[k] = scratch[k];
78
+ }
79
+ }
80
+ }
81
+
42
82
  static xt_value xt_array_sort(xt_value value, xt_value fn) {
43
83
  xt_array *array = xt_as_array(value);
44
84
  if (!array || array->length < 2) return value;
45
85
  int hasFn = XT_IS_FUNCTION(fn);
46
- for (uint32_t i = 1; i < array->length; i++) {
47
- xt_value key = array->items[i];
48
- int32_t j = (int32_t)i - 1;
49
- while (j >= 0) {
50
- int cmp;
51
- if (hasFn) {
52
- xt_value args[2] = {array->items[j], key};
53
- cmp = (int)xt_to_number(xt_closure_call(fn, 2, args));
54
- } else {
55
- cmp = xt_array_compare_values(array->items[j], key);
56
- }
57
- if (cmp <= 0) break;
58
- array->items[j + 1] = array->items[j];
59
- j--;
60
- }
61
- array->items[j + 1] = key;
86
+ xt_value *scratch = (xt_value *)malloc(sizeof(xt_value) * array->length);
87
+ if (!scratch) {
88
+ xt_throw(xt_string_from_cstr("RangeError: out of memory in Array.prototype.sort"));
89
+ return value;
62
90
  }
91
+ xt_array_merge_sort(array->items, scratch, array->length, fn, hasFn);
92
+ free(scratch);
63
93
  return value;
64
94
  }
65
95
 
@@ -130,6 +160,78 @@ static xt_value xt_array_copy_within(xt_value value, xt_value targetValue, xt_va
130
160
  return value;
131
161
  }
132
162
 
163
+ static xt_value xt_array_splice(xt_value value, int32_t argc, xt_value *argv) {
164
+ xt_array *array = xt_as_array(value);
165
+ if (!array) return XT_UNDEFINED;
166
+ int32_t len = (int32_t)array->length;
167
+ int32_t start = argc >= 1 ? xt_to_int32(argv[0]) : 0;
168
+ if (start < 0) start += len;
169
+ if (start < 0) start = 0;
170
+ if (start > len) start = len;
171
+ int32_t deleteCount;
172
+ if (argc < 2) {
173
+ deleteCount = len - start;
174
+ } else {
175
+ deleteCount = xt_to_int32(argv[1]);
176
+ if (deleteCount < 0) deleteCount = 0;
177
+ if (deleteCount > len - start) deleteCount = len - start;
178
+ }
179
+ int32_t insertCount = argc > 2 ? argc - 2 : 0;
180
+ xt_value removed = xt_array_new(deleteCount, deleteCount > 0 ? array->items + start : NULL);
181
+ int32_t newLength = len - deleteCount + insertCount;
182
+ xt_array_reserve(array, (uint32_t)(newLength > 0 ? newLength : 0));
183
+ if (insertCount != deleteCount) {
184
+ memmove(array->items + start + insertCount, array->items + start + deleteCount,
185
+ sizeof(xt_value) * (size_t)(len - start - deleteCount));
186
+ }
187
+ for (int32_t i = 0; i < insertCount; i++) array->items[start + i] = argv[2 + i];
188
+ array->length = (uint32_t)newLength;
189
+ return removed;
190
+ }
191
+
192
+ /* Copy the own elements of `value` into a fresh array. */
193
+ static xt_value xt_array_copy(xt_value value) {
194
+ xt_value out = xt_array_new(0, NULL);
195
+ xt_array *array = xt_as_array(value);
196
+ if (!array) return out;
197
+ for (uint32_t i = 0; i < array->length; i++) xt_array_push(out, array->items[i]);
198
+ return out;
199
+ }
200
+
201
+ static xt_value xt_array_to_reversed(xt_value value) {
202
+ xt_value copy = xt_array_copy(value);
203
+ xt_array *array = xt_as_array(copy);
204
+ for (uint32_t i = 0; i + 1 < array->length - i; i++) {
205
+ xt_value tmp = array->items[i];
206
+ array->items[i] = array->items[array->length - 1 - i];
207
+ array->items[array->length - 1 - i] = tmp;
208
+ }
209
+ return copy;
210
+ }
211
+
212
+ static xt_value xt_array_to_sorted(xt_value value, xt_value fn) {
213
+ return xt_array_sort(xt_array_copy(value), fn);
214
+ }
215
+
216
+ static xt_value xt_array_to_spliced(xt_value value, int32_t argc, xt_value *argv) {
217
+ xt_value copy = xt_array_copy(value);
218
+ xt_array_splice(copy, argc, argv);
219
+ return copy;
220
+ }
221
+
222
+ static xt_value xt_array_with(xt_value value, xt_value indexValue, xt_value newValue) {
223
+ xt_value copy = xt_array_copy(value);
224
+ xt_array *array = xt_as_array(copy);
225
+ int32_t index = xt_to_int32(indexValue);
226
+ if (index < 0) index += (int32_t)array->length;
227
+ if (index < 0 || index >= (int32_t)array->length) {
228
+ xt_throw(xt_add(xt_string_from_cstr("RangeError: Invalid index : "), xt_to_string(indexValue)));
229
+ return copy;
230
+ }
231
+ array->items[index] = newValue;
232
+ return copy;
233
+ }
234
+
133
235
  xt_value xt_ext_array_method(xt_value target, const char *method, int32_t argc, xt_value *argv, int *handled) {
134
236
  *handled = 1;
135
237
  if (strcmp(method, "at") == 0) return xt_array_at(target, xt_arg_at(argc, argv, 0));
@@ -151,6 +253,11 @@ xt_value xt_ext_array_method(xt_value target, const char *method, int32_t argc,
151
253
  if (strcmp(method, "lastIndexOf") == 0) return xt_array_last_index_of(target, xt_arg_at(argc, argv, 0), xt_arg_at(argc, argv, 1));
152
254
  if (strcmp(method, "fill") == 0) return xt_array_fill(target, xt_arg_at(argc, argv, 0), xt_arg_at(argc, argv, 1), xt_arg_at(argc, argv, 2));
153
255
  if (strcmp(method, "copyWithin") == 0) return xt_array_copy_within(target, xt_arg_at(argc, argv, 0), xt_arg_at(argc, argv, 1), xt_arg_at(argc, argv, 2));
256
+ if (strcmp(method, "splice") == 0) return xt_array_splice(target, argc, argv);
257
+ if (strcmp(method, "toReversed") == 0) return xt_array_to_reversed(target);
258
+ if (strcmp(method, "toSorted") == 0) return xt_array_to_sorted(target, xt_arg_at(argc, argv, 0));
259
+ if (strcmp(method, "toSpliced") == 0) return xt_array_to_spliced(target, argc, argv);
260
+ if (strcmp(method, "with") == 0) return xt_array_with(target, xt_arg_at(argc, argv, 0), xt_arg_at(argc, argv, 1));
154
261
  if (strcmp(method, "reduceRight") == 0) {
155
262
  xt_array *array = xt_as_array(target);
156
263
  if (!array) return XT_UNDEFINED;
@@ -40,18 +40,25 @@ xt_value xt_date_static(xt_value name, int32_t argc, xt_value *argv) {
40
40
  }
41
41
 
42
42
  static xt_value xt_date_method(xt_value target, const char *method, int32_t argc, xt_value *argv, int *handled) {
43
+ (void)argc;
44
+ (void)argv;
43
45
  *handled = 1;
44
46
  xt_date *date = (xt_date *)XT_GET_PTR(target);
47
+ if (strcmp(method, "getTime") == 0 || strcmp(method, "valueOf") == 0) return xt_number(date->time);
45
48
  time_t seconds = (time_t)(date->time / 1000.0);
46
49
  struct tm tm;
47
- xt_gmtime_r(&seconds, &tm);
48
- if (strcmp(method, "getTime") == 0 || strcmp(method, "valueOf") == 0) return xt_number(date->time);
49
50
  const char *name = method;
50
51
  char utc[32];
52
+ int isUtc = 0;
51
53
  if (strncmp(method, "getUTC", 6) == 0) {
52
54
  snprintf(utc, sizeof(utc), "get%s", method + 6);
53
55
  name = utc;
56
+ isUtc = 1;
54
57
  }
58
+ /* Bare getters and `toString` report local time; `getUTC*` and
59
+ * `toISOString` report UTC. */
60
+ if (isUtc || strcmp(method, "toISOString") == 0) xt_gmtime_r(&seconds, &tm);
61
+ else xt_localtime_r(&seconds, &tm);
55
62
  if (strcmp(name, "getFullYear") == 0) return xt_number(tm.tm_year + 1900);
56
63
  if (strcmp(name, "getMonth") == 0) return xt_number(tm.tm_mon);
57
64
  if (strcmp(name, "getDate") == 0) return xt_number(tm.tm_mday);
@@ -1,21 +1,171 @@
1
- xt_value xt_error_ctor(int32_t argc, xt_value *argv) {
1
+ /* ------------------------------------------------------------------------- */
2
+ /* Error family: prototype chain, first-class constructors and formatting. */
3
+
4
+ /* Every constructor in the Error family shares one implementation; the name
5
+ * is what distinguishes them. Prototypes chain `TypeError.prototype` (etc.)
6
+ * to `Error.prototype`, so `instanceof Error` succeeds for every member. */
7
+ static const char *const XT_ERROR_NAMES[] = {
8
+ "Error", "TypeError", "RangeError", "SyntaxError",
9
+ "ReferenceError", "EvalError", "URIError", "AggregateError",
10
+ };
11
+
12
+ static int xt_error_is_family(const char *name) {
13
+ for (size_t i = 0; i < sizeof(XT_ERROR_NAMES) / sizeof(XT_ERROR_NAMES[0]); i++) {
14
+ if (strcmp(name, XT_ERROR_NAMES[i]) == 0) return 1;
15
+ }
16
+ return 0;
17
+ }
18
+
19
+ static xt_value xt_error_root_proto = XT_UNDEFINED;
20
+
21
+ static xt_value xt_error_proto_for(const char *name) {
22
+ if (!XT_IS_OBJECT(xt_error_root_proto)) {
23
+ xt_error_root_proto = xt_object_new();
24
+ xt_object_set(xt_error_root_proto, xt_string_from_cstr("name"),
25
+ xt_string_from_cstr("Error"));
26
+ xt_object_set(xt_error_root_proto, xt_string_from_cstr("message"),
27
+ xt_string_from_cstr(""));
28
+ }
29
+ if (strcmp(name, "Error") == 0) return xt_error_root_proto;
30
+ xt_value key = xt_string_from_cstr(name);
31
+ xt_value registry = xt_object_get(xt_error_root_proto, xt_string_from_cstr("__children"));
32
+ if (!XT_IS_OBJECT(registry)) {
33
+ registry = xt_object_new();
34
+ xt_object_set(xt_error_root_proto, xt_string_from_cstr("__children"), registry);
35
+ }
36
+ xt_value existing = xt_object_get(registry, key);
37
+ if (XT_IS_OBJECT(existing)) return existing;
38
+ xt_value proto = xt_object_new();
39
+ xt_object_set_prototype(proto, xt_error_root_proto);
40
+ xt_object_set(proto, xt_string_from_cstr("name"), key);
41
+ xt_object_set(proto, xt_string_from_cstr("message"), xt_string_from_cstr(""));
42
+ xt_object_set(registry, key, proto);
43
+ return proto;
44
+ }
45
+
46
+ xt_value xt_error_prototype(xt_value nameValue) {
47
+ const char *name = XT_IS_STRING(nameValue) ? xt_string_data(nameValue) : "Error";
48
+ if (!xt_error_is_family(name)) name = "Error";
49
+ return xt_error_proto_for(name);
50
+ }
51
+
52
+ static xt_value xt_make_error(const char *name, int32_t argc, xt_value *argv) {
53
+ if (!xt_error_is_family(name)) name = "Error";
2
54
  xt_value error = xt_object_new();
3
- xt_value name = xt_arg_at(argc, argv, 1);
4
- xt_value message = xt_arg_at(argc, argv, 0);
5
- xt_object_set(error, xt_string_from_cstr("name"),
6
- name == XT_UNDEFINED ? xt_string_from_cstr("Error") : name);
55
+ ((xt_object *)XT_GET_PTR(error))->header.kind = XT_OBJECT_KIND_ERROR;
56
+ xt_object_set_prototype(error, xt_error_proto_for(name));
57
+ xt_value message;
58
+ if (strcmp(name, "AggregateError") == 0) {
59
+ xt_value errors = xt_arg_at(argc, argv, 0);
60
+ xt_object_set(error, xt_string_from_cstr("errors"),
61
+ errors == XT_UNDEFINED ? xt_array_new(0, NULL) : errors);
62
+ message = xt_arg_at(argc, argv, 1);
63
+ } else {
64
+ message = xt_arg_at(argc, argv, 0);
65
+ }
66
+ xt_object_set(error, xt_string_from_cstr("name"), xt_string_from_cstr(name));
7
67
  xt_object_set(error, xt_string_from_cstr("message"),
8
68
  message == XT_UNDEFINED ? xt_string_from_cstr("") : message);
9
69
  return error;
10
70
  }
11
71
 
12
- /* `super(message)` inside a subclass of the builtin `Error`: initialize the
13
- * already-allocated instance instead of creating a fresh object. */
14
- xt_value xt_error_init(xt_value thisValue, int32_t argc, xt_value *argv) {
15
- xt_value name = xt_arg_at(argc, argv, 1);
16
- xt_value message = xt_arg_at(argc, argv, 0);
17
- xt_object_set(thisValue, xt_string_from_cstr("name"),
18
- name == XT_UNDEFINED ? xt_string_from_cstr("Error") : name);
72
+ static xt_value xt_error_ctor_invoke(xt_value thisValue, xt_value env, int32_t argc,
73
+ xt_value *argv) {
74
+ (void)thisValue;
75
+ xt_value name = xt_closure_env(env, 0);
76
+ return xt_make_error(xt_string_data(name), argc, argv);
77
+ }
78
+
79
+ /* The first-class value of a family constructor (used by `instanceof`,
80
+ * `typeof`, and `class X extends Error`). Cached on the prototype. */
81
+ xt_value xt_error_constructor(xt_value nameValue) {
82
+ const char *name = XT_IS_STRING(nameValue) ? xt_string_data(nameValue) : "Error";
83
+ if (!xt_error_is_family(name)) name = "Error";
84
+ xt_value proto = xt_error_proto_for(name);
85
+ xt_value key = xt_string_from_cstr("__ctor");
86
+ /* Own-property lookup only: a prototype walk would find `Error`'s
87
+ * constructor (stored on the root prototype) for every child. */
88
+ xt_property *existing =
89
+ xt_object_find_property((xt_object *)XT_GET_PTR(proto), key);
90
+ if (existing && XT_IS_FUNCTION(existing->value)) return existing->value;
91
+ xt_value env[1];
92
+ env[0] = xt_string_from_cstr(name);
93
+ xt_value closure = xt_closure_new((void *)xt_error_ctor_invoke, 1, env);
94
+ xt_function_set_prototype(closure, proto);
95
+ xt_function_set_metadata(closure, env[0], 1);
96
+ xt_object_set(proto, key, closure);
97
+ return closure;
98
+ }
99
+
100
+ xt_value xt_error_to_string(xt_value error) {
101
+ xt_value name = xt_object_get(error, xt_string_from_cstr("name"));
102
+ xt_value message = xt_object_get(error, xt_string_from_cstr("message"));
103
+ xt_value nameStr = name == XT_UNDEFINED ? xt_string_from_cstr("Error") : xt_to_string(name);
104
+ xt_value messageStr =
105
+ message == XT_UNDEFINED ? xt_string_from_cstr("") : xt_to_string(message);
106
+ xt_string *ns = xt_as_string(nameStr);
107
+ xt_string *ms = xt_as_string(messageStr);
108
+ if (ns->length == 0) return messageStr;
109
+ if (ms->length == 0) return nameStr;
110
+ size_t total = ns->length + 2 + ms->length;
111
+ char *buffer = (char *)malloc(total + 1);
112
+ memcpy(buffer, ns->data, ns->length);
113
+ buffer[ns->length] = ':';
114
+ buffer[ns->length + 1] = ' ';
115
+ memcpy(buffer + ns->length + 2, ms->data, ms->length);
116
+ buffer[total] = '\0';
117
+ xt_value result = xt_string_new(buffer, total);
118
+ free(buffer);
119
+ return result;
120
+ }
121
+
122
+ xt_value xt_error_ctor(int32_t argc, xt_value *argv) {
123
+ return xt_make_error("Error", argc, argv);
124
+ }
125
+
126
+ xt_value xt_type_error_ctor(int32_t argc, xt_value *argv) {
127
+ return xt_make_error("TypeError", argc, argv);
128
+ }
129
+
130
+ xt_value xt_range_error_ctor(int32_t argc, xt_value *argv) {
131
+ return xt_make_error("RangeError", argc, argv);
132
+ }
133
+
134
+ xt_value xt_syntax_error_ctor(int32_t argc, xt_value *argv) {
135
+ return xt_make_error("SyntaxError", argc, argv);
136
+ }
137
+
138
+ xt_value xt_reference_error_ctor(int32_t argc, xt_value *argv) {
139
+ return xt_make_error("ReferenceError", argc, argv);
140
+ }
141
+
142
+ xt_value xt_eval_error_ctor(int32_t argc, xt_value *argv) {
143
+ return xt_make_error("EvalError", argc, argv);
144
+ }
145
+
146
+ xt_value xt_uri_error_ctor(int32_t argc, xt_value *argv) {
147
+ return xt_make_error("URIError", argc, argv);
148
+ }
149
+
150
+ xt_value xt_aggregate_error_ctor(int32_t argc, xt_value *argv) {
151
+ return xt_make_error("AggregateError", argc, argv);
152
+ }
153
+
154
+ /* `super(message)` inside a subclass of the builtin Error family: initialize
155
+ * the already-allocated instance instead of creating a fresh object. The
156
+ * parent's name selects the default `name` (`TypeError`, ...). */
157
+ xt_value xt_error_init(xt_value thisValue, xt_value nameValue, int32_t argc,
158
+ xt_value *argv) {
159
+ const char *name = XT_IS_STRING(nameValue) ? xt_string_data(nameValue) : "Error";
160
+ if (!xt_error_is_family(name)) name = "Error";
161
+ xt_object_set(thisValue, xt_string_from_cstr("name"), xt_string_from_cstr(name));
162
+ xt_value message;
163
+ if (strcmp(name, "AggregateError") == 0) {
164
+ xt_object_set(thisValue, xt_string_from_cstr("errors"), xt_arg_at(argc, argv, 0));
165
+ message = xt_arg_at(argc, argv, 1);
166
+ } else {
167
+ message = xt_arg_at(argc, argv, 0);
168
+ }
19
169
  xt_object_set(thisValue, xt_string_from_cstr("message"),
20
170
  message == XT_UNDEFINED ? xt_string_from_cstr("") : message);
21
171
  return thisValue;
@@ -9,9 +9,13 @@ static time_t xt_timegm(struct tm *tm) { return (time_t)_mkgmtime64(tm); }
9
9
  static struct tm *xt_gmtime_r(const time_t *timer, struct tm *buf) {
10
10
  return gmtime_s(buf, timer) == 0 ? buf : NULL;
11
11
  }
12
+ static struct tm *xt_localtime_r(const time_t *timer, struct tm *buf) {
13
+ return localtime_s(buf, timer) == 0 ? buf : NULL;
14
+ }
12
15
  #else
13
16
  static time_t xt_timegm(struct tm *tm) { return timegm(tm); }
14
17
  static struct tm *xt_gmtime_r(const time_t *timer, struct tm *buf) { return gmtime_r(timer, buf); }
18
+ static struct tm *xt_localtime_r(const time_t *timer, struct tm *buf) { return localtime_r(timer, buf); }
15
19
  #endif
16
20
 
17
21
  int xt_value_equals(xt_value a, xt_value b) {
@@ -148,24 +148,35 @@ static void xt_json_stringify_array(xt_value value, int indent, int depth, xt_va
148
148
  static void xt_json_stringify_object(xt_value value, int indent, int depth, xt_value *out) {
149
149
  xt_value keys = xt_object_keys(value);
150
150
  xt_array *array = xt_as_array(keys);
151
- if (!array || array->length == 0) { *out = xt_add(*out, xt_string_from_cstr("{}")); return; }
151
+ if (!array) { *out = xt_add(*out, xt_string_from_cstr("{}")); return; }
152
+ int any = 0;
153
+ for (uint32_t i = 0; i < array->length; i++) {
154
+ xt_value item = xt_object_get(value, array->items[i]);
155
+ if (item != XT_UNDEFINED && !XT_IS_FUNCTION(item) && !xt_is_symbol(item)) { any = 1; break; }
156
+ }
157
+ if (!any) { *out = xt_add(*out, xt_string_from_cstr("{}")); return; }
152
158
  *out = xt_add(*out, xt_string_from_cstr("{"));
159
+ int written = 0;
153
160
  for (uint32_t i = 0; i < array->length; i++) {
154
- if (i > 0) *out = xt_add(*out, xt_string_from_cstr(","));
155
- xt_json_indent(out, indent, depth + 1);
156
161
  xt_value key = array->items[i];
162
+ xt_value item = xt_object_get(value, key);
163
+ /* `undefined`, functions and symbols are omitted from objects. */
164
+ if (item == XT_UNDEFINED || XT_IS_FUNCTION(item) || xt_is_symbol(item)) continue;
165
+ if (written > 0) *out = xt_add(*out, xt_string_from_cstr(","));
166
+ written++;
167
+ xt_json_indent(out, indent, depth + 1);
157
168
  *out = xt_add(*out, xt_string_from_cstr("\""));
158
169
  *out = xt_add(*out, key);
159
170
  *out = xt_add(*out, xt_string_from_cstr("\":"));
160
171
  if (indent > 0) *out = xt_add(*out, xt_string_from_cstr(" "));
161
- xt_json_stringify_value(xt_object_get(value, key), indent, depth + 1, out);
172
+ xt_json_stringify_value(item, indent, depth + 1, out);
162
173
  }
163
174
  xt_json_indent(out, indent, depth);
164
175
  *out = xt_add(*out, xt_string_from_cstr("}"));
165
176
  }
166
177
 
167
178
  static void xt_json_stringify_value(xt_value value, int indent, int depth, xt_value *out) {
168
- if (value == XT_UNDEFINED || value == XT_NULL || XT_IS_FUNCTION(value)) {
179
+ if (value == XT_UNDEFINED || value == XT_NULL || XT_IS_FUNCTION(value) || xt_is_symbol(value)) {
169
180
  *out = xt_add(*out, xt_string_from_cstr("null"));
170
181
  return;
171
182
  }
@@ -207,7 +218,9 @@ xt_value xt_json_stringify(int32_t argc, xt_value *argv) {
207
218
  else if (XT_IS_STRING(space)) indent = (int)((xt_string *)XT_GET_PTR(space))->length;
208
219
  if (indent < 0) indent = 0;
209
220
  if (indent > 10) indent = 10;
221
+ xt_value value = xt_arg_at(argc, argv, 0);
222
+ if (value == XT_UNDEFINED || XT_IS_FUNCTION(value) || xt_is_symbol(value)) return XT_UNDEFINED;
210
223
  xt_value out = xt_string_from_cstr("");
211
- xt_json_stringify_value(xt_arg_at(argc, argv, 0), indent, 0, &out);
224
+ xt_json_stringify_value(value, indent, 0, &out);
212
225
  return out;
213
226
  }
@@ -60,7 +60,7 @@ xt_value xt_string_static(xt_value name, int32_t argc, xt_value *argv) {
60
60
  }
61
61
  if (strcmp(fn, "raw") == 0) {
62
62
  xt_value strings = xt_arg_at(argc, argv, 0);
63
- xt_value raw = xt_object_get(strings, xt_string_from_cstr("raw"));
63
+ xt_value raw = xt_get(strings, xt_string_from_cstr("raw"));
64
64
  xt_value out = xt_string_from_cstr("");
65
65
  double length = xt_to_number(xt_get(raw, xt_string_from_cstr("length")));
66
66
  for (int32_t i = 0; i < (int32_t)length; i++) {
@@ -8,7 +8,7 @@ xt_value xt_ext_number_method(xt_value target, const char *method, int32_t argc,
8
8
  if (precision < 0) precision = 0;
9
9
  if (precision > 100) precision = 100;
10
10
  char buffer[512];
11
- snprintf(buffer, sizeof(buffer), "%.*f", precision, n);
11
+ xt_number_to_fixed(n, precision, buffer, sizeof(buffer));
12
12
  return xt_string_from_cstr(buffer);
13
13
  }
14
14
  if (strcmp(method, "toPrecision") == 0) {
@@ -17,15 +17,14 @@ xt_value xt_ext_number_method(xt_value target, const char *method, int32_t argc,
17
17
  if (precision < 1) precision = 1;
18
18
  if (precision > 100) precision = 100;
19
19
  char buffer[512];
20
- snprintf(buffer, sizeof(buffer), "%.*g", precision, n);
20
+ xt_number_to_precision(n, precision, buffer, sizeof(buffer));
21
21
  return xt_string_from_cstr(buffer);
22
22
  }
23
23
  if (strcmp(method, "toExponential") == 0) {
24
- int precision = argc >= 1 ? xt_to_int32(argv[0]) : 6;
25
- if (precision < 0) precision = 0;
24
+ int precision = (argc >= 1 && argv[0] != XT_UNDEFINED) ? xt_to_int32(argv[0]) : -1;
26
25
  if (precision > 100) precision = 100;
27
26
  char buffer[512];
28
- snprintf(buffer, sizeof(buffer), "%.*e", precision, n);
27
+ xt_number_to_exponential(n, precision, buffer, sizeof(buffer));
29
28
  return xt_string_from_cstr(buffer);
30
29
  }
31
30
  if (strcmp(method, "toString") == 0) {
@@ -9,10 +9,39 @@ xt_value xt_ext_object_method(xt_value target, const char *method, int32_t argc,
9
9
 
10
10
  /* -- Object statics ------------------------------------------------------- */
11
11
 
12
+ /* `Object.getOwnPropertyNames` differs from `Object.keys` only in that it also
13
+ * reports the non-enumerable `length` of arrays and strings. */
14
+ static xt_value xt_object_own_property_names(xt_value value) {
15
+ xt_value names = xt_object_keys(value);
16
+ if (XT_IS_ARRAY(value) || XT_IS_STRING(value)) xt_array_push(names, xt_string_from_cstr("length"));
17
+ return names;
18
+ }
19
+
20
+ /* `Object.groupBy(items, fn)`: bucket each element by the key `fn` returns. */
21
+ static xt_value xt_object_group_by(xt_value value, xt_value fn) {
22
+ xt_value result = xt_object_new();
23
+ xt_array *array = xt_as_array(value);
24
+ if (!array) return result;
25
+ for (uint32_t i = 0; i < array->length; i++) {
26
+ xt_value args[2] = {array->items[i], xt_number((double)i)};
27
+ xt_value keyString = xt_to_string(xt_closure_call(fn, 2, args));
28
+ xt_value group = xt_object_get(result, keyString);
29
+ if (!XT_IS_ARRAY(group)) {
30
+ group = xt_array_new(0, NULL);
31
+ xt_object_set(result, keyString, group);
32
+ }
33
+ xt_array_push(group, array->items[i]);
34
+ }
35
+ return result;
36
+ }
37
+
12
38
  xt_value xt_object_static(xt_value name, int32_t argc, xt_value *argv) {
13
39
  const char *fn = xt_string_data(name);
14
40
  if (!fn) return XT_UNDEFINED;
15
41
  if (strcmp(fn, "keys") == 0) return xt_object_keys(xt_arg_at(argc, argv, 0));
42
+ if (strcmp(fn, "getOwnPropertyNames") == 0) return xt_object_own_property_names(xt_arg_at(argc, argv, 0));
43
+ if (strcmp(fn, "getOwnPropertySymbols") == 0) return xt_object_own_property_symbols(xt_arg_at(argc, argv, 0));
44
+ if (strcmp(fn, "groupBy") == 0) return xt_object_group_by(xt_arg_at(argc, argv, 0), xt_arg_at(argc, argv, 1));
16
45
  if (strcmp(fn, "values") == 0) return xt_object_values(xt_arg_at(argc, argv, 0));
17
46
  if (strcmp(fn, "entries") == 0) return xt_object_entries(xt_arg_at(argc, argv, 0));
18
47
  if (strcmp(fn, "assign") == 0) return xt_object_assign(argc, argv);