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
@@ -52,19 +52,85 @@ xt_object *xt_as_object(xt_value value) {
52
52
  return NULL;
53
53
  }
54
54
 
55
- static xt_property *xt_object_find(xt_object *obj, xt_string *key) {
55
+ static xt_property *xt_object_find(xt_object *obj, xt_value key) {
56
56
  for (uint32_t i = 0; i < obj->count; i++) {
57
- if (xt_string_equals(obj->properties[i].key, key)) return &obj->properties[i];
57
+ if (xt_property_key_equals(obj->properties[i].key, key)) return &obj->properties[i];
58
58
  }
59
59
  return NULL;
60
60
  }
61
61
 
62
- xt_property *xt_object_find_property(xt_object *obj, xt_string *key) {
62
+ /*
63
+ * JavaScript enumerates own string keys in a canonical order: array-index
64
+ * keys (the canonical decimal form of an integer in [0, 2^32 - 2]) first, in
65
+ * ascending numeric order, then the remaining keys in insertion order. We keep
66
+ * the physical property array in exactly that order so every consumer
67
+ * (`Object.keys`, `for...in`, `JSON.stringify`, ...) is correct for free.
68
+ */
69
+ static int xt_key_is_array_index(xt_string *key, uint32_t *out) {
70
+ if (key->length == 0 || key->length > 10) return 0;
71
+ if (key->data[0] == '0' && key->length != 1) return 0; /* "0" is canonical, "01" is not */
72
+ uint64_t value = 0;
73
+ for (uint32_t i = 0; i < key->length; i++) {
74
+ char ch = key->data[i];
75
+ if (ch < '0' || ch > '9') return 0;
76
+ value = value * 10 + (uint64_t)(ch - '0');
77
+ }
78
+ if (value > 4294967294ULL) return 0; /* 2^32 - 2 is the largest array index */
79
+ *out = (uint32_t)value;
80
+ return 1;
81
+ }
82
+
83
+ /* Insert a new property for `key`, preserving JavaScript key order. Symbols
84
+ * are not array indices and simply append in insertion order. */
85
+ static xt_property *xt_object_insert_property(xt_object *obj, xt_value key) {
86
+ uint32_t index = 0;
87
+ uint32_t position = obj->count;
88
+ if (XT_IS_STRING(key) && xt_key_is_array_index(xt_as_string(key), &index)) {
89
+ for (uint32_t i = 0; i < obj->count; i++) {
90
+ uint32_t existing = 0;
91
+ xt_value existingKey = obj->properties[i].key;
92
+ if (!XT_IS_STRING(existingKey) ||
93
+ !xt_key_is_array_index(xt_as_string(existingKey), &existing)) {
94
+ position = i; /* first non-index key: the integer prefix ends here */
95
+ break;
96
+ }
97
+ if (existing > index) {
98
+ position = i;
99
+ break;
100
+ }
101
+ }
102
+ }
103
+ xt_object_reserve(obj, obj->count + 1);
104
+ for (uint32_t i = obj->count; i > position; i--) obj->properties[i] = obj->properties[i - 1];
105
+ xt_property *prop = &obj->properties[position];
106
+ prop->key = key;
107
+ prop->value = XT_UNDEFINED;
108
+ prop->getter = XT_UNDEFINED;
109
+ prop->setter = XT_UNDEFINED;
110
+ obj->count++;
111
+ return prop;
112
+ }
113
+
114
+ xt_property *xt_object_find_property(xt_object *obj, xt_value key) {
63
115
  return obj ? xt_object_find(obj, key) : NULL;
64
116
  }
65
117
 
118
+ /* Normalise a property key to a string value or a symbol value. */
119
+ xt_value xt_to_property_key(xt_value key) {
120
+ if (xt_is_symbol(key)) return key;
121
+ return xt_to_string(key);
122
+ }
123
+
124
+ /* Two property keys are equal when they are the same symbol, or equal
125
+ * strings. */
126
+ int xt_property_key_equals(xt_value a, xt_value b) {
127
+ if (XT_IS_STRING(a) && XT_IS_STRING(b)) return xt_string_equals(xt_as_string(a), xt_as_string(b));
128
+ return a == b;
129
+ }
130
+
66
131
  static int xt_key_is_prototype(xt_value key) {
67
- xt_string *k = xt_as_string(xt_to_string(key));
132
+ if (!XT_IS_STRING(key)) return 0;
133
+ xt_string *k = xt_as_string(key);
68
134
  return k && k->length == 9 && memcmp(k->data, "prototype", 9) == 0;
69
135
  }
70
136
 
@@ -81,9 +147,9 @@ xt_value xt_object_get(xt_value value, xt_value key) {
81
147
  layout. Containers such as Map/Set/Date/RegExp/Promise/Array must not be
82
148
  interpreted as property tables, or property lookup reads past the end of
83
149
  their real representation. */
84
- if (obj->header.kind != XT_OBJECT_KIND_OBJECT) return XT_UNDEFINED;
85
- xt_value keyString = xt_to_string(key);
86
- xt_string *k = xt_as_string(keyString);
150
+ if (obj->header.kind != XT_OBJECT_KIND_OBJECT && obj->header.kind != XT_OBJECT_KIND_ERROR)
151
+ return XT_UNDEFINED;
152
+ xt_value k = xt_to_property_key(key);
87
153
  /* Walk the prototype chain like JavaScript property lookup. */
88
154
  xt_object *cur = obj;
89
155
  while (cur) {
@@ -109,10 +175,10 @@ xt_value xt_object_set(xt_value value, xt_value key, xt_value newValue) {
109
175
  }
110
176
  xt_object *obj = xt_as_object(value);
111
177
  if (!obj) return newValue;
112
- if (obj->header.kind != XT_OBJECT_KIND_OBJECT) return newValue;
178
+ if (obj->header.kind != XT_OBJECT_KIND_OBJECT && obj->header.kind != XT_OBJECT_KIND_ERROR)
179
+ return newValue;
113
180
  if (obj->frozen) return newValue;
114
- xt_value keyString = xt_to_string(key);
115
- xt_string *k = xt_as_string(keyString);
181
+ xt_value k = xt_to_property_key(key);
116
182
  xt_property *prop = xt_object_find(obj, k);
117
183
  if (prop) {
118
184
  if (prop->setter != XT_UNDEFINED) {
@@ -144,39 +210,29 @@ xt_value xt_object_set(xt_value value, xt_value key, xt_value newValue) {
144
210
  }
145
211
  ancestor = XT_IS_OBJECT(ancestor->prototype) ? (xt_object *)XT_GET_PTR(ancestor->prototype) : NULL;
146
212
  }
147
- xt_object_reserve(obj, obj->count + 1);
148
- obj->properties[obj->count].key = k;
149
- obj->properties[obj->count].value = newValue;
150
- obj->properties[obj->count].getter = XT_UNDEFINED;
151
- obj->properties[obj->count].setter = XT_UNDEFINED;
152
- obj->count++;
213
+ xt_property *inserted = xt_object_insert_property(obj, k);
214
+ inserted->value = newValue;
153
215
  return newValue;
154
216
  }
155
217
 
156
- static xt_property *xt_object_ensure_property(xt_object *obj, xt_value keyString) {
157
- xt_property *prop = xt_object_find(obj, xt_as_string(keyString));
218
+ static xt_property *xt_object_ensure_property(xt_object *obj, xt_value key) {
219
+ xt_value propertyKey = xt_to_property_key(key);
220
+ xt_property *prop = xt_object_find(obj, propertyKey);
158
221
  if (prop) return prop;
159
- xt_object_reserve(obj, obj->count + 1);
160
- prop = &obj->properties[obj->count];
161
- prop->key = xt_as_string(keyString);
162
- prop->value = XT_UNDEFINED;
163
- prop->getter = XT_UNDEFINED;
164
- prop->setter = XT_UNDEFINED;
165
- obj->count++;
166
- return prop;
222
+ return xt_object_insert_property(obj, propertyKey);
167
223
  }
168
224
 
169
225
  xt_value xt_object_define_getter(xt_value value, xt_value key, xt_value getter) {
170
226
  xt_object *obj = xt_as_object(value);
171
227
  if (!obj) return value;
172
- xt_object_ensure_property(obj, xt_to_string(key))->getter = getter;
228
+ xt_object_ensure_property(obj, key)->getter = getter;
173
229
  return value;
174
230
  }
175
231
 
176
232
  xt_value xt_object_define_setter(xt_value value, xt_value key, xt_value setter) {
177
233
  xt_object *obj = xt_as_object(value);
178
234
  if (!obj) return value;
179
- xt_object_ensure_property(obj, xt_to_string(key))->setter = setter;
235
+ xt_object_ensure_property(obj, key)->setter = setter;
180
236
  return value;
181
237
  }
182
238
 
@@ -206,8 +262,7 @@ int xt_object_is_frozen(xt_value value) {
206
262
  xt_value xt_object_has_own(xt_value value, xt_value key) {
207
263
  xt_object *obj = xt_as_object(value);
208
264
  if (!obj) return XT_FALSE;
209
- xt_value keyString = xt_to_string(key);
210
- return xt_bool(xt_object_find(obj, xt_as_string(keyString)) != NULL);
265
+ return xt_bool(xt_object_find(obj, xt_to_property_key(key)) != NULL);
211
266
  }
212
267
 
213
268
  xt_value xt_object_from_entries(xt_value entries) {
@@ -225,8 +280,21 @@ xt_value xt_object_from_entries(xt_value entries) {
225
280
  xt_value xt_object_has(xt_value value, xt_value key) {
226
281
  if (!XT_IS_OBJECT(value)) return XT_FALSE;
227
282
  xt_object *obj = (xt_object *)XT_GET_PTR(value);
228
- xt_value keyString = xt_to_string(key);
229
- return xt_bool(xt_object_find(obj, xt_as_string(keyString)) != NULL);
283
+ return xt_bool(xt_object_find(obj, xt_to_property_key(key)) != NULL);
284
+ }
285
+
286
+ /* `Object.getOwnPropertySymbols`: the symbol-keyed own properties, in
287
+ * insertion order. String keys are skipped. */
288
+ xt_value xt_object_own_property_symbols(xt_value value) {
289
+ xt_value result = xt_array_new(0, NULL);
290
+ if (!XT_IS_OBJECT(value)) return result;
291
+ xt_object *obj = (xt_object *)XT_GET_PTR(value);
292
+ if (obj->header.kind != XT_OBJECT_KIND_OBJECT && obj->header.kind != XT_OBJECT_KIND_ERROR)
293
+ return result;
294
+ for (uint32_t i = 0; i < obj->count; i++) {
295
+ if (xt_is_symbol(obj->properties[i].key)) xt_array_push(result, obj->properties[i].key);
296
+ }
297
+ return result;
230
298
  }
231
299
 
232
300
  xt_value xt_object_keys(xt_value value) {
@@ -234,7 +302,7 @@ xt_value xt_object_keys(xt_value value) {
234
302
  if (XT_IS_OBJECT(value)) {
235
303
  xt_object *obj = (xt_object *)XT_GET_PTR(value);
236
304
  for (uint32_t i = 0; i < obj->count; i++) {
237
- xt_array_push(result, XT_FROM_PTR(XT_TAG_STRING, obj->properties[i].key));
305
+ if (XT_IS_STRING(obj->properties[i].key)) xt_array_push(result, obj->properties[i].key);
238
306
  }
239
307
  return result;
240
308
  }
@@ -245,6 +313,12 @@ xt_value xt_object_keys(xt_value value) {
245
313
  snprintf(buffer, sizeof(buffer), "%u", i);
246
314
  xt_array_push(result, xt_string_from_cstr(buffer));
247
315
  }
316
+ if (XT_IS_OBJECT(array->extra)) {
317
+ xt_object *extra = (xt_object *)XT_GET_PTR(array->extra);
318
+ for (uint32_t i = 0; i < extra->count; i++) {
319
+ if (XT_IS_STRING(extra->properties[i].key)) xt_array_push(result, extra->properties[i].key);
320
+ }
321
+ }
248
322
  return result;
249
323
  }
250
324
  if (XT_IS_STRING(value)) {
@@ -287,6 +361,7 @@ xt_value xt_array_new(int32_t count, xt_value *items) {
287
361
  array->length = 0;
288
362
  array->capacity = 0;
289
363
  array->items = NULL;
364
+ array->extra = XT_UNDEFINED;
290
365
  if (count > 0) {
291
366
  xt_array_reserve(array, (uint32_t)count);
292
367
  for (int32_t i = 0; i < count; i++) array->items[i] = items[i];
@@ -295,6 +370,16 @@ xt_value xt_array_new(int32_t count, xt_value *items) {
295
370
  return XT_FROM_PTR(XT_TAG_ARRAY, array);
296
371
  }
297
372
 
373
+ /* Named properties of an array live in a lazily-created side object. Returns
374
+ * XT_UNDEFINED when `create` is false and no such object exists yet. */
375
+ static xt_value xt_array_extra(xt_array *array, int create) {
376
+ if (!XT_IS_OBJECT(array->extra)) {
377
+ if (!create) return XT_UNDEFINED;
378
+ array->extra = xt_object_new();
379
+ }
380
+ return array->extra;
381
+ }
382
+
298
383
  xt_value xt_array_get(xt_value value, xt_value index) {
299
384
  if (!XT_IS_ARRAY(value)) return XT_UNDEFINED;
300
385
  xt_array *array = (xt_array *)XT_GET_PTR(value);
@@ -326,6 +411,11 @@ xt_value xt_array_push(xt_value value, xt_value newValue) {
326
411
  xt_value xt_array_length(xt_value value) {
327
412
  if (XT_IS_ARRAY(value)) return xt_number((double)((xt_array *)XT_GET_PTR(value))->length);
328
413
  if (XT_IS_STRING(value)) return xt_number((double)xt_string_length(xt_as_string(value)));
414
+ /* Functions expose their arity as `.length`. */
415
+ if (XT_IS_FUNCTION(value)) {
416
+ int32_t arity = ((xt_function *)XT_GET_PTR(value))->arity;
417
+ return xt_number(arity >= 0 ? (double)arity : 0.0);
418
+ }
329
419
  /* Objects such as Buffers expose a `length` property of their own. */
330
420
  if (XT_IS_OBJECT(value)) return xt_object_get_cstr(value, "length");
331
421
  return XT_UNDEFINED;
@@ -340,9 +430,9 @@ xt_value xt_array_spread(xt_value target, xt_value source) {
340
430
  for (uint32_t i = 0; i < src->length; i++) dst->items[dst->length++] = src->items[i];
341
431
  return target;
342
432
  }
343
- /* Spreading a string/Map/Set iterates exactly like `for...of`. */
344
- int32_t n = xt_to_int32(xt_iter_length(source));
345
- for (int32_t i = 0; i < n; i++) {
433
+ /* Spreading a string/Map/Set/iterable iterates exactly like `for...of`. */
434
+ source = xt_iter_open(source);
435
+ for (int32_t i = 0; xt_truthy(xt_iter_has(source, xt_number((double)i))); i++) {
346
436
  xt_array_push(target, xt_iter_value(source, xt_number((double)i)));
347
437
  }
348
438
  return target;
@@ -363,21 +453,89 @@ xt_value *xt_array_items(xt_value value) {
363
453
  /* Generic member access */
364
454
  /* ------------------------------------------------------------------------- */
365
455
 
456
+ /*
457
+ * Reading a property of `null` / `undefined` is a TypeError in JavaScript.
458
+ * Short-circuiting `?.` guards forbid reaching this point for a nullish
459
+ * receiver, so a non-optional access on a nullish base is a genuine bug in
460
+ * the compiled program (e.g. `o?.a.b` where `o.a` is null).
461
+ */
462
+ static void xt_throw_read_of_nullish(xt_value target, xt_value key) {
463
+ const char *base = target == XT_NULL ? "null" : "undefined";
464
+ char keybuf[128];
465
+ xt_string *k = XT_IS_STRING(key) ? xt_as_string(key) : xt_as_string(xt_to_string(key));
466
+ uint32_t n = k->length < sizeof(keybuf) - 1 ? k->length : (uint32_t)(sizeof(keybuf) - 1);
467
+ memcpy(keybuf, k->data, n);
468
+ keybuf[n] = 0;
469
+ char message[256];
470
+ snprintf(message, sizeof(message),
471
+ "TypeError: Cannot read properties of %s (reading '%s')", base, keybuf);
472
+ xt_throw(xt_string_from_cstr(message));
473
+ }
474
+
366
475
  xt_value xt_get(xt_value target, xt_value key) {
476
+ if (target == XT_UNDEFINED || target == XT_NULL) xt_throw_read_of_nullish(target, key);
367
477
  if (XT_IS_ARRAY(target)) {
368
478
  /* `arr.length` must read the array's length, never an element. */
369
479
  if (XT_IS_STRING(key)) {
370
480
  xt_string *k = xt_as_string(key);
371
481
  if (k->length == 6 && memcmp(k->data, "length", 6) == 0)
372
482
  return xt_number((double)((xt_array *)XT_GET_PTR(target))->length);
483
+ /* Named (non-index) properties such as `raw`/`index`/`input`. */
484
+ uint32_t ignored = 0;
485
+ if (!xt_key_is_array_index(k, &ignored)) {
486
+ xt_value extra = xt_array_extra((xt_array *)XT_GET_PTR(target), 0);
487
+ if (extra != XT_UNDEFINED) {
488
+ xt_value value = xt_object_get(extra, key);
489
+ if (value != XT_UNDEFINED) return value;
490
+ }
491
+ /* Fall back to first-class built-in methods (`arr.map`). */
492
+ return xt_method_value(target, key);
493
+ }
494
+ return xt_array_get(target, key);
495
+ }
496
+ /* Symbol-keyed properties live in the side object too. */
497
+ if (xt_is_symbol(key)) {
498
+ xt_value extra = xt_array_extra((xt_array *)XT_GET_PTR(target), 0);
499
+ if (extra != XT_UNDEFINED) {
500
+ xt_value value = xt_object_get(extra, key);
501
+ if (value != XT_UNDEFINED) return value;
502
+ }
503
+ return xt_method_value(target, key);
373
504
  }
374
505
  return xt_array_get(target, key);
375
506
  }
376
- if (XT_IS_FUNCTION(target)) return xt_object_get(target, key);
507
+ if (XT_IS_FUNCTION(target)) {
508
+ /* Functions expose own `name` / `length` (arity) properties. */
509
+ if (XT_IS_STRING(key)) {
510
+ xt_string *k = xt_as_string(key);
511
+ if (k->length == 6 && memcmp(k->data, "length", 6) == 0) {
512
+ int32_t arity = ((xt_function *)XT_GET_PTR(target))->arity;
513
+ return xt_number(arity >= 0 ? (double)arity : 0.0);
514
+ }
515
+ if (k->length == 4 && memcmp(k->data, "name", 4) == 0) {
516
+ xt_string *name = ((xt_function *)XT_GET_PTR(target))->name;
517
+ return name ? XT_FROM_PTR(XT_TAG_STRING, name) : xt_string_from_cstr("");
518
+ }
519
+ }
520
+ {
521
+ xt_value value = xt_object_get(target, key);
522
+ if (value != XT_UNDEFINED) return value;
523
+ return xt_method_value(target, key);
524
+ }
525
+ }
377
526
  if (XT_IS_OBJECT(target)) {
378
527
  /* Map/Set expose `.size`; promises, dates and regexps have their own
379
528
  * getters implemented in the standard library. */
380
529
  xt_object *obj = (xt_object *)XT_GET_PTR(target);
530
+ if (obj->header.kind == XT_OBJECT_KIND_SYMBOL) {
531
+ if (XT_IS_STRING(key)) {
532
+ xt_string *k = xt_as_string(key);
533
+ if (k->length == 11 && memcmp(k->data, "description", 11) == 0) {
534
+ return xt_symbol_description(target);
535
+ }
536
+ }
537
+ return xt_method_value(target, key);
538
+ }
381
539
  if (obj->header.kind == XT_OBJECT_KIND_MAP || obj->header.kind == XT_OBJECT_KIND_SET) {
382
540
  if (XT_IS_STRING(key)) {
383
541
  xt_string *k = xt_as_string(key);
@@ -387,20 +545,46 @@ xt_value xt_get(xt_value target, xt_value key) {
387
545
  }
388
546
  }
389
547
  }
390
- if (obj->header.kind == XT_OBJECT_KIND_REGEXP) return xt_regexp_get_property(target, key);
391
- return xt_object_get(target, key);
548
+ if (obj->header.kind == XT_OBJECT_KIND_REGEXP) {
549
+ xt_value regexpValue = xt_regexp_get_property(target, key);
550
+ if (regexpValue != XT_UNDEFINED) return regexpValue;
551
+ }
552
+ if (obj->header.kind == XT_OBJECT_KIND_ERROR && XT_IS_STRING(key)) {
553
+ xt_string *k = xt_as_string(key);
554
+ if (k->length == 5 && memcmp(k->data, "stack", 5) == 0) {
555
+ xt_value own = xt_object_get(target, key);
556
+ return own != XT_UNDEFINED ? own : xt_error_to_string(target);
557
+ }
558
+ }
559
+ {
560
+ xt_value value = xt_object_get(target, key);
561
+ if (value != XT_UNDEFINED) return value;
562
+ return xt_method_value(target, key);
563
+ }
392
564
  }
393
565
  if (XT_IS_STRING(target)) {
394
- /* Property access on a string: `.length` and numeric indexing. */
566
+ /* Property access on a string: `.length`, numeric indexing and built-in
567
+ * methods. */
395
568
  xt_string *s = xt_as_string(target);
396
569
  if (XT_IS_STRING(key)) {
397
570
  xt_string *k = xt_as_string(key);
398
571
  if (k->length == 6 && memcmp(k->data, "length", 6) == 0) return xt_number((double)s->length);
572
+ uint32_t index = 0;
573
+ if (xt_key_is_array_index(k, &index) && index < s->length) {
574
+ return xt_string_new(s->data + index, 1);
575
+ }
576
+ return xt_method_value(target, key);
577
+ }
578
+ if (XT_IS_NUMBER(key)) {
579
+ int32_t index = xt_to_int32(key);
580
+ if (index >= 0 && (uint32_t)index < s->length) return xt_string_new(s->data + index, 1);
399
581
  }
400
- int32_t index = xt_to_int32(key);
401
- if (index >= 0 && (uint32_t)index < s->length) return xt_string_new(s->data + index, 1);
402
582
  return XT_UNDEFINED;
403
583
  }
584
+ if (XT_IS_STRING(key) &&
585
+ (XT_IS_NUMBER(target) || XT_IS_BIGINT(target) || target == XT_TRUE || target == XT_FALSE)) {
586
+ return xt_method_value(target, key);
587
+ }
404
588
  return XT_UNDEFINED;
405
589
  }
406
590
 
@@ -418,6 +602,14 @@ xt_value xt_set(xt_value target, xt_value key, xt_value value) {
418
602
  array->length = (uint32_t)n;
419
603
  return value;
420
604
  }
605
+ uint32_t ignored = 0;
606
+ if (!xt_key_is_array_index(k, &ignored)) {
607
+ return xt_object_set(xt_array_extra((xt_array *)XT_GET_PTR(target), 1), key, value);
608
+ }
609
+ return xt_array_set(target, key, value);
610
+ }
611
+ if (xt_is_symbol(key)) {
612
+ return xt_object_set(xt_array_extra((xt_array *)XT_GET_PTR(target), 1), key, value);
421
613
  }
422
614
  return xt_array_set(target, key, value);
423
615
  }