xbintsc 0.3.87 → 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 +4 -3
- package/README.zh-CN.md +2 -2
- 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 +4 -3
- package/dist/src/codegen/generator/calls/invocation.js.map +1 -1
- package/dist/src/codegen/generator/expressions/primary.js +6 -1
- package/dist/src/codegen/generator/expressions/primary.js.map +1 -1
- package/dist/src/codegen/generator/module.d.ts +0 -1
- package/dist/src/codegen/generator/module.js +10 -10
- package/dist/src/codegen/generator/module.js.map +1 -1
- package/dist/src/codegen/generator/state.js +4 -0
- package/dist/src/codegen/generator/state.js.map +1 -1
- package/dist/src/codegen/generator/statements/control-flow.js +1 -3
- package/dist/src/codegen/generator/statements/control-flow.js.map +1 -1
- package/dist/src/codegen/generator/tables.d.ts +9 -0
- package/dist/src/codegen/generator/tables.js +74 -2
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/driver/compiler.d.ts +2 -2
- package/dist/src/driver/compiler.js +4 -1
- package/dist/src/driver/compiler.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/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/runtime-coverage-globals.test.js +46 -0
- package/dist/tests/e2e/runtime-coverage-globals.test.js.map +1 -1
- 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/doc/ai/contributing.md +9 -7
- package/doc/ai/extensions.md +3 -2
- package/doc/ai/language-support.md +11 -8
- package/doc/ai/troubleshooting.md +4 -3
- package/doc/ai/zh-CN/contributing.md +5 -5
- package/doc/ai/zh-CN/extensions.md +2 -1
- package/doc/ai/zh-CN/language-support.md +11 -8
- package/doc/ai/zh-CN/troubleshooting.md +3 -2
- package/doc/implemented.md +15 -12
- package/doc/unimplemented.md +11 -10
- package/doc/zh-CN/implemented.md +14 -11
- package/doc/zh-CN/unimplemented.md +11 -10
- package/package.json +1 -1
- package/runtime/rt.h +32 -0
- package/runtime/rt_internal.h +99 -10
- package/runtime/xt_alloc.c +6 -0
- package/runtime/xt_async.c +197 -0
- package/runtime/xt_builtins.c +40 -36
- package/runtime/xt_constructors.c +389 -0
- package/runtime/xt_containers/access.inc +47 -11
- package/runtime/xt_containers/arrays.inc +26 -2
- package/runtime/xt_containers/objects.inc +202 -5
- package/runtime/xt_coroutine.c +212 -0
- package/runtime/xt_generator.c +31 -162
- package/runtime/xt_io/inspect.inc +35 -2
- package/runtime/xt_promise.c +62 -17
- package/runtime/xt_stdlib/dispatch.inc +18 -2
- package/runtime/xt_stdlib/strings.inc +73 -16
- package/runtime/xt_stdlib2/array-statics.inc +3 -3
- package/runtime/xt_stdlib2/error.inc +11 -0
- package/runtime/xt_stdlib2/json.inc +44 -15
- package/runtime/xt_stdlib2/number-string-statics.inc +24 -15
- package/runtime/xt_stdlib2/objects.inc +1 -1
- package/runtime/xt_stdlib2/regexp-match.inc +2 -2
- package/runtime/xt_stdlib2/set.inc +6 -0
- package/runtime/xt_stdlib2/strings.inc +70 -40
- package/runtime/xt_typed_array/construction.inc +43 -2
- package/runtime/xt_typed_array.c +4 -0
- package/runtime/xt_values/constructors.inc +8 -0
- package/runtime/xt_values/strings.inc +19 -3
- package/runtime/xt_values/utf16.inc +340 -0
- package/runtime/xt_values.c +1 -0
- 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 +4 -3
- package/src/codegen/generator/expressions/primary.ts +6 -1
- package/src/codegen/generator/module.ts +10 -12
- package/src/codegen/generator/state.ts +4 -0
- package/src/codegen/generator/statements/control-flow.ts +1 -2
- package/src/codegen/generator/tables.ts +84 -2
- package/src/driver/compiler.ts +4 -1
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc runtime — real `async` / `await`.
|
|
3
|
+
*
|
|
4
|
+
* An `async` function runs its body on a stackful coroutine (the same
|
|
5
|
+
* machinery generators use, see `xt_coroutine.c`) and immediately returns a
|
|
6
|
+
* promise. The body runs synchronously until it reaches an `await` whose
|
|
7
|
+
* operand is still pending; at that point the coroutine suspends and control
|
|
8
|
+
* returns to the caller, so the program body or the event loop can keep going.
|
|
9
|
+
* When the awaited promise settles, the promise runtime queues a microtask that
|
|
10
|
+
* resumes the coroutine exactly where it stopped — because the coroutine keeps
|
|
11
|
+
* a real C stack, `try`/`finally`, loops and locals all survive the suspension
|
|
12
|
+
* without a state-machine transform.
|
|
13
|
+
*
|
|
14
|
+
* `await` on an already-settled value keeps the historical fast path: it
|
|
15
|
+
* returns (or throws) synchronously instead of forcing a microtask turn. This
|
|
16
|
+
* makes the common "await a synchronous builtin" case cheap and preserves the
|
|
17
|
+
* existing ordering guarantees.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
#include "rt_internal.h"
|
|
21
|
+
|
|
22
|
+
#include <stdlib.h>
|
|
23
|
+
#include <string.h>
|
|
24
|
+
|
|
25
|
+
typedef struct xt_async {
|
|
26
|
+
xt_header header;
|
|
27
|
+
xt_value function;
|
|
28
|
+
xt_value this_value;
|
|
29
|
+
xt_value *args;
|
|
30
|
+
int32_t argc;
|
|
31
|
+
/* Promise returned to the caller and settled when the body finishes. */
|
|
32
|
+
xt_value promise;
|
|
33
|
+
/* Completion value, or the exception that escaped the body. */
|
|
34
|
+
xt_value value;
|
|
35
|
+
/* Values handed over by the waiter and consumed by `xt_await`. */
|
|
36
|
+
xt_value resume_value;
|
|
37
|
+
int await_threw;
|
|
38
|
+
int done;
|
|
39
|
+
int threw;
|
|
40
|
+
xt_coroutine *coroutine;
|
|
41
|
+
} xt_async;
|
|
42
|
+
|
|
43
|
+
/* The async coroutine currently running on its private stack, if any. It is
|
|
44
|
+
* only reachable through this pointer while it runs, so it is a GC root. */
|
|
45
|
+
static xt_async *g_active_async = NULL;
|
|
46
|
+
|
|
47
|
+
static void xt_async_gc_register(void);
|
|
48
|
+
|
|
49
|
+
/* Runs on the coroutine's private stack and never returns normally: it
|
|
50
|
+
* suspends back to the resumer once the body has completed. */
|
|
51
|
+
static void xt_async_run(void *arg) {
|
|
52
|
+
xt_async *async = (xt_async *)arg;
|
|
53
|
+
void *boundary = xt_try_enter();
|
|
54
|
+
if (xt_try_setjmp(boundary) == 0) {
|
|
55
|
+
xt_function *function = (xt_function *)XT_GET_PTR(async->function);
|
|
56
|
+
async->value = function->code(async->this_value, async->function, async->argc, async->args);
|
|
57
|
+
async->threw = 0;
|
|
58
|
+
} else {
|
|
59
|
+
/* `throw` inside the body (not caught by a `try`) rejects the promise. */
|
|
60
|
+
async->value = xt_try_exception(boundary);
|
|
61
|
+
async->threw = 1;
|
|
62
|
+
}
|
|
63
|
+
async->done = 1;
|
|
64
|
+
xt_coroutine_leave(async->coroutine);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* Switch onto the coroutine. It either suspends at an `await` or completes. */
|
|
68
|
+
static void xt_async_resume(xt_async *async) {
|
|
69
|
+
xt_async *previous = g_active_async;
|
|
70
|
+
g_active_async = async;
|
|
71
|
+
xt_coroutine_enter(async->coroutine);
|
|
72
|
+
g_active_async = previous;
|
|
73
|
+
if (!async->done) return;
|
|
74
|
+
xt_coroutine_release(async->coroutine);
|
|
75
|
+
async->coroutine = NULL;
|
|
76
|
+
if (async->threw) xt_promise_reject_value(async->promise, async->value);
|
|
77
|
+
else xt_promise_fulfill(async->promise, async->value);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
xt_value xt_async_call(xt_value function, xt_value thisValue, int32_t argc, xt_value *argv) {
|
|
81
|
+
xt_async *async = (xt_async *)xt_alloc(sizeof(xt_async), XT_OBJECT_KIND_ASYNC);
|
|
82
|
+
async->function = function;
|
|
83
|
+
async->this_value = thisValue;
|
|
84
|
+
async->argc = argc;
|
|
85
|
+
async->args = NULL;
|
|
86
|
+
if (argc > 0 && argv) {
|
|
87
|
+
async->args = (xt_value *)malloc(sizeof(xt_value) * (size_t)argc);
|
|
88
|
+
if (!async->args) abort();
|
|
89
|
+
memcpy(async->args, argv, sizeof(xt_value) * (size_t)argc);
|
|
90
|
+
}
|
|
91
|
+
async->promise = xt_promise_create();
|
|
92
|
+
async->value = XT_UNDEFINED;
|
|
93
|
+
async->resume_value = XT_UNDEFINED;
|
|
94
|
+
async->await_threw = 0;
|
|
95
|
+
async->done = 0;
|
|
96
|
+
async->threw = 0;
|
|
97
|
+
async->coroutine = xt_coroutine_create(xt_async_run, async);
|
|
98
|
+
xt_async_gc_register();
|
|
99
|
+
/* Start synchronously, matching JavaScript: the body runs up to its first
|
|
100
|
+
* pending `await` before the call returns. */
|
|
101
|
+
xt_async_resume(async);
|
|
102
|
+
return async->promise;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
void xt_async_wake(void *asyncPointer, xt_value value, int rejected) {
|
|
106
|
+
xt_async *async = (xt_async *)asyncPointer;
|
|
107
|
+
if (!async || async->done || !async->coroutine) return;
|
|
108
|
+
async->resume_value = value;
|
|
109
|
+
async->await_threw = rejected;
|
|
110
|
+
xt_async_resume(async);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/* `await value`: unwrap promises, suspending the running async coroutine when
|
|
114
|
+
* the promise is still pending. */
|
|
115
|
+
xt_value xt_await(xt_value value) {
|
|
116
|
+
int state = xt_promise_state_of(value);
|
|
117
|
+
if (state < 0) return value;
|
|
118
|
+
if (state == XT_PROMISE_REJECTED) {
|
|
119
|
+
xt_throw(xt_promise_value_of(value));
|
|
120
|
+
return XT_UNDEFINED;
|
|
121
|
+
}
|
|
122
|
+
if (state == XT_PROMISE_FULFILLED) return xt_promise_value_of(value);
|
|
123
|
+
|
|
124
|
+
xt_async *async = g_active_async;
|
|
125
|
+
if (async && async->coroutine) {
|
|
126
|
+
async->await_threw = 0;
|
|
127
|
+
async->resume_value = XT_UNDEFINED;
|
|
128
|
+
xt_promise_add_async_waiter(value, async);
|
|
129
|
+
xt_coroutine_leave(async->coroutine);
|
|
130
|
+
/* The waiter resumed us. A rejection is rethrown inside the coroutine so
|
|
131
|
+
* its own `try`/`catch` and `finally` see it. */
|
|
132
|
+
if (async->await_threw) {
|
|
133
|
+
async->await_threw = 0;
|
|
134
|
+
xt_throw(async->resume_value);
|
|
135
|
+
return XT_UNDEFINED;
|
|
136
|
+
}
|
|
137
|
+
return async->resume_value;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* No coroutine to suspend (top-level `await`, or `await` reached outside an
|
|
141
|
+
* async function): block on the reactor until the promise settles, exactly
|
|
142
|
+
* like a synchronous embedder waiting for the event loop. The loop returns
|
|
143
|
+
* immediately when no work remains, so a promise nothing can settle does not
|
|
144
|
+
* hang the process here. */
|
|
145
|
+
for (int guard = 0; guard < 100000; guard++) {
|
|
146
|
+
xt_drain_microtasks();
|
|
147
|
+
state = xt_promise_state_of(value);
|
|
148
|
+
if (state != XT_PROMISE_PENDING) break;
|
|
149
|
+
if (!xt_loop_has_work()) break;
|
|
150
|
+
if (xt_loop_poll(-1) < 0) break;
|
|
151
|
+
}
|
|
152
|
+
state = xt_promise_state_of(value);
|
|
153
|
+
if (state == XT_PROMISE_REJECTED) {
|
|
154
|
+
xt_throw(xt_promise_value_of(value));
|
|
155
|
+
return XT_UNDEFINED;
|
|
156
|
+
}
|
|
157
|
+
return state == XT_PROMISE_FULFILLED ? xt_promise_value_of(value) : XT_UNDEFINED;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/* While a coroutine runs, the async object is only reachable through this
|
|
161
|
+
* static pointer. */
|
|
162
|
+
static void xt_async_gc_scan_roots(void) {
|
|
163
|
+
if (!g_active_async) return;
|
|
164
|
+
xt_gc_mark_header((xt_header *)g_active_async);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
static void xt_async_gc_register(void) {
|
|
168
|
+
static int registered = 0;
|
|
169
|
+
if (registered) return;
|
|
170
|
+
registered = 1;
|
|
171
|
+
xt_gc_register_root_provider(xt_async_gc_scan_roots);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
void xt_async_gc_trace(xt_header *header) {
|
|
175
|
+
xt_async *async = (xt_async *)header;
|
|
176
|
+
xt_gc_mark_value(async->function);
|
|
177
|
+
xt_gc_mark_value(async->this_value);
|
|
178
|
+
xt_gc_mark_value(async->promise);
|
|
179
|
+
xt_gc_mark_value(async->value);
|
|
180
|
+
xt_gc_mark_value(async->resume_value);
|
|
181
|
+
if (async->args && async->argc > 0) {
|
|
182
|
+
xt_gc_scan_region(async->args, sizeof(xt_value) * (size_t)async->argc);
|
|
183
|
+
}
|
|
184
|
+
xt_coroutine_gc_trace(async->coroutine);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
void xt_async_gc_release(xt_header *header) {
|
|
188
|
+
xt_async *async = (xt_async *)header;
|
|
189
|
+
free(async->args);
|
|
190
|
+
xt_coroutine_release(async->coroutine);
|
|
191
|
+
async->coroutine = NULL;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
xt_value xt_function_set_async(xt_value value) {
|
|
195
|
+
if (XT_IS_FUNCTION(value)) ((xt_function *)XT_GET_PTR(value))->is_async = 1;
|
|
196
|
+
return value;
|
|
197
|
+
}
|
package/runtime/xt_builtins.c
CHANGED
|
@@ -18,23 +18,22 @@
|
|
|
18
18
|
|
|
19
19
|
xt_value xt_object_values(xt_value value) {
|
|
20
20
|
xt_value result = xt_array_new(0, NULL);
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
21
|
+
xt_value keys = xt_object_keys(value);
|
|
22
|
+
xt_array *array = xt_as_array(keys);
|
|
23
|
+
if (!array) return result;
|
|
24
|
+
for (uint32_t i = 0; i < array->length; i++) xt_array_push(result, xt_get(value, array->items[i]));
|
|
26
25
|
return result;
|
|
27
26
|
}
|
|
28
27
|
|
|
29
28
|
xt_value xt_object_entries(xt_value value) {
|
|
30
29
|
xt_value result = xt_array_new(0, NULL);
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
xt_value keys = xt_object_keys(value);
|
|
31
|
+
xt_array *array = xt_as_array(keys);
|
|
32
|
+
if (!array) return result;
|
|
33
|
+
for (uint32_t i = 0; i < array->length; i++) {
|
|
35
34
|
xt_value pair = xt_array_new(0, NULL);
|
|
36
|
-
xt_array_push(pair,
|
|
37
|
-
xt_array_push(pair,
|
|
35
|
+
xt_array_push(pair, array->items[i]);
|
|
36
|
+
xt_array_push(pair, xt_get(value, array->items[i]));
|
|
38
37
|
xt_array_push(result, pair);
|
|
39
38
|
}
|
|
40
39
|
return result;
|
|
@@ -45,8 +44,11 @@ xt_value xt_object_assign(int32_t argc, xt_value *argv) {
|
|
|
45
44
|
xt_value target = argv[0];
|
|
46
45
|
if (!XT_IS_OBJECT(target)) return target;
|
|
47
46
|
for (int32_t i = 1; i < argc; i++) {
|
|
48
|
-
if (!XT_IS_OBJECT(argv[i])) continue;
|
|
49
|
-
|
|
47
|
+
if (!XT_IS_OBJECT(argv[i]) && !XT_IS_ARRAY(argv[i]) && !XT_IS_STRING(argv[i])) continue;
|
|
48
|
+
/* Arrays and strings contribute their index properties, exactly like
|
|
49
|
+
* `{ ..."ab" }` / `Object.assign({}, [1, 2])` in ECMAScript. */
|
|
50
|
+
xt_value properties = xt_object_rest(argv[i], 0, NULL);
|
|
51
|
+
xt_object *source = (xt_object *)XT_GET_PTR(properties);
|
|
50
52
|
for (uint32_t j = 0; j < source->count; j++) {
|
|
51
53
|
xt_object_set(target, source->properties[j].key, source->properties[j].value);
|
|
52
54
|
}
|
|
@@ -102,11 +104,11 @@ xt_value xt_object_rest(xt_value source, int32_t argc, xt_value *argv) {
|
|
|
102
104
|
|
|
103
105
|
if (XT_IS_STRING(source)) {
|
|
104
106
|
xt_string *string = (xt_string *)XT_GET_PTR(source);
|
|
105
|
-
|
|
106
|
-
for (uint32_t i = 0; i <
|
|
107
|
+
uint32_t units = xt_string_utf16_length(string);
|
|
108
|
+
for (uint32_t i = 0; i < units; i++) {
|
|
107
109
|
char buffer[16];
|
|
108
110
|
snprintf(buffer, sizeof(buffer), "%u", i);
|
|
109
|
-
xt_object_rest_copy(result, xt_string_from_cstr(buffer),
|
|
111
|
+
xt_object_rest_copy(result, xt_string_from_cstr(buffer), xt_string_unit_value(string, (int32_t)i), argc, argv);
|
|
110
112
|
}
|
|
111
113
|
return result;
|
|
112
114
|
}
|
|
@@ -395,7 +397,7 @@ xt_value xt_in(xt_value key, xt_value value) {
|
|
|
395
397
|
xt_string *k = xt_as_string(xt_to_string(key));
|
|
396
398
|
if (k->length == 6 && memcmp(xt_string_bytes(k), "length", 6) == 0) return XT_TRUE;
|
|
397
399
|
int32_t index = xt_to_int32(key);
|
|
398
|
-
return xt_bool(index >= 0 && (uint32_t)index < s
|
|
400
|
+
return xt_bool(index >= 0 && (uint32_t)index < xt_string_utf16_length(s));
|
|
399
401
|
}
|
|
400
402
|
return XT_FALSE;
|
|
401
403
|
}
|
|
@@ -446,6 +448,9 @@ xt_value xt_closure_new(void *fn, int32_t env_count, xt_value *env) {
|
|
|
446
448
|
function->properties = NULL;
|
|
447
449
|
function->environment = NULL;
|
|
448
450
|
function->prototype = XT_UNDEFINED;
|
|
451
|
+
function->is_generator = 0;
|
|
452
|
+
function->is_async = 0;
|
|
453
|
+
function->builtin_name = NULL;
|
|
449
454
|
if (env_count > 0) {
|
|
450
455
|
function->environment = (xt_value *)malloc(sizeof(xt_value) * (size_t)env_count);
|
|
451
456
|
for (int32_t i = 0; i < env_count; i++) function->environment[i] = env[i];
|
|
@@ -466,6 +471,9 @@ xt_value xt_call_with_this(xt_value value, xt_value thisValue, int32_t argc, xt_
|
|
|
466
471
|
/* Calling a generator function returns a suspended generator instead of
|
|
467
472
|
* running the body. */
|
|
468
473
|
if (function->is_generator) return xt_generator_new(value, thisValue, argc, argv);
|
|
474
|
+
/* An `async` function runs its body on a coroutine and returns a promise,
|
|
475
|
+
* which may already be settled or may resolve after an event-loop turn. */
|
|
476
|
+
if (function->is_async) return xt_async_call(value, thisValue, argc, argv);
|
|
469
477
|
/* The closure value itself is threaded through as `env` so the callee can
|
|
470
478
|
* read its captures with xt_closure_env(env, i). */
|
|
471
479
|
return function->code(thisValue, value, argc, argv);
|
|
@@ -478,11 +486,6 @@ xt_value xt_function_set_prototype(xt_value value, xt_value proto) {
|
|
|
478
486
|
return value;
|
|
479
487
|
}
|
|
480
488
|
|
|
481
|
-
xt_value xt_function_set_generator(xt_value value) {
|
|
482
|
-
if (XT_IS_FUNCTION(value)) ((xt_function *)XT_GET_PTR(value))->is_generator = 1;
|
|
483
|
-
return value;
|
|
484
|
-
}
|
|
485
|
-
|
|
486
489
|
xt_value xt_function_get_prototype(xt_value value) {
|
|
487
490
|
if (XT_IS_FUNCTION(value)) return ((xt_function *)XT_GET_PTR(value))->prototype;
|
|
488
491
|
return XT_UNDEFINED;
|
|
@@ -497,22 +500,22 @@ xt_value xt_new(xt_value ctor, int32_t argc, xt_value *argv) {
|
|
|
497
500
|
xt_value proto = function->prototype;
|
|
498
501
|
xt_value instance = xt_object_new_with_proto(XT_IS_OBJECT(proto) ? proto : XT_UNDEFINED);
|
|
499
502
|
xt_value result = function->code(instance, ctor, argc, argv);
|
|
500
|
-
if (XT_IS_OBJECT(result) || XT_IS_ARRAY(result)) return result;
|
|
503
|
+
if (XT_IS_OBJECT(result) || XT_IS_ARRAY(result) || XT_IS_FUNCTION(result)) return result;
|
|
501
504
|
return instance;
|
|
502
505
|
}
|
|
503
506
|
|
|
504
507
|
xt_value xt_instance_of(xt_value value, xt_value ctor) {
|
|
505
508
|
if (!XT_IS_FUNCTION(ctor)) return XT_FALSE;
|
|
506
509
|
xt_value proto = ((xt_function *)XT_GET_PTR(ctor))->prototype;
|
|
507
|
-
if (
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
510
|
+
if (XT_IS_OBJECT(proto) || XT_IS_ARRAY(proto) || XT_IS_FUNCTION(proto)) {
|
|
511
|
+
/* Walk the value's [[Prototype]] chain. `xt_object_get_prototype` knows the
|
|
512
|
+
* layout of every kind (arrays carry a prototype field; Map/Set/Date/... use
|
|
513
|
+
* their built-in prototype), so this is safe for containers too. */
|
|
514
|
+
xt_value cursor = xt_object_get_prototype(value);
|
|
515
|
+
while (cursor != XT_UNDEFINED && cursor != XT_NULL) {
|
|
516
|
+
if (cursor == proto) return XT_TRUE;
|
|
517
|
+
cursor = xt_object_get_prototype(cursor);
|
|
518
|
+
}
|
|
516
519
|
}
|
|
517
520
|
return XT_FALSE;
|
|
518
521
|
}
|
|
@@ -520,10 +523,11 @@ xt_value xt_instance_of(xt_value value, xt_value ctor) {
|
|
|
520
523
|
/*
|
|
521
524
|
* `value instanceof Array` (and the rest of the built-in constructors).
|
|
522
525
|
*
|
|
523
|
-
*
|
|
524
|
-
*
|
|
525
|
-
*
|
|
526
|
-
*
|
|
526
|
+
* The codegen recognises the direct spelling and asks this helper instead of
|
|
527
|
+
* materialising the constructor value; an alias (`const A = Array`) goes
|
|
528
|
+
* through the ordinary prototype chain in `xt_instance_of`. Ids match
|
|
529
|
+
* `BUILTIN_INSTANCEOF` in `src/codegen/generator/tables.ts`; 16..24 map onto
|
|
530
|
+
* the runtime's `XT_TA_*` order (Uint8Array first).
|
|
527
531
|
*/
|
|
528
532
|
enum {
|
|
529
533
|
XT_INSTANCE_ARRAY = 0,
|