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,389 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc runtime - built-in constructors as first-class values.
|
|
3
|
+
*
|
|
4
|
+
* `Array`, `Object`, `Map`, the Error family, typed arrays and the other
|
|
5
|
+
* globals were previously only recognised by name in the code generator. This
|
|
6
|
+
* unit turns them into ordinary function values:
|
|
7
|
+
*
|
|
8
|
+
* const A = Array;
|
|
9
|
+
* const a = new A(1, 2, 3);
|
|
10
|
+
* A.isArray(a); // statics travel with the value
|
|
11
|
+
* a instanceof A; // prototype chain, not the name
|
|
12
|
+
*
|
|
13
|
+
* Every constructor is cached (and registered as a GC root), has metadata
|
|
14
|
+
* (`name` / `length`), a `prototype` object and a `builtin_name` that the
|
|
15
|
+
* property path uses to resolve statics.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
#include "rt_internal.h"
|
|
19
|
+
|
|
20
|
+
#include <math.h>
|
|
21
|
+
#include <stdio.h>
|
|
22
|
+
#include <stdlib.h>
|
|
23
|
+
#include <string.h>
|
|
24
|
+
|
|
25
|
+
/* -- constructor table ---------------------------------------------------- */
|
|
26
|
+
|
|
27
|
+
enum {
|
|
28
|
+
XT_PROTO_OBJECT,
|
|
29
|
+
XT_PROTO_ARRAY,
|
|
30
|
+
XT_PROTO_FUNCTION,
|
|
31
|
+
XT_PROTO_PLAIN,
|
|
32
|
+
XT_PROTO_ERROR,
|
|
33
|
+
XT_PROTO_TYPED
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
typedef struct {
|
|
37
|
+
const char *name;
|
|
38
|
+
int32_t arity;
|
|
39
|
+
int32_t prototype;
|
|
40
|
+
} xt_ctor_spec;
|
|
41
|
+
|
|
42
|
+
#define XT_CTOR_SPECS(X) \
|
|
43
|
+
X(Array, 1, XT_PROTO_ARRAY) \
|
|
44
|
+
X(Object, 1, XT_PROTO_OBJECT) \
|
|
45
|
+
X(Function, 1, XT_PROTO_FUNCTION) \
|
|
46
|
+
X(Map, 0, XT_PROTO_PLAIN) \
|
|
47
|
+
X(Set, 0, XT_PROTO_PLAIN) \
|
|
48
|
+
X(Date, 7, XT_PROTO_PLAIN) \
|
|
49
|
+
X(RegExp, 2, XT_PROTO_PLAIN) \
|
|
50
|
+
X(Promise, 1, XT_PROTO_PLAIN) \
|
|
51
|
+
X(Uint8Array, 3, XT_PROTO_TYPED) \
|
|
52
|
+
X(Int8Array, 3, XT_PROTO_TYPED) \
|
|
53
|
+
X(Uint8ClampedArray, 3, XT_PROTO_TYPED) \
|
|
54
|
+
X(Uint16Array, 3, XT_PROTO_TYPED) \
|
|
55
|
+
X(Int16Array, 3, XT_PROTO_TYPED) \
|
|
56
|
+
X(Uint32Array, 3, XT_PROTO_TYPED) \
|
|
57
|
+
X(Int32Array, 3, XT_PROTO_TYPED) \
|
|
58
|
+
X(Float32Array, 3, XT_PROTO_TYPED) \
|
|
59
|
+
X(Float64Array, 3, XT_PROTO_TYPED) \
|
|
60
|
+
X(Error, 1, XT_PROTO_ERROR) \
|
|
61
|
+
X(TypeError, 1, XT_PROTO_ERROR) \
|
|
62
|
+
X(RangeError, 1, XT_PROTO_ERROR) \
|
|
63
|
+
X(SyntaxError, 1, XT_PROTO_ERROR) \
|
|
64
|
+
X(ReferenceError, 1, XT_PROTO_ERROR) \
|
|
65
|
+
X(EvalError, 1, XT_PROTO_ERROR) \
|
|
66
|
+
X(URIError, 1, XT_PROTO_ERROR) \
|
|
67
|
+
X(AggregateError, 2, XT_PROTO_ERROR) \
|
|
68
|
+
X(Number, 1, XT_PROTO_PLAIN) \
|
|
69
|
+
X(String, 1, XT_PROTO_PLAIN) \
|
|
70
|
+
X(Boolean, 1, XT_PROTO_PLAIN) \
|
|
71
|
+
X(BigInt, 1, XT_PROTO_PLAIN) \
|
|
72
|
+
X(Symbol, 0, XT_PROTO_PLAIN)
|
|
73
|
+
|
|
74
|
+
#define XT_CTOR_ENUM(name, arity, proto) XT_CTOR_##name,
|
|
75
|
+
enum { XT_CTOR_SPECS(XT_CTOR_ENUM) XT_CTOR_COUNT };
|
|
76
|
+
#undef XT_CTOR_ENUM
|
|
77
|
+
|
|
78
|
+
#define XT_CTOR_ENTRY(name, arity, proto) {#name, arity, proto},
|
|
79
|
+
static const xt_ctor_spec XT_CTOR_TABLE[XT_CTOR_COUNT] = {XT_CTOR_SPECS(XT_CTOR_ENTRY)};
|
|
80
|
+
#undef XT_CTOR_ENTRY
|
|
81
|
+
|
|
82
|
+
static xt_value xt_ctor_cache[XT_CTOR_COUNT];
|
|
83
|
+
static xt_value xt_ctor_plain_protos[XT_CTOR_COUNT];
|
|
84
|
+
static int xt_ctor_cache_ready = 0;
|
|
85
|
+
|
|
86
|
+
static void xt_ctor_cache_init(void) {
|
|
87
|
+
if (xt_ctor_cache_ready) return;
|
|
88
|
+
xt_ctor_cache_ready = 1;
|
|
89
|
+
xt_gc_add_root_range(xt_ctor_cache, XT_CTOR_COUNT);
|
|
90
|
+
xt_gc_add_root_range(xt_ctor_plain_protos, XT_CTOR_COUNT);
|
|
91
|
+
for (int i = 0; i < XT_CTOR_COUNT; i++) {
|
|
92
|
+
xt_ctor_cache[i] = XT_UNDEFINED;
|
|
93
|
+
xt_ctor_plain_protos[i] = XT_UNDEFINED;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
static int xt_ctor_index(const char *name) {
|
|
98
|
+
if (!name) return -1;
|
|
99
|
+
for (int i = 0; i < XT_CTOR_COUNT; i++) {
|
|
100
|
+
if (strcmp(XT_CTOR_TABLE[i].name, name) == 0) return i;
|
|
101
|
+
}
|
|
102
|
+
return -1;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* -- construction --------------------------------------------------------- */
|
|
106
|
+
|
|
107
|
+
static int xt_ctor_is_typed_array(const char *name) {
|
|
108
|
+
int index = xt_ctor_index(name);
|
|
109
|
+
return index >= XT_CTOR_Uint8Array && index <= XT_CTOR_Float64Array;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
static int xt_ctor_is_error(const char *name) {
|
|
113
|
+
static const char *const names[] = {
|
|
114
|
+
"Error", "TypeError", "RangeError", "SyntaxError",
|
|
115
|
+
"ReferenceError", "EvalError", "URIError", "AggregateError",
|
|
116
|
+
};
|
|
117
|
+
for (size_t i = 0; i < sizeof(names) / sizeof(names[0]); i++) {
|
|
118
|
+
if (strcmp(name, names[i]) == 0) return 1;
|
|
119
|
+
}
|
|
120
|
+
return 0;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
xt_value xt_array_ctor(int32_t argc, xt_value *argv) {
|
|
124
|
+
/* `Array(3)` allocates a length-3 array; any other argument list becomes the
|
|
125
|
+
* elements. */
|
|
126
|
+
if (argc == 1 && XT_IS_NUMBER(argv[0])) {
|
|
127
|
+
double length = xt_to_number(argv[0]);
|
|
128
|
+
if (!(length >= 0) || floor(length) != length || length > 4294967295.0) {
|
|
129
|
+
xt_throw(xt_string_from_cstr("RangeError: Invalid array length"));
|
|
130
|
+
return XT_UNDEFINED;
|
|
131
|
+
}
|
|
132
|
+
xt_value array = xt_array_new(0, NULL);
|
|
133
|
+
if (length > 0) xt_array_set(array, xt_number(length - 1), XT_UNDEFINED);
|
|
134
|
+
return array;
|
|
135
|
+
}
|
|
136
|
+
return xt_array_new(argc, argv);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
xt_value xt_object_ctor(int32_t argc, xt_value *argv) {
|
|
140
|
+
if (argc == 0) return xt_object_new();
|
|
141
|
+
xt_value value = argv[0];
|
|
142
|
+
if (value == XT_UNDEFINED || value == XT_NULL) return xt_object_new();
|
|
143
|
+
/* `Object(value)` returns the object unchanged. Primitives are not boxed in
|
|
144
|
+
* this runtime, so they pass through like `new Object(primitive)` did. */
|
|
145
|
+
return value;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/* `Function()` / `new Function()`: source evaluation is not supported, so the
|
|
149
|
+
* constructor gives an empty function (declared functions get their metadata
|
|
150
|
+
* from the codegen; this body simply does nothing). */
|
|
151
|
+
static xt_value xt_function_ctor_body(xt_value thisValue, xt_value env, int32_t argc,
|
|
152
|
+
xt_value *argv) {
|
|
153
|
+
(void)thisValue;
|
|
154
|
+
(void)env;
|
|
155
|
+
(void)argc;
|
|
156
|
+
(void)argv;
|
|
157
|
+
return XT_UNDEFINED;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
static xt_value xt_builtin_ctor_call(const char *name, int32_t argc, xt_value *argv) {
|
|
161
|
+
xt_value nameValue = xt_string_from_cstr(name);
|
|
162
|
+
if (strcmp(name, "Array") == 0) return xt_array_ctor(argc, argv);
|
|
163
|
+
if (strcmp(name, "Object") == 0) return xt_object_ctor(argc, argv);
|
|
164
|
+
if (strcmp(name, "Function") == 0) {
|
|
165
|
+
return xt_closure_new((void *)xt_function_ctor_body, 0, NULL);
|
|
166
|
+
}
|
|
167
|
+
if (strcmp(name, "Map") == 0) return xt_map_ctor(argc, argv);
|
|
168
|
+
if (strcmp(name, "Set") == 0) return xt_set_ctor(argc, argv);
|
|
169
|
+
if (strcmp(name, "Date") == 0) return xt_date_ctor(argc, argv);
|
|
170
|
+
if (strcmp(name, "RegExp") == 0) return xt_regexp_ctor(argc, argv);
|
|
171
|
+
if (strcmp(name, "Promise") == 0) return xt_promise_ctor(argc, argv);
|
|
172
|
+
if (strcmp(name, "Number") == 0) return xt_number_ctor(argc, argv);
|
|
173
|
+
if (strcmp(name, "String") == 0) return xt_string_ctor(argc, argv);
|
|
174
|
+
if (strcmp(name, "Boolean") == 0) return xt_boolean_ctor(argc, argv);
|
|
175
|
+
if (strcmp(name, "BigInt") == 0) return xt_bigint_ctor(argc, argv);
|
|
176
|
+
if (strcmp(name, "Symbol") == 0) return xt_symbol(argc, argv);
|
|
177
|
+
if (xt_ctor_is_typed_array(name)) return xt_typed_array_ctor_by_name(nameValue, argc, argv);
|
|
178
|
+
if (xt_ctor_is_error(name)) return xt_error_ctor_by_name(nameValue, argc, argv);
|
|
179
|
+
return XT_UNDEFINED;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/* The closure body of every first-class constructor. The name travels in
|
|
183
|
+
* environment slot 0. */
|
|
184
|
+
static xt_value xt_builtin_ctor_invoke(xt_value thisValue, xt_value env, int32_t argc,
|
|
185
|
+
xt_value *argv) {
|
|
186
|
+
(void)thisValue;
|
|
187
|
+
xt_value nameValue = xt_closure_env(env, 0);
|
|
188
|
+
const char *name = xt_string_data(nameValue);
|
|
189
|
+
if (!name) return XT_UNDEFINED;
|
|
190
|
+
return xt_builtin_ctor_call(name, argc, argv);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/* -- prototypes ----------------------------------------------------------- */
|
|
194
|
+
|
|
195
|
+
static xt_value xt_ctor_prototype(int index, xt_value nameValue) {
|
|
196
|
+
const xt_ctor_spec *spec = &XT_CTOR_TABLE[index];
|
|
197
|
+
switch (spec->prototype) {
|
|
198
|
+
case XT_PROTO_OBJECT:
|
|
199
|
+
return xt_object_prototype();
|
|
200
|
+
case XT_PROTO_ARRAY:
|
|
201
|
+
return xt_array_prototype();
|
|
202
|
+
case XT_PROTO_FUNCTION:
|
|
203
|
+
return xt_function_prototype();
|
|
204
|
+
case XT_PROTO_ERROR:
|
|
205
|
+
return xt_error_prototype(nameValue);
|
|
206
|
+
case XT_PROTO_TYPED:
|
|
207
|
+
return xt_typed_array_prototype(nameValue);
|
|
208
|
+
case XT_PROTO_PLAIN:
|
|
209
|
+
default:
|
|
210
|
+
if (xt_ctor_plain_protos[index] == XT_UNDEFINED) {
|
|
211
|
+
xt_ctor_plain_protos[index] = xt_object_new();
|
|
212
|
+
}
|
|
213
|
+
return xt_ctor_plain_protos[index];
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
xt_value xt_builtin_constructor(xt_value nameValue) {
|
|
218
|
+
if (!XT_IS_STRING(nameValue)) return XT_UNDEFINED;
|
|
219
|
+
xt_ctor_cache_init();
|
|
220
|
+
const char *name = xt_string_data(nameValue);
|
|
221
|
+
int index = xt_ctor_index(name);
|
|
222
|
+
if (index < 0) return XT_UNDEFINED;
|
|
223
|
+
if (xt_ctor_cache[index] != XT_UNDEFINED) return xt_ctor_cache[index];
|
|
224
|
+
|
|
225
|
+
/* The Error family already has a first-class constructor factory that keeps
|
|
226
|
+
* one closure per prototype (`__ctor`), so reuse it to keep
|
|
227
|
+
* `err.constructor === Error` stable no matter which side is evaluated
|
|
228
|
+
* first. */
|
|
229
|
+
if (xt_ctor_is_error(name)) {
|
|
230
|
+
xt_value errorCtor = xt_error_constructor(nameValue);
|
|
231
|
+
if (errorCtor != XT_UNDEFINED) {
|
|
232
|
+
xt_ctor_cache[index] = errorCtor;
|
|
233
|
+
return errorCtor;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
xt_value env[1] = {nameValue};
|
|
238
|
+
xt_value closure = xt_closure_new((void *)xt_builtin_ctor_invoke, 1, env);
|
|
239
|
+
xt_function *function = (xt_function *)XT_GET_PTR(closure);
|
|
240
|
+
function->builtin_name = XT_CTOR_TABLE[index].name;
|
|
241
|
+
xt_function_set_metadata(closure, nameValue, XT_CTOR_TABLE[index].arity);
|
|
242
|
+
/* Cache before resolving the prototype: `Array.prototype` and
|
|
243
|
+
* `Object.prototype` install their `constructor` property by asking for the
|
|
244
|
+
* constructor again. */
|
|
245
|
+
xt_ctor_cache[index] = closure;
|
|
246
|
+
|
|
247
|
+
xt_value proto = xt_ctor_prototype(index, nameValue);
|
|
248
|
+
if (proto != XT_UNDEFINED) {
|
|
249
|
+
xt_function_set_prototype(closure, proto);
|
|
250
|
+
/* `Array.prototype` and `Object.prototype` install their own constructor
|
|
251
|
+
* while resolving the prototype above; every other prototype gets it here. */
|
|
252
|
+
int32_t kind = XT_CTOR_TABLE[index].prototype;
|
|
253
|
+
if (kind != XT_PROTO_ARRAY && kind != XT_PROTO_OBJECT) {
|
|
254
|
+
xt_object_set(proto, xt_string_from_cstr("constructor"), closure);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return closure;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/* -- statics -------------------------------------------------------------- */
|
|
261
|
+
|
|
262
|
+
static const char *const XT_ARRAY_STATICS[] = {"isArray", "of", "from"};
|
|
263
|
+
static const char *const XT_OBJECT_STATICS[] = {
|
|
264
|
+
"keys", "values", "entries", "assign", "getOwnPropertyNames",
|
|
265
|
+
"getOwnPropertySymbols", "groupBy", "freeze", "isFrozen", "fromEntries",
|
|
266
|
+
"getPrototypeOf", "setPrototypeOf", "hasOwn", "is", "create",
|
|
267
|
+
};
|
|
268
|
+
static const char *const XT_NUMBER_STATICS[] = {
|
|
269
|
+
"isInteger", "isSafeInteger", "isFinite", "isNaN", "parseFloat", "parseInt",
|
|
270
|
+
};
|
|
271
|
+
static const char *const XT_NUMBER_CONSTANTS[] = {
|
|
272
|
+
"MAX_SAFE_INTEGER", "MIN_SAFE_INTEGER", "MAX_VALUE", "MIN_VALUE", "EPSILON",
|
|
273
|
+
"POSITIVE_INFINITY", "NEGATIVE_INFINITY", "NaN",
|
|
274
|
+
};
|
|
275
|
+
static const char *const XT_STRING_STATICS[] = {"fromCharCode", "fromCodePoint", "raw"};
|
|
276
|
+
static const char *const XT_BIGINT_STATICS[] = {"asIntN", "asUintN"};
|
|
277
|
+
static const char *const XT_SYMBOL_STATICS[] = {"for", "keyFor"};
|
|
278
|
+
static const char *const XT_DATE_STATICS[] = {"now", "parse", "UTC"};
|
|
279
|
+
static const char *const XT_PROMISE_STATICS[] = {
|
|
280
|
+
"resolve", "reject", "all", "allSettled", "race", "any",
|
|
281
|
+
};
|
|
282
|
+
static const char *const XT_TYPED_ARRAY_STATICS[] = {"from", "of"};
|
|
283
|
+
|
|
284
|
+
static int xt_name_in(const char *name, const char *const *names, size_t count) {
|
|
285
|
+
for (size_t i = 0; i < count; i++) {
|
|
286
|
+
if (strcmp(name, names[i]) == 0) return 1;
|
|
287
|
+
}
|
|
288
|
+
return 0;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
#define XT_NAMES_COUNT(names) (sizeof(names) / sizeof((names)[0]))
|
|
292
|
+
|
|
293
|
+
static int xt_ctor_has_static(const char *namespace, const char *method) {
|
|
294
|
+
if (strcmp(namespace, "Array") == 0)
|
|
295
|
+
return xt_name_in(method, XT_ARRAY_STATICS, XT_NAMES_COUNT(XT_ARRAY_STATICS));
|
|
296
|
+
if (strcmp(namespace, "Object") == 0)
|
|
297
|
+
return xt_name_in(method, XT_OBJECT_STATICS, XT_NAMES_COUNT(XT_OBJECT_STATICS));
|
|
298
|
+
if (strcmp(namespace, "Number") == 0)
|
|
299
|
+
return xt_name_in(method, XT_NUMBER_STATICS, XT_NAMES_COUNT(XT_NUMBER_STATICS));
|
|
300
|
+
if (strcmp(namespace, "String") == 0)
|
|
301
|
+
return xt_name_in(method, XT_STRING_STATICS, XT_NAMES_COUNT(XT_STRING_STATICS));
|
|
302
|
+
if (strcmp(namespace, "BigInt") == 0)
|
|
303
|
+
return xt_name_in(method, XT_BIGINT_STATICS, XT_NAMES_COUNT(XT_BIGINT_STATICS));
|
|
304
|
+
if (strcmp(namespace, "Symbol") == 0)
|
|
305
|
+
return xt_name_in(method, XT_SYMBOL_STATICS, XT_NAMES_COUNT(XT_SYMBOL_STATICS));
|
|
306
|
+
if (strcmp(namespace, "Date") == 0)
|
|
307
|
+
return xt_name_in(method, XT_DATE_STATICS, XT_NAMES_COUNT(XT_DATE_STATICS));
|
|
308
|
+
if (strcmp(namespace, "Promise") == 0)
|
|
309
|
+
return xt_name_in(method, XT_PROMISE_STATICS, XT_NAMES_COUNT(XT_PROMISE_STATICS));
|
|
310
|
+
if (xt_ctor_is_typed_array(namespace))
|
|
311
|
+
return xt_name_in(method, XT_TYPED_ARRAY_STATICS, XT_NAMES_COUNT(XT_TYPED_ARRAY_STATICS));
|
|
312
|
+
return 0;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
static xt_value xt_builtin_static_call(const char *namespace, const char *method, int32_t argc,
|
|
316
|
+
xt_value *argv) {
|
|
317
|
+
xt_value nameValue = xt_string_from_cstr(namespace);
|
|
318
|
+
xt_value methodValue = xt_string_from_cstr(method);
|
|
319
|
+
if (strcmp(namespace, "Array") == 0) return xt_array_static(methodValue, argc, argv);
|
|
320
|
+
if (strcmp(namespace, "Object") == 0) return xt_object_static(methodValue, argc, argv);
|
|
321
|
+
if (strcmp(namespace, "Number") == 0) return xt_number_static(methodValue, argc, argv);
|
|
322
|
+
if (strcmp(namespace, "String") == 0) return xt_string_static(methodValue, argc, argv);
|
|
323
|
+
if (strcmp(namespace, "BigInt") == 0) return xt_bigint_static(methodValue, argc, argv);
|
|
324
|
+
if (strcmp(namespace, "Symbol") == 0) return xt_symbol_static(methodValue, argc, argv);
|
|
325
|
+
if (strcmp(namespace, "Date") == 0) return xt_date_static(methodValue, argc, argv);
|
|
326
|
+
if (strcmp(namespace, "Promise") == 0) return xt_promise_static(methodValue, argc, argv);
|
|
327
|
+
if (xt_ctor_is_typed_array(namespace))
|
|
328
|
+
return xt_typed_array_static_by_name(nameValue, methodValue, argc, argv);
|
|
329
|
+
return XT_UNDEFINED;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
static xt_value xt_builtin_static_invoke(xt_value thisValue, xt_value env, int32_t argc,
|
|
333
|
+
xt_value *argv) {
|
|
334
|
+
(void)thisValue;
|
|
335
|
+
const char *namespace = xt_string_data(xt_closure_env(env, 0));
|
|
336
|
+
const char *method = xt_string_data(xt_closure_env(env, 1));
|
|
337
|
+
if (!namespace || !method) return XT_UNDEFINED;
|
|
338
|
+
return xt_builtin_static_call(namespace, method, argc, argv);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
xt_value xt_builtin_static_value(xt_value ctor, xt_value key) {
|
|
342
|
+
if (!XT_IS_FUNCTION(ctor) || !XT_IS_STRING(key)) return XT_UNDEFINED;
|
|
343
|
+
xt_function *function = (xt_function *)XT_GET_PTR(ctor);
|
|
344
|
+
const char *namespace = function->builtin_name;
|
|
345
|
+
if (!namespace) return XT_UNDEFINED;
|
|
346
|
+
const char *method = xt_string_data(key);
|
|
347
|
+
if (!method) return XT_UNDEFINED;
|
|
348
|
+
|
|
349
|
+
/* Values that are not functions. */
|
|
350
|
+
if (strcmp(namespace, "Symbol") == 0) {
|
|
351
|
+
xt_value wellKnown = xt_symbol_get(key);
|
|
352
|
+
if (wellKnown != XT_UNDEFINED) return wellKnown;
|
|
353
|
+
}
|
|
354
|
+
if (strcmp(namespace, "Number") == 0 &&
|
|
355
|
+
xt_name_in(method, XT_NUMBER_CONSTANTS, XT_NAMES_COUNT(XT_NUMBER_CONSTANTS))) {
|
|
356
|
+
return xt_number_static(key, 0, NULL);
|
|
357
|
+
}
|
|
358
|
+
if (xt_ctor_is_typed_array(namespace) && strcmp(method, "BYTES_PER_ELEMENT") == 0) {
|
|
359
|
+
xt_value nameValue = xt_string_from_cstr(namespace);
|
|
360
|
+
xt_value proto = xt_typed_array_prototype(nameValue);
|
|
361
|
+
return xt_object_get(proto, key);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (!xt_ctor_has_static(namespace, method)) return XT_UNDEFINED;
|
|
365
|
+
if (xt_truthy(xt_object_has_own(ctor, key))) return xt_object_get(ctor, key);
|
|
366
|
+
|
|
367
|
+
xt_value env[2] = {xt_string_from_cstr(namespace), key};
|
|
368
|
+
xt_value closure = xt_closure_new((void *)xt_builtin_static_invoke, 2, env);
|
|
369
|
+
xt_function_set_metadata(closure, key, 0);
|
|
370
|
+
xt_object_set(ctor, key, closure);
|
|
371
|
+
return closure;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/* -- prototype lookup for runtime kinds ----------------------------------- */
|
|
375
|
+
|
|
376
|
+
xt_value xt_builtin_prototype_for_kind(int32_t kind) {
|
|
377
|
+
const char *name = NULL;
|
|
378
|
+
switch (kind) {
|
|
379
|
+
case XT_OBJECT_KIND_MAP: name = "Map"; break;
|
|
380
|
+
case XT_OBJECT_KIND_SET: name = "Set"; break;
|
|
381
|
+
case XT_OBJECT_KIND_DATE: name = "Date"; break;
|
|
382
|
+
case XT_OBJECT_KIND_REGEXP: name = "RegExp"; break;
|
|
383
|
+
case XT_OBJECT_KIND_PROMISE: name = "Promise"; break;
|
|
384
|
+
case XT_OBJECT_KIND_SYMBOL: name = "Symbol"; break;
|
|
385
|
+
default: return XT_UNDEFINED;
|
|
386
|
+
}
|
|
387
|
+
xt_value constructor = xt_builtin_constructor(xt_string_from_cstr(name));
|
|
388
|
+
return xt_function_get_prototype(constructor);
|
|
389
|
+
}
|
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
/* Generic member access */
|
|
3
3
|
/* ------------------------------------------------------------------------- */
|
|
4
4
|
|
|
5
|
+
/* Inherited lookup for values whose representation is not an `xt_object`
|
|
6
|
+
* property table (Map/Set/Date/RegExp/Promise/Symbol). `__proto__` returns the
|
|
7
|
+
* prototype itself instead of invoking Object.prototype's getter with the
|
|
8
|
+
* wrong receiver. */
|
|
9
|
+
static xt_value xt_get_inherited(xt_value target, xt_value key) {
|
|
10
|
+
xt_value proto = xt_object_get_prototype(target);
|
|
11
|
+
if (proto == XT_UNDEFINED || proto == XT_NULL || proto == target) return XT_UNDEFINED;
|
|
12
|
+
if (XT_IS_STRING(key)) {
|
|
13
|
+
xt_string *k = xt_as_string(key);
|
|
14
|
+
if (k->length == 9 && memcmp(xt_string_bytes(k), "__proto__", 9) == 0) return proto;
|
|
15
|
+
}
|
|
16
|
+
return xt_get(proto, key);
|
|
17
|
+
}
|
|
18
|
+
|
|
5
19
|
/*
|
|
6
20
|
* Reading a property of `null` / `undefined` is a TypeError in JavaScript.
|
|
7
21
|
* Short-circuiting `?.` guards forbid reaching this point for a nullish
|
|
@@ -37,8 +51,16 @@ xt_value xt_get(xt_value target, xt_value key) {
|
|
|
37
51
|
xt_value value = xt_object_get(extra, key);
|
|
38
52
|
if (value != XT_UNDEFINED) return value;
|
|
39
53
|
}
|
|
40
|
-
/* Fall back to first-class built-in methods (`arr.map`)
|
|
41
|
-
|
|
54
|
+
/* Fall back to first-class built-in methods (`arr.map`) and then to
|
|
55
|
+
* the array's [[Prototype]] (`arr.hasOwnProperty`). */
|
|
56
|
+
xt_value method = xt_method_value(target, key);
|
|
57
|
+
if (method != XT_UNDEFINED) return method;
|
|
58
|
+
xt_value proto = ((xt_array *)XT_GET_PTR(target))->prototype;
|
|
59
|
+
if (proto != XT_UNDEFINED && proto != XT_NULL) {
|
|
60
|
+
if (k->length == 9 && memcmp(xt_string_bytes(k), "__proto__", 9) == 0) return proto;
|
|
61
|
+
return xt_get(proto, key);
|
|
62
|
+
}
|
|
63
|
+
return XT_UNDEFINED;
|
|
42
64
|
}
|
|
43
65
|
return xt_array_get(target, key);
|
|
44
66
|
}
|
|
@@ -49,7 +71,11 @@ xt_value xt_get(xt_value target, xt_value key) {
|
|
|
49
71
|
xt_value value = xt_object_get(extra, key);
|
|
50
72
|
if (value != XT_UNDEFINED) return value;
|
|
51
73
|
}
|
|
52
|
-
|
|
74
|
+
xt_value method = xt_method_value(target, key);
|
|
75
|
+
if (method != XT_UNDEFINED) return method;
|
|
76
|
+
xt_value proto = ((xt_array *)XT_GET_PTR(target))->prototype;
|
|
77
|
+
if (proto != XT_UNDEFINED && proto != XT_NULL) return xt_get(proto, key);
|
|
78
|
+
return XT_UNDEFINED;
|
|
53
79
|
}
|
|
54
80
|
return xt_array_get(target, key);
|
|
55
81
|
}
|
|
@@ -69,6 +95,9 @@ xt_value xt_get(xt_value target, xt_value key) {
|
|
|
69
95
|
{
|
|
70
96
|
xt_value value = xt_object_get(target, key);
|
|
71
97
|
if (value != XT_UNDEFINED) return value;
|
|
98
|
+
/* `const A = Array; A.isArray`, `const S = Symbol; S.iterator`, ... */
|
|
99
|
+
value = xt_builtin_static_value(target, key);
|
|
100
|
+
if (value != XT_UNDEFINED) return value;
|
|
72
101
|
return xt_method_value(target, key);
|
|
73
102
|
}
|
|
74
103
|
}
|
|
@@ -83,7 +112,9 @@ xt_value xt_get(xt_value target, xt_value key) {
|
|
|
83
112
|
return xt_symbol_description(target);
|
|
84
113
|
}
|
|
85
114
|
}
|
|
86
|
-
|
|
115
|
+
xt_value method = xt_method_value(target, key);
|
|
116
|
+
if (method != XT_UNDEFINED) return method;
|
|
117
|
+
return xt_get_inherited(target, key);
|
|
87
118
|
}
|
|
88
119
|
if (obj->header.kind == XT_OBJECT_KIND_MAP || obj->header.kind == XT_OBJECT_KIND_SET) {
|
|
89
120
|
if (XT_IS_STRING(key)) {
|
|
@@ -109,25 +140,30 @@ xt_value xt_get(xt_value target, xt_value key) {
|
|
|
109
140
|
{
|
|
110
141
|
xt_value value = xt_object_get(target, key);
|
|
111
142
|
if (value != XT_UNDEFINED) return value;
|
|
112
|
-
|
|
143
|
+
xt_value method = xt_method_value(target, key);
|
|
144
|
+
if (method != XT_UNDEFINED) return method;
|
|
145
|
+
if (obj->header.kind != XT_OBJECT_KIND_OBJECT && obj->header.kind != XT_OBJECT_KIND_ERROR) {
|
|
146
|
+
return xt_get_inherited(target, key);
|
|
147
|
+
}
|
|
148
|
+
return XT_UNDEFINED;
|
|
113
149
|
}
|
|
114
150
|
}
|
|
115
151
|
if (XT_IS_STRING(target)) {
|
|
116
152
|
/* Property access on a string: `.length`, numeric indexing and built-in
|
|
117
|
-
* methods.
|
|
153
|
+
* methods. Indices are UTF-16 code units, so `s[0]` of a Chinese string is
|
|
154
|
+
* the whole character and an emoji index into its surrogate pair. */
|
|
118
155
|
xt_string *s = xt_as_string(target);
|
|
156
|
+
uint32_t units = xt_string_utf16_length(s);
|
|
119
157
|
if (XT_IS_STRING(key)) {
|
|
120
158
|
xt_string *k = xt_as_string(key);
|
|
121
|
-
if (k->length == 6 && memcmp(xt_string_bytes(k), "length", 6) == 0) return xt_number((double)
|
|
159
|
+
if (k->length == 6 && memcmp(xt_string_bytes(k), "length", 6) == 0) return xt_number((double)units);
|
|
122
160
|
uint32_t index = 0;
|
|
123
|
-
if (xt_key_is_array_index(k, &index) && index < s
|
|
124
|
-
return xt_string_new(xt_string_bytes(s) + index, 1);
|
|
125
|
-
}
|
|
161
|
+
if (xt_key_is_array_index(k, &index) && index < units) return xt_string_unit_value(s, (int32_t)index);
|
|
126
162
|
return xt_method_value(target, key);
|
|
127
163
|
}
|
|
128
164
|
if (XT_IS_NUMBER(key)) {
|
|
129
165
|
int32_t index = xt_to_int32(key);
|
|
130
|
-
if (index >= 0 && (uint32_t)index <
|
|
166
|
+
if (index >= 0 && (uint32_t)index < units) return xt_string_unit_value(s, index);
|
|
131
167
|
}
|
|
132
168
|
return XT_UNDEFINED;
|
|
133
169
|
}
|
|
@@ -15,12 +15,20 @@ void xt_array_reserve(xt_array *array, uint32_t needed) {
|
|
|
15
15
|
array->capacity = capacity;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
/* Array.prototype: every ordinary array inherits from it. The prototype itself
|
|
19
|
+
* is an array, like ECMAScript, so `Array.isArray(Array.prototype)` holds. It
|
|
20
|
+
* is created with the ordinary object prototype as its own [[Prototype]], which
|
|
21
|
+
* terminates the array-specific link. */
|
|
22
|
+
static xt_value xt_array_proto_root = XT_UNDEFINED;
|
|
23
|
+
static int xt_array_proto_ready = 0;
|
|
24
|
+
|
|
25
|
+
static xt_value xt_array_new_with_proto(int32_t count, xt_value *items, xt_value proto) {
|
|
19
26
|
xt_array *array = (xt_array *)xt_alloc(sizeof(xt_array), XT_OBJECT_KIND_ARRAY);
|
|
20
27
|
array->length = 0;
|
|
21
28
|
array->capacity = 0;
|
|
22
29
|
array->items = NULL;
|
|
23
30
|
array->extra = XT_UNDEFINED;
|
|
31
|
+
array->prototype = proto;
|
|
24
32
|
if (count > 0) {
|
|
25
33
|
xt_array_reserve(array, (uint32_t)count);
|
|
26
34
|
for (int32_t i = 0; i < count; i++) array->items[i] = items[i];
|
|
@@ -29,6 +37,22 @@ xt_value xt_array_new(int32_t count, xt_value *items) {
|
|
|
29
37
|
return XT_FROM_PTR(XT_TAG_ARRAY, array);
|
|
30
38
|
}
|
|
31
39
|
|
|
40
|
+
xt_value xt_array_prototype(void) {
|
|
41
|
+
if (xt_array_proto_ready) return xt_array_proto_root;
|
|
42
|
+
xt_array_proto_ready = 1;
|
|
43
|
+
xt_array_proto_root = xt_array_new_with_proto(0, NULL, xt_object_prototype());
|
|
44
|
+
xt_gc_add_root(&xt_array_proto_root);
|
|
45
|
+
xt_value constructor = xt_builtin_constructor(xt_string_from_cstr("Array"));
|
|
46
|
+
if (constructor != XT_UNDEFINED) {
|
|
47
|
+
xt_set(xt_array_proto_root, xt_string_from_cstr("constructor"), constructor);
|
|
48
|
+
}
|
|
49
|
+
return xt_array_proto_root;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
xt_value xt_array_new(int32_t count, xt_value *items) {
|
|
53
|
+
return xt_array_new_with_proto(count, items, xt_array_prototype());
|
|
54
|
+
}
|
|
55
|
+
|
|
32
56
|
/* Named properties of an array live in a lazily-created side object. Returns
|
|
33
57
|
* XT_UNDEFINED when `create` is false and no such object exists yet. */
|
|
34
58
|
static xt_value xt_array_extra(xt_array *array, int create) {
|
|
@@ -69,7 +93,7 @@ xt_value xt_array_push(xt_value value, xt_value newValue) {
|
|
|
69
93
|
|
|
70
94
|
xt_value xt_array_length(xt_value value) {
|
|
71
95
|
if (XT_IS_ARRAY(value)) return xt_number((double)((xt_array *)XT_GET_PTR(value))->length);
|
|
72
|
-
if (XT_IS_STRING(value)) return xt_number((double)
|
|
96
|
+
if (XT_IS_STRING(value)) return xt_number((double)xt_string_utf16_length(xt_as_string(value)));
|
|
73
97
|
/* Functions expose their arity as `.length`. */
|
|
74
98
|
if (XT_IS_FUNCTION(value)) {
|
|
75
99
|
int32_t arity = ((xt_function *)XT_GET_PTR(value))->arity;
|