xbintsc 0.3.3 → 0.3.4
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/dist/src/ast/expressions.d.ts +8 -0
- package/dist/src/binder/binder.js +1 -0
- package/dist/src/binder/binder.js.map +1 -1
- package/dist/src/binder/references.d.ts +1 -1
- package/dist/src/binder/references.js +34 -5
- package/dist/src/binder/references.js.map +1 -1
- package/dist/src/binder/types.d.ts +4 -0
- package/dist/src/binder/types.js.map +1 -1
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/codegen/generator/calls/access.d.ts +9 -1
- package/dist/src/codegen/generator/calls/access.js +146 -13
- package/dist/src/codegen/generator/calls/access.js.map +1 -1
- package/dist/src/codegen/generator/calls/closures.d.ts +1 -0
- package/dist/src/codegen/generator/calls/closures.js +25 -0
- package/dist/src/codegen/generator/calls/closures.js.map +1 -1
- package/dist/src/codegen/generator/calls/invocation.js +3 -1
- package/dist/src/codegen/generator/calls/invocation.js.map +1 -1
- package/dist/src/codegen/generator/calls/literals.js +12 -4
- package/dist/src/codegen/generator/calls/literals.js.map +1 -1
- package/dist/src/codegen/generator/calls/super.js +4 -7
- package/dist/src/codegen/generator/calls/super.js.map +1 -1
- package/dist/src/codegen/generator/context.d.ts +6 -1
- package/dist/src/codegen/generator/context.js +13 -1
- package/dist/src/codegen/generator/context.js.map +1 -1
- package/dist/src/codegen/generator/expressions/assignment.js +1 -3
- package/dist/src/codegen/generator/expressions/assignment.js.map +1 -1
- package/dist/src/codegen/generator/expressions/primary.d.ts +3 -1
- package/dist/src/codegen/generator/expressions/primary.js +99 -1
- package/dist/src/codegen/generator/expressions/primary.js.map +1 -1
- package/dist/src/codegen/generator/generator.js.map +1 -1
- package/dist/src/codegen/generator/module.js +25 -15
- package/dist/src/codegen/generator/module.js.map +1 -1
- package/dist/src/codegen/generator/state.d.ts +15 -0
- package/dist/src/codegen/generator/state.js +28 -1
- package/dist/src/codegen/generator/state.js.map +1 -1
- package/dist/src/codegen/generator/statements/control-flow.d.ts +3 -1
- package/dist/src/codegen/generator/statements/control-flow.js +85 -0
- package/dist/src/codegen/generator/statements/control-flow.js.map +1 -1
- package/dist/src/codegen/generator/statements/dispatch.js +15 -6
- package/dist/src/codegen/generator/statements/dispatch.js.map +1 -1
- package/dist/src/codegen/generator/statements/loops.d.ts +3 -0
- package/dist/src/codegen/generator/statements/loops.js +25 -11
- package/dist/src/codegen/generator/statements/loops.js.map +1 -1
- package/dist/src/codegen/generator/tables.d.ts +7 -0
- package/dist/src/codegen/generator/tables.js +41 -1
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/driver/compiler.d.ts +1 -1
- package/dist/src/driver/compiler.js +33 -1
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/driver/modules.d.ts +4 -3
- package/dist/src/driver/modules.js +61 -4
- package/dist/src/driver/modules.js.map +1 -1
- package/dist/src/driver/toolchain.js +1 -1
- package/dist/src/driver/toolchain.js.map +1 -1
- package/dist/src/lexer/scanner.js.map +1 -1
- package/dist/src/parser/declarations/classes.js +4 -0
- package/dist/src/parser/declarations/classes.js.map +1 -1
- package/dist/src/parser/declarations/type-members.js.map +1 -1
- package/dist/src/parser/expressions.d.ts +2 -1
- package/dist/src/parser/expressions.js +10 -1
- package/dist/src/parser/expressions.js.map +1 -1
- package/dist/src/parser/helpers.js +2 -2
- package/dist/src/parser/helpers.js.map +1 -1
- package/dist/src/parser/literals.js +79 -6
- package/dist/src/parser/literals.js.map +1 -1
- package/dist/src/parser/modules.js.map +1 -1
- package/dist/src/parser/parser.js +0 -1
- package/dist/src/parser/parser.js.map +1 -1
- package/dist/src/parser/statements.d.ts +1 -0
- package/dist/src/parser/statements.js +13 -1
- package/dist/src/parser/statements.js.map +1 -1
- package/dist/src/parser/types.js +2 -2
- package/dist/src/parser/types.js.map +1 -1
- package/dist/tests/driver/toolchain.test.js +11 -1
- package/dist/tests/driver/toolchain.test.js.map +1 -1
- package/dist/tests/e2e/compile-node.test.js +3 -1
- package/dist/tests/e2e/compile-node.test.js.map +1 -1
- package/dist/tests/e2e/compile.test.js +465 -2
- package/dist/tests/e2e/compile.test.js.map +1 -1
- package/dist/tests/e2e/differential.test.d.ts +7 -0
- package/dist/tests/e2e/differential.test.js +581 -0
- package/dist/tests/e2e/differential.test.js.map +1 -0
- package/dist/tests/e2e/harness.d.ts +21 -0
- package/dist/tests/e2e/harness.js +26 -0
- package/dist/tests/e2e/harness.js.map +1 -1
- package/package.json +6 -1
- package/runtime/ext_node/fs/fs_ops.c +36 -1
- package/runtime/rt.h +17 -0
- package/runtime/rt_internal.h +88 -2
- package/runtime/xt_builtins.c +174 -6
- package/runtime/xt_containers.c +234 -42
- package/runtime/xt_generator.c +304 -0
- package/runtime/xt_io.c +170 -30
- package/runtime/xt_promise.c +6 -1
- package/runtime/xt_regex.h +105 -35
- package/runtime/xt_stdlib/dispatch.inc +255 -4
- package/runtime/xt_stdlib/strings.inc +17 -13
- package/runtime/xt_stdlib2/arrays.inc +126 -19
- package/runtime/xt_stdlib2/date.inc +9 -2
- package/runtime/xt_stdlib2/error.inc +162 -12
- package/runtime/xt_stdlib2/helpers.inc +4 -0
- package/runtime/xt_stdlib2/json.inc +19 -6
- package/runtime/xt_stdlib2/number-string-statics.inc +1 -1
- package/runtime/xt_stdlib2/numbers.inc +4 -5
- package/runtime/xt_stdlib2/objects.inc +29 -0
- package/runtime/xt_stdlib2/regexp-match.inc +293 -51
- package/runtime/xt_stdlib2/set.inc +58 -2
- package/runtime/xt_stdlib2/strings.inc +38 -0
- package/runtime/xt_symbol.c +149 -0
- package/runtime/xt_values.c +221 -15
- package/scripts/build-runtime.ts +1 -1
- package/src/ast/expressions.ts +8 -0
- package/src/binder/binder.ts +1 -1
- package/src/binder/references.ts +47 -4
- package/src/binder/types.ts +4 -0
- package/src/cli/main.ts +1 -2
- package/src/codegen/generator/calls/access.ts +158 -13
- package/src/codegen/generator/calls/closures.ts +24 -0
- package/src/codegen/generator/calls/invocation.ts +3 -1
- package/src/codegen/generator/calls/literals.ts +10 -4
- package/src/codegen/generator/calls/super.ts +6 -9
- package/src/codegen/generator/context.ts +14 -3
- package/src/codegen/generator/expressions/assignment.ts +1 -3
- package/src/codegen/generator/expressions/primary.ts +108 -2
- package/src/codegen/generator/generator.ts +0 -1
- package/src/codegen/generator/module.ts +24 -18
- package/src/codegen/generator/state.ts +44 -1
- package/src/codegen/generator/statements/control-flow.ts +86 -0
- package/src/codegen/generator/statements/dispatch.ts +18 -6
- package/src/codegen/generator/statements/loops.ts +29 -11
- package/src/codegen/generator/tables.ts +44 -1
- package/src/driver/compiler.ts +32 -2
- package/src/driver/modules.ts +64 -4
- package/src/driver/toolchain.ts +1 -1
- package/src/lexer/scanner.ts +0 -1
- package/src/parser/declarations/classes.ts +4 -0
- package/src/parser/declarations/type-members.ts +0 -2
- package/src/parser/expressions.ts +13 -1
- package/src/parser/helpers.ts +2 -2
- package/src/parser/literals.ts +82 -7
- package/src/parser/modules.ts +0 -1
- package/src/parser/parser.ts +0 -1
- package/src/parser/statements.ts +14 -0
- package/src/parser/types.ts +2 -2
package/runtime/xt_containers.c
CHANGED
|
@@ -52,19 +52,85 @@ xt_object *xt_as_object(xt_value value) {
|
|
|
52
52
|
return NULL;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
static xt_property *xt_object_find(xt_object *obj,
|
|
55
|
+
static xt_property *xt_object_find(xt_object *obj, xt_value key) {
|
|
56
56
|
for (uint32_t i = 0; i < obj->count; i++) {
|
|
57
|
-
if (
|
|
57
|
+
if (xt_property_key_equals(obj->properties[i].key, key)) return &obj->properties[i];
|
|
58
58
|
}
|
|
59
59
|
return NULL;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
/*
|
|
63
|
+
* JavaScript enumerates own string keys in a canonical order: array-index
|
|
64
|
+
* keys (the canonical decimal form of an integer in [0, 2^32 - 2]) first, in
|
|
65
|
+
* ascending numeric order, then the remaining keys in insertion order. We keep
|
|
66
|
+
* the physical property array in exactly that order so every consumer
|
|
67
|
+
* (`Object.keys`, `for...in`, `JSON.stringify`, ...) is correct for free.
|
|
68
|
+
*/
|
|
69
|
+
static int xt_key_is_array_index(xt_string *key, uint32_t *out) {
|
|
70
|
+
if (key->length == 0 || key->length > 10) return 0;
|
|
71
|
+
if (key->data[0] == '0' && key->length != 1) return 0; /* "0" is canonical, "01" is not */
|
|
72
|
+
uint64_t value = 0;
|
|
73
|
+
for (uint32_t i = 0; i < key->length; i++) {
|
|
74
|
+
char ch = key->data[i];
|
|
75
|
+
if (ch < '0' || ch > '9') return 0;
|
|
76
|
+
value = value * 10 + (uint64_t)(ch - '0');
|
|
77
|
+
}
|
|
78
|
+
if (value > 4294967294ULL) return 0; /* 2^32 - 2 is the largest array index */
|
|
79
|
+
*out = (uint32_t)value;
|
|
80
|
+
return 1;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/* Insert a new property for `key`, preserving JavaScript key order. Symbols
|
|
84
|
+
* are not array indices and simply append in insertion order. */
|
|
85
|
+
static xt_property *xt_object_insert_property(xt_object *obj, xt_value key) {
|
|
86
|
+
uint32_t index = 0;
|
|
87
|
+
uint32_t position = obj->count;
|
|
88
|
+
if (XT_IS_STRING(key) && xt_key_is_array_index(xt_as_string(key), &index)) {
|
|
89
|
+
for (uint32_t i = 0; i < obj->count; i++) {
|
|
90
|
+
uint32_t existing = 0;
|
|
91
|
+
xt_value existingKey = obj->properties[i].key;
|
|
92
|
+
if (!XT_IS_STRING(existingKey) ||
|
|
93
|
+
!xt_key_is_array_index(xt_as_string(existingKey), &existing)) {
|
|
94
|
+
position = i; /* first non-index key: the integer prefix ends here */
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
if (existing > index) {
|
|
98
|
+
position = i;
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
xt_object_reserve(obj, obj->count + 1);
|
|
104
|
+
for (uint32_t i = obj->count; i > position; i--) obj->properties[i] = obj->properties[i - 1];
|
|
105
|
+
xt_property *prop = &obj->properties[position];
|
|
106
|
+
prop->key = key;
|
|
107
|
+
prop->value = XT_UNDEFINED;
|
|
108
|
+
prop->getter = XT_UNDEFINED;
|
|
109
|
+
prop->setter = XT_UNDEFINED;
|
|
110
|
+
obj->count++;
|
|
111
|
+
return prop;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
xt_property *xt_object_find_property(xt_object *obj, xt_value key) {
|
|
63
115
|
return obj ? xt_object_find(obj, key) : NULL;
|
|
64
116
|
}
|
|
65
117
|
|
|
118
|
+
/* Normalise a property key to a string value or a symbol value. */
|
|
119
|
+
xt_value xt_to_property_key(xt_value key) {
|
|
120
|
+
if (xt_is_symbol(key)) return key;
|
|
121
|
+
return xt_to_string(key);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/* Two property keys are equal when they are the same symbol, or equal
|
|
125
|
+
* strings. */
|
|
126
|
+
int xt_property_key_equals(xt_value a, xt_value b) {
|
|
127
|
+
if (XT_IS_STRING(a) && XT_IS_STRING(b)) return xt_string_equals(xt_as_string(a), xt_as_string(b));
|
|
128
|
+
return a == b;
|
|
129
|
+
}
|
|
130
|
+
|
|
66
131
|
static int xt_key_is_prototype(xt_value key) {
|
|
67
|
-
|
|
132
|
+
if (!XT_IS_STRING(key)) return 0;
|
|
133
|
+
xt_string *k = xt_as_string(key);
|
|
68
134
|
return k && k->length == 9 && memcmp(k->data, "prototype", 9) == 0;
|
|
69
135
|
}
|
|
70
136
|
|
|
@@ -81,9 +147,9 @@ xt_value xt_object_get(xt_value value, xt_value key) {
|
|
|
81
147
|
layout. Containers such as Map/Set/Date/RegExp/Promise/Array must not be
|
|
82
148
|
interpreted as property tables, or property lookup reads past the end of
|
|
83
149
|
their real representation. */
|
|
84
|
-
if (obj->header.kind != XT_OBJECT_KIND_OBJECT
|
|
85
|
-
|
|
86
|
-
|
|
150
|
+
if (obj->header.kind != XT_OBJECT_KIND_OBJECT && obj->header.kind != XT_OBJECT_KIND_ERROR)
|
|
151
|
+
return XT_UNDEFINED;
|
|
152
|
+
xt_value k = xt_to_property_key(key);
|
|
87
153
|
/* Walk the prototype chain like JavaScript property lookup. */
|
|
88
154
|
xt_object *cur = obj;
|
|
89
155
|
while (cur) {
|
|
@@ -109,10 +175,10 @@ xt_value xt_object_set(xt_value value, xt_value key, xt_value newValue) {
|
|
|
109
175
|
}
|
|
110
176
|
xt_object *obj = xt_as_object(value);
|
|
111
177
|
if (!obj) return newValue;
|
|
112
|
-
if (obj->header.kind != XT_OBJECT_KIND_OBJECT
|
|
178
|
+
if (obj->header.kind != XT_OBJECT_KIND_OBJECT && obj->header.kind != XT_OBJECT_KIND_ERROR)
|
|
179
|
+
return newValue;
|
|
113
180
|
if (obj->frozen) return newValue;
|
|
114
|
-
xt_value
|
|
115
|
-
xt_string *k = xt_as_string(keyString);
|
|
181
|
+
xt_value k = xt_to_property_key(key);
|
|
116
182
|
xt_property *prop = xt_object_find(obj, k);
|
|
117
183
|
if (prop) {
|
|
118
184
|
if (prop->setter != XT_UNDEFINED) {
|
|
@@ -144,39 +210,29 @@ xt_value xt_object_set(xt_value value, xt_value key, xt_value newValue) {
|
|
|
144
210
|
}
|
|
145
211
|
ancestor = XT_IS_OBJECT(ancestor->prototype) ? (xt_object *)XT_GET_PTR(ancestor->prototype) : NULL;
|
|
146
212
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
obj->properties[obj->count].value = newValue;
|
|
150
|
-
obj->properties[obj->count].getter = XT_UNDEFINED;
|
|
151
|
-
obj->properties[obj->count].setter = XT_UNDEFINED;
|
|
152
|
-
obj->count++;
|
|
213
|
+
xt_property *inserted = xt_object_insert_property(obj, k);
|
|
214
|
+
inserted->value = newValue;
|
|
153
215
|
return newValue;
|
|
154
216
|
}
|
|
155
217
|
|
|
156
|
-
static xt_property *xt_object_ensure_property(xt_object *obj, xt_value
|
|
157
|
-
|
|
218
|
+
static xt_property *xt_object_ensure_property(xt_object *obj, xt_value key) {
|
|
219
|
+
xt_value propertyKey = xt_to_property_key(key);
|
|
220
|
+
xt_property *prop = xt_object_find(obj, propertyKey);
|
|
158
221
|
if (prop) return prop;
|
|
159
|
-
|
|
160
|
-
prop = &obj->properties[obj->count];
|
|
161
|
-
prop->key = xt_as_string(keyString);
|
|
162
|
-
prop->value = XT_UNDEFINED;
|
|
163
|
-
prop->getter = XT_UNDEFINED;
|
|
164
|
-
prop->setter = XT_UNDEFINED;
|
|
165
|
-
obj->count++;
|
|
166
|
-
return prop;
|
|
222
|
+
return xt_object_insert_property(obj, propertyKey);
|
|
167
223
|
}
|
|
168
224
|
|
|
169
225
|
xt_value xt_object_define_getter(xt_value value, xt_value key, xt_value getter) {
|
|
170
226
|
xt_object *obj = xt_as_object(value);
|
|
171
227
|
if (!obj) return value;
|
|
172
|
-
xt_object_ensure_property(obj,
|
|
228
|
+
xt_object_ensure_property(obj, key)->getter = getter;
|
|
173
229
|
return value;
|
|
174
230
|
}
|
|
175
231
|
|
|
176
232
|
xt_value xt_object_define_setter(xt_value value, xt_value key, xt_value setter) {
|
|
177
233
|
xt_object *obj = xt_as_object(value);
|
|
178
234
|
if (!obj) return value;
|
|
179
|
-
xt_object_ensure_property(obj,
|
|
235
|
+
xt_object_ensure_property(obj, key)->setter = setter;
|
|
180
236
|
return value;
|
|
181
237
|
}
|
|
182
238
|
|
|
@@ -206,8 +262,7 @@ int xt_object_is_frozen(xt_value value) {
|
|
|
206
262
|
xt_value xt_object_has_own(xt_value value, xt_value key) {
|
|
207
263
|
xt_object *obj = xt_as_object(value);
|
|
208
264
|
if (!obj) return XT_FALSE;
|
|
209
|
-
|
|
210
|
-
return xt_bool(xt_object_find(obj, xt_as_string(keyString)) != NULL);
|
|
265
|
+
return xt_bool(xt_object_find(obj, xt_to_property_key(key)) != NULL);
|
|
211
266
|
}
|
|
212
267
|
|
|
213
268
|
xt_value xt_object_from_entries(xt_value entries) {
|
|
@@ -225,8 +280,21 @@ xt_value xt_object_from_entries(xt_value entries) {
|
|
|
225
280
|
xt_value xt_object_has(xt_value value, xt_value key) {
|
|
226
281
|
if (!XT_IS_OBJECT(value)) return XT_FALSE;
|
|
227
282
|
xt_object *obj = (xt_object *)XT_GET_PTR(value);
|
|
228
|
-
|
|
229
|
-
|
|
283
|
+
return xt_bool(xt_object_find(obj, xt_to_property_key(key)) != NULL);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/* `Object.getOwnPropertySymbols`: the symbol-keyed own properties, in
|
|
287
|
+
* insertion order. String keys are skipped. */
|
|
288
|
+
xt_value xt_object_own_property_symbols(xt_value value) {
|
|
289
|
+
xt_value result = xt_array_new(0, NULL);
|
|
290
|
+
if (!XT_IS_OBJECT(value)) return result;
|
|
291
|
+
xt_object *obj = (xt_object *)XT_GET_PTR(value);
|
|
292
|
+
if (obj->header.kind != XT_OBJECT_KIND_OBJECT && obj->header.kind != XT_OBJECT_KIND_ERROR)
|
|
293
|
+
return result;
|
|
294
|
+
for (uint32_t i = 0; i < obj->count; i++) {
|
|
295
|
+
if (xt_is_symbol(obj->properties[i].key)) xt_array_push(result, obj->properties[i].key);
|
|
296
|
+
}
|
|
297
|
+
return result;
|
|
230
298
|
}
|
|
231
299
|
|
|
232
300
|
xt_value xt_object_keys(xt_value value) {
|
|
@@ -234,7 +302,7 @@ xt_value xt_object_keys(xt_value value) {
|
|
|
234
302
|
if (XT_IS_OBJECT(value)) {
|
|
235
303
|
xt_object *obj = (xt_object *)XT_GET_PTR(value);
|
|
236
304
|
for (uint32_t i = 0; i < obj->count; i++) {
|
|
237
|
-
xt_array_push(result,
|
|
305
|
+
if (XT_IS_STRING(obj->properties[i].key)) xt_array_push(result, obj->properties[i].key);
|
|
238
306
|
}
|
|
239
307
|
return result;
|
|
240
308
|
}
|
|
@@ -245,6 +313,12 @@ xt_value xt_object_keys(xt_value value) {
|
|
|
245
313
|
snprintf(buffer, sizeof(buffer), "%u", i);
|
|
246
314
|
xt_array_push(result, xt_string_from_cstr(buffer));
|
|
247
315
|
}
|
|
316
|
+
if (XT_IS_OBJECT(array->extra)) {
|
|
317
|
+
xt_object *extra = (xt_object *)XT_GET_PTR(array->extra);
|
|
318
|
+
for (uint32_t i = 0; i < extra->count; i++) {
|
|
319
|
+
if (XT_IS_STRING(extra->properties[i].key)) xt_array_push(result, extra->properties[i].key);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
248
322
|
return result;
|
|
249
323
|
}
|
|
250
324
|
if (XT_IS_STRING(value)) {
|
|
@@ -287,6 +361,7 @@ xt_value xt_array_new(int32_t count, xt_value *items) {
|
|
|
287
361
|
array->length = 0;
|
|
288
362
|
array->capacity = 0;
|
|
289
363
|
array->items = NULL;
|
|
364
|
+
array->extra = XT_UNDEFINED;
|
|
290
365
|
if (count > 0) {
|
|
291
366
|
xt_array_reserve(array, (uint32_t)count);
|
|
292
367
|
for (int32_t i = 0; i < count; i++) array->items[i] = items[i];
|
|
@@ -295,6 +370,16 @@ xt_value xt_array_new(int32_t count, xt_value *items) {
|
|
|
295
370
|
return XT_FROM_PTR(XT_TAG_ARRAY, array);
|
|
296
371
|
}
|
|
297
372
|
|
|
373
|
+
/* Named properties of an array live in a lazily-created side object. Returns
|
|
374
|
+
* XT_UNDEFINED when `create` is false and no such object exists yet. */
|
|
375
|
+
static xt_value xt_array_extra(xt_array *array, int create) {
|
|
376
|
+
if (!XT_IS_OBJECT(array->extra)) {
|
|
377
|
+
if (!create) return XT_UNDEFINED;
|
|
378
|
+
array->extra = xt_object_new();
|
|
379
|
+
}
|
|
380
|
+
return array->extra;
|
|
381
|
+
}
|
|
382
|
+
|
|
298
383
|
xt_value xt_array_get(xt_value value, xt_value index) {
|
|
299
384
|
if (!XT_IS_ARRAY(value)) return XT_UNDEFINED;
|
|
300
385
|
xt_array *array = (xt_array *)XT_GET_PTR(value);
|
|
@@ -326,6 +411,11 @@ xt_value xt_array_push(xt_value value, xt_value newValue) {
|
|
|
326
411
|
xt_value xt_array_length(xt_value value) {
|
|
327
412
|
if (XT_IS_ARRAY(value)) return xt_number((double)((xt_array *)XT_GET_PTR(value))->length);
|
|
328
413
|
if (XT_IS_STRING(value)) return xt_number((double)xt_string_length(xt_as_string(value)));
|
|
414
|
+
/* Functions expose their arity as `.length`. */
|
|
415
|
+
if (XT_IS_FUNCTION(value)) {
|
|
416
|
+
int32_t arity = ((xt_function *)XT_GET_PTR(value))->arity;
|
|
417
|
+
return xt_number(arity >= 0 ? (double)arity : 0.0);
|
|
418
|
+
}
|
|
329
419
|
/* Objects such as Buffers expose a `length` property of their own. */
|
|
330
420
|
if (XT_IS_OBJECT(value)) return xt_object_get_cstr(value, "length");
|
|
331
421
|
return XT_UNDEFINED;
|
|
@@ -340,9 +430,9 @@ xt_value xt_array_spread(xt_value target, xt_value source) {
|
|
|
340
430
|
for (uint32_t i = 0; i < src->length; i++) dst->items[dst->length++] = src->items[i];
|
|
341
431
|
return target;
|
|
342
432
|
}
|
|
343
|
-
/* Spreading a string/Map/Set iterates exactly like `for...of`. */
|
|
344
|
-
|
|
345
|
-
for (int32_t i = 0; i
|
|
433
|
+
/* Spreading a string/Map/Set/iterable iterates exactly like `for...of`. */
|
|
434
|
+
source = xt_iter_open(source);
|
|
435
|
+
for (int32_t i = 0; xt_truthy(xt_iter_has(source, xt_number((double)i))); i++) {
|
|
346
436
|
xt_array_push(target, xt_iter_value(source, xt_number((double)i)));
|
|
347
437
|
}
|
|
348
438
|
return target;
|
|
@@ -363,21 +453,89 @@ xt_value *xt_array_items(xt_value value) {
|
|
|
363
453
|
/* Generic member access */
|
|
364
454
|
/* ------------------------------------------------------------------------- */
|
|
365
455
|
|
|
456
|
+
/*
|
|
457
|
+
* Reading a property of `null` / `undefined` is a TypeError in JavaScript.
|
|
458
|
+
* Short-circuiting `?.` guards forbid reaching this point for a nullish
|
|
459
|
+
* receiver, so a non-optional access on a nullish base is a genuine bug in
|
|
460
|
+
* the compiled program (e.g. `o?.a.b` where `o.a` is null).
|
|
461
|
+
*/
|
|
462
|
+
static void xt_throw_read_of_nullish(xt_value target, xt_value key) {
|
|
463
|
+
const char *base = target == XT_NULL ? "null" : "undefined";
|
|
464
|
+
char keybuf[128];
|
|
465
|
+
xt_string *k = XT_IS_STRING(key) ? xt_as_string(key) : xt_as_string(xt_to_string(key));
|
|
466
|
+
uint32_t n = k->length < sizeof(keybuf) - 1 ? k->length : (uint32_t)(sizeof(keybuf) - 1);
|
|
467
|
+
memcpy(keybuf, k->data, n);
|
|
468
|
+
keybuf[n] = 0;
|
|
469
|
+
char message[256];
|
|
470
|
+
snprintf(message, sizeof(message),
|
|
471
|
+
"TypeError: Cannot read properties of %s (reading '%s')", base, keybuf);
|
|
472
|
+
xt_throw(xt_string_from_cstr(message));
|
|
473
|
+
}
|
|
474
|
+
|
|
366
475
|
xt_value xt_get(xt_value target, xt_value key) {
|
|
476
|
+
if (target == XT_UNDEFINED || target == XT_NULL) xt_throw_read_of_nullish(target, key);
|
|
367
477
|
if (XT_IS_ARRAY(target)) {
|
|
368
478
|
/* `arr.length` must read the array's length, never an element. */
|
|
369
479
|
if (XT_IS_STRING(key)) {
|
|
370
480
|
xt_string *k = xt_as_string(key);
|
|
371
481
|
if (k->length == 6 && memcmp(k->data, "length", 6) == 0)
|
|
372
482
|
return xt_number((double)((xt_array *)XT_GET_PTR(target))->length);
|
|
483
|
+
/* Named (non-index) properties such as `raw`/`index`/`input`. */
|
|
484
|
+
uint32_t ignored = 0;
|
|
485
|
+
if (!xt_key_is_array_index(k, &ignored)) {
|
|
486
|
+
xt_value extra = xt_array_extra((xt_array *)XT_GET_PTR(target), 0);
|
|
487
|
+
if (extra != XT_UNDEFINED) {
|
|
488
|
+
xt_value value = xt_object_get(extra, key);
|
|
489
|
+
if (value != XT_UNDEFINED) return value;
|
|
490
|
+
}
|
|
491
|
+
/* Fall back to first-class built-in methods (`arr.map`). */
|
|
492
|
+
return xt_method_value(target, key);
|
|
493
|
+
}
|
|
494
|
+
return xt_array_get(target, key);
|
|
495
|
+
}
|
|
496
|
+
/* Symbol-keyed properties live in the side object too. */
|
|
497
|
+
if (xt_is_symbol(key)) {
|
|
498
|
+
xt_value extra = xt_array_extra((xt_array *)XT_GET_PTR(target), 0);
|
|
499
|
+
if (extra != XT_UNDEFINED) {
|
|
500
|
+
xt_value value = xt_object_get(extra, key);
|
|
501
|
+
if (value != XT_UNDEFINED) return value;
|
|
502
|
+
}
|
|
503
|
+
return xt_method_value(target, key);
|
|
373
504
|
}
|
|
374
505
|
return xt_array_get(target, key);
|
|
375
506
|
}
|
|
376
|
-
if (XT_IS_FUNCTION(target))
|
|
507
|
+
if (XT_IS_FUNCTION(target)) {
|
|
508
|
+
/* Functions expose own `name` / `length` (arity) properties. */
|
|
509
|
+
if (XT_IS_STRING(key)) {
|
|
510
|
+
xt_string *k = xt_as_string(key);
|
|
511
|
+
if (k->length == 6 && memcmp(k->data, "length", 6) == 0) {
|
|
512
|
+
int32_t arity = ((xt_function *)XT_GET_PTR(target))->arity;
|
|
513
|
+
return xt_number(arity >= 0 ? (double)arity : 0.0);
|
|
514
|
+
}
|
|
515
|
+
if (k->length == 4 && memcmp(k->data, "name", 4) == 0) {
|
|
516
|
+
xt_string *name = ((xt_function *)XT_GET_PTR(target))->name;
|
|
517
|
+
return name ? XT_FROM_PTR(XT_TAG_STRING, name) : xt_string_from_cstr("");
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
{
|
|
521
|
+
xt_value value = xt_object_get(target, key);
|
|
522
|
+
if (value != XT_UNDEFINED) return value;
|
|
523
|
+
return xt_method_value(target, key);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
377
526
|
if (XT_IS_OBJECT(target)) {
|
|
378
527
|
/* Map/Set expose `.size`; promises, dates and regexps have their own
|
|
379
528
|
* getters implemented in the standard library. */
|
|
380
529
|
xt_object *obj = (xt_object *)XT_GET_PTR(target);
|
|
530
|
+
if (obj->header.kind == XT_OBJECT_KIND_SYMBOL) {
|
|
531
|
+
if (XT_IS_STRING(key)) {
|
|
532
|
+
xt_string *k = xt_as_string(key);
|
|
533
|
+
if (k->length == 11 && memcmp(k->data, "description", 11) == 0) {
|
|
534
|
+
return xt_symbol_description(target);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
return xt_method_value(target, key);
|
|
538
|
+
}
|
|
381
539
|
if (obj->header.kind == XT_OBJECT_KIND_MAP || obj->header.kind == XT_OBJECT_KIND_SET) {
|
|
382
540
|
if (XT_IS_STRING(key)) {
|
|
383
541
|
xt_string *k = xt_as_string(key);
|
|
@@ -387,20 +545,46 @@ xt_value xt_get(xt_value target, xt_value key) {
|
|
|
387
545
|
}
|
|
388
546
|
}
|
|
389
547
|
}
|
|
390
|
-
if (obj->header.kind == XT_OBJECT_KIND_REGEXP)
|
|
391
|
-
|
|
548
|
+
if (obj->header.kind == XT_OBJECT_KIND_REGEXP) {
|
|
549
|
+
xt_value regexpValue = xt_regexp_get_property(target, key);
|
|
550
|
+
if (regexpValue != XT_UNDEFINED) return regexpValue;
|
|
551
|
+
}
|
|
552
|
+
if (obj->header.kind == XT_OBJECT_KIND_ERROR && XT_IS_STRING(key)) {
|
|
553
|
+
xt_string *k = xt_as_string(key);
|
|
554
|
+
if (k->length == 5 && memcmp(k->data, "stack", 5) == 0) {
|
|
555
|
+
xt_value own = xt_object_get(target, key);
|
|
556
|
+
return own != XT_UNDEFINED ? own : xt_error_to_string(target);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
{
|
|
560
|
+
xt_value value = xt_object_get(target, key);
|
|
561
|
+
if (value != XT_UNDEFINED) return value;
|
|
562
|
+
return xt_method_value(target, key);
|
|
563
|
+
}
|
|
392
564
|
}
|
|
393
565
|
if (XT_IS_STRING(target)) {
|
|
394
|
-
/* Property access on a string: `.length
|
|
566
|
+
/* Property access on a string: `.length`, numeric indexing and built-in
|
|
567
|
+
* methods. */
|
|
395
568
|
xt_string *s = xt_as_string(target);
|
|
396
569
|
if (XT_IS_STRING(key)) {
|
|
397
570
|
xt_string *k = xt_as_string(key);
|
|
398
571
|
if (k->length == 6 && memcmp(k->data, "length", 6) == 0) return xt_number((double)s->length);
|
|
572
|
+
uint32_t index = 0;
|
|
573
|
+
if (xt_key_is_array_index(k, &index) && index < s->length) {
|
|
574
|
+
return xt_string_new(s->data + index, 1);
|
|
575
|
+
}
|
|
576
|
+
return xt_method_value(target, key);
|
|
577
|
+
}
|
|
578
|
+
if (XT_IS_NUMBER(key)) {
|
|
579
|
+
int32_t index = xt_to_int32(key);
|
|
580
|
+
if (index >= 0 && (uint32_t)index < s->length) return xt_string_new(s->data + index, 1);
|
|
399
581
|
}
|
|
400
|
-
int32_t index = xt_to_int32(key);
|
|
401
|
-
if (index >= 0 && (uint32_t)index < s->length) return xt_string_new(s->data + index, 1);
|
|
402
582
|
return XT_UNDEFINED;
|
|
403
583
|
}
|
|
584
|
+
if (XT_IS_STRING(key) &&
|
|
585
|
+
(XT_IS_NUMBER(target) || XT_IS_BIGINT(target) || target == XT_TRUE || target == XT_FALSE)) {
|
|
586
|
+
return xt_method_value(target, key);
|
|
587
|
+
}
|
|
404
588
|
return XT_UNDEFINED;
|
|
405
589
|
}
|
|
406
590
|
|
|
@@ -418,6 +602,14 @@ xt_value xt_set(xt_value target, xt_value key, xt_value value) {
|
|
|
418
602
|
array->length = (uint32_t)n;
|
|
419
603
|
return value;
|
|
420
604
|
}
|
|
605
|
+
uint32_t ignored = 0;
|
|
606
|
+
if (!xt_key_is_array_index(k, &ignored)) {
|
|
607
|
+
return xt_object_set(xt_array_extra((xt_array *)XT_GET_PTR(target), 1), key, value);
|
|
608
|
+
}
|
|
609
|
+
return xt_array_set(target, key, value);
|
|
610
|
+
}
|
|
611
|
+
if (xt_is_symbol(key)) {
|
|
612
|
+
return xt_object_set(xt_array_extra((xt_array *)XT_GET_PTR(target), 1), key, value);
|
|
421
613
|
}
|
|
422
614
|
return xt_array_set(target, key, value);
|
|
423
615
|
}
|