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.
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/src/ast/expressions.d.ts +6 -2
- package/dist/src/ast/kinds.d.ts +1 -0
- package/dist/src/ast/kinds.js +1 -0
- package/dist/src/ast/kinds.js.map +1 -1
- package/dist/src/binder/binder.js +29 -0
- package/dist/src/binder/binder.js.map +1 -1
- package/dist/src/codegen/generator/calls.d.ts +1 -1
- package/dist/src/codegen/generator/calls.js +57 -14
- package/dist/src/codegen/generator/calls.js.map +1 -1
- package/dist/src/codegen/generator/context.d.ts +1 -1
- package/dist/src/codegen/generator/expressions.js +14 -3
- package/dist/src/codegen/generator/expressions.js.map +1 -1
- package/dist/src/codegen/generator/module.js +57 -2
- package/dist/src/codegen/generator/module.js.map +1 -1
- package/dist/src/codegen/generator/state.js +15 -0
- package/dist/src/codegen/generator/state.js.map +1 -1
- package/dist/src/codegen/generator/statements.js +25 -8
- package/dist/src/codegen/generator/statements.js.map +1 -1
- package/dist/src/codegen/generator/tables.d.ts +3 -0
- package/dist/src/codegen/generator/tables.js +12 -0
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/codegen/values.d.ts +18 -11
- package/dist/src/codegen/values.js +92 -15
- package/dist/src/codegen/values.js.map +1 -1
- package/dist/src/diagnostics/source.d.ts +1 -1
- package/dist/src/diagnostics/source.js +12 -7
- package/dist/src/diagnostics/source.js.map +1 -1
- package/dist/src/driver/compiler.js +16 -2
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/extensions/node/child_process/index.d.ts +7 -0
- package/dist/src/extensions/node/child_process/index.js +19 -0
- package/dist/src/extensions/node/child_process/index.js.map +1 -0
- package/dist/src/extensions/node/crypto/index.d.ts +6 -0
- package/dist/src/extensions/node/crypto/index.js +18 -0
- package/dist/src/extensions/node/crypto/index.js.map +1 -0
- package/dist/src/extensions/node/index.js +6 -0
- package/dist/src/extensions/node/index.js.map +1 -1
- package/dist/src/extensions/node/module.d.ts +9 -1
- package/dist/src/extensions/node/module.js +14 -1
- package/dist/src/extensions/node/module.js.map +1 -1
- package/dist/src/extensions/node/url/index.d.ts +7 -0
- package/dist/src/extensions/node/url/index.js +21 -0
- package/dist/src/extensions/node/url/index.js.map +1 -0
- package/dist/src/lexer/scanner.js +4 -5
- package/dist/src/lexer/scanner.js.map +1 -1
- package/dist/src/parser/declarations.js +10 -9
- package/dist/src/parser/declarations.js.map +1 -1
- package/dist/src/parser/expressions.js +1 -1
- package/dist/src/parser/expressions.js.map +1 -1
- package/dist/tests/e2e/compile.test.js +36 -0
- package/dist/tests/e2e/compile.test.js.map +1 -1
- package/package.json +1 -1
- package/runtime/ext_node/child_process/child_process.c +213 -0
- package/runtime/ext_node/crypto/crypto.c +177 -0
- package/runtime/ext_node/process/process.c +37 -3
- package/runtime/ext_node/url/url.c +85 -0
- package/runtime/rt.h +20 -0
- package/runtime/rt_internal.h +11 -0
- package/runtime/xt_builtins.c +23 -0
- package/runtime/xt_containers.c +136 -10
- package/runtime/xt_io.c +53 -0
- package/runtime/xt_regex.h +16 -6
- package/runtime/xt_stdlib.c +11 -2
- package/runtime/xt_stdlib2.c +239 -13
- package/runtime/xt_values.c +2 -1
- package/src/ast/expressions.ts +7 -2
- package/src/ast/kinds.ts +1 -0
- package/src/binder/binder.ts +29 -0
- package/src/codegen/generator/calls.ts +59 -16
- package/src/codegen/generator/context.ts +1 -1
- package/src/codegen/generator/expressions.ts +14 -3
- package/src/codegen/generator/module.ts +60 -1
- package/src/codegen/generator/state.ts +15 -0
- package/src/codegen/generator/statements.ts +23 -8
- package/src/codegen/generator/tables.ts +13 -0
- package/src/codegen/values.ts +85 -16
- package/src/diagnostics/source.ts +12 -7
- package/src/driver/compiler.ts +15 -2
- package/src/extensions/node/child_process/index.ts +24 -0
- package/src/extensions/node/crypto/index.ts +23 -0
- package/src/extensions/node/index.ts +6 -0
- package/src/extensions/node/module.ts +13 -1
- package/src/extensions/node/url/index.ts +26 -0
- package/src/lexer/scanner.ts +4 -4
- package/src/parser/declarations.ts +10 -7
- 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);
|
package/runtime/rt_internal.h
CHANGED
|
@@ -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 {
|
package/runtime/xt_builtins.c
CHANGED
|
@@ -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)) {
|
package/runtime/xt_containers.c
CHANGED
|
@@ -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)
|
|
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
|
-
|
|
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 =
|
|
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)
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
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))
|
|
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))
|
|
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();
|
package/runtime/xt_regex.h
CHANGED
|
@@ -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
|
-
|
|
571
|
-
|
|
572
|
-
|
|
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
|
-
|
|
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
|
}
|
package/runtime/xt_stdlib.c
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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)) {
|