xbintsc 0.3.81 → 0.5.6
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/AGENTS.md +8 -4
- package/README.md +16 -5
- package/README.zh-CN.md +14 -6
- package/dist/src/ast/common.d.ts +23 -0
- package/dist/src/cli/main.js +68 -24
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/codegen/generator/calls/access.js +6 -2
- package/dist/src/codegen/generator/calls/access.js.map +1 -1
- package/dist/src/codegen/generator/calls/closures.js +4 -0
- package/dist/src/codegen/generator/calls/closures.js.map +1 -1
- package/dist/src/codegen/generator/calls/invocation.js +9 -4
- package/dist/src/codegen/generator/calls/invocation.js.map +1 -1
- package/dist/src/codegen/generator/context.d.ts +50 -0
- package/dist/src/codegen/generator/context.js +124 -13
- package/dist/src/codegen/generator/context.js.map +1 -1
- package/dist/src/codegen/generator/debug.d.ts +33 -0
- package/dist/src/codegen/generator/debug.js +97 -0
- package/dist/src/codegen/generator/debug.js.map +1 -0
- package/dist/src/codegen/generator/expressions/assignment.js +9 -5
- package/dist/src/codegen/generator/expressions/assignment.js.map +1 -1
- package/dist/src/codegen/generator/expressions/operators.js +35 -9
- package/dist/src/codegen/generator/expressions/operators.js.map +1 -1
- package/dist/src/codegen/generator/expressions/primary.js +11 -3
- package/dist/src/codegen/generator/expressions/primary.js.map +1 -1
- package/dist/src/codegen/generator/generator.d.ts +3 -1
- package/dist/src/codegen/generator/generator.js +3 -1
- package/dist/src/codegen/generator/generator.js.map +1 -1
- package/dist/src/codegen/generator/module.d.ts +1 -1
- package/dist/src/codegen/generator/module.js +82 -12
- package/dist/src/codegen/generator/module.js.map +1 -1
- package/dist/src/codegen/generator/numeric.d.ts +26 -0
- package/dist/src/codegen/generator/numeric.js +127 -0
- package/dist/src/codegen/generator/numeric.js.map +1 -0
- package/dist/src/codegen/generator/state.d.ts +17 -0
- package/dist/src/codegen/generator/state.js +9 -0
- package/dist/src/codegen/generator/state.js.map +1 -1
- package/dist/src/codegen/generator/statements/control-flow.js +2 -4
- package/dist/src/codegen/generator/statements/control-flow.js.map +1 -1
- package/dist/src/codegen/generator/statements/dispatch.js +3 -0
- package/dist/src/codegen/generator/statements/dispatch.js.map +1 -1
- package/dist/src/codegen/generator/statements/variables.js +8 -2
- package/dist/src/codegen/generator/statements/variables.js.map +1 -1
- package/dist/src/codegen/generator/tables.d.ts +16 -0
- package/dist/src/codegen/generator/tables.js +99 -2
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/driver/bundler/merge.js +41 -5
- package/dist/src/driver/bundler/merge.js.map +1 -1
- package/dist/src/driver/compiler.d.ts +23 -4
- package/dist/src/driver/compiler.js +46 -57
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/driver/fingerprint.d.ts +27 -0
- package/dist/src/driver/fingerprint.js +88 -0
- package/dist/src/driver/fingerprint.js.map +1 -0
- package/dist/src/driver/toolchain-provider.d.ts +9 -2
- package/dist/src/driver/toolchain-provider.js +15 -15
- package/dist/src/driver/toolchain-provider.js.map +1 -1
- package/dist/src/driver/toolchain.d.ts +58 -1
- package/dist/src/driver/toolchain.js +169 -22
- package/dist/src/driver/toolchain.js.map +1 -1
- package/dist/src/parser/expressions.js +20 -1
- package/dist/src/parser/expressions.js.map +1 -1
- package/dist/tests/cli/main.test.js +41 -1
- package/dist/tests/cli/main.test.js.map +1 -1
- package/dist/tests/codegen/closures.test.js +8 -0
- package/dist/tests/codegen/closures.test.js.map +1 -1
- package/dist/tests/codegen/expressions.test.js +14 -3
- package/dist/tests/codegen/expressions.test.js.map +1 -1
- package/dist/tests/codegen/llvm.test.js +23 -0
- package/dist/tests/codegen/llvm.test.js.map +1 -1
- package/dist/tests/driver/toolchain-provider.test.js +49 -2
- package/dist/tests/driver/toolchain-provider.test.js.map +1 -1
- package/dist/tests/driver/toolchain.test.js +62 -3
- package/dist/tests/driver/toolchain.test.js.map +1 -1
- package/dist/tests/e2e/async-await.test.d.ts +10 -0
- package/dist/tests/e2e/async-await.test.js +247 -0
- package/dist/tests/e2e/async-await.test.js.map +1 -0
- package/dist/tests/e2e/child-process.test.d.ts +10 -0
- package/dist/tests/e2e/child-process.test.js +59 -0
- package/dist/tests/e2e/child-process.test.js.map +1 -0
- package/dist/tests/e2e/compile-objects-classes.test.js +47 -0
- package/dist/tests/e2e/compile-objects-classes.test.js.map +1 -1
- package/dist/tests/e2e/error-stack.test.d.ts +9 -0
- package/dist/tests/e2e/error-stack.test.js +94 -0
- package/dist/tests/e2e/error-stack.test.js.map +1 -0
- package/dist/tests/e2e/runtime-coverage-globals.test.js +46 -0
- package/dist/tests/e2e/runtime-coverage-globals.test.js.map +1 -1
- package/dist/tests/e2e/string-concat.test.d.ts +9 -0
- package/dist/tests/e2e/string-concat.test.js +102 -0
- package/dist/tests/e2e/string-concat.test.js.map +1 -0
- package/dist/tests/e2e/string-utf16.test.d.ts +6 -0
- package/dist/tests/e2e/string-utf16.test.js +133 -0
- package/dist/tests/e2e/string-utf16.test.js.map +1 -0
- package/dist/tests/helpers.d.ts +9 -0
- package/dist/tests/helpers.js +26 -3
- package/dist/tests/helpers.js.map +1 -1
- package/dist/tests/parser/expressions.test.js +7 -0
- package/dist/tests/parser/expressions.test.js.map +1 -1
- package/dist/tests/version.test.d.ts +1 -0
- package/dist/tests/version.test.js +24 -0
- package/dist/tests/version.test.js.map +1 -0
- package/doc/DESIGN.md +20 -12
- package/doc/ai/cli.md +2 -0
- package/doc/ai/contributing.md +16 -8
- package/doc/ai/extensions.md +3 -2
- package/doc/ai/language-support.md +15 -12
- package/doc/ai/troubleshooting.md +43 -5
- package/doc/ai/zh-CN/cli.md +2 -0
- package/doc/ai/zh-CN/contributing.md +11 -7
- package/doc/ai/zh-CN/extensions.md +2 -1
- package/doc/ai/zh-CN/language-support.md +15 -12
- package/doc/ai/zh-CN/troubleshooting.md +38 -4
- package/doc/implemented.md +16 -13
- package/doc/requirements.md +13 -4
- package/doc/unimplemented.md +20 -18
- package/doc/zh-CN/DESIGN.md +15 -9
- package/doc/zh-CN/implemented.md +15 -12
- package/doc/zh-CN/requirements.md +12 -3
- package/doc/zh-CN/unimplemented.md +19 -17
- package/package.json +3 -1
- package/runtime/ext_node/buffer/parts/construction.inc +2 -2
- package/runtime/ext_node/buffer/parts/methods.inc +1 -1
- package/runtime/ext_node/buffer/parts/search.inc +1 -1
- package/runtime/ext_node/child_process/child_process.c +25 -0
- package/runtime/ext_node/fs/fd_ops.c +1 -1
- package/runtime/rt.h +49 -1
- package/runtime/rt_internal.h +136 -4
- package/runtime/xt_alloc.c +20 -2
- package/runtime/xt_async.c +197 -0
- package/runtime/xt_bigint/conversions.inc +1 -1
- package/runtime/xt_builtins.c +169 -43
- package/runtime/xt_constructors.c +389 -0
- package/runtime/xt_containers/access.inc +61 -24
- package/runtime/xt_containers/arrays.inc +26 -2
- package/runtime/xt_containers/objects.inc +206 -8
- package/runtime/xt_coroutine.c +212 -0
- package/runtime/xt_generator.c +31 -162
- package/runtime/xt_io/exception.inc +171 -2
- package/runtime/xt_io/inspect.inc +48 -13
- package/runtime/xt_io.c +5 -0
- package/runtime/xt_promise.c +62 -17
- package/runtime/xt_stdlib/arrays.inc +2 -2
- package/runtime/xt_stdlib/dispatch.inc +20 -4
- package/runtime/xt_stdlib/strings.inc +98 -29
- package/runtime/xt_stdlib2/array-statics.inc +3 -2
- package/runtime/xt_stdlib2/arrays.inc +1 -1
- package/runtime/xt_stdlib2/error.inc +33 -6
- package/runtime/xt_stdlib2/json.inc +46 -16
- package/runtime/xt_stdlib2/number-string-statics.inc +24 -13
- package/runtime/xt_stdlib2/objects.inc +1 -1
- package/runtime/xt_stdlib2/regexp-match.inc +50 -35
- package/runtime/xt_stdlib2/set.inc +6 -0
- package/runtime/xt_stdlib2/strings.inc +81 -42
- package/runtime/xt_symbol.c +1 -1
- package/runtime/xt_typed_array/construction.inc +43 -2
- package/runtime/xt_typed_array.c +4 -0
- package/runtime/xt_values/comparison.inc +2 -2
- package/runtime/xt_values/constructors.inc +18 -5
- package/runtime/xt_values/conversions.inc +4 -3
- package/runtime/xt_values/strings.inc +94 -4
- package/runtime/xt_values/utf16.inc +340 -0
- package/runtime/xt_values.c +1 -0
- package/scripts/bench.ts +172 -0
- package/scripts/build-runtime.ts +2 -1
- package/scripts/check-version.ts +53 -0
- package/src/ast/common.ts +23 -0
- package/src/cli/main.ts +68 -26
- package/src/codegen/generator/calls/access.ts +7 -2
- package/src/codegen/generator/calls/closures.ts +3 -0
- package/src/codegen/generator/calls/invocation.ts +9 -3
- package/src/codegen/generator/context.ts +129 -18
- package/src/codegen/generator/debug.ts +132 -0
- package/src/codegen/generator/expressions/assignment.ts +9 -5
- package/src/codegen/generator/expressions/operators.ts +47 -8
- package/src/codegen/generator/expressions/primary.ts +11 -3
- package/src/codegen/generator/generator.ts +18 -2
- package/src/codegen/generator/module.ts +83 -13
- package/src/codegen/generator/numeric.ts +158 -0
- package/src/codegen/generator/state.ts +26 -0
- package/src/codegen/generator/statements/control-flow.ts +2 -3
- package/src/codegen/generator/statements/dispatch.ts +3 -0
- package/src/codegen/generator/statements/variables.ts +8 -2
- package/src/codegen/generator/tables.ts +110 -2
- package/src/driver/bundler/merge.ts +39 -5
- package/src/driver/compiler.ts +65 -55
- package/src/driver/fingerprint.ts +85 -0
- package/src/driver/toolchain-provider.ts +17 -16
- package/src/driver/toolchain.ts +210 -21
- package/src/parser/expressions.ts +20 -1
package/runtime/xt_promise.c
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* xbintsc runtime — promises and
|
|
2
|
+
* xbintsc runtime — promises and the microtask queue.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Promise reactions are queued as microtasks and run when the program body
|
|
5
|
+
* finishes, by the event loop between callbacks, and on demand. `await` lives
|
|
6
|
+
* in `xt_async.c`: an `async` function runs on a coroutine, so awaiting a
|
|
7
|
+
* promise that is still pending suspends it and registers a waiter here. When
|
|
8
|
+
* the promise settles, the waiter becomes a microtask that resumes the
|
|
9
|
+
* coroutine. Everything is single-threaded and non-blocking.
|
|
8
10
|
*/
|
|
9
11
|
|
|
10
12
|
#include "rt_internal.h"
|
|
@@ -12,10 +14,6 @@
|
|
|
12
14
|
#include <stdlib.h>
|
|
13
15
|
#include <string.h>
|
|
14
16
|
|
|
15
|
-
#define XT_PROMISE_PENDING 0
|
|
16
|
-
#define XT_PROMISE_FULFILLED 1
|
|
17
|
-
#define XT_PROMISE_REJECTED 2
|
|
18
|
-
|
|
19
17
|
typedef struct xt_promise xt_promise;
|
|
20
18
|
|
|
21
19
|
typedef struct {
|
|
@@ -23,6 +21,9 @@ typedef struct {
|
|
|
23
21
|
xt_value on_fulfilled;
|
|
24
22
|
xt_value on_rejected;
|
|
25
23
|
xt_promise *result;
|
|
24
|
+
/* Non-NULL for an `async` coroutine waiting on this promise. Such a reaction
|
|
25
|
+
* has no handlers: running it resumes the coroutine instead. */
|
|
26
|
+
void *async;
|
|
26
27
|
} xt_reaction;
|
|
27
28
|
|
|
28
29
|
struct xt_promise {
|
|
@@ -87,6 +88,7 @@ static void xt_promise_gc_scan_roots(void) {
|
|
|
87
88
|
xt_gc_mark_value(reaction->on_fulfilled);
|
|
88
89
|
xt_gc_mark_value(reaction->on_rejected);
|
|
89
90
|
xt_gc_mark_header((xt_header *)reaction->result);
|
|
91
|
+
if (reaction->async) xt_gc_mark_header((xt_header *)reaction->async);
|
|
90
92
|
}
|
|
91
93
|
}
|
|
92
94
|
|
|
@@ -107,6 +109,7 @@ void xt_promise_gc_trace(xt_header *header) {
|
|
|
107
109
|
xt_gc_mark_value(reaction->on_fulfilled);
|
|
108
110
|
xt_gc_mark_value(reaction->on_rejected);
|
|
109
111
|
xt_gc_mark_header((xt_header *)reaction->result);
|
|
112
|
+
if (reaction->async) xt_gc_mark_header((xt_header *)reaction->async);
|
|
110
113
|
}
|
|
111
114
|
}
|
|
112
115
|
|
|
@@ -133,6 +136,7 @@ static void xt_promise_resolve_into(xt_promise *result, xt_value value) {
|
|
|
133
136
|
reaction->on_fulfilled = XT_UNDEFINED;
|
|
134
137
|
reaction->on_rejected = XT_UNDEFINED;
|
|
135
138
|
reaction->result = result;
|
|
139
|
+
reaction->async = NULL;
|
|
136
140
|
if (other->reaction_count >= other->reaction_capacity) {
|
|
137
141
|
other->reaction_capacity = other->reaction_capacity == 0 ? 4 : other->reaction_capacity * 2;
|
|
138
142
|
other->reactions = (xt_reaction **)realloc(other->reactions, sizeof(xt_reaction *) * other->reaction_capacity);
|
|
@@ -156,6 +160,16 @@ static xt_value xt_promise_run_reactions(void) {
|
|
|
156
160
|
while (xt_microtask_head < xt_microtask_count) {
|
|
157
161
|
xt_reaction *reaction = xt_microtasks[xt_microtask_head++];
|
|
158
162
|
xt_promise *promise = reaction->promise;
|
|
163
|
+
if (reaction->async) {
|
|
164
|
+
/* A suspended `async` coroutine: resume it with the settled value. It
|
|
165
|
+
* runs until it completes or awaits another pending promise. */
|
|
166
|
+
void *async = reaction->async;
|
|
167
|
+
int rejected = promise->state == XT_PROMISE_REJECTED;
|
|
168
|
+
xt_value settled = promise->value;
|
|
169
|
+
free(reaction);
|
|
170
|
+
xt_async_wake(async, settled, rejected);
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
159
173
|
xt_value handler = promise->state == XT_PROMISE_FULFILLED ? reaction->on_fulfilled : reaction->on_rejected;
|
|
160
174
|
if (XT_IS_FUNCTION(handler)) {
|
|
161
175
|
xt_value arg = promise->value;
|
|
@@ -181,16 +195,46 @@ void xt_drain_microtasks(void) {
|
|
|
181
195
|
}
|
|
182
196
|
}
|
|
183
197
|
|
|
184
|
-
|
|
198
|
+
/* -- helpers used by the async coroutine runtime -------------------------- */
|
|
199
|
+
|
|
200
|
+
xt_value xt_promise_create(void) { return xt_promise_value(xt_promise_new()); }
|
|
201
|
+
|
|
202
|
+
void xt_promise_fulfill(xt_value promise, xt_value value) {
|
|
203
|
+
xt_promise *target = xt_as_promise(promise);
|
|
204
|
+
if (target) xt_promise_resolve_into(target, value);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
void xt_promise_reject_value(xt_value promise, xt_value value) {
|
|
208
|
+
xt_promise *target = xt_as_promise(promise);
|
|
209
|
+
if (target) xt_promise_settle(target, XT_PROMISE_REJECTED, value);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
int xt_promise_state_of(xt_value value) {
|
|
185
213
|
xt_promise *promise = xt_as_promise(value);
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
214
|
+
return promise ? promise->state : -1;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
xt_value xt_promise_value_of(xt_value value) {
|
|
218
|
+
xt_promise *promise = xt_as_promise(value);
|
|
219
|
+
return promise ? promise->value : value;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
void xt_promise_add_async_waiter(xt_value promiseValue, void *async) {
|
|
223
|
+
xt_promise *promise = xt_as_promise(promiseValue);
|
|
224
|
+
if (!promise || promise->state != XT_PROMISE_PENDING) return;
|
|
225
|
+
xt_reaction *reaction = (xt_reaction *)malloc(sizeof(xt_reaction));
|
|
226
|
+
if (!reaction) abort();
|
|
227
|
+
reaction->promise = promise;
|
|
228
|
+
reaction->on_fulfilled = XT_UNDEFINED;
|
|
229
|
+
reaction->on_rejected = XT_UNDEFINED;
|
|
230
|
+
reaction->result = NULL;
|
|
231
|
+
reaction->async = async;
|
|
232
|
+
if (promise->reaction_count >= promise->reaction_capacity) {
|
|
233
|
+
promise->reaction_capacity = promise->reaction_capacity == 0 ? 4 : promise->reaction_capacity * 2;
|
|
234
|
+
promise->reactions = (xt_reaction **)realloc(promise->reactions, sizeof(xt_reaction *) * promise->reaction_capacity);
|
|
235
|
+
if (!promise->reactions) abort();
|
|
192
236
|
}
|
|
193
|
-
|
|
237
|
+
promise->reactions[promise->reaction_count++] = reaction;
|
|
194
238
|
}
|
|
195
239
|
|
|
196
240
|
/* -- C-callable resolve / reject ------------------------------------------ */
|
|
@@ -316,6 +360,7 @@ xt_value xt_promise_method(xt_value target, const char *method, int32_t argc, xt
|
|
|
316
360
|
reaction->on_fulfilled = xt_arg_at(argc, argv, 0);
|
|
317
361
|
reaction->on_rejected = xt_arg_at(argc, argv, 1);
|
|
318
362
|
reaction->result = result;
|
|
363
|
+
reaction->async = NULL;
|
|
319
364
|
if (promise->state == XT_PROMISE_PENDING) {
|
|
320
365
|
if (promise->reaction_count >= promise->reaction_capacity) {
|
|
321
366
|
promise->reaction_capacity = promise->reaction_capacity == 0 ? 4 : promise->reaction_capacity * 2;
|
|
@@ -47,11 +47,11 @@ static xt_value xt_array_join(xt_value value, xt_value separator) {
|
|
|
47
47
|
if (!out) abort();
|
|
48
48
|
out[0] = '\0';
|
|
49
49
|
for (uint32_t i = 0; i < array->length; i++) {
|
|
50
|
-
if (i > 0) xt_buffer_append(&out, &length, &capacity, sep
|
|
50
|
+
if (i > 0) xt_buffer_append(&out, &length, &capacity, xt_string_bytes(sep), sep->length);
|
|
51
51
|
xt_value item = array->items[i];
|
|
52
52
|
if (item == XT_UNDEFINED || item == XT_NULL) continue;
|
|
53
53
|
xt_string *str = xt_as_string(xt_to_string(item));
|
|
54
|
-
xt_buffer_append(&out, &length, &capacity, str
|
|
54
|
+
xt_buffer_append(&out, &length, &capacity, xt_string_bytes(str), str->length);
|
|
55
55
|
}
|
|
56
56
|
xt_value result = xt_string_new(out, length);
|
|
57
57
|
free(out);
|
|
@@ -40,6 +40,7 @@ static xt_value xt_string_method(xt_value target, const char *method, int32_t ar
|
|
|
40
40
|
*handled = 1;
|
|
41
41
|
if (strcmp(method, "charAt") == 0) return xt_string_char_at(target, xt_arg_at(argc, argv, 0));
|
|
42
42
|
if (strcmp(method, "charCodeAt") == 0) return xt_string_char_code_at(target, xt_arg_at(argc, argv, 0));
|
|
43
|
+
if (strcmp(method, "codePointAt") == 0) return xt_string_code_point_at_value(target, xt_arg_at(argc, argv, 0));
|
|
43
44
|
if (strcmp(method, "indexOf") == 0) return xt_string_index_of(target, xt_arg_at(argc, argv, 0), xt_arg_at(argc, argv, 1));
|
|
44
45
|
if (strcmp(method, "includes") == 0) return xt_bool(xt_truthy(xt_ne(xt_string_index_of(target, xt_arg_at(argc, argv, 0), xt_arg_at(argc, argv, 1)), xt_number(-1))));
|
|
45
46
|
if (strcmp(method, "slice") == 0) return xt_string_slice(target, xt_arg_at(argc, argv, 0), xt_arg_at(argc, argv, 1));
|
|
@@ -51,8 +52,8 @@ static xt_value xt_string_method(xt_value target, const char *method, int32_t ar
|
|
|
51
52
|
if (strcmp(method, "trim") == 0) return xt_string_trim(target);
|
|
52
53
|
if (strcmp(method, "replace") == 0) return xt_string_replace(target, xt_arg_at(argc, argv, 0), xt_arg_at(argc, argv, 1));
|
|
53
54
|
if (strcmp(method, "repeat") == 0) return xt_string_repeat(target, xt_arg_at(argc, argv, 0));
|
|
54
|
-
if (strcmp(method, "startsWith") == 0) return xt_string_starts_with(target, xt_arg_at(argc, argv, 0));
|
|
55
|
-
if (strcmp(method, "endsWith") == 0) return xt_string_ends_with(target, xt_arg_at(argc, argv, 0));
|
|
55
|
+
if (strcmp(method, "startsWith") == 0) return xt_string_starts_with(target, xt_arg_at(argc, argv, 0), xt_arg_at(argc, argv, 1));
|
|
56
|
+
if (strcmp(method, "endsWith") == 0) return xt_string_ends_with(target, xt_arg_at(argc, argv, 0), xt_arg_at(argc, argv, 1));
|
|
56
57
|
if (strcmp(method, "concat") == 0) return xt_string_concat_args(target, argc, argv);
|
|
57
58
|
*handled = 0;
|
|
58
59
|
return XT_UNDEFINED;
|
|
@@ -286,11 +287,19 @@ xt_value xt_call_method(xt_value target, xt_value name, int32_t argc, xt_value *
|
|
|
286
287
|
if (kind != XT_OBJECT_KIND_OBJECT && kind != XT_OBJECT_KIND_FUNCTION) {
|
|
287
288
|
xt_value result = xt_ext_container_method(target, method, argc, argv, &handled);
|
|
288
289
|
if (handled) return result;
|
|
290
|
+
/* Containers keep their methods in dedicated C dispatch tables, but
|
|
291
|
+
* still inherit `Object.prototype` (`new Map().toString()`). */
|
|
292
|
+
xt_value inherited = xt_get(target, name);
|
|
293
|
+
if (XT_IS_FUNCTION(inherited)) return xt_call_with_this(inherited, target, argc, argv);
|
|
289
294
|
}
|
|
290
295
|
xt_value fn = xt_object_get(target, name);
|
|
291
296
|
if (XT_IS_FUNCTION(fn)) return xt_call_with_this(fn, target, argc, argv);
|
|
292
297
|
xt_value result = xt_ext_object_method(target, method, argc, argv, &handled);
|
|
293
298
|
if (handled) return result;
|
|
299
|
+
/* Inherited Object.prototype methods on containers without a property bag
|
|
300
|
+
* (`new Map().toString()`, ...). */
|
|
301
|
+
xt_value inherited = xt_get(target, name);
|
|
302
|
+
if (XT_IS_FUNCTION(inherited)) return xt_call_with_this(inherited, target, argc, argv);
|
|
294
303
|
{
|
|
295
304
|
char message[256];
|
|
296
305
|
snprintf(message, sizeof(message), "TypeError: object has no callable method '%s'", method);
|
|
@@ -322,7 +331,7 @@ xt_value xt_call_method(xt_value target, xt_value name, int32_t argc, xt_value *
|
|
|
322
331
|
/* Bound functions report `name` as "bound <target>" and reduce `length`
|
|
323
332
|
* by the number of pre-bound arguments, like the specification. */
|
|
324
333
|
xt_function *targetFn = (xt_function *)XT_GET_PTR(target);
|
|
325
|
-
const char *base = targetFn->name ? targetFn->name
|
|
334
|
+
const char *base = targetFn->name ? xt_string_bytes(targetFn->name) : "";
|
|
326
335
|
char boundName[256];
|
|
327
336
|
snprintf(boundName, sizeof(boundName), "bound %s", base);
|
|
328
337
|
int32_t prebound = argc > 1 ? argc - 1 : 0;
|
|
@@ -333,10 +342,17 @@ xt_value xt_call_method(xt_value target, xt_value name, int32_t argc, xt_value *
|
|
|
333
342
|
}
|
|
334
343
|
xt_value fn = xt_object_get(target, name);
|
|
335
344
|
if (XT_IS_FUNCTION(fn)) return xt_call_with_this(fn, target, argc, argv);
|
|
345
|
+
/* Statics of a first-class built-in constructor (`const A = Array;
|
|
346
|
+
* A.isArray(x)`). */
|
|
347
|
+
xt_value builtinStatic = xt_builtin_static_value(target, name);
|
|
348
|
+
if (builtinStatic != XT_UNDEFINED) {
|
|
349
|
+
if (XT_IS_FUNCTION(builtinStatic)) return xt_call_with_this(builtinStatic, target, argc, argv);
|
|
350
|
+
return builtinStatic;
|
|
351
|
+
}
|
|
336
352
|
if (strcmp(method, "toString") == 0) {
|
|
337
353
|
/* Source text is not retained; report a native-style representation. */
|
|
338
354
|
xt_function *self = (xt_function *)XT_GET_PTR(target);
|
|
339
|
-
const char *selfName = self->name ? self->name
|
|
355
|
+
const char *selfName = self->name ? xt_string_bytes(self->name) : "";
|
|
340
356
|
char buffer[256];
|
|
341
357
|
snprintf(buffer, sizeof(buffer), "function %s() { [native code] }", selfName);
|
|
342
358
|
return xt_string_from_cstr(buffer);
|
|
@@ -1,53 +1,79 @@
|
|
|
1
1
|
/* -- string methods ------------------------------------------------------- */
|
|
2
2
|
|
|
3
|
+
/* Byte-level search. `from` is a byte offset; returns a byte offset or -1. */
|
|
3
4
|
static int32_t xt_string_find(xt_string *haystack, xt_string *needle, int32_t from) {
|
|
4
5
|
if (from < 0) from = 0;
|
|
5
6
|
if ((uint32_t)from > haystack->length) return -1;
|
|
6
7
|
if (needle->length == 0) return from;
|
|
8
|
+
const char *hay = xt_string_bytes(haystack);
|
|
9
|
+
const char *need = xt_string_bytes(needle);
|
|
7
10
|
for (uint32_t i = (uint32_t)from; i + needle->length <= haystack->length; i++) {
|
|
8
|
-
if (memcmp(
|
|
11
|
+
if (memcmp(hay + i, need, needle->length) == 0) return (int32_t)i;
|
|
9
12
|
}
|
|
10
13
|
return -1;
|
|
11
14
|
}
|
|
12
15
|
|
|
16
|
+
/* JavaScript indexes strings in UTF-16 code units; the byte search above is
|
|
17
|
+
* still correct because every JS index maps to a code-point boundary. */
|
|
13
18
|
static xt_value xt_string_char_at(xt_value value, xt_value indexValue) {
|
|
14
19
|
xt_string *s = xt_as_string(value);
|
|
15
20
|
if (!s) return xt_string_from_cstr("");
|
|
16
21
|
int32_t index = xt_to_int32(indexValue);
|
|
17
|
-
if (index < 0 || (uint32_t)index >= s
|
|
18
|
-
return
|
|
22
|
+
if (index < 0 || (uint32_t)index >= xt_string_utf16_length(s)) return xt_string_from_cstr("");
|
|
23
|
+
return xt_string_unit_value(s, index);
|
|
19
24
|
}
|
|
20
25
|
|
|
21
26
|
static xt_value xt_string_char_code_at(xt_value value, xt_value indexValue) {
|
|
22
27
|
xt_string *s = xt_as_string(value);
|
|
23
28
|
if (!s) return xt_number(NAN);
|
|
24
29
|
int32_t index = xt_to_int32(indexValue);
|
|
25
|
-
|
|
26
|
-
return xt_number(
|
|
30
|
+
int32_t unit = xt_string_code_unit(s, index);
|
|
31
|
+
if (unit < 0) return xt_number(NAN);
|
|
32
|
+
return xt_number((double)unit);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
static xt_value xt_string_code_point_at_value(xt_value value, xt_value indexValue) {
|
|
36
|
+
xt_string *s = xt_as_string(value);
|
|
37
|
+
if (!s) return XT_UNDEFINED;
|
|
38
|
+
int32_t index = xt_to_int32(indexValue);
|
|
39
|
+
int32_t code = xt_string_code_point_at(s, index);
|
|
40
|
+
if (code < 0) return XT_UNDEFINED;
|
|
41
|
+
return xt_number((double)code);
|
|
27
42
|
}
|
|
28
43
|
|
|
29
44
|
static xt_value xt_string_index_of(xt_value value, xt_value needleValue, xt_value fromValue) {
|
|
30
45
|
xt_string *s = xt_as_string(value);
|
|
31
46
|
if (!s) return xt_number(-1);
|
|
32
47
|
xt_string *needle = xt_as_string(xt_to_string(needleValue));
|
|
48
|
+
int32_t units = (int32_t)xt_string_utf16_length(s);
|
|
33
49
|
int32_t from = fromValue == XT_UNDEFINED ? 0 : xt_to_int32(fromValue);
|
|
34
|
-
|
|
50
|
+
if (from < 0) from = 0;
|
|
51
|
+
if (from > units) from = units;
|
|
52
|
+
/* A start position inside a surrogate pair leaves a lone low half; slice the
|
|
53
|
+
* tail so a search for the whole pair correctly fails. */
|
|
54
|
+
if (!xt_string_is_boundary(s, from)) {
|
|
55
|
+
xt_string *tail = xt_as_string(xt_string_slice_units(s, from, units));
|
|
56
|
+
int32_t at = xt_string_find(tail, needle, 0);
|
|
57
|
+
return xt_number(at < 0 ? -1.0 : (double)(from + xt_string_unit_offset(tail, at)));
|
|
58
|
+
}
|
|
59
|
+
int32_t at = xt_string_find(s, needle, xt_string_byte_offset(s, from));
|
|
60
|
+
return xt_number(at < 0 ? -1.0 : (double)xt_string_unit_offset(s, at));
|
|
35
61
|
}
|
|
36
62
|
|
|
37
63
|
static xt_value xt_string_slice(xt_value value, xt_value startValue, xt_value endValue) {
|
|
38
64
|
xt_string *s = xt_as_string(value);
|
|
39
65
|
if (!s) return xt_string_from_cstr("");
|
|
40
|
-
int32_t len = (int32_t)s
|
|
66
|
+
int32_t len = (int32_t)xt_string_utf16_length(s);
|
|
41
67
|
int32_t start = startValue == XT_UNDEFINED ? 0 : xt_to_int32(startValue);
|
|
42
68
|
int32_t end = endValue == XT_UNDEFINED ? len : xt_to_int32(endValue);
|
|
43
69
|
xt_normalize_slice(len, &start, &end);
|
|
44
|
-
return
|
|
70
|
+
return xt_string_slice_units(s, start, end);
|
|
45
71
|
}
|
|
46
72
|
|
|
47
73
|
static xt_value xt_string_substring(xt_value value, xt_value startValue, xt_value endValue) {
|
|
48
74
|
xt_string *s = xt_as_string(value);
|
|
49
75
|
if (!s) return xt_string_from_cstr("");
|
|
50
|
-
int32_t len = (int32_t)s
|
|
76
|
+
int32_t len = (int32_t)xt_string_utf16_length(s);
|
|
51
77
|
int32_t start = startValue == XT_UNDEFINED ? 0 : xt_to_int32(startValue);
|
|
52
78
|
int32_t end = endValue == XT_UNDEFINED ? len : xt_to_int32(endValue);
|
|
53
79
|
if (start < 0) start = 0;
|
|
@@ -59,13 +85,13 @@ static xt_value xt_string_substring(xt_value value, xt_value startValue, xt_valu
|
|
|
59
85
|
start = end;
|
|
60
86
|
end = swap;
|
|
61
87
|
}
|
|
62
|
-
return
|
|
88
|
+
return xt_string_slice_units(s, start, end);
|
|
63
89
|
}
|
|
64
90
|
|
|
65
91
|
static xt_value xt_string_substr(xt_value value, xt_value startValue, xt_value lengthValue) {
|
|
66
92
|
xt_string *s = xt_as_string(value);
|
|
67
93
|
if (!s) return xt_string_from_cstr("");
|
|
68
|
-
int32_t len = (int32_t)s
|
|
94
|
+
int32_t len = (int32_t)xt_string_utf16_length(s);
|
|
69
95
|
int32_t start = startValue == XT_UNDEFINED ? 0 : xt_to_int32(startValue);
|
|
70
96
|
if (start < 0) start += len;
|
|
71
97
|
if (start < 0) start = 0;
|
|
@@ -73,7 +99,7 @@ static xt_value xt_string_substr(xt_value value, xt_value startValue, xt_value l
|
|
|
73
99
|
int32_t length = lengthValue == XT_UNDEFINED ? len - start : xt_to_int32(lengthValue);
|
|
74
100
|
if (length < 0) length = 0;
|
|
75
101
|
if (start + length > len) length = len - start;
|
|
76
|
-
return
|
|
102
|
+
return xt_string_slice_units(s, start, start + length);
|
|
77
103
|
}
|
|
78
104
|
|
|
79
105
|
static xt_value xt_string_split(xt_value value, xt_value separatorValue, xt_value limitValue) {
|
|
@@ -87,21 +113,26 @@ static xt_value xt_string_split(xt_value value, xt_value separatorValue, xt_valu
|
|
|
87
113
|
return result;
|
|
88
114
|
}
|
|
89
115
|
xt_string *sep = xt_as_string(xt_to_string(separatorValue));
|
|
116
|
+
const char *bytes = xt_string_bytes(s);
|
|
117
|
+
const char *sepBytes = xt_string_bytes(sep);
|
|
90
118
|
if (sep->length == 0) {
|
|
91
|
-
|
|
119
|
+
/* Splitting on the empty string yields one string per UTF-16 code unit,
|
|
120
|
+
* so an emoji becomes its two surrogate halves. */
|
|
121
|
+
int32_t units = (int32_t)xt_string_utf16_length(s);
|
|
122
|
+
for (int32_t i = 0; i < units; i++) xt_array_push(result, xt_string_unit_value(s, i));
|
|
92
123
|
} else {
|
|
93
124
|
uint32_t start = 0;
|
|
94
125
|
uint32_t i = 0;
|
|
95
126
|
while (i + sep->length <= s->length) {
|
|
96
|
-
if (memcmp(
|
|
97
|
-
xt_array_push(result, xt_string_new(
|
|
127
|
+
if (memcmp(bytes + i, sepBytes, sep->length) == 0) {
|
|
128
|
+
xt_array_push(result, xt_string_new(bytes + start, i - start));
|
|
98
129
|
i += sep->length;
|
|
99
130
|
start = i;
|
|
100
131
|
} else {
|
|
101
132
|
i++;
|
|
102
133
|
}
|
|
103
134
|
}
|
|
104
|
-
xt_array_push(result, xt_string_new(
|
|
135
|
+
xt_array_push(result, xt_string_new(bytes + start, s->length - start));
|
|
105
136
|
}
|
|
106
137
|
xt_array *array = xt_as_array(result);
|
|
107
138
|
if ((int64_t)array->length > limit) array->length = (uint32_t)limit;
|
|
@@ -112,21 +143,25 @@ static xt_value xt_string_case(xt_value value, int upper) {
|
|
|
112
143
|
xt_string *s = xt_as_string(value);
|
|
113
144
|
if (!s) return value;
|
|
114
145
|
xt_string *out = xt_string_alloc(s->length);
|
|
146
|
+
const char *bytes = xt_string_bytes(s);
|
|
147
|
+
char *mutable = xt_string_mutable(out);
|
|
115
148
|
for (uint32_t i = 0; i < s->length; i++) {
|
|
116
|
-
unsigned char c = (unsigned char)
|
|
117
|
-
|
|
149
|
+
unsigned char c = (unsigned char)bytes[i];
|
|
150
|
+
mutable[i] = (char)(upper ? toupper((int)c) : tolower((int)c));
|
|
118
151
|
}
|
|
152
|
+
xt_string_finish(out);
|
|
119
153
|
return XT_FROM_PTR(XT_TAG_STRING, out);
|
|
120
154
|
}
|
|
121
155
|
|
|
122
156
|
static xt_value xt_string_trim(xt_value value) {
|
|
123
157
|
xt_string *s = xt_as_string(value);
|
|
124
158
|
if (!s) return value;
|
|
159
|
+
const char *bytes = xt_string_bytes(s);
|
|
125
160
|
uint32_t start = 0;
|
|
126
161
|
uint32_t end = s->length;
|
|
127
|
-
while (start < end && isspace((unsigned char)
|
|
128
|
-
while (end > start && isspace((unsigned char)
|
|
129
|
-
return xt_string_new(
|
|
162
|
+
while (start < end && isspace((unsigned char)bytes[start])) start++;
|
|
163
|
+
while (end > start && isspace((unsigned char)bytes[end - 1])) end--;
|
|
164
|
+
return xt_string_new(bytes + start, end - start);
|
|
130
165
|
}
|
|
131
166
|
|
|
132
167
|
static xt_value xt_string_replace(xt_value value, xt_value searchValue, xt_value replacementValue) {
|
|
@@ -137,12 +172,16 @@ static xt_value xt_string_replace(xt_value value, xt_value searchValue, xt_value
|
|
|
137
172
|
xt_string *replacement = xt_as_string(xt_to_string(replacementValue));
|
|
138
173
|
int32_t at = xt_string_find(s, search, 0);
|
|
139
174
|
if (at < 0) return value;
|
|
175
|
+
const char *text = xt_string_bytes(s);
|
|
176
|
+
const char *repl = xt_string_bytes(replacement);
|
|
140
177
|
size_t total = s->length - search->length + replacement->length;
|
|
141
178
|
xt_string *out = xt_string_alloc(total);
|
|
142
|
-
|
|
143
|
-
memcpy(
|
|
144
|
-
memcpy(
|
|
179
|
+
char *mutable = xt_string_mutable(out);
|
|
180
|
+
memcpy(mutable, text, (size_t)at);
|
|
181
|
+
memcpy(mutable + at, repl, replacement->length);
|
|
182
|
+
memcpy(mutable + at + replacement->length, text + at + search->length,
|
|
145
183
|
s->length - (size_t)at - search->length);
|
|
184
|
+
xt_string_finish(out);
|
|
146
185
|
return XT_FROM_PTR(XT_TAG_STRING, out);
|
|
147
186
|
}
|
|
148
187
|
|
|
@@ -155,24 +194,54 @@ static xt_value xt_string_repeat(xt_value value, xt_value countValue) {
|
|
|
155
194
|
return XT_UNDEFINED;
|
|
156
195
|
}
|
|
157
196
|
xt_string *out = xt_string_alloc((size_t)s->length * (size_t)count);
|
|
158
|
-
|
|
197
|
+
const char *bytes = xt_string_bytes(s);
|
|
198
|
+
char *mutable = xt_string_mutable(out);
|
|
199
|
+
for (int32_t i = 0; i < count; i++) memcpy(mutable + (size_t)i * s->length, bytes, s->length);
|
|
200
|
+
xt_string_finish(out);
|
|
159
201
|
return XT_FROM_PTR(XT_TAG_STRING, out);
|
|
160
202
|
}
|
|
161
203
|
|
|
162
|
-
static xt_value xt_string_starts_with(xt_value value, xt_value searchValue) {
|
|
204
|
+
static xt_value xt_string_starts_with(xt_value value, xt_value searchValue, xt_value positionValue) {
|
|
163
205
|
xt_string *s = xt_as_string(value);
|
|
164
206
|
if (!s) return XT_FALSE;
|
|
165
207
|
xt_string *search = xt_as_string(xt_to_string(searchValue));
|
|
208
|
+
const char *sBytes = xt_string_bytes(s);
|
|
209
|
+
const char *searchBytes = xt_string_bytes(search);
|
|
210
|
+
int32_t units = (int32_t)xt_string_utf16_length(s);
|
|
211
|
+
int32_t position = positionValue == XT_UNDEFINED ? 0 : xt_to_int32(positionValue);
|
|
212
|
+
if (position < 0) position = 0;
|
|
213
|
+
if (position > units) position = units;
|
|
166
214
|
if (search->length > s->length) return XT_FALSE;
|
|
167
|
-
|
|
215
|
+
if (!xt_string_is_boundary(s, position)) {
|
|
216
|
+
xt_string *tail = xt_as_string(xt_string_slice_units(s, position, units));
|
|
217
|
+
if (search->length > tail->length) return XT_FALSE;
|
|
218
|
+
return xt_bool(memcmp(xt_string_bytes(tail), searchBytes, search->length) == 0);
|
|
219
|
+
}
|
|
220
|
+
int32_t at = xt_string_byte_offset(s, position);
|
|
221
|
+
if ((uint32_t)at + search->length > s->length) return XT_FALSE;
|
|
222
|
+
return xt_bool(memcmp(sBytes + at, searchBytes, search->length) == 0);
|
|
168
223
|
}
|
|
169
224
|
|
|
170
|
-
static xt_value xt_string_ends_with(xt_value value, xt_value searchValue) {
|
|
225
|
+
static xt_value xt_string_ends_with(xt_value value, xt_value searchValue, xt_value endValue) {
|
|
171
226
|
xt_string *s = xt_as_string(value);
|
|
172
227
|
if (!s) return XT_FALSE;
|
|
173
228
|
xt_string *search = xt_as_string(xt_to_string(searchValue));
|
|
229
|
+
const char *sBytes = xt_string_bytes(s);
|
|
230
|
+
const char *searchBytes = xt_string_bytes(search);
|
|
231
|
+
int32_t units = (int32_t)xt_string_utf16_length(s);
|
|
232
|
+
int32_t end = endValue == XT_UNDEFINED ? units : xt_to_int32(endValue);
|
|
233
|
+
if (end < 0) end = 0;
|
|
234
|
+
if (end > units) end = units;
|
|
174
235
|
if (search->length > s->length) return XT_FALSE;
|
|
175
|
-
|
|
236
|
+
if (!xt_string_is_boundary(s, end)) {
|
|
237
|
+
xt_string *prefix = xt_as_string(xt_string_slice_units(s, 0, end));
|
|
238
|
+
if (search->length > prefix->length) return XT_FALSE;
|
|
239
|
+
return xt_bool(memcmp(xt_string_bytes(prefix) + prefix->length - search->length,
|
|
240
|
+
searchBytes, search->length) == 0);
|
|
241
|
+
}
|
|
242
|
+
int32_t at = xt_string_byte_offset(s, end);
|
|
243
|
+
if ((uint32_t)at < search->length) return XT_FALSE;
|
|
244
|
+
return xt_bool(memcmp(sBytes + at - search->length, searchBytes, search->length) == 0);
|
|
176
245
|
}
|
|
177
246
|
|
|
178
247
|
static xt_value xt_string_concat_args(xt_value value, int32_t argc, xt_value *argv) {
|
|
@@ -18,8 +18,9 @@ xt_value xt_array_static(xt_value name, int32_t argc, xt_value *argv) {
|
|
|
18
18
|
}
|
|
19
19
|
} else if (XT_IS_STRING(source)) {
|
|
20
20
|
xt_string *s = xt_as_string(source);
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
uint32_t points = xt_string_code_point_count(s);
|
|
22
|
+
for (uint32_t i = 0; i < points; i++) {
|
|
23
|
+
xt_value item = xt_string_code_point_value(s, (int32_t)i);
|
|
23
24
|
if (XT_IS_FUNCTION(mapper)) { xt_value args[2] = {item, xt_number((double)i)}; item = xt_closure_call(mapper, 2, args); }
|
|
24
25
|
xt_array_push(out, item);
|
|
25
26
|
}
|
|
@@ -46,7 +46,7 @@ static int xt_array_default_compare(xt_value a, xt_value b) {
|
|
|
46
46
|
xt_string *sa = xt_as_string(xt_to_string(a));
|
|
47
47
|
xt_string *sb = xt_as_string(xt_to_string(b));
|
|
48
48
|
uint32_t n = sa->length < sb->length ? sa->length : sb->length;
|
|
49
|
-
int cmp = n ? memcmp(sa
|
|
49
|
+
int cmp = n ? memcmp(xt_string_bytes(sa), xt_string_bytes(sb), n) : 0;
|
|
50
50
|
if (cmp != 0) return cmp < 0 ? -1 : 1;
|
|
51
51
|
if (sa->length == sb->length) return 0;
|
|
52
52
|
return sa->length < sb->length ? -1 : 1;
|
|
@@ -26,6 +26,8 @@ static xt_value xt_error_proto_for(const char *name) {
|
|
|
26
26
|
xt_string_from_cstr("Error"));
|
|
27
27
|
xt_object_set(xt_error_root_proto, xt_string_from_cstr("message"),
|
|
28
28
|
xt_string_from_cstr(""));
|
|
29
|
+
xt_value constructor = xt_error_constructor(xt_string_from_cstr("Error"));
|
|
30
|
+
xt_object_set(xt_error_root_proto, xt_string_from_cstr("constructor"), constructor);
|
|
29
31
|
}
|
|
30
32
|
if (strcmp(name, "Error") == 0) return xt_error_root_proto;
|
|
31
33
|
xt_value key = xt_string_from_cstr(name);
|
|
@@ -41,6 +43,8 @@ static xt_value xt_error_proto_for(const char *name) {
|
|
|
41
43
|
xt_object_set(proto, xt_string_from_cstr("name"), key);
|
|
42
44
|
xt_object_set(proto, xt_string_from_cstr("message"), xt_string_from_cstr(""));
|
|
43
45
|
xt_object_set(registry, key, proto);
|
|
46
|
+
xt_value constructor = xt_error_constructor(key);
|
|
47
|
+
xt_object_set(proto, xt_string_from_cstr("constructor"), constructor);
|
|
44
48
|
return proto;
|
|
45
49
|
}
|
|
46
50
|
|
|
@@ -50,6 +54,15 @@ xt_value xt_error_prototype(xt_value nameValue) {
|
|
|
50
54
|
return xt_error_proto_for(name);
|
|
51
55
|
}
|
|
52
56
|
|
|
57
|
+
/* Capture `Error.stack` into the object's private field (not a property, so it
|
|
58
|
+
* stays out of Object.keys/JSON.stringify, like V8's own non-enumerable stack). */
|
|
59
|
+
static void xt_error_capture_stack(xt_value error, const char *name, xt_value message) {
|
|
60
|
+
if (!XT_IS_OBJECT(error)) return;
|
|
61
|
+
xt_string *text = xt_as_string(xt_to_string(message));
|
|
62
|
+
xt_value stack = xt_stack_capture(name, text ? xt_string_bytes(text) : "");
|
|
63
|
+
((xt_object *)XT_GET_PTR(error))->error_stack = stack;
|
|
64
|
+
}
|
|
65
|
+
|
|
53
66
|
static xt_value xt_make_error(const char *name, int32_t argc, xt_value *argv) {
|
|
54
67
|
if (!xt_error_is_family(name)) name = "Error";
|
|
55
68
|
xt_value error = xt_object_new();
|
|
@@ -64,9 +77,10 @@ static xt_value xt_make_error(const char *name, int32_t argc, xt_value *argv) {
|
|
|
64
77
|
} else {
|
|
65
78
|
message = xt_arg_at(argc, argv, 0);
|
|
66
79
|
}
|
|
80
|
+
xt_value messageValue = message == XT_UNDEFINED ? xt_string_from_cstr("") : message;
|
|
67
81
|
xt_object_set(error, xt_string_from_cstr("name"), xt_string_from_cstr(name));
|
|
68
|
-
xt_object_set(error, xt_string_from_cstr("message"),
|
|
69
|
-
|
|
82
|
+
xt_object_set(error, xt_string_from_cstr("message"), messageValue);
|
|
83
|
+
xt_error_capture_stack(error, name, messageValue);
|
|
70
84
|
return error;
|
|
71
85
|
}
|
|
72
86
|
|
|
@@ -110,10 +124,10 @@ xt_value xt_error_to_string(xt_value error) {
|
|
|
110
124
|
if (ms->length == 0) return nameStr;
|
|
111
125
|
size_t total = ns->length + 2 + ms->length;
|
|
112
126
|
char *buffer = (char *)malloc(total + 1);
|
|
113
|
-
memcpy(buffer, ns
|
|
127
|
+
memcpy(buffer, xt_string_bytes(ns), ns->length);
|
|
114
128
|
buffer[ns->length] = ':';
|
|
115
129
|
buffer[ns->length + 1] = ' ';
|
|
116
|
-
memcpy(buffer + ns->length + 2, ms
|
|
130
|
+
memcpy(buffer + ns->length + 2, xt_string_bytes(ms), ms->length);
|
|
117
131
|
buffer[total] = '\0';
|
|
118
132
|
xt_value result = xt_string_new(buffer, total);
|
|
119
133
|
free(buffer);
|
|
@@ -152,6 +166,13 @@ xt_value xt_aggregate_error_ctor(int32_t argc, xt_value *argv) {
|
|
|
152
166
|
return xt_make_error("AggregateError", argc, argv);
|
|
153
167
|
}
|
|
154
168
|
|
|
169
|
+
/* Dispatch used by the first-class constructor factory (`const E = TypeError`). */
|
|
170
|
+
xt_value xt_error_ctor_by_name(xt_value nameValue, int32_t argc, xt_value *argv) {
|
|
171
|
+
const char *name = XT_IS_STRING(nameValue) ? xt_string_data(nameValue) : "Error";
|
|
172
|
+
if (!xt_error_is_family(name)) name = "Error";
|
|
173
|
+
return xt_make_error(name, argc, argv);
|
|
174
|
+
}
|
|
175
|
+
|
|
155
176
|
/* `super(message)` inside a subclass of the builtin Error family: initialize
|
|
156
177
|
* the already-allocated instance instead of creating a fresh object. The
|
|
157
178
|
* parent's name selects the default `name` (`TypeError`, ...). */
|
|
@@ -159,6 +180,11 @@ xt_value xt_error_init(xt_value thisValue, xt_value nameValue, int32_t argc,
|
|
|
159
180
|
xt_value *argv) {
|
|
160
181
|
const char *name = XT_IS_STRING(nameValue) ? xt_string_data(nameValue) : "Error";
|
|
161
182
|
if (!xt_error_is_family(name)) name = "Error";
|
|
183
|
+
/* `class MyError extends Error` allocates a plain object first; mark it as an
|
|
184
|
+
* Error so `.stack` and the error-specific property access apply. */
|
|
185
|
+
if (XT_IS_OBJECT(thisValue)) {
|
|
186
|
+
((xt_object *)XT_GET_PTR(thisValue))->header.kind = XT_OBJECT_KIND_ERROR;
|
|
187
|
+
}
|
|
162
188
|
xt_object_set(thisValue, xt_string_from_cstr("name"), xt_string_from_cstr(name));
|
|
163
189
|
xt_value message;
|
|
164
190
|
if (strcmp(name, "AggregateError") == 0) {
|
|
@@ -167,7 +193,8 @@ xt_value xt_error_init(xt_value thisValue, xt_value nameValue, int32_t argc,
|
|
|
167
193
|
} else {
|
|
168
194
|
message = xt_arg_at(argc, argv, 0);
|
|
169
195
|
}
|
|
170
|
-
|
|
171
|
-
|
|
196
|
+
xt_value messageValue = message == XT_UNDEFINED ? xt_string_from_cstr("") : message;
|
|
197
|
+
xt_object_set(thisValue, xt_string_from_cstr("message"), messageValue);
|
|
198
|
+
xt_error_capture_stack(thisValue, name, messageValue);
|
|
172
199
|
return thisValue;
|
|
173
200
|
}
|