xbintsc 0.3.81 → 0.3.87
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -2
- package/README.zh-CN.md +12 -4
- 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/invocation.js +5 -1
- 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 +5 -2
- 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 -0
- package/dist/src/codegen/generator/module.js +76 -6
- 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 +5 -0
- package/dist/src/codegen/generator/state.js.map +1 -1
- package/dist/src/codegen/generator/statements/control-flow.js +1 -1
- 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 +7 -0
- package/dist/src/codegen/generator/tables.js +25 -0
- 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 +22 -3
- package/dist/src/driver/compiler.js +43 -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/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/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/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/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 +7 -1
- package/doc/ai/language-support.md +4 -4
- package/doc/ai/troubleshooting.md +39 -2
- package/doc/ai/zh-CN/cli.md +2 -0
- package/doc/ai/zh-CN/contributing.md +6 -2
- package/doc/ai/zh-CN/language-support.md +4 -4
- package/doc/ai/zh-CN/troubleshooting.md +35 -2
- package/doc/implemented.md +3 -3
- package/doc/requirements.md +13 -4
- package/doc/unimplemented.md +11 -10
- package/doc/zh-CN/DESIGN.md +15 -9
- package/doc/zh-CN/implemented.md +3 -3
- package/doc/zh-CN/requirements.md +12 -3
- package/doc/zh-CN/unimplemented.md +10 -9
- 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 +17 -1
- package/runtime/rt_internal.h +47 -4
- package/runtime/xt_alloc.c +14 -2
- package/runtime/xt_bigint/conversions.inc +1 -1
- package/runtime/xt_builtins.c +136 -14
- package/runtime/xt_containers/access.inc +17 -16
- package/runtime/xt_containers/objects.inc +4 -3
- package/runtime/xt_io/exception.inc +171 -2
- package/runtime/xt_io/inspect.inc +15 -13
- package/runtime/xt_io.c +5 -0
- package/runtime/xt_stdlib/arrays.inc +2 -2
- package/runtime/xt_stdlib/dispatch.inc +2 -2
- package/runtime/xt_stdlib/strings.inc +33 -21
- package/runtime/xt_stdlib2/array-statics.inc +2 -1
- package/runtime/xt_stdlib2/arrays.inc +1 -1
- package/runtime/xt_stdlib2/error.inc +22 -6
- package/runtime/xt_stdlib2/json.inc +3 -2
- package/runtime/xt_stdlib2/number-string-statics.inc +3 -1
- package/runtime/xt_stdlib2/regexp-match.inc +48 -33
- package/runtime/xt_stdlib2/strings.inc +23 -14
- package/runtime/xt_symbol.c +1 -1
- package/runtime/xt_values/comparison.inc +2 -2
- package/runtime/xt_values/constructors.inc +10 -5
- package/runtime/xt_values/conversions.inc +4 -3
- package/runtime/xt_values/strings.inc +78 -4
- 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/invocation.ts +5 -0
- 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 +5 -2
- package/src/codegen/generator/generator.ts +18 -2
- package/src/codegen/generator/module.ts +78 -6
- package/src/codegen/generator/numeric.ts +158 -0
- package/src/codegen/generator/state.ts +22 -0
- package/src/codegen/generator/statements/control-flow.ts +1 -1
- package/src/codegen/generator/statements/dispatch.ts +3 -0
- package/src/codegen/generator/statements/variables.ts +8 -2
- package/src/codegen/generator/tables.ts +26 -0
- package/src/driver/bundler/merge.ts +39 -5
- package/src/driver/compiler.ts +62 -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_builtins.c
CHANGED
|
@@ -62,9 +62,77 @@ xt_value xt_object_spread(xt_value target, xt_value source) {
|
|
|
62
62
|
return xt_object_assign(2, args);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
/* True when `key` is one of the explicitly destructured properties. */
|
|
66
|
+
static int xt_object_rest_excludes(xt_value key, int32_t argc, xt_value *argv) {
|
|
67
|
+
for (int32_t i = 0; i < argc; i++) {
|
|
68
|
+
if (xt_property_key_equals(key, argv[i])) return 1;
|
|
69
|
+
}
|
|
70
|
+
return 0;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
static void xt_object_rest_copy(xt_value target, xt_value key, xt_value value, int32_t argc, xt_value *argv) {
|
|
74
|
+
if (xt_object_rest_excludes(key, argc, argv)) return;
|
|
75
|
+
xt_object_set(target, key, value);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/*
|
|
79
|
+
* `const { a, ...rest } = source` (and the assignment / parameter forms).
|
|
80
|
+
*
|
|
81
|
+
* Copies the *own enumerable* properties that the pattern did not name, in
|
|
82
|
+
* insertion order. Arrays and strings contribute their index properties, like
|
|
83
|
+
* ECMAScript; accessor properties are read through their getter, with the
|
|
84
|
+
* source as the receiver.
|
|
85
|
+
*/
|
|
86
|
+
xt_value xt_object_rest(xt_value source, int32_t argc, xt_value *argv) {
|
|
87
|
+
if (source == XT_UNDEFINED || source == XT_NULL) {
|
|
88
|
+
xt_throw(xt_string_from_cstr("TypeError: Cannot destructure null or undefined"));
|
|
89
|
+
return XT_UNDEFINED;
|
|
90
|
+
}
|
|
91
|
+
xt_value result = xt_object_new();
|
|
92
|
+
|
|
93
|
+
if (XT_IS_ARRAY(source)) {
|
|
94
|
+
xt_array *array = (xt_array *)XT_GET_PTR(source);
|
|
95
|
+
for (uint32_t i = 0; i < array->length; i++) {
|
|
96
|
+
char buffer[16];
|
|
97
|
+
snprintf(buffer, sizeof(buffer), "%u", i);
|
|
98
|
+
xt_object_rest_copy(result, xt_string_from_cstr(buffer), array->items[i], argc, argv);
|
|
99
|
+
}
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (XT_IS_STRING(source)) {
|
|
104
|
+
xt_string *string = (xt_string *)XT_GET_PTR(source);
|
|
105
|
+
const char *bytes = xt_string_bytes(string);
|
|
106
|
+
for (uint32_t i = 0; i < string->length; i++) {
|
|
107
|
+
char buffer[16];
|
|
108
|
+
snprintf(buffer, sizeof(buffer), "%u", i);
|
|
109
|
+
xt_object_rest_copy(result, xt_string_from_cstr(buffer), xt_string_new(bytes + i, 1), argc, argv);
|
|
110
|
+
}
|
|
111
|
+
return result;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (!XT_IS_OBJECT(source) && !XT_IS_FUNCTION(source)) return result;
|
|
115
|
+
xt_object *object = xt_as_object(source);
|
|
116
|
+
if (!object) return result;
|
|
117
|
+
if (object->header.kind != XT_OBJECT_KIND_OBJECT && object->header.kind != XT_OBJECT_KIND_ERROR) {
|
|
118
|
+
/* Map/Set/Date/RegExp/Promise have no own enumerable string properties. */
|
|
119
|
+
return result;
|
|
120
|
+
}
|
|
121
|
+
for (uint32_t i = 0; i < object->count; i++) {
|
|
122
|
+
xt_property *property = &object->properties[i];
|
|
123
|
+
if (xt_object_rest_excludes(property->key, argc, argv)) continue;
|
|
124
|
+
xt_value value = property->value;
|
|
125
|
+
if (property->getter != XT_UNDEFINED && XT_IS_FUNCTION(property->getter)) {
|
|
126
|
+
value = xt_call_with_this(property->getter, source, 0, NULL);
|
|
127
|
+
}
|
|
128
|
+
xt_object_set(result, property->key, value);
|
|
129
|
+
}
|
|
130
|
+
return result;
|
|
131
|
+
}
|
|
132
|
+
|
|
65
133
|
xt_value xt_parse_int(int32_t argc, xt_value *argv) {
|
|
66
134
|
xt_string *s = xt_as_string(xt_to_string(xt_arg_at(argc, argv, 0)));
|
|
67
|
-
const char *p = s
|
|
135
|
+
const char *p = xt_string_bytes(s);
|
|
68
136
|
/* JavaScript `parseInt` semantics: skip leading whitespace, accept an
|
|
69
137
|
optional sign, pick the radix (auto-detecting a `0x` prefix), read the
|
|
70
138
|
longest run of valid digits and accumulate into a double. Accumulating as
|
|
@@ -107,9 +175,10 @@ xt_value xt_parse_int(int32_t argc, xt_value *argv) {
|
|
|
107
175
|
|
|
108
176
|
xt_value xt_parse_float(int32_t argc, xt_value *argv) {
|
|
109
177
|
xt_string *s = xt_as_string(xt_to_string(xt_arg_at(argc, argv, 0)));
|
|
178
|
+
const char *bytes = xt_string_bytes(s);
|
|
110
179
|
char *end = NULL;
|
|
111
|
-
double value = strtod(
|
|
112
|
-
if (end ==
|
|
180
|
+
double value = strtod(bytes, &end);
|
|
181
|
+
if (end == bytes) return xt_number(NAN);
|
|
113
182
|
return xt_number(value);
|
|
114
183
|
}
|
|
115
184
|
|
|
@@ -140,6 +209,7 @@ static int xt_uri_is_kept(const char *allowed, unsigned char c) {
|
|
|
140
209
|
|
|
141
210
|
static xt_value xt_uri_encode(xt_value input, const char *allowed) {
|
|
142
211
|
xt_string *s = xt_as_string(xt_to_string(input));
|
|
212
|
+
const char *bytes = xt_string_bytes(s);
|
|
143
213
|
static const char hex[] = "0123456789ABCDEF";
|
|
144
214
|
char *out = (char *)malloc((size_t)s->length * 3 + 1);
|
|
145
215
|
if (!out) {
|
|
@@ -148,7 +218,7 @@ static xt_value xt_uri_encode(xt_value input, const char *allowed) {
|
|
|
148
218
|
}
|
|
149
219
|
size_t written = 0;
|
|
150
220
|
for (uint32_t i = 0; i < s->length; i++) {
|
|
151
|
-
unsigned char c = (unsigned char)
|
|
221
|
+
unsigned char c = (unsigned char)bytes[i];
|
|
152
222
|
if (xt_uri_is_kept(allowed, c)) {
|
|
153
223
|
out[written++] = (char)c;
|
|
154
224
|
} else {
|
|
@@ -173,6 +243,7 @@ static int xt_hex_value(int c) {
|
|
|
173
243
|
* escapes for reserved characters are copied through unchanged. */
|
|
174
244
|
static xt_value xt_uri_decode(xt_value input, int component) {
|
|
175
245
|
xt_string *s = xt_as_string(xt_to_string(input));
|
|
246
|
+
const char *bytes = xt_string_bytes(s);
|
|
176
247
|
char *out = (char *)malloc((size_t)s->length + 1);
|
|
177
248
|
if (!out) {
|
|
178
249
|
fprintf(stderr, "xbintsc: out of memory\n");
|
|
@@ -181,22 +252,22 @@ static xt_value xt_uri_decode(xt_value input, int component) {
|
|
|
181
252
|
size_t written = 0;
|
|
182
253
|
uint32_t i = 0;
|
|
183
254
|
while (i < s->length) {
|
|
184
|
-
unsigned char c = (unsigned char)
|
|
255
|
+
unsigned char c = (unsigned char)bytes[i];
|
|
185
256
|
if (c != '%') {
|
|
186
257
|
out[written++] = (char)c;
|
|
187
258
|
i++;
|
|
188
259
|
continue;
|
|
189
260
|
}
|
|
190
261
|
if (i + 2 >= s->length) xt_throw_uri_error("URI malformed");
|
|
191
|
-
int h1 = xt_hex_value((unsigned char)
|
|
192
|
-
int h2 = xt_hex_value((unsigned char)
|
|
262
|
+
int h1 = xt_hex_value((unsigned char)bytes[i + 1]);
|
|
263
|
+
int h2 = xt_hex_value((unsigned char)bytes[i + 2]);
|
|
193
264
|
if (h1 < 0 || h2 < 0) xt_throw_uri_error("URI malformed");
|
|
194
265
|
unsigned char b = (unsigned char)((h1 << 4) | h2);
|
|
195
266
|
if (b < 0x80) {
|
|
196
267
|
if (!component && xt_uri_is_kept(";/?:@&=+$,#", b)) {
|
|
197
|
-
out[written++] =
|
|
198
|
-
out[written++] =
|
|
199
|
-
out[written++] =
|
|
268
|
+
out[written++] = bytes[i];
|
|
269
|
+
out[written++] = bytes[i + 1];
|
|
270
|
+
out[written++] = bytes[i + 2];
|
|
200
271
|
} else {
|
|
201
272
|
out[written++] = (char)b;
|
|
202
273
|
}
|
|
@@ -213,9 +284,9 @@ static xt_value xt_uri_decode(xt_value input, int component) {
|
|
|
213
284
|
for (int k = 1; k <= extra; k++) {
|
|
214
285
|
uint32_t pos = i + (uint32_t)k * 3;
|
|
215
286
|
if (pos + 2 >= s->length) xt_throw_uri_error("URI malformed");
|
|
216
|
-
if (
|
|
217
|
-
int c1 = xt_hex_value((unsigned char)
|
|
218
|
-
int c2 = xt_hex_value((unsigned char)
|
|
287
|
+
if (bytes[pos] != '%') xt_throw_uri_error("URI malformed");
|
|
288
|
+
int c1 = xt_hex_value((unsigned char)bytes[pos + 1]);
|
|
289
|
+
int c2 = xt_hex_value((unsigned char)bytes[pos + 2]);
|
|
219
290
|
if (c1 < 0 || c2 < 0) xt_throw_uri_error("URI malformed");
|
|
220
291
|
unsigned char cb = (unsigned char)((c1 << 4) | c2);
|
|
221
292
|
if ((cb & 0xC0) != 0x80) xt_throw_uri_error("URI malformed");
|
|
@@ -322,7 +393,7 @@ xt_value xt_in(xt_value key, xt_value value) {
|
|
|
322
393
|
if (xt_is_symbol(key)) return XT_FALSE;
|
|
323
394
|
xt_string *s = xt_as_string(value);
|
|
324
395
|
xt_string *k = xt_as_string(xt_to_string(key));
|
|
325
|
-
if (k->length == 6 && memcmp(k
|
|
396
|
+
if (k->length == 6 && memcmp(xt_string_bytes(k), "length", 6) == 0) return XT_TRUE;
|
|
326
397
|
int32_t index = xt_to_int32(key);
|
|
327
398
|
return xt_bool(index >= 0 && (uint32_t)index < s->length);
|
|
328
399
|
}
|
|
@@ -446,6 +517,57 @@ xt_value xt_instance_of(xt_value value, xt_value ctor) {
|
|
|
446
517
|
return XT_FALSE;
|
|
447
518
|
}
|
|
448
519
|
|
|
520
|
+
/*
|
|
521
|
+
* `value instanceof Array` (and the rest of the built-in constructors).
|
|
522
|
+
*
|
|
523
|
+
* Built-in constructors are not first-class values: the codegen recognises the
|
|
524
|
+
* name and asks this helper instead. Ids match `BUILTIN_INSTANCEOF` in
|
|
525
|
+
* `src/codegen/generator/tables.ts`; 16..24 map onto the runtime's `XT_TA_*`
|
|
526
|
+
* order (Uint8Array first).
|
|
527
|
+
*/
|
|
528
|
+
enum {
|
|
529
|
+
XT_INSTANCE_ARRAY = 0,
|
|
530
|
+
XT_INSTANCE_OBJECT = 1,
|
|
531
|
+
XT_INSTANCE_FUNCTION = 2,
|
|
532
|
+
XT_INSTANCE_MAP = 3,
|
|
533
|
+
XT_INSTANCE_SET = 4,
|
|
534
|
+
XT_INSTANCE_DATE = 5,
|
|
535
|
+
XT_INSTANCE_REGEXP = 6,
|
|
536
|
+
XT_INSTANCE_PROMISE = 7,
|
|
537
|
+
XT_INSTANCE_TYPED_ARRAY_FIRST = 16,
|
|
538
|
+
XT_INSTANCE_TYPED_ARRAY_LAST = 24
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
static int xt_value_has_kind(xt_value value, uint8_t kind) {
|
|
542
|
+
return XT_IS_OBJECT(value) && ((xt_object *)XT_GET_PTR(value))->header.kind == kind;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
xt_value xt_instance_of_builtin(xt_value value, int32_t id) {
|
|
546
|
+
switch (id) {
|
|
547
|
+
case XT_INSTANCE_ARRAY:
|
|
548
|
+
return xt_bool(XT_IS_ARRAY(value));
|
|
549
|
+
case XT_INSTANCE_OBJECT:
|
|
550
|
+
return xt_bool(XT_IS_OBJECT(value) || XT_IS_ARRAY(value) || XT_IS_FUNCTION(value));
|
|
551
|
+
case XT_INSTANCE_FUNCTION:
|
|
552
|
+
return xt_bool(XT_IS_FUNCTION(value));
|
|
553
|
+
case XT_INSTANCE_MAP:
|
|
554
|
+
return xt_bool(xt_value_has_kind(value, XT_OBJECT_KIND_MAP));
|
|
555
|
+
case XT_INSTANCE_SET:
|
|
556
|
+
return xt_bool(xt_value_has_kind(value, XT_OBJECT_KIND_SET));
|
|
557
|
+
case XT_INSTANCE_DATE:
|
|
558
|
+
return xt_bool(xt_value_has_kind(value, XT_OBJECT_KIND_DATE));
|
|
559
|
+
case XT_INSTANCE_REGEXP:
|
|
560
|
+
return xt_bool(xt_value_has_kind(value, XT_OBJECT_KIND_REGEXP));
|
|
561
|
+
case XT_INSTANCE_PROMISE:
|
|
562
|
+
return xt_bool(xt_value_has_kind(value, XT_OBJECT_KIND_PROMISE));
|
|
563
|
+
default:
|
|
564
|
+
if (id >= XT_INSTANCE_TYPED_ARRAY_FIRST && id <= XT_INSTANCE_TYPED_ARRAY_LAST) {
|
|
565
|
+
return xt_bool(xt_typed_array_kind_of(value) == id - XT_INSTANCE_TYPED_ARRAY_FIRST);
|
|
566
|
+
}
|
|
567
|
+
return XT_FALSE;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
449
571
|
xt_value xt_closure_env(xt_value value, int32_t index) {
|
|
450
572
|
if (!XT_IS_FUNCTION(value)) return XT_UNDEFINED;
|
|
451
573
|
xt_function *function = (xt_function *)XT_GET_PTR(value);
|
|
@@ -13,7 +13,7 @@ static void xt_throw_read_of_nullish(xt_value target, xt_value key) {
|
|
|
13
13
|
char keybuf[128];
|
|
14
14
|
xt_string *k = XT_IS_STRING(key) ? xt_as_string(key) : xt_as_string(xt_to_string(key));
|
|
15
15
|
uint32_t n = k->length < sizeof(keybuf) - 1 ? k->length : (uint32_t)(sizeof(keybuf) - 1);
|
|
16
|
-
memcpy(keybuf, k
|
|
16
|
+
memcpy(keybuf, xt_string_bytes(k), n);
|
|
17
17
|
keybuf[n] = 0;
|
|
18
18
|
char message[256];
|
|
19
19
|
snprintf(message, sizeof(message),
|
|
@@ -27,7 +27,7 @@ xt_value xt_get(xt_value target, xt_value key) {
|
|
|
27
27
|
/* `arr.length` must read the array's length, never an element. */
|
|
28
28
|
if (XT_IS_STRING(key)) {
|
|
29
29
|
xt_string *k = xt_as_string(key);
|
|
30
|
-
if (k->length == 6 && memcmp(k
|
|
30
|
+
if (k->length == 6 && memcmp(xt_string_bytes(k), "length", 6) == 0)
|
|
31
31
|
return xt_number((double)((xt_array *)XT_GET_PTR(target))->length);
|
|
32
32
|
/* Named (non-index) properties such as `raw`/`index`/`input`. */
|
|
33
33
|
uint32_t ignored = 0;
|
|
@@ -57,11 +57,11 @@ xt_value xt_get(xt_value target, xt_value key) {
|
|
|
57
57
|
/* Functions expose own `name` / `length` (arity) properties. */
|
|
58
58
|
if (XT_IS_STRING(key)) {
|
|
59
59
|
xt_string *k = xt_as_string(key);
|
|
60
|
-
if (k->length == 6 && memcmp(k
|
|
60
|
+
if (k->length == 6 && memcmp(xt_string_bytes(k), "length", 6) == 0) {
|
|
61
61
|
int32_t arity = ((xt_function *)XT_GET_PTR(target))->arity;
|
|
62
62
|
return xt_number(arity >= 0 ? (double)arity : 0.0);
|
|
63
63
|
}
|
|
64
|
-
if (k->length == 4 && memcmp(k
|
|
64
|
+
if (k->length == 4 && memcmp(xt_string_bytes(k), "name", 4) == 0) {
|
|
65
65
|
xt_string *name = ((xt_function *)XT_GET_PTR(target))->name;
|
|
66
66
|
return name ? XT_FROM_PTR(XT_TAG_STRING, name) : xt_string_from_cstr("");
|
|
67
67
|
}
|
|
@@ -79,7 +79,7 @@ xt_value xt_get(xt_value target, xt_value key) {
|
|
|
79
79
|
if (obj->header.kind == XT_OBJECT_KIND_SYMBOL) {
|
|
80
80
|
if (XT_IS_STRING(key)) {
|
|
81
81
|
xt_string *k = xt_as_string(key);
|
|
82
|
-
if (k->length == 11 && memcmp(k
|
|
82
|
+
if (k->length == 11 && memcmp(xt_string_bytes(k), "description", 11) == 0) {
|
|
83
83
|
return xt_symbol_description(target);
|
|
84
84
|
}
|
|
85
85
|
}
|
|
@@ -88,7 +88,7 @@ xt_value xt_get(xt_value target, xt_value key) {
|
|
|
88
88
|
if (obj->header.kind == XT_OBJECT_KIND_MAP || obj->header.kind == XT_OBJECT_KIND_SET) {
|
|
89
89
|
if (XT_IS_STRING(key)) {
|
|
90
90
|
xt_string *k = xt_as_string(key);
|
|
91
|
-
if (k->length == 4 && memcmp(k
|
|
91
|
+
if (k->length == 4 && memcmp(xt_string_bytes(k), "size", 4) == 0) {
|
|
92
92
|
int32_t size = obj->header.kind == XT_OBJECT_KIND_MAP ? xt_map_size(target) : xt_set_size(target);
|
|
93
93
|
return xt_number((double)size);
|
|
94
94
|
}
|
|
@@ -100,9 +100,10 @@ xt_value xt_get(xt_value target, xt_value key) {
|
|
|
100
100
|
}
|
|
101
101
|
if (obj->header.kind == XT_OBJECT_KIND_ERROR && XT_IS_STRING(key)) {
|
|
102
102
|
xt_string *k = xt_as_string(key);
|
|
103
|
-
if (k->length == 5 && memcmp(k
|
|
104
|
-
xt_value
|
|
105
|
-
|
|
103
|
+
if (k->length == 5 && memcmp(xt_string_bytes(k), "stack", 5) == 0) {
|
|
104
|
+
xt_value captured = xt_error_stack(target);
|
|
105
|
+
if (captured != XT_UNDEFINED) return captured;
|
|
106
|
+
return xt_error_to_string(target);
|
|
106
107
|
}
|
|
107
108
|
}
|
|
108
109
|
{
|
|
@@ -117,16 +118,16 @@ xt_value xt_get(xt_value target, xt_value key) {
|
|
|
117
118
|
xt_string *s = xt_as_string(target);
|
|
118
119
|
if (XT_IS_STRING(key)) {
|
|
119
120
|
xt_string *k = xt_as_string(key);
|
|
120
|
-
if (k->length == 6 && memcmp(k
|
|
121
|
+
if (k->length == 6 && memcmp(xt_string_bytes(k), "length", 6) == 0) return xt_number((double)s->length);
|
|
121
122
|
uint32_t index = 0;
|
|
122
123
|
if (xt_key_is_array_index(k, &index) && index < s->length) {
|
|
123
|
-
return xt_string_new(s
|
|
124
|
+
return xt_string_new(xt_string_bytes(s) + index, 1);
|
|
124
125
|
}
|
|
125
126
|
return xt_method_value(target, key);
|
|
126
127
|
}
|
|
127
128
|
if (XT_IS_NUMBER(key)) {
|
|
128
129
|
int32_t index = xt_to_int32(key);
|
|
129
|
-
if (index >= 0 && (uint32_t)index < s->length) return xt_string_new(s
|
|
130
|
+
if (index >= 0 && (uint32_t)index < s->length) return xt_string_new(xt_string_bytes(s) + index, 1);
|
|
130
131
|
}
|
|
131
132
|
return XT_UNDEFINED;
|
|
132
133
|
}
|
|
@@ -142,7 +143,7 @@ xt_value xt_set(xt_value target, xt_value key, xt_value value) {
|
|
|
142
143
|
/* Assigning `arr.length` truncates or extends the array. */
|
|
143
144
|
if (XT_IS_STRING(key)) {
|
|
144
145
|
xt_string *k = xt_as_string(key);
|
|
145
|
-
if (k->length == 6 && memcmp(k
|
|
146
|
+
if (k->length == 6 && memcmp(xt_string_bytes(k), "length", 6) == 0) {
|
|
146
147
|
xt_array *array = (xt_array *)XT_GET_PTR(target);
|
|
147
148
|
int32_t n = xt_to_int32(value);
|
|
148
149
|
if (n < 0) n = 0;
|
|
@@ -169,9 +170,9 @@ xt_value xt_set(xt_value target, xt_value key, xt_value value) {
|
|
|
169
170
|
if (typed >= 0) {
|
|
170
171
|
if (XT_IS_STRING(key)) {
|
|
171
172
|
xt_string *k = xt_as_string(key);
|
|
172
|
-
if ((k->length == 6 && memcmp(k
|
|
173
|
-
(k->length == 10 && memcmp(k
|
|
174
|
-
(k->length == 10 && memcmp(k
|
|
173
|
+
if ((k->length == 6 && memcmp(xt_string_bytes(k), "length", 6) == 0) ||
|
|
174
|
+
(k->length == 10 && memcmp(xt_string_bytes(k), "byteLength", 10) == 0) ||
|
|
175
|
+
(k->length == 10 && memcmp(xt_string_bytes(k), "byteOffset", 10) == 0)) {
|
|
175
176
|
xt_throw(xt_string_from_cstr("TypeError: Cannot assign to read only property"));
|
|
176
177
|
return value;
|
|
177
178
|
}
|
|
@@ -54,10 +54,11 @@ static xt_property *xt_object_find(xt_object *obj, xt_value key) {
|
|
|
54
54
|
*/
|
|
55
55
|
static int xt_key_is_array_index(xt_string *key, uint32_t *out) {
|
|
56
56
|
if (key->length == 0 || key->length > 10) return 0;
|
|
57
|
-
|
|
57
|
+
const char *bytes = xt_string_bytes(key);
|
|
58
|
+
if (bytes[0] == '0' && key->length != 1) return 0; /* "0" is canonical, "01" is not */
|
|
58
59
|
uint64_t value = 0;
|
|
59
60
|
for (uint32_t i = 0; i < key->length; i++) {
|
|
60
|
-
char ch =
|
|
61
|
+
char ch = bytes[i];
|
|
61
62
|
if (ch < '0' || ch > '9') return 0;
|
|
62
63
|
value = value * 10 + (uint64_t)(ch - '0');
|
|
63
64
|
}
|
|
@@ -117,7 +118,7 @@ int xt_property_key_equals(xt_value a, xt_value b) {
|
|
|
117
118
|
static int xt_key_is_prototype(xt_value key) {
|
|
118
119
|
if (!XT_IS_STRING(key)) return 0;
|
|
119
120
|
xt_string *k = xt_as_string(key);
|
|
120
|
-
return k && k->length == 9 && memcmp(k
|
|
121
|
+
return k && k->length == 9 && memcmp(xt_string_bytes(k), "prototype", 9) == 0;
|
|
121
122
|
}
|
|
122
123
|
|
|
123
124
|
xt_value xt_object_get(xt_value value, xt_value key) {
|
|
@@ -14,6 +14,162 @@ static xt_try_frame *g_try_top = NULL;
|
|
|
14
14
|
int32_t xt_program_argc = 0;
|
|
15
15
|
char **xt_program_argv = NULL;
|
|
16
16
|
|
|
17
|
+
/* ------------------------------------------------------------------------- */
|
|
18
|
+
/* Shadow call stack */
|
|
19
|
+
/* ------------------------------------------------------------------------- */
|
|
20
|
+
|
|
21
|
+
/*
|
|
22
|
+
* Compiled functions push one frame on entry and pop it before returning, so
|
|
23
|
+
* `Error` can capture a JavaScript-level stack without any debug metadata.
|
|
24
|
+
* Generator bodies are not tracked (they switch stacks and can be suspended
|
|
25
|
+
* indefinitely); a normal function called from a generator is tracked normally.
|
|
26
|
+
*
|
|
27
|
+
* `name`/`file` point into the module's static string constants; the runtime
|
|
28
|
+
* never frees them. The depth is trimmed when a `try` frame is left, so frames
|
|
29
|
+
* from calls unwound by a throw cannot leak into a later stack.
|
|
30
|
+
*/
|
|
31
|
+
#define XT_STACK_MAX_DEPTH 256
|
|
32
|
+
|
|
33
|
+
typedef struct {
|
|
34
|
+
const char *name;
|
|
35
|
+
const char *file;
|
|
36
|
+
int32_t line;
|
|
37
|
+
} xt_stack_entry;
|
|
38
|
+
|
|
39
|
+
static xt_stack_entry g_stack[XT_STACK_MAX_DEPTH];
|
|
40
|
+
static int32_t g_stack_depth = 0;
|
|
41
|
+
|
|
42
|
+
void xt_stack_push(const char *name, const char *file, int32_t line) {
|
|
43
|
+
if (g_stack_depth >= XT_STACK_MAX_DEPTH) return;
|
|
44
|
+
xt_stack_entry *entry = &g_stack[g_stack_depth++];
|
|
45
|
+
entry->name = name;
|
|
46
|
+
entry->file = file;
|
|
47
|
+
entry->line = line;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
void xt_stack_pop(void) {
|
|
51
|
+
if (g_stack_depth > 0) g_stack_depth--;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
int32_t xt_stack_depth(void) { return g_stack_depth; }
|
|
55
|
+
|
|
56
|
+
void xt_stack_set_line(int32_t line) {
|
|
57
|
+
/* Called just before an `Error` is constructed, so the innermost frame
|
|
58
|
+
* reports the construction line rather than the function's declaration. */
|
|
59
|
+
if (g_stack_depth > 0) g_stack[g_stack_depth - 1].line = line;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
void xt_stack_trim(int32_t depth) {
|
|
63
|
+
if (depth < 0) depth = 0;
|
|
64
|
+
if (depth < g_stack_depth) g_stack_depth = depth;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
static void xt_stack_append(char **buffer, size_t *length, size_t *capacity, const char *text) {
|
|
68
|
+
size_t count = strlen(text);
|
|
69
|
+
if (*length + count + 1 > *capacity) {
|
|
70
|
+
size_t next = *capacity ? *capacity : 256;
|
|
71
|
+
while (next < *length + count + 1) next *= 2;
|
|
72
|
+
char *grown = (char *)realloc(*buffer, next);
|
|
73
|
+
if (!grown) return;
|
|
74
|
+
*buffer = grown;
|
|
75
|
+
*capacity = next;
|
|
76
|
+
}
|
|
77
|
+
memcpy(*buffer + *length, text, count);
|
|
78
|
+
*length += count;
|
|
79
|
+
(*buffer)[*length] = 0;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
xt_value xt_stack_capture(const char *name, const char *message) {
|
|
83
|
+
const char *header = name && name[0] ? name : "Error";
|
|
84
|
+
char *buffer = NULL;
|
|
85
|
+
size_t length = 0;
|
|
86
|
+
size_t capacity = 0;
|
|
87
|
+
xt_stack_append(&buffer, &length, &capacity, header);
|
|
88
|
+
if (message && message[0]) {
|
|
89
|
+
xt_stack_append(&buffer, &length, &capacity, ": ");
|
|
90
|
+
xt_stack_append(&buffer, &length, &capacity, message);
|
|
91
|
+
}
|
|
92
|
+
xt_stack_append(&buffer, &length, &capacity, "\n");
|
|
93
|
+
for (int32_t index = g_stack_depth - 1; index >= 0; index--) {
|
|
94
|
+
const xt_stack_entry *entry = &g_stack[index];
|
|
95
|
+
char line[24];
|
|
96
|
+
xt_stack_append(&buffer, &length, &capacity, " at ");
|
|
97
|
+
xt_stack_append(&buffer, &length, &capacity, entry->name && entry->name[0] ? entry->name : "anonymous");
|
|
98
|
+
xt_stack_append(&buffer, &length, &capacity, " (");
|
|
99
|
+
xt_stack_append(&buffer, &length, &capacity, entry->file && entry->file[0] ? entry->file : "<anonymous>");
|
|
100
|
+
if (entry->line > 0) {
|
|
101
|
+
snprintf(line, sizeof(line), ":%d", (int)entry->line);
|
|
102
|
+
xt_stack_append(&buffer, &length, &capacity, line);
|
|
103
|
+
}
|
|
104
|
+
xt_stack_append(&buffer, &length, &capacity, ")\n");
|
|
105
|
+
}
|
|
106
|
+
xt_value result = xt_string_new(buffer ? buffer : "", length);
|
|
107
|
+
free(buffer);
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
xt_value xt_error_stack(xt_value error) {
|
|
112
|
+
if (!XT_IS_OBJECT(error)) return XT_UNDEFINED;
|
|
113
|
+
xt_object *object = (xt_object *)XT_GET_PTR(error);
|
|
114
|
+
if (object->header.kind != XT_OBJECT_KIND_ERROR) return XT_UNDEFINED;
|
|
115
|
+
return object->error_stack;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* ------------------------------------------------------------------------- */
|
|
119
|
+
/* Fatal-signal diagnostics */
|
|
120
|
+
/* ------------------------------------------------------------------------- */
|
|
121
|
+
|
|
122
|
+
static char g_crash_report[16384];
|
|
123
|
+
|
|
124
|
+
static int xt_format_stack(char *out, size_t size) {
|
|
125
|
+
int written = 0;
|
|
126
|
+
for (int32_t index = g_stack_depth - 1; index >= 0 && written < (int)size; index--) {
|
|
127
|
+
const xt_stack_entry *entry = &g_stack[index];
|
|
128
|
+
int count = snprintf(out + written, size - (size_t)written, " at %s (%s:%d)\n",
|
|
129
|
+
entry->name && entry->name[0] ? entry->name : "anonymous",
|
|
130
|
+
entry->file && entry->file[0] ? entry->file : "<anonymous>", (int)entry->line);
|
|
131
|
+
if (count < 0) break;
|
|
132
|
+
written += count;
|
|
133
|
+
}
|
|
134
|
+
return written < (int)size ? written : (int)size - 1;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
#if defined(_WIN32)
|
|
138
|
+
|
|
139
|
+
static LONG WINAPI xt_fatal_exception(EXCEPTION_POINTERS *info) {
|
|
140
|
+
unsigned long code =
|
|
141
|
+
info && info->ExceptionRecord ? (unsigned long)info->ExceptionRecord->ExceptionCode : 0UL;
|
|
142
|
+
int written = snprintf(g_crash_report, sizeof(g_crash_report), "\nxbintsc: fatal exception 0x%08lx\n", code);
|
|
143
|
+
written += xt_format_stack(g_crash_report + written, sizeof(g_crash_report) - (size_t)written);
|
|
144
|
+
_write(2, g_crash_report, (unsigned int)written);
|
|
145
|
+
_exit(1);
|
|
146
|
+
return EXCEPTION_EXECUTE_HANDLER;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
void xt_install_crash_handler(void) { SetUnhandledExceptionFilter(xt_fatal_exception); }
|
|
150
|
+
|
|
151
|
+
#else
|
|
152
|
+
|
|
153
|
+
static void xt_fatal_signal(int sig) {
|
|
154
|
+
int written = snprintf(g_crash_report, sizeof(g_crash_report), "\nxbintsc: fatal signal %d\n", sig);
|
|
155
|
+
written += xt_format_stack(g_crash_report + written, sizeof(g_crash_report) - (size_t)written);
|
|
156
|
+
ssize_t ignored = write(STDERR_FILENO, g_crash_report, (size_t)written);
|
|
157
|
+
(void)ignored;
|
|
158
|
+
_exit(128 + sig);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
void xt_install_crash_handler(void) {
|
|
162
|
+
signal(SIGSEGV, xt_fatal_signal);
|
|
163
|
+
signal(SIGABRT, xt_fatal_signal);
|
|
164
|
+
signal(SIGFPE, xt_fatal_signal);
|
|
165
|
+
signal(SIGBUS, xt_fatal_signal);
|
|
166
|
+
#ifdef SIGILL
|
|
167
|
+
signal(SIGILL, xt_fatal_signal);
|
|
168
|
+
#endif
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
#endif
|
|
172
|
+
|
|
17
173
|
#if defined(_WIN32)
|
|
18
174
|
/*
|
|
19
175
|
* The UCRT validates its low-level I/O arguments and, when one is out of
|
|
@@ -107,18 +263,23 @@ void *xt_try_enter(void) {
|
|
|
107
263
|
if (!frame) abort();
|
|
108
264
|
frame->prev = g_try_top;
|
|
109
265
|
frame->exception = XT_UNDEFINED;
|
|
266
|
+
frame->stack_depth = g_stack_depth;
|
|
110
267
|
g_try_top = frame;
|
|
111
268
|
return (void *)frame;
|
|
112
269
|
}
|
|
113
270
|
|
|
114
271
|
xt_value xt_try_exception(void *framePtr) {
|
|
115
272
|
xt_try_frame *frame = (xt_try_frame *)framePtr;
|
|
116
|
-
|
|
273
|
+
if (!frame) return XT_UNDEFINED;
|
|
274
|
+
/* A throw longjmps past the calls it unwinds; drop their shadow frames. */
|
|
275
|
+
xt_stack_trim(frame->stack_depth);
|
|
276
|
+
return frame->exception;
|
|
117
277
|
}
|
|
118
278
|
|
|
119
279
|
void xt_try_leave(void *framePtr) {
|
|
120
280
|
xt_try_frame *frame = (xt_try_frame *)framePtr;
|
|
121
281
|
if (frame && g_try_top == frame) {
|
|
282
|
+
xt_stack_trim(frame->stack_depth);
|
|
122
283
|
g_try_top = frame->prev;
|
|
123
284
|
free(frame);
|
|
124
285
|
}
|
|
@@ -135,8 +296,16 @@ void xt_throw(xt_value v) {
|
|
|
135
296
|
g_try_top->exception = v;
|
|
136
297
|
longjmp(g_try_top->buf, 1);
|
|
137
298
|
}
|
|
299
|
+
xt_value stack = xt_error_stack(v);
|
|
300
|
+
if (stack != XT_UNDEFINED) {
|
|
301
|
+
xt_string *text = xt_as_string(stack);
|
|
302
|
+
if (text) {
|
|
303
|
+
fprintf(stderr, "Uncaught %s\n", xt_string_bytes(text));
|
|
304
|
+
exit(1);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
138
307
|
xt_value message = xt_to_string(v);
|
|
139
308
|
xt_string *s = xt_as_string(message);
|
|
140
|
-
fprintf(stderr, "Uncaught %s\n", s
|
|
309
|
+
fprintf(stderr, "Uncaught %s\n", xt_string_bytes(s));
|
|
141
310
|
exit(1);
|
|
142
311
|
}
|
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
static void xt_print_value(xt_value v, FILE *out) {
|
|
6
6
|
if (XT_IS_STRING(v)) {
|
|
7
7
|
xt_string *s = xt_as_string(v);
|
|
8
|
-
fwrite(s
|
|
8
|
+
fwrite(xt_string_bytes(s), 1, s->length, out);
|
|
9
9
|
return;
|
|
10
10
|
}
|
|
11
11
|
xt_value text = xt_to_string(v);
|
|
12
12
|
xt_string *s = xt_as_string(text);
|
|
13
|
-
fwrite(s
|
|
13
|
+
fwrite(xt_string_bytes(s), 1, s->length, out);
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
void xt_print(xt_value v) { xt_print_value(v, stdout); }
|
|
@@ -48,10 +48,11 @@ static void xt_inspect_pop(void) {
|
|
|
48
48
|
|
|
49
49
|
static int xt_inspect_identifier_key(xt_string *key) {
|
|
50
50
|
if (key->length == 0) return 0;
|
|
51
|
-
char
|
|
51
|
+
const char *bytes = xt_string_bytes(key);
|
|
52
|
+
char c = bytes[0];
|
|
52
53
|
if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '$')) return 0;
|
|
53
54
|
for (uint32_t i = 1; i < key->length; i++) {
|
|
54
|
-
c =
|
|
55
|
+
c = bytes[i];
|
|
55
56
|
if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c == '$')) return 0;
|
|
56
57
|
}
|
|
57
58
|
return 1;
|
|
@@ -60,16 +61,17 @@ static int xt_inspect_identifier_key(xt_string *key) {
|
|
|
60
61
|
/* Render a string the way `util.inspect` does inside a container: quoted,
|
|
61
62
|
* preferring whichever quote character needs fewer escapes. */
|
|
62
63
|
static void xt_inspect_string(xt_string *s, FILE *out) {
|
|
64
|
+
const char *bytes = xt_string_bytes(s);
|
|
63
65
|
int hasSingle = 0;
|
|
64
66
|
int hasDouble = 0;
|
|
65
67
|
for (uint32_t i = 0; i < s->length; i++) {
|
|
66
|
-
if (
|
|
67
|
-
else if (
|
|
68
|
+
if (bytes[i] == '\'') hasSingle = 1;
|
|
69
|
+
else if (bytes[i] == '"') hasDouble = 1;
|
|
68
70
|
}
|
|
69
71
|
char quote = (hasSingle && !hasDouble) ? '"' : '\'';
|
|
70
72
|
fputc(quote, out);
|
|
71
73
|
for (uint32_t i = 0; i < s->length; i++) {
|
|
72
|
-
unsigned char c = (unsigned char)
|
|
74
|
+
unsigned char c = (unsigned char)bytes[i];
|
|
73
75
|
switch (c) {
|
|
74
76
|
case '\\': fputs("\\\\", out); break;
|
|
75
77
|
case '\n': fputs("\\n", out); break;
|
|
@@ -90,13 +92,13 @@ static void xt_inspect_string(xt_string *s, FILE *out) {
|
|
|
90
92
|
static void xt_inspect(xt_value v, FILE *out, int nested) {
|
|
91
93
|
if (xt_is_symbol(v)) {
|
|
92
94
|
xt_string *text = xt_as_string(xt_symbol_to_string(v));
|
|
93
|
-
fwrite(text
|
|
95
|
+
fwrite(xt_string_bytes(text), 1, text->length, out);
|
|
94
96
|
return;
|
|
95
97
|
}
|
|
96
98
|
if (XT_IS_BIGINT(v)) {
|
|
97
99
|
xt_value text = xt_bigint_to_decimal(v);
|
|
98
100
|
xt_string *s = xt_as_string(text);
|
|
99
|
-
fwrite(s
|
|
101
|
+
fwrite(xt_string_bytes(s), 1, s->length, out);
|
|
100
102
|
fputc('n', out);
|
|
101
103
|
return;
|
|
102
104
|
}
|
|
@@ -130,10 +132,10 @@ static void xt_inspect(xt_value v, FILE *out, int nested) {
|
|
|
130
132
|
xt_value key = extra->properties[i].key;
|
|
131
133
|
if (xt_is_symbol(key)) {
|
|
132
134
|
xt_string *text = xt_as_string(xt_symbol_to_string(key));
|
|
133
|
-
fwrite(text
|
|
135
|
+
fwrite(xt_string_bytes(text), 1, text->length, out);
|
|
134
136
|
} else {
|
|
135
137
|
xt_string *keyString = xt_as_string(key);
|
|
136
|
-
fwrite(keyString
|
|
138
|
+
fwrite(xt_string_bytes(keyString), 1, keyString->length, out);
|
|
137
139
|
}
|
|
138
140
|
fputs(": ", out);
|
|
139
141
|
xt_inspect(extra->properties[i].value, out, 1);
|
|
@@ -170,10 +172,10 @@ static void xt_inspect(xt_value v, FILE *out, int nested) {
|
|
|
170
172
|
written++;
|
|
171
173
|
if (isSymbol) {
|
|
172
174
|
xt_string *text = xt_as_string(xt_symbol_to_string(key));
|
|
173
|
-
fwrite(text
|
|
175
|
+
fwrite(xt_string_bytes(text), 1, text->length, out);
|
|
174
176
|
} else {
|
|
175
177
|
xt_string *keyString = xt_as_string(key);
|
|
176
|
-
if (xt_inspect_identifier_key(keyString)) fwrite(keyString
|
|
178
|
+
if (xt_inspect_identifier_key(keyString)) fwrite(xt_string_bytes(keyString), 1, keyString->length, out);
|
|
177
179
|
else xt_inspect_string(keyString, out);
|
|
178
180
|
}
|
|
179
181
|
fputs(": ", out);
|
package/runtime/xt_io.c
CHANGED
|
@@ -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);
|