xbintsc 0.2.5 → 0.2.7

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 (88) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/dist/src/ast/expressions.d.ts +6 -2
  4. package/dist/src/ast/kinds.d.ts +1 -0
  5. package/dist/src/ast/kinds.js +1 -0
  6. package/dist/src/ast/kinds.js.map +1 -1
  7. package/dist/src/binder/binder.js +29 -0
  8. package/dist/src/binder/binder.js.map +1 -1
  9. package/dist/src/codegen/generator/calls.d.ts +1 -1
  10. package/dist/src/codegen/generator/calls.js +57 -14
  11. package/dist/src/codegen/generator/calls.js.map +1 -1
  12. package/dist/src/codegen/generator/context.d.ts +1 -1
  13. package/dist/src/codegen/generator/expressions.js +14 -3
  14. package/dist/src/codegen/generator/expressions.js.map +1 -1
  15. package/dist/src/codegen/generator/module.js +57 -2
  16. package/dist/src/codegen/generator/module.js.map +1 -1
  17. package/dist/src/codegen/generator/state.js +15 -0
  18. package/dist/src/codegen/generator/state.js.map +1 -1
  19. package/dist/src/codegen/generator/statements.js +25 -8
  20. package/dist/src/codegen/generator/statements.js.map +1 -1
  21. package/dist/src/codegen/generator/tables.d.ts +3 -0
  22. package/dist/src/codegen/generator/tables.js +12 -0
  23. package/dist/src/codegen/generator/tables.js.map +1 -1
  24. package/dist/src/codegen/values.d.ts +18 -11
  25. package/dist/src/codegen/values.js +92 -15
  26. package/dist/src/codegen/values.js.map +1 -1
  27. package/dist/src/diagnostics/source.d.ts +1 -1
  28. package/dist/src/diagnostics/source.js +12 -7
  29. package/dist/src/diagnostics/source.js.map +1 -1
  30. package/dist/src/driver/compiler.js +16 -2
  31. package/dist/src/driver/compiler.js.map +1 -1
  32. package/dist/src/extensions/node/child_process/index.d.ts +7 -0
  33. package/dist/src/extensions/node/child_process/index.js +19 -0
  34. package/dist/src/extensions/node/child_process/index.js.map +1 -0
  35. package/dist/src/extensions/node/crypto/index.d.ts +6 -0
  36. package/dist/src/extensions/node/crypto/index.js +18 -0
  37. package/dist/src/extensions/node/crypto/index.js.map +1 -0
  38. package/dist/src/extensions/node/index.js +6 -0
  39. package/dist/src/extensions/node/index.js.map +1 -1
  40. package/dist/src/extensions/node/module.d.ts +9 -1
  41. package/dist/src/extensions/node/module.js +14 -1
  42. package/dist/src/extensions/node/module.js.map +1 -1
  43. package/dist/src/extensions/node/url/index.d.ts +7 -0
  44. package/dist/src/extensions/node/url/index.js +21 -0
  45. package/dist/src/extensions/node/url/index.js.map +1 -0
  46. package/dist/src/lexer/scanner.js +4 -5
  47. package/dist/src/lexer/scanner.js.map +1 -1
  48. package/dist/src/parser/declarations.js +10 -9
  49. package/dist/src/parser/declarations.js.map +1 -1
  50. package/dist/src/parser/expressions.js +1 -1
  51. package/dist/src/parser/expressions.js.map +1 -1
  52. package/dist/tests/e2e/compile.test.js +36 -0
  53. package/dist/tests/e2e/compile.test.js.map +1 -1
  54. package/package.json +1 -1
  55. package/runtime/ext_node/child_process/child_process.c +213 -0
  56. package/runtime/ext_node/crypto/crypto.c +177 -0
  57. package/runtime/ext_node/process/process.c +37 -3
  58. package/runtime/ext_node/url/url.c +85 -0
  59. package/runtime/rt.h +20 -0
  60. package/runtime/rt_internal.h +11 -0
  61. package/runtime/xt_builtins.c +23 -0
  62. package/runtime/xt_containers.c +136 -10
  63. package/runtime/xt_io.c +53 -0
  64. package/runtime/xt_regex.h +16 -6
  65. package/runtime/xt_stdlib.c +11 -2
  66. package/runtime/xt_stdlib2.c +239 -13
  67. package/runtime/xt_values.c +2 -1
  68. package/src/ast/expressions.ts +7 -2
  69. package/src/ast/kinds.ts +1 -0
  70. package/src/binder/binder.ts +29 -0
  71. package/src/codegen/generator/calls.ts +59 -16
  72. package/src/codegen/generator/context.ts +1 -1
  73. package/src/codegen/generator/expressions.ts +14 -3
  74. package/src/codegen/generator/module.ts +60 -1
  75. package/src/codegen/generator/state.ts +15 -0
  76. package/src/codegen/generator/statements.ts +23 -8
  77. package/src/codegen/generator/tables.ts +13 -0
  78. package/src/codegen/values.ts +85 -16
  79. package/src/diagnostics/source.ts +12 -7
  80. package/src/driver/compiler.ts +15 -2
  81. package/src/extensions/node/child_process/index.ts +24 -0
  82. package/src/extensions/node/crypto/index.ts +23 -0
  83. package/src/extensions/node/index.ts +6 -0
  84. package/src/extensions/node/module.ts +13 -1
  85. package/src/extensions/node/url/index.ts +26 -0
  86. package/src/lexer/scanner.ts +4 -4
  87. package/src/parser/declarations.ts +10 -7
  88. package/src/parser/expressions.ts +1 -1
@@ -0,0 +1,85 @@
1
+ /*
2
+ * Node.js `url` module for xbintsc.
3
+ *
4
+ * Implements the two helpers the compiler itself relies on:
5
+ * - `pathToFileURL(path)` -> URL object with an `href` property
6
+ * - `fileURLToPath(url)` -> filesystem path
7
+ *
8
+ * Both accept the `file://` scheme and percent-encoded URLs. On Windows a
9
+ * leading slash before a drive letter (`/C:/`) is removed, matching Node.
10
+ */
11
+
12
+ #include "rt.h"
13
+
14
+ #include <ctype.h>
15
+ #include <stdlib.h>
16
+ #include <string.h>
17
+
18
+ static int url_hex(int c) {
19
+ if (c >= '0' && c <= '9') return c - '0';
20
+ if (c >= 'a' && c <= 'f') return c - 'a' + 10;
21
+ if (c >= 'A' && c <= 'F') return c - 'A' + 10;
22
+ return -1;
23
+ }
24
+
25
+ static xt_value url_percent_decode(const char *in) {
26
+ size_t length = strlen(in);
27
+ char *out = (char *)malloc(length + 1);
28
+ if (!out) return xt_string_from_cstr(in);
29
+ size_t j = 0;
30
+ for (size_t i = 0; i < length; i++) {
31
+ if (in[i] == '%' && i + 2 < length && url_hex(in[i + 1]) >= 0 && url_hex(in[i + 2]) >= 0) {
32
+ out[j++] = (char)((url_hex(in[i + 1]) << 4) | url_hex(in[i + 2]));
33
+ i += 2;
34
+ } else {
35
+ out[j++] = in[i];
36
+ }
37
+ }
38
+ out[j] = 0;
39
+ xt_value result = xt_string_from_cstr(out);
40
+ free(out);
41
+ return result;
42
+ }
43
+
44
+ xt_value xt_url_path_to_file_url(int32_t argc, xt_value *argv) {
45
+ const char *raw = argc > 0 ? xt_string_data(xt_to_string(argv[0])) : "";
46
+ if (!raw) raw = "";
47
+ /* Resolve to an absolute path so the URL is comparable to `import.meta.url`
48
+ (which is built from realpath) even when given a relative path. */
49
+ char resolved[4096];
50
+ const char *path = raw;
51
+ #if defined(_WIN32)
52
+ if (_fullpath(resolved, raw, sizeof(resolved))) path = resolved;
53
+ #else
54
+ if (realpath(raw, resolved)) path = resolved;
55
+ #endif
56
+ char href[4300];
57
+ size_t j = 0;
58
+ const char *prefix = "file://";
59
+ for (; *prefix && j < sizeof(href) - 1; prefix++) href[j++] = *prefix;
60
+ #if defined(_WIN32)
61
+ if (j < sizeof(href) - 1) href[j++] = '/';
62
+ #endif
63
+ for (const char *p = path; *p && j < sizeof(href) - 1; p++) href[j++] = (*p == '\\') ? '/' : *p;
64
+ href[j] = 0;
65
+
66
+ xt_value url = xt_object_new();
67
+ xt_set(url, xt_string_from_cstr("href"), xt_string_from_cstr(href));
68
+ xt_set(url, xt_string_from_cstr("protocol"), xt_string_from_cstr("file:"));
69
+ xt_set(url, xt_string_from_cstr("pathname"), xt_string_from_cstr(path));
70
+ return url;
71
+ }
72
+
73
+ xt_value xt_url_file_url_to_path(int32_t argc, xt_value *argv) {
74
+ const char *url = argc > 0 ? xt_string_data(xt_to_string(argv[0])) : "";
75
+ const char *path = url;
76
+ if (strncmp(url, "file://", 7) == 0) {
77
+ path = url + 7;
78
+ /* `file://localhost/...` is equivalent to `file:///...`. */
79
+ if (strncmp(path, "localhost", 9) == 0) path += 9;
80
+ }
81
+ #if defined(_WIN32)
82
+ if (path[0] == '/' && isalpha((unsigned char)path[1]) && path[2] == ':') path++;
83
+ #endif
84
+ return url_percent_decode(path);
85
+ }
package/runtime/rt.h CHANGED
@@ -147,6 +147,9 @@ xt_value xt_object_new_with_proto(xt_value proto);
147
147
  xt_value xt_object_get(xt_value obj, xt_value key);
148
148
  xt_value xt_object_get_prototype(xt_value obj);
149
149
  xt_value xt_object_set_prototype(xt_value obj, xt_value proto);
150
+ /** Define `get name()` / `set name(v)` accessors on an object. */
151
+ xt_value xt_object_define_getter(xt_value obj, xt_value key, xt_value getter);
152
+ xt_value xt_object_define_setter(xt_value obj, xt_value key, xt_value setter);
150
153
  xt_value xt_object_freeze(xt_value obj);
151
154
  int xt_object_is_frozen(xt_value obj);
152
155
  xt_value xt_object_from_entries(xt_value entries);
@@ -182,6 +185,16 @@ xt_value xt_date_ctor(int32_t argc, xt_value *argv);
182
185
  xt_value xt_date_static(xt_value name, int32_t argc, xt_value *argv);
183
186
  xt_value xt_regexp_ctor(int32_t argc, xt_value *argv);
184
187
 
188
+ /* -- node:url ------------------------------------------------------------- */
189
+ xt_value xt_url_path_to_file_url(int32_t argc, xt_value *argv);
190
+ xt_value xt_url_file_url_to_path(int32_t argc, xt_value *argv);
191
+
192
+ /* -- node:crypto ---------------------------------------------------------- */
193
+ xt_value xt_crypto_create_hash(int32_t argc, xt_value *argv);
194
+
195
+ /* -- node:child_process --------------------------------------------------- */
196
+ xt_value xt_child_process_spawn_sync(int32_t argc, xt_value *argv);
197
+
185
198
  /* -- promises / async ----------------------------------------------------- */
186
199
  xt_value xt_promise_resolve(xt_value value);
187
200
  xt_value xt_promise_reject(xt_value value);
@@ -223,6 +236,7 @@ xt_value xt_rest_args(int32_t argc, xt_value *argv, int32_t start);
223
236
  /* -- program arguments ---------------------------------------------------- */
224
237
  /* Captured from `main` so host extensions can expose `process.argv`. */
225
238
  void xt_set_program_args(int32_t argc, char **argv);
239
+ xt_value xt_import_meta(xt_value name);
226
240
  extern int32_t xt_program_argc;
227
241
  extern char **xt_program_argv;
228
242
 
@@ -233,6 +247,12 @@ xt_value xt_array_set(xt_value arr, xt_value index, xt_value value);
233
247
  xt_value xt_array_push(xt_value arr, xt_value value);
234
248
  xt_value xt_array_length(xt_value arr);
235
249
  xt_value xt_array_spread(xt_value target, xt_value source);
250
+ int32_t xt_array_size(xt_value value);
251
+ xt_value *xt_array_items(xt_value value);
252
+
253
+ /* `for...of` iteration: dispatch over arrays, strings, Maps and Sets. */
254
+ xt_value xt_iter_length(xt_value value);
255
+ xt_value xt_iter_value(xt_value value, xt_value index);
236
256
 
237
257
  /* -- generic member access ------------------------------------------------ */
238
258
  xt_value xt_get(xt_value target, xt_value key);
@@ -66,6 +66,10 @@ typedef struct {
66
66
  typedef struct {
67
67
  xt_string *key;
68
68
  xt_value value;
69
+ /* Accessor properties (`get x()` / `set x(v)`) store the functions here;
70
+ * when set, `value` is ignored and property access invokes the getter. */
71
+ xt_value getter;
72
+ xt_value setter;
69
73
  } xt_property;
70
74
 
71
75
  typedef struct {
@@ -120,8 +124,15 @@ int xt_is_map(xt_value value);
120
124
  int xt_is_set(xt_value value);
121
125
  int xt_is_date(xt_value value);
122
126
  int xt_is_regexp(xt_value value);
127
+ int xt_regexp_find(xt_value regexp, xt_value input, int32_t start, int32_t *matchStart, int32_t *matchEnd);
128
+ xt_value xt_regexp_exec(xt_value regexp, xt_value input);
129
+ xt_value xt_regexp_replace(xt_value value, xt_value regexp, xt_value replacement);
130
+ xt_value xt_regexp_get_property(xt_value regexp, xt_value key);
123
131
  int32_t xt_map_size(xt_value value);
124
132
  int32_t xt_set_size(xt_value value);
133
+ /* `for...of` iteration: dispatch over arrays, strings, Maps and Sets. */
134
+ xt_value xt_iter_length(xt_value value);
135
+ xt_value xt_iter_value(xt_value value, xt_value index);
125
136
 
126
137
  /* -- collection / date / regexp representations --------------------------- */
127
138
  typedef struct {
@@ -100,6 +100,29 @@ xt_value xt_boolean_ctor(int32_t argc, xt_value *argv) {
100
100
  return xt_bool(xt_truthy(argv[0]));
101
101
  }
102
102
 
103
+ /*
104
+ * Builtin function values. Global constructors like `Boolean` are usually
105
+ * called directly, but JavaScript also allows passing them around
106
+ * (`arr.filter(Boolean)`). These trampolines adapt the uniform `(argc, argv)`
107
+ * builtin ABI to the closure ABI so the codegen can wrap them with
108
+ * `xt_closure_new`.
109
+ */
110
+ #define XT_BUILTIN_TRAMPOLINE(name, target) \
111
+ xt_value name(xt_value thisValue, xt_value env, int32_t argc, \
112
+ xt_value *argv) { \
113
+ (void)thisValue; \
114
+ (void)env; \
115
+ return target(argc, argv); \
116
+ }
117
+
118
+ XT_BUILTIN_TRAMPOLINE(xt_builtin_value_boolean, xt_boolean_ctor)
119
+ XT_BUILTIN_TRAMPOLINE(xt_builtin_value_number, xt_number_ctor)
120
+ XT_BUILTIN_TRAMPOLINE(xt_builtin_value_string, xt_string_ctor)
121
+ XT_BUILTIN_TRAMPOLINE(xt_builtin_value_parseInt, xt_parse_int)
122
+ XT_BUILTIN_TRAMPOLINE(xt_builtin_value_parseFloat, xt_parse_float)
123
+ XT_BUILTIN_TRAMPOLINE(xt_builtin_value_isNaN, xt_is_nan)
124
+ XT_BUILTIN_TRAMPOLINE(xt_builtin_value_isFinite, xt_is_finite)
125
+
103
126
  xt_value xt_in(xt_value key, xt_value value) {
104
127
  if (XT_IS_OBJECT(value)) return xt_object_has(value, key);
105
128
  if (XT_IS_ARRAY(value)) {
@@ -63,16 +63,40 @@ xt_property *xt_object_find_property(xt_object *obj, xt_string *key) {
63
63
  return obj ? xt_object_find(obj, key) : NULL;
64
64
  }
65
65
 
66
+ static int xt_key_is_prototype(xt_value key) {
67
+ xt_string *k = xt_as_string(xt_to_string(key));
68
+ return k && k->length == 9 && memcmp(k->data, "prototype", 9) == 0;
69
+ }
70
+
66
71
  xt_value xt_object_get(xt_value value, xt_value key) {
72
+ /* A function's `.prototype` lives in a dedicated field (set by
73
+ `xt_function_set_prototype`), not in its property bag. Expose it as a
74
+ normal property so `Class.prototype` returns the object instances use. */
75
+ if (XT_IS_FUNCTION(value) && xt_key_is_prototype(key)) {
76
+ return ((xt_function *)XT_GET_PTR(value))->prototype;
77
+ }
67
78
  xt_object *obj = xt_as_object(value);
68
79
  if (!obj) return XT_UNDEFINED;
80
+ /* Only plain objects (and function property bags) use the `xt_object`
81
+ layout. Containers such as Map/Set/Date/RegExp/Promise/Array must not be
82
+ interpreted as property tables, or property lookup reads past the end of
83
+ their real representation. */
84
+ if (obj->header.kind != XT_OBJECT_KIND_OBJECT) return XT_UNDEFINED;
69
85
  xt_value keyString = xt_to_string(key);
70
86
  xt_string *k = xt_as_string(keyString);
71
87
  /* Walk the prototype chain like JavaScript property lookup. */
72
88
  xt_object *cur = obj;
73
89
  while (cur) {
74
90
  xt_property *prop = xt_object_find(cur, k);
75
- if (prop) return prop->value;
91
+ if (prop) {
92
+ /* Invoke accessors with the original receiver as `this`, so getters
93
+ inherited from a prototype still see the instance. */
94
+ if (prop->getter != XT_UNDEFINED && XT_IS_FUNCTION(prop->getter)) {
95
+ return xt_call_with_this(prop->getter, value, 0, NULL);
96
+ }
97
+ if (prop->getter != XT_UNDEFINED) return XT_UNDEFINED;
98
+ return prop->value;
99
+ }
76
100
  if (!XT_IS_OBJECT(cur->prototype)) break;
77
101
  cur = (xt_object *)XT_GET_PTR(cur->prototype);
78
102
  }
@@ -80,22 +104,82 @@ xt_value xt_object_get(xt_value value, xt_value key) {
80
104
  }
81
105
 
82
106
  xt_value xt_object_set(xt_value value, xt_value key, xt_value newValue) {
107
+ if (XT_IS_FUNCTION(value) && xt_key_is_prototype(key)) {
108
+ return xt_function_set_prototype(value, newValue);
109
+ }
83
110
  xt_object *obj = xt_as_object(value);
84
111
  if (!obj) return newValue;
112
+ if (obj->header.kind != XT_OBJECT_KIND_OBJECT) return newValue;
85
113
  if (obj->frozen) return newValue;
86
114
  xt_value keyString = xt_to_string(key);
87
- xt_property *prop = xt_object_find(obj, xt_as_string(keyString));
115
+ xt_string *k = xt_as_string(keyString);
116
+ xt_property *prop = xt_object_find(obj, k);
88
117
  if (prop) {
118
+ if (prop->setter != XT_UNDEFINED) {
119
+ if (XT_IS_FUNCTION(prop->setter)) {
120
+ xt_value argument = newValue;
121
+ xt_call_with_this(prop->setter, value, 1, &argument);
122
+ }
123
+ return newValue;
124
+ }
125
+ if (prop->getter != XT_UNDEFINED) return newValue; /* accessor without setter */
89
126
  prop->value = newValue;
90
127
  return newValue;
91
128
  }
129
+ /* No own property: an inherited accessor still intercepts the assignment
130
+ (JavaScript assigns through prototype setters instead of shadowing). */
131
+ xt_object *ancestor = XT_IS_OBJECT(obj->prototype) ? (xt_object *)XT_GET_PTR(obj->prototype) : NULL;
132
+ while (ancestor) {
133
+ xt_property *inherited = xt_object_find(ancestor, k);
134
+ if (inherited) {
135
+ if (inherited->setter != XT_UNDEFINED) {
136
+ if (XT_IS_FUNCTION(inherited->setter)) {
137
+ xt_value argument = newValue;
138
+ xt_call_with_this(inherited->setter, value, 1, &argument);
139
+ }
140
+ return newValue;
141
+ }
142
+ if (inherited->getter != XT_UNDEFINED) return newValue; /* getter-only accessor */
143
+ break; /* plain inherited data property: create an own shadowing property */
144
+ }
145
+ ancestor = XT_IS_OBJECT(ancestor->prototype) ? (xt_object *)XT_GET_PTR(ancestor->prototype) : NULL;
146
+ }
92
147
  xt_object_reserve(obj, obj->count + 1);
93
- obj->properties[obj->count].key = xt_as_string(keyString);
148
+ obj->properties[obj->count].key = k;
94
149
  obj->properties[obj->count].value = newValue;
150
+ obj->properties[obj->count].getter = XT_UNDEFINED;
151
+ obj->properties[obj->count].setter = XT_UNDEFINED;
95
152
  obj->count++;
96
153
  return newValue;
97
154
  }
98
155
 
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));
158
+ 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;
167
+ }
168
+
169
+ xt_value xt_object_define_getter(xt_value value, xt_value key, xt_value getter) {
170
+ xt_object *obj = xt_as_object(value);
171
+ if (!obj) return value;
172
+ xt_object_ensure_property(obj, xt_to_string(key))->getter = getter;
173
+ return value;
174
+ }
175
+
176
+ xt_value xt_object_define_setter(xt_value value, xt_value key, xt_value setter) {
177
+ xt_object *obj = xt_as_object(value);
178
+ if (!obj) return value;
179
+ xt_object_ensure_property(obj, xt_to_string(key))->setter = setter;
180
+ return value;
181
+ }
182
+
99
183
  xt_value xt_object_get_prototype(xt_value value) {
100
184
  xt_object *obj = xt_as_object(value);
101
185
  if (obj) return obj->prototype;
@@ -248,21 +332,47 @@ xt_value xt_array_length(xt_value value) {
248
332
  }
249
333
 
250
334
  xt_value xt_array_spread(xt_value target, xt_value source) {
251
- if (!XT_IS_ARRAY(target) || !XT_IS_ARRAY(source)) return target;
252
- xt_array *dst = (xt_array *)XT_GET_PTR(target);
253
- xt_array *src = (xt_array *)XT_GET_PTR(source);
254
- xt_array_reserve(dst, dst->length + src->length);
255
- for (uint32_t i = 0; i < src->length; i++) dst->items[dst->length++] = src->items[i];
335
+ if (!XT_IS_ARRAY(target)) return target;
336
+ if (XT_IS_ARRAY(source)) {
337
+ xt_array *dst = (xt_array *)XT_GET_PTR(target);
338
+ xt_array *src = (xt_array *)XT_GET_PTR(source);
339
+ xt_array_reserve(dst, dst->length + src->length);
340
+ for (uint32_t i = 0; i < src->length; i++) dst->items[dst->length++] = src->items[i];
341
+ return target;
342
+ }
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++) {
346
+ xt_array_push(target, xt_iter_value(source, xt_number((double)i)));
347
+ }
256
348
  return target;
257
349
  }
258
350
 
351
+ int32_t xt_array_size(xt_value value) {
352
+ if (!XT_IS_ARRAY(value)) return 0;
353
+ return (int32_t)((xt_array *)XT_GET_PTR(value))->length;
354
+ }
355
+
356
+ xt_value *xt_array_items(xt_value value) {
357
+ if (!XT_IS_ARRAY(value)) return NULL;
358
+ return ((xt_array *)XT_GET_PTR(value))->items;
359
+ }
360
+
259
361
 
260
362
  /* ------------------------------------------------------------------------- */
261
363
  /* Generic member access */
262
364
  /* ------------------------------------------------------------------------- */
263
365
 
264
366
  xt_value xt_get(xt_value target, xt_value key) {
265
- if (XT_IS_ARRAY(target)) return xt_array_get(target, key);
367
+ if (XT_IS_ARRAY(target)) {
368
+ /* `arr.length` must read the array's length, never an element. */
369
+ if (XT_IS_STRING(key)) {
370
+ xt_string *k = xt_as_string(key);
371
+ if (k->length == 6 && memcmp(k->data, "length", 6) == 0)
372
+ return xt_number((double)((xt_array *)XT_GET_PTR(target))->length);
373
+ }
374
+ return xt_array_get(target, key);
375
+ }
266
376
  if (XT_IS_FUNCTION(target)) return xt_object_get(target, key);
267
377
  if (XT_IS_OBJECT(target)) {
268
378
  /* Map/Set expose `.size`; promises, dates and regexps have their own
@@ -277,6 +387,7 @@ xt_value xt_get(xt_value target, xt_value key) {
277
387
  }
278
388
  }
279
389
  }
390
+ if (obj->header.kind == XT_OBJECT_KIND_REGEXP) return xt_regexp_get_property(target, key);
280
391
  return xt_object_get(target, key);
281
392
  }
282
393
  if (XT_IS_STRING(target)) {
@@ -294,7 +405,22 @@ xt_value xt_get(xt_value target, xt_value key) {
294
405
  }
295
406
 
296
407
  xt_value xt_set(xt_value target, xt_value key, xt_value value) {
297
- if (XT_IS_ARRAY(target)) return xt_array_set(target, key, value);
408
+ if (XT_IS_ARRAY(target)) {
409
+ /* Assigning `arr.length` truncates or extends the array. */
410
+ if (XT_IS_STRING(key)) {
411
+ xt_string *k = xt_as_string(key);
412
+ if (k->length == 6 && memcmp(k->data, "length", 6) == 0) {
413
+ xt_array *array = (xt_array *)XT_GET_PTR(target);
414
+ int32_t n = xt_to_int32(value);
415
+ if (n < 0) n = 0;
416
+ xt_array_reserve(array, (uint32_t)n);
417
+ for (uint32_t j = array->length; j < (uint32_t)n; j++) array->items[j] = XT_UNDEFINED;
418
+ array->length = (uint32_t)n;
419
+ return value;
420
+ }
421
+ }
422
+ return xt_array_set(target, key, value);
423
+ }
298
424
  if (XT_IS_FUNCTION(target)) return xt_object_set(target, key, value);
299
425
  if (XT_IS_OBJECT(target)) return xt_object_set(target, key, value);
300
426
  return value;
package/runtime/xt_io.c CHANGED
@@ -69,6 +69,59 @@ void xt_set_program_args(int32_t argc, char **argv) {
69
69
  xt_program_argv = argv;
70
70
  }
71
71
 
72
+ /*
73
+ * `import.meta` support. The compiler resolves module URLs at build time, but
74
+ * a self-hosted binary only knows where *it* lives, so `import.meta.url` is the
75
+ * executable's own `file://` URL (and `dirname`/`filename` its path parts).
76
+ * This lets the compiler locate an adjacent `runtime/` directory at run time.
77
+ */
78
+ static xt_value xt_file_url(const char *path) {
79
+ char url[4300];
80
+ size_t j = 0;
81
+ const char *prefix = "file://";
82
+ for (; *prefix; prefix++) url[j++] = *prefix;
83
+ #if defined(_WIN32)
84
+ url[j++] = '/';
85
+ #endif
86
+ for (const char *p = path; *p && j < sizeof(url) - 1; p++) url[j++] = (*p == '\\') ? '/' : *p;
87
+ url[j] = 0;
88
+ return xt_string_from_cstr(url);
89
+ }
90
+
91
+ xt_value xt_import_meta(xt_value name) {
92
+ const char *prop = xt_string_data(name);
93
+ const char *raw = (xt_program_argv && xt_program_argv[0]) ? xt_program_argv[0] : "";
94
+ char resolved[4096];
95
+ resolved[0] = 0;
96
+ if (raw[0]) {
97
+ #if defined(_WIN32)
98
+ if (_fullpath(resolved, raw, sizeof(resolved)) == NULL) resolved[0] = 0;
99
+ #else
100
+ if (realpath(raw, resolved) == NULL) resolved[0] = 0;
101
+ #endif
102
+ }
103
+ const char *abs = resolved[0] ? resolved : raw;
104
+ if (prop && strcmp(prop, "dirname") == 0) {
105
+ char dir[4096];
106
+ size_t n = strlen(abs);
107
+ if (n >= sizeof(dir)) n = sizeof(dir) - 1;
108
+ memcpy(dir, abs, n);
109
+ dir[n] = 0;
110
+ char *slash = strrchr(dir, '/');
111
+ #if defined(_WIN32)
112
+ char *back = strrchr(dir, '\\');
113
+ if (back && (!slash || back > slash)) slash = back;
114
+ #endif
115
+ if (slash) {
116
+ if (slash == dir) slash[1] = 0;
117
+ else *slash = 0;
118
+ }
119
+ return xt_string_from_cstr(dir);
120
+ }
121
+ if (prop && strcmp(prop, "filename") == 0) return xt_string_from_cstr(abs);
122
+ return xt_file_url(abs);
123
+ }
124
+
72
125
  void *xt_try_enter(void) {
73
126
  xt_try_frame *frame = (xt_try_frame *)calloc(1, sizeof(xt_try_frame));
74
127
  if (!frame) abort();
@@ -567,9 +567,13 @@ static int xt_re_match_alt(xt_re_alt *alt, const xt_re_input *in, size_t pos,
567
567
  return 0;
568
568
  }
569
569
 
570
- static int xt_re_accept(void *ctx, size_t pos) {
571
- (void)ctx;
572
- (void)pos;
570
+ typedef struct {
571
+ size_t end;
572
+ } xt_re_match_result;
573
+
574
+ static int xt_re_record_match(void *ctx, size_t pos) {
575
+ xt_re_match_result *result = (xt_re_match_result *)ctx;
576
+ result->end = pos;
573
577
  return 1;
574
578
  }
575
579
 
@@ -597,15 +601,21 @@ static int regexec(const regex_t *preg, const char *string, size_t nmatch,
597
601
  regmatch_t pmatch[], int eflags) {
598
602
  xt_re_input in;
599
603
  size_t start;
600
- (void)nmatch;
601
- (void)pmatch;
602
604
  (void)eflags;
603
605
  if (!preg || !preg->root || !string) return 1;
604
606
  in.text = string;
605
607
  in.len = strlen(string);
606
608
  in.icase = preg->icase;
607
609
  for (start = 0; start <= in.len; start++) {
608
- if (xt_re_match_alt(preg->root, &in, start, xt_re_accept, NULL)) return 0;
610
+ xt_re_match_result result;
611
+ result.end = 0;
612
+ if (xt_re_match_alt(preg->root, &in, start, xt_re_record_match, &result)) {
613
+ if (nmatch > 0 && pmatch) {
614
+ pmatch[0].rm_so = (int)start;
615
+ pmatch[0].rm_eo = (int)result.end;
616
+ }
617
+ return 0;
618
+ }
609
619
  }
610
620
  return 1;
611
621
  }
@@ -327,6 +327,7 @@ static xt_value xt_string_trim(xt_value value) {
327
327
  }
328
328
 
329
329
  static xt_value xt_string_replace(xt_value value, xt_value searchValue, xt_value replacementValue) {
330
+ if (xt_is_regexp(searchValue)) return xt_regexp_replace(value, searchValue, replacementValue);
330
331
  xt_string *s = xt_as_string(value);
331
332
  if (!s) return value;
332
333
  xt_string *search = xt_as_string(xt_to_string(searchValue));
@@ -456,13 +457,21 @@ xt_value xt_call_method(xt_value target, xt_value name, int32_t argc, xt_value *
456
457
  if (XT_IS_FUNCTION(fn)) return xt_call_with_this(fn, target, argc, argv);
457
458
  xt_value result = xt_ext_object_method(target, method, argc, argv, &handled);
458
459
  if (handled) return result;
459
- xt_throw(xt_string_from_cstr("TypeError: target does not have a callable property of that name"));
460
+ {
461
+ char message[256];
462
+ snprintf(message, sizeof(message), "TypeError: object has no callable method '%s'", method);
463
+ xt_throw(xt_string_from_cstr(message));
464
+ }
460
465
  return XT_UNDEFINED;
461
466
  }
462
467
  if (XT_IS_FUNCTION(target)) {
463
468
  xt_value fn = xt_object_get(target, name);
464
469
  if (XT_IS_FUNCTION(fn)) return xt_call_with_this(fn, target, argc, argv);
465
- xt_throw(xt_string_from_cstr("TypeError: target does not have a callable property of that name"));
470
+ {
471
+ char message[256];
472
+ snprintf(message, sizeof(message), "TypeError: function has no callable method '%s'", method);
473
+ xt_throw(xt_string_from_cstr(message));
474
+ }
466
475
  return XT_UNDEFINED;
467
476
  }
468
477
  if (XT_IS_ARRAY(target)) {