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.
Files changed (157) hide show
  1. package/README.md +12 -2
  2. package/README.zh-CN.md +12 -4
  3. package/dist/src/ast/common.d.ts +23 -0
  4. package/dist/src/cli/main.js +68 -24
  5. package/dist/src/cli/main.js.map +1 -1
  6. package/dist/src/codegen/generator/calls/invocation.js +5 -1
  7. package/dist/src/codegen/generator/calls/invocation.js.map +1 -1
  8. package/dist/src/codegen/generator/context.d.ts +50 -0
  9. package/dist/src/codegen/generator/context.js +124 -13
  10. package/dist/src/codegen/generator/context.js.map +1 -1
  11. package/dist/src/codegen/generator/debug.d.ts +33 -0
  12. package/dist/src/codegen/generator/debug.js +97 -0
  13. package/dist/src/codegen/generator/debug.js.map +1 -0
  14. package/dist/src/codegen/generator/expressions/assignment.js +9 -5
  15. package/dist/src/codegen/generator/expressions/assignment.js.map +1 -1
  16. package/dist/src/codegen/generator/expressions/operators.js +35 -9
  17. package/dist/src/codegen/generator/expressions/operators.js.map +1 -1
  18. package/dist/src/codegen/generator/expressions/primary.js +5 -2
  19. package/dist/src/codegen/generator/expressions/primary.js.map +1 -1
  20. package/dist/src/codegen/generator/generator.d.ts +3 -1
  21. package/dist/src/codegen/generator/generator.js +3 -1
  22. package/dist/src/codegen/generator/generator.js.map +1 -1
  23. package/dist/src/codegen/generator/module.d.ts +1 -0
  24. package/dist/src/codegen/generator/module.js +76 -6
  25. package/dist/src/codegen/generator/module.js.map +1 -1
  26. package/dist/src/codegen/generator/numeric.d.ts +26 -0
  27. package/dist/src/codegen/generator/numeric.js +127 -0
  28. package/dist/src/codegen/generator/numeric.js.map +1 -0
  29. package/dist/src/codegen/generator/state.d.ts +17 -0
  30. package/dist/src/codegen/generator/state.js +5 -0
  31. package/dist/src/codegen/generator/state.js.map +1 -1
  32. package/dist/src/codegen/generator/statements/control-flow.js +1 -1
  33. package/dist/src/codegen/generator/statements/control-flow.js.map +1 -1
  34. package/dist/src/codegen/generator/statements/dispatch.js +3 -0
  35. package/dist/src/codegen/generator/statements/dispatch.js.map +1 -1
  36. package/dist/src/codegen/generator/statements/variables.js +8 -2
  37. package/dist/src/codegen/generator/statements/variables.js.map +1 -1
  38. package/dist/src/codegen/generator/tables.d.ts +7 -0
  39. package/dist/src/codegen/generator/tables.js +25 -0
  40. package/dist/src/codegen/generator/tables.js.map +1 -1
  41. package/dist/src/driver/bundler/merge.js +41 -5
  42. package/dist/src/driver/bundler/merge.js.map +1 -1
  43. package/dist/src/driver/compiler.d.ts +22 -3
  44. package/dist/src/driver/compiler.js +43 -57
  45. package/dist/src/driver/compiler.js.map +1 -1
  46. package/dist/src/driver/fingerprint.d.ts +27 -0
  47. package/dist/src/driver/fingerprint.js +88 -0
  48. package/dist/src/driver/fingerprint.js.map +1 -0
  49. package/dist/src/driver/toolchain-provider.d.ts +9 -2
  50. package/dist/src/driver/toolchain-provider.js +15 -15
  51. package/dist/src/driver/toolchain-provider.js.map +1 -1
  52. package/dist/src/driver/toolchain.d.ts +58 -1
  53. package/dist/src/driver/toolchain.js +169 -22
  54. package/dist/src/driver/toolchain.js.map +1 -1
  55. package/dist/src/parser/expressions.js +20 -1
  56. package/dist/src/parser/expressions.js.map +1 -1
  57. package/dist/tests/cli/main.test.js +41 -1
  58. package/dist/tests/cli/main.test.js.map +1 -1
  59. package/dist/tests/codegen/expressions.test.js +14 -3
  60. package/dist/tests/codegen/expressions.test.js.map +1 -1
  61. package/dist/tests/codegen/llvm.test.js +23 -0
  62. package/dist/tests/codegen/llvm.test.js.map +1 -1
  63. package/dist/tests/driver/toolchain-provider.test.js +49 -2
  64. package/dist/tests/driver/toolchain-provider.test.js.map +1 -1
  65. package/dist/tests/driver/toolchain.test.js +62 -3
  66. package/dist/tests/driver/toolchain.test.js.map +1 -1
  67. package/dist/tests/e2e/child-process.test.d.ts +10 -0
  68. package/dist/tests/e2e/child-process.test.js +59 -0
  69. package/dist/tests/e2e/child-process.test.js.map +1 -0
  70. package/dist/tests/e2e/compile-objects-classes.test.js +47 -0
  71. package/dist/tests/e2e/compile-objects-classes.test.js.map +1 -1
  72. package/dist/tests/e2e/error-stack.test.d.ts +9 -0
  73. package/dist/tests/e2e/error-stack.test.js +94 -0
  74. package/dist/tests/e2e/error-stack.test.js.map +1 -0
  75. package/dist/tests/e2e/string-concat.test.d.ts +9 -0
  76. package/dist/tests/e2e/string-concat.test.js +102 -0
  77. package/dist/tests/e2e/string-concat.test.js.map +1 -0
  78. package/dist/tests/helpers.d.ts +9 -0
  79. package/dist/tests/helpers.js +26 -3
  80. package/dist/tests/helpers.js.map +1 -1
  81. package/dist/tests/parser/expressions.test.js +7 -0
  82. package/dist/tests/parser/expressions.test.js.map +1 -1
  83. package/dist/tests/version.test.d.ts +1 -0
  84. package/dist/tests/version.test.js +24 -0
  85. package/dist/tests/version.test.js.map +1 -0
  86. package/doc/DESIGN.md +20 -12
  87. package/doc/ai/cli.md +2 -0
  88. package/doc/ai/contributing.md +7 -1
  89. package/doc/ai/language-support.md +4 -4
  90. package/doc/ai/troubleshooting.md +39 -2
  91. package/doc/ai/zh-CN/cli.md +2 -0
  92. package/doc/ai/zh-CN/contributing.md +6 -2
  93. package/doc/ai/zh-CN/language-support.md +4 -4
  94. package/doc/ai/zh-CN/troubleshooting.md +35 -2
  95. package/doc/implemented.md +3 -3
  96. package/doc/requirements.md +13 -4
  97. package/doc/unimplemented.md +11 -10
  98. package/doc/zh-CN/DESIGN.md +15 -9
  99. package/doc/zh-CN/implemented.md +3 -3
  100. package/doc/zh-CN/requirements.md +12 -3
  101. package/doc/zh-CN/unimplemented.md +10 -9
  102. package/package.json +3 -1
  103. package/runtime/ext_node/buffer/parts/construction.inc +2 -2
  104. package/runtime/ext_node/buffer/parts/methods.inc +1 -1
  105. package/runtime/ext_node/buffer/parts/search.inc +1 -1
  106. package/runtime/ext_node/child_process/child_process.c +25 -0
  107. package/runtime/ext_node/fs/fd_ops.c +1 -1
  108. package/runtime/rt.h +17 -1
  109. package/runtime/rt_internal.h +47 -4
  110. package/runtime/xt_alloc.c +14 -2
  111. package/runtime/xt_bigint/conversions.inc +1 -1
  112. package/runtime/xt_builtins.c +136 -14
  113. package/runtime/xt_containers/access.inc +17 -16
  114. package/runtime/xt_containers/objects.inc +4 -3
  115. package/runtime/xt_io/exception.inc +171 -2
  116. package/runtime/xt_io/inspect.inc +15 -13
  117. package/runtime/xt_io.c +5 -0
  118. package/runtime/xt_stdlib/arrays.inc +2 -2
  119. package/runtime/xt_stdlib/dispatch.inc +2 -2
  120. package/runtime/xt_stdlib/strings.inc +33 -21
  121. package/runtime/xt_stdlib2/array-statics.inc +2 -1
  122. package/runtime/xt_stdlib2/arrays.inc +1 -1
  123. package/runtime/xt_stdlib2/error.inc +22 -6
  124. package/runtime/xt_stdlib2/json.inc +3 -2
  125. package/runtime/xt_stdlib2/number-string-statics.inc +3 -1
  126. package/runtime/xt_stdlib2/regexp-match.inc +48 -33
  127. package/runtime/xt_stdlib2/strings.inc +23 -14
  128. package/runtime/xt_symbol.c +1 -1
  129. package/runtime/xt_values/comparison.inc +2 -2
  130. package/runtime/xt_values/constructors.inc +10 -5
  131. package/runtime/xt_values/conversions.inc +4 -3
  132. package/runtime/xt_values/strings.inc +78 -4
  133. package/scripts/bench.ts +172 -0
  134. package/scripts/build-runtime.ts +2 -1
  135. package/scripts/check-version.ts +53 -0
  136. package/src/ast/common.ts +23 -0
  137. package/src/cli/main.ts +68 -26
  138. package/src/codegen/generator/calls/invocation.ts +5 -0
  139. package/src/codegen/generator/context.ts +129 -18
  140. package/src/codegen/generator/debug.ts +132 -0
  141. package/src/codegen/generator/expressions/assignment.ts +9 -5
  142. package/src/codegen/generator/expressions/operators.ts +47 -8
  143. package/src/codegen/generator/expressions/primary.ts +5 -2
  144. package/src/codegen/generator/generator.ts +18 -2
  145. package/src/codegen/generator/module.ts +78 -6
  146. package/src/codegen/generator/numeric.ts +158 -0
  147. package/src/codegen/generator/state.ts +22 -0
  148. package/src/codegen/generator/statements/control-flow.ts +1 -1
  149. package/src/codegen/generator/statements/dispatch.ts +3 -0
  150. package/src/codegen/generator/statements/variables.ts +8 -2
  151. package/src/codegen/generator/tables.ts +26 -0
  152. package/src/driver/bundler/merge.ts +39 -5
  153. package/src/driver/compiler.ts +62 -55
  154. package/src/driver/fingerprint.ts +85 -0
  155. package/src/driver/toolchain-provider.ts +17 -16
  156. package/src/driver/toolchain.ts +210 -21
  157. package/src/parser/expressions.ts +20 -1
@@ -68,8 +68,9 @@ None — generators are implemented.
68
68
  > Tagged templates are implemented, including raw strings and `String.raw`.
69
69
  > Generators (`function*`, `yield`, `yield*`) are implemented with stackful
70
70
  > coroutines, including `next`/`throw`/`return`, `for...of`, spread and
71
- > delegation. `import.meta` is parsed but has no runtime value; `async`
72
- > generators are not yet supported.
71
+ > delegation. `import.meta` exposes `url`, `dirname` and `filename`, resolved
72
+ > from the running executable's path. `async function*` is **rejected with
73
+ > `UnsupportedFeature`** instead of silently producing a synchronous generator.
73
74
 
74
75
  ### 2.2 Not supported by the parser
75
76
 
@@ -79,7 +80,7 @@ None known at the expression level.
79
80
 
80
81
  | Operator | Status | Notes |
81
82
  | --- | --- | --- |
82
- | `new.target` | ✗ | not implemented |
83
+ | `new.target` | ✗ | parsed, then rejected with `UnsupportedFeature` (no `new.target` semantics) |
83
84
  | `typeof` / `void` / `in` / `delete` / `instanceof` | ✓ | mapped to runtime helpers (`xt_typeof`, `xt_in`, `xt_delete`, `xt_instance_of`) |
84
85
 
85
86
  ---
@@ -132,7 +133,7 @@ element writes, and `fill` / `set` / `slice` / `subarray` / `join` / `toString`
132
133
  | `keys` / `values` / `entries` | ~ return arrays, not iterator objects |
133
134
  | `ArrayBuffer` / `DataView` / `.buffer` / `.byteOffset` semantics | ✗ no backing buffer type; `.byteOffset` is always 0 |
134
135
  | `BigInt64Array` / `BigUint64Array` | ✗ not implemented |
135
- | `instanceof Uint8Array` | ✗ global constructors are not first-class values, so `x instanceof Uint8Array` fails with `Cannot find name 'Uint8Array'` |
136
+ | `instanceof` built-in constructors | ✓ `instanceof Array` / `Object` / `Map` / `Set` / `Date` / `RegExp` / `Promise` / `Error` family / typed arrays are recognised by name; the constructors are still not first-class values (`const A = Array` fails) |
136
137
  | Typed-array views over a `Buffer` / `ArrayBuffer` | ✗ constructors copy from a length / array-like / typed array only |
137
138
 
138
139
  ---
@@ -169,8 +170,8 @@ element writes, and `fill` / `set` / `slice` / `subarray` / `join` / `toString`
169
170
  | `arguments` object | ✓ (implicit) | ✓ implemented (arrow functions get their own parameters, not the outer function's `arguments`, unlike JS) |
170
171
  | `this` binding / method call semantics | ✓ | ✓ implemented (`this` is threaded as the first ABI parameter; arrow functions inherit lexically) |
171
172
  | `async` / `await` / Promise | ✓ | ✓ implemented (synchronous microtask model) |
172
- | `new.target` | ✗ | not implemented |
173
- | Generators / iterators / `yield` | ✓ implemented | `function*`, `yield`, `yield*`, `.next/.throw/.return`; `async` generators not supported |
173
+ | `new.target` | ✗ | parsed, then rejected with `UnsupportedFeature` (no `new.target` semantics) |
174
+ | Generators / iterators / `yield` | ✓ implemented | `function*`, `yield`, `yield*`, `.next/.throw/.return`; `async` generators are rejected with `UnsupportedFeature` |
174
175
  | Closure `arity` | — | `xt_closure_arity` is always -1, never filled in |
175
176
  | `fn.call` / `fn.apply` / `fn.bind` | ✓ | ✓ implemented (the bound closure does not track partial-argument `length`) |
176
177
  | `fn.name` / `fn.length` | ✓ | ✓ implemented (inferred from the declaration / assignment / property key; bound functions use `"bound ..."` and adjusted arity) |
@@ -232,9 +233,9 @@ These features **compile and run**, but the result does not fully match ECMAScri
232
233
  | Function `arity` / argument count | No argument count validation; `fn.length` reports the declared arity but calls are never checked against it |
233
234
  | `async` / `await` | **Synchronous microtask model**: `await` on an already-settled promise continues synchronously. Timers and sockets run on the event loop only after the program body, so a promise settled from a timer callback cannot be awaited |
234
235
  | `super` | `super.x` / `super(...)` takes the prototype of `this`'s prototype; single-level inheritance is correct, but depth > 1 may be inaccurate |
235
- | `Error.stack` | Not captured |
236
+ | `Error.stack` | Function-level frames only: each frame is `at name (file:line)` with the function's declaration line (updated to the construction line for the innermost frame); no column numbers and no frames for generator bodies (they switch stacks) |
236
237
  | Module live bindings | Namespace imports and imported bindings are snapshots (see section 4) |
237
- | `import.meta` | Parsed but has no value |
238
+ | `import.meta` | ✓ `import.meta.url` / `.dirname` / `.filename`, resolved from the executable path |
238
239
 
239
240
  > Number formatting, loose equality, `+` / relational `ToPrimitive`, whole-chain
240
241
  > optional chaining, `finally` on early exit and object-key ordering have all
@@ -263,9 +264,9 @@ These features **compile and run**, but the result does not fully match ECMAScri
263
264
  ```
264
265
  Unimplemented (statements): namespace/module declarations
265
266
 
266
- Unimplemented (expressions): new.target, import.meta value
267
+ Unimplemented (expressions): new.target (clear UnsupportedFeature)
267
268
 
268
- Unimplemented (functions): `fn.toString()` source text, async generators
269
+ Unimplemented (functions): `fn.toString()` source text, async generators (rejected)
269
270
 
270
271
  Unimplemented (classes/OO): abstract/implements, access control,
271
272
  parent/child #x collision
@@ -39,13 +39,18 @@ xt_value fn(xt_value env, int32_t argc, xt_value *argv);
39
39
  ```
40
40
 
41
41
  `env` 通过 box 以引用方式传递捕获变量,直接调用与闭包调用共用一条代码路径。
42
- 不易内联的 JS 语义(`+` 隐式转换、关系比较、属性访问、打印)委托给 `@xt_*` 运行时调用。
42
+ 不易内联的 JS 语义(`+` 隐式转换、属性访问、打印)委托给 `@xt_*` 运行时调用。
43
+ 数值算术与比较是例外:codegen 内联 double 快路径(`numeric.ts`),需要隐式转换时
44
+ 才调用运行时。
43
45
 
44
46
  ### 运行时
45
47
 
46
- `runtime/xt_runtime.c` 实现字符串、对象、数组、闭包、算术、比较、异常与
47
- 类 Node 的 `console.log` 打印。现在 `xt_alloc` 背后使用非移动标记-清扫回收器
48
- (显式根、子系统根提供者与保守 C 栈扫描),其内部实现对编译器保持隔离。
48
+ `runtime/` 实现字符串、对象、数组、闭包、算术、比较、异常与类 Node 的
49
+ `console.log` 打印。它按职责拆分为多个编译单元(`xt_values.c`、
50
+ `xt_containers.c`、`xt_stdlib.c`、`xt_builtins.c`、`xt_io.c`、`xt_alloc.c` 等),
51
+ 每个单元 `#include` 自己的实现片段(`.inc`),这样文件保持小巧而不改变链接范围。
52
+ `xt_alloc` 背后使用非移动标记-清扫回收器(显式根、子系统根提供者与保守 C 栈扫描),
53
+ 其内部实现对编译器保持隔离。
49
54
 
50
55
  ### llc 的替代
51
56
 
@@ -92,13 +97,14 @@ xt_value fn(xt_value env, int32_t argc, xt_value *argv);
92
97
  同样覆盖:Windows 下 xbintsc 使用系统 clang 按 MSVC ABI 链接,示例即针对该 ABI 构建。
93
98
  参见 `examples/extensions/` 与 `runtime/xt_ext.h` / `runtime/xt_ext.rs`。
94
99
 
95
- ## 自举路线
100
+ ## 自举
96
101
 
97
- 编译器本身使用 TypeScript 编写并输出 IR 文本,`runtime` 已与编译器解耦。后续可
98
- 用 TS 重写运行时,并由 xbintsc 编译自身,逐步达成自举。
102
+ 编译器已经能编译自身:`xbintsc build src/cli/main.ts --ext node` 会产出一个可用的
103
+ `xbintsc` 二进制,且从第一代起生成的 IR 就是不动点。CI 会为每个支持的平台构建这些
104
+ 二进制并检查不动点。运行时仍使用 C;以后是否用 TS 重写只是可选项,并不是自举的前提。
99
105
 
100
106
  ## 测试
101
107
 
102
108
  按模块划分:`tests/lexer`、`tests/parser`、`tests/binder`、`tests/codegen`、
103
- `tests/driver`、`tests/extensions`、`tests/cli`、`tests/e2e`。e2e 会在存在
104
- `clang` 时真正编译并运行二进制,否则自动跳过。
109
+ `tests/driver`、`tests/extensions`、`tests/cli`、`tests/e2e`。存在受支持的 clang
110
+ (16+,与 `build` 使用的版本门槛一致)时,e2e 会真正编译并运行二进制,否则自动跳过。
@@ -233,7 +233,7 @@ xt_value fn(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv);
233
233
 
234
234
  - 分配器:`xt_alloc` 背后的非移动标记-清扫回收器(显式根、子系统根提供者与保守 C 栈扫描)。
235
235
  - 值构造:`xt_undefined/xt_null/xt_bool/xt_number/xt_string_new/xt_string_from_cstr`。
236
- - 字符串:UTF-8 存储、拼接、相等比较、格式化数字转字符串。
236
+ - 字符串:UTF-8 存储、惰性 cons 节点拼接(首次观察时才扁平化,因此循环追加保持线性复杂度)、相等比较、格式化数字转字符串。
237
237
  - 类型转换:`xt_truthy`、`xt_to_number`、`xt_to_string`、`xt_typeof`。
238
238
  - 算术:`add/sub/mul/div/mod/pow/neg/pos`。
239
239
  - 位运算:`and/or/xor/not/shl/shr/ushr`(含 `ToInt32` 语义)。
@@ -247,7 +247,7 @@ xt_value fn(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv);
247
247
  - 运算符辅助:`xt_in`(`in`)、`xt_delete`(`delete`)、`xt_rest_args`(剩余参数 / `arguments`)。
248
248
  - Box:`box_new/get/set`(用于闭包捕获变量)。
249
249
  - 函数与闭包:`arg`、`closure_new/call/env/arity`。
250
- - 异常:`xt_try_enter` / `xt_try_exception` / `xt_try_leave` 维护 `_setjmp` 帧栈;`xt_throw` 在存在帧时长跳到最近 `try`,否则打印 `Uncaught ...` 后退出。
250
+ - 异常:`xt_try_enter` / `xt_try_exception` / `xt_try_leave` 维护 `_setjmp` 帧栈;`xt_throw` 在存在帧时长跳到最近 `try`,否则打印 `Uncaught ...` 后退出。codegen 还会维护一条影子调用栈(函数名、源文件、行号):`Error.stack` 用它生成堆栈,原生崩溃时致命信号处理器也会打印同一组帧。`--debug` 还会生成 `!DICompileUnit` / `!DISubprogram` / `!DILocation` 元数据,并用 `-g -fno-omit-frame-pointer` 编译,因此 gdb/lldb/WinDbg 可以把指令映射回源码行。
251
251
  - 输出:`xt_print/xt_println/xt_console_log/info/warn/error`(Node 风格 inspect:数组 `[ a, b ]`、对象 `{ k: v }`;`info`/`log` 到 stdout,`warn`/`error` 到 stderr),以及 `dir/trace/assert/count/countReset/group/groupEnd/table/time/timeEnd/timeLog`。
252
252
  - 对象 / 函数:`xt_object` 带原型链,`xt_function` 带属性包(静态成员与 `prototype`);`xt_new`(实例化)、`xt_instance_of`(原型链)、`xt_object_freeze/is_frozen/from_entries`。
253
253
  - 标准库扩展(`xt_stdlib2.c`):数组 / 字符串 / 数字 / 对象的扩展方法;`Object` / `Array` / `Number` / `String` 静态方法;`JSON.parse` / `JSON.stringify`;`Map` / `Set`;`Date`(`gmtime_r`);`RegExp`(POSIX ERE `regcomp`/`regexec` 的 `test`/`exec`)。
@@ -338,7 +338,7 @@ const result = build("program.ts", { emit: "exe", outDir: "build" });
338
338
  实现位置:`tests/`(`lexer` / `parser` / `binder` / `codegen` / `driver` / `extensions` / `cli` / `e2e`)
339
339
 
340
340
  - 各模块单元测试;e2e 在存在 `clang` 时真正编译并运行二进制,否则自动跳过。
341
- - e2e 覆盖:算术与打印、递归函数、循环 / 数组 / 字符串拼接、闭包按引用捕获、对象 / 数组 JS 风格打印、Node `fs` 扩展(经 `import`)、`switch` 穿透、数组 / 字符串方法、`Math` 与全局函数与 `console` 各等级、默认 / 剩余参数与 `arguments`、`Object` 助手与展开与 `in`/`delete`、`for...in` 对象键枚举、`try/catch/finally`、可选链、类与 `new`/`this`/`static`/`extends`/`super`/`instanceof`、`async`/`await` 与 `Promise`、`Map`/`Set`/`JSON` 与扩展标准库、`Map`/`Set` 的 `for...of`、数组 `length` 赋值与可迭代展开、多文件 `import`/`export`、类型化数组(元素强制转换、`from`/`of`、迭代与原型方法),以及全局 `fetch`(HTTP GET/POST、请求头、重定向、JSON/文本/二进制响应体与 `TypeError` 拒绝)。
341
+ - e2e 覆盖:算术与打印、递归函数、循环 / 数组 / 字符串拼接、闭包按引用捕获、对象 / 数组 JS 风格打印、Node `fs` 扩展(经 `import`)、`switch` 穿透、数组 / 字符串方法、`Math` 与全局函数与 `console` 各等级、默认 / 剩余参数与 `arguments`、`Object` 助手与展开与 `in`/`delete`、`for...in` 对象键枚举、`try/catch/finally`、可选链、类与 `new`/`this`/`static`/`extends`/`super`/`instanceof`、`async`/`await` 与 `Promise`、`Map`/`Set`/`JSON` 与扩展标准库、`Map`/`Set` 的 `for...of`、数组 `length` 赋值与可迭代展开、多文件 `import`/`export`、类型化数组(元素强制转换、`from`/`of`、迭代与原型方法)、对象 rest 解构与内建 `instanceof`,以及全局 `fetch`(HTTP GET/POST、请求头、重定向、JSON/文本/二进制响应体与 `TypeError` 拒绝)。
342
342
 
343
343
  ---
344
344
 
@@ -19,9 +19,18 @@ build/app.ll:382:37: error: '@.str.0' defined with type '[6 x i8]*' but expected
19
19
  ```
20
20
 
21
21
  有类型指针在 LLVM 16 中被移除([发布说明](https://github.com/llvm/llvm-project/blob/release/15.x/llvm/docs/ReleaseNotes.rst#changes-to-the-llvm-ir)),
22
- 所以请先确认 `clang --version`;系统 clang 较旧时用 `xbintsc_CLANG` 指向新版。
23
- `xbintsc doctor` 会报告解析到的工具链及其版本。把 IR 拿到别处编译来绕过该限制
24
- 是不受支持的。
22
+ 所以 xbintsc 现在**在编译任何东西之前先检查版本**:
23
+
24
+ - `xbintsc build` / `run` 一旦发现解析到的驱动低于 16 就立即停止,并给出可操作的错误,
25
+ 而不是生成无法编译的 IR。
26
+ - `xbintsc doctor` 会打印 `status : ok` 或 `status : too old`,在工具链无法构建二进制时
27
+ 以非零码退出,脚本和 CI 可以把它当作预检。
28
+ - 搜索时会优先选择 16+ 的 clang:Windows 上即使 `PATH` 里靠前的是旧 clang,
29
+ 也能找到 Visual Studio 自带的 LLVM
30
+ (`<VS 安装目录>\VC\Tools\Llvm\x64\bin\clang.exe`)。可以用 `xbintsc_CLANG` 覆盖;
31
+ 显式指定的旧版本会得到 "too old" 错误,而不是被静默忽略。
32
+
33
+ 把 IR 拿到别处编译来绕过该限制是不受支持的。
25
34
 
26
35
  ## 总览
27
36
 
@@ -40,7 +40,8 @@
40
40
 
41
41
  > 标记模板已实现,含 raw 字符串与 `String.raw`。生成器(`function*`、`yield`、`yield*`)
42
42
  > 基于有栈协程实现,支持 `next`/`throw`/`return`、`for...of`、展开与委托;
43
- > `import.meta` 可解析,但无运行时取值;异步生成器尚未支持。
43
+ > `import.meta` 提供 `url`、`dirname`、`filename`,由正在运行的可执行文件路径解析。
44
+ > `async function*` 现在会以 `UnsupportedFeature` **明确拒绝**,不再静默产出同步生成器。
44
45
 
45
46
  ### 2.2 解析器不支持
46
47
 
@@ -50,7 +51,7 @@
50
51
 
51
52
  | 运算符 | 现状 | 说明 |
52
53
  | --- | --- | --- |
53
- | `new.target` | ✗ | 未实现 |
54
+ | `new.target` | ✗ | 已解析,但以 `UnsupportedFeature` 明确拒绝(无 `new.target` 语义) |
54
55
  | `typeof` / `void` / `in` / `delete` / `instanceof` | ✓ | 映射到运行时辅助函数(`xt_typeof`、`xt_in`、`xt_delete`、`xt_instance_of`) |
55
56
 
56
57
  ---
@@ -103,7 +104,7 @@
103
104
  | `keys` / `values` / `entries` | ~ 返回数组,而非迭代器对象 |
104
105
  | `ArrayBuffer` / `DataView` / `.buffer` / `.byteOffset` 语义 | ✗ 无底层缓冲区类型;`.byteOffset` 恒为 0 |
105
106
  | `BigInt64Array` / `BigUint64Array` | ✗ 未实现 |
106
- | `instanceof Uint8Array` | ✗ 全局构造器不是一等公民值,`x instanceof Uint8Array` 会报 `Cannot find name 'Uint8Array'` |
107
+ | `instanceof` 内建构造器 | ✓ `instanceof Array` / `Object` / `Map` / `Set` / `Date` / `RegExp` / `Promise` / `Error` 家族 / 类型化数组都能按名字识别;但构造器本身仍不是一等公民值(`const A = Array` 仍会失败) |
107
108
  | 基于 `Buffer` / `ArrayBuffer` 的类型化数组视图 | ✗ 构造器仅能从长度 / 数组样 / 类型化数组复制 |
108
109
 
109
110
  ---
@@ -137,8 +138,8 @@
137
138
  | `arguments` 对象 | ✓(隐式) | ✓ 已实现(箭头函数取的是自身参数,而非外层函数的 `arguments`,与 JS 不同) |
138
139
  | `this` 绑定 / 方法调用语义 | ✓ | ✓ 已实现(`this` 作为函数首个 ABI 参数线程化;箭头函数词法继承) |
139
140
  | `async` / `await` / Promise | ✓ | ✓ 已实现(同步微任务模型) |
140
- | `new.target` | ✗ | 未实现 |
141
- | 生成器 / 迭代器 / `yield` | ✓ 已实现 | `function*`、`yield`、`yield*`、`.next/.throw/.return`;不支持异步生成器 |
141
+ | `new.target` | ✗ | 已解析,但以 `UnsupportedFeature` 明确拒绝(无 `new.target` 语义) |
142
+ | 生成器 / 迭代器 / `yield` | ✓ 已实现 | `function*`、`yield`、`yield*`、`.next/.throw/.return`;异步生成器以 `UnsupportedFeature` 拒绝 |
142
143
  | 闭包 `arity` | — | `xt_closure_arity` 恒为 -1,未填充 |
143
144
  | `fn.call` / `fn.apply` / `fn.bind` | ✓ | ✓ 已实现(绑定闭包不跟踪部分参数的 `length`) |
144
145
  | `fn.name` / `fn.length` | ✓ | ✓ 已实现(从声明 / 赋值 / 属性键推断;绑定函数为 `"bound ..."` 并调整 arity) |
@@ -199,9 +200,9 @@
199
200
  | 函数 `arity` / 调用参数个数 | 无参数个数校验;`fn.length` 报告声明的 arity,但调用不做校验 |
200
201
  | `async` / `await` | **同步微任务模型**:`await` 在已 settle 的 promise 上同步继续。定时器与套接字仅在程序主体执行完后由事件循环运行,因此无法等待由定时器回调 settle 的 promise |
201
202
  | `super` | `super.x` / `super(...)` 取 `this` 原型的原型;单级继承正确,继承深度 > 1 时可能不准确 |
202
- | `Error.stack` | 未捕获 |
203
+ | `Error.stack` | 仅函数级帧:每帧为 `at 名称 (文件:行)`,行号取自函数声明(最内层帧会更新为错误构造处);没有列号,生成器函数体不产生帧(它们会切换栈) |
203
204
  | 模块实时绑定 | 命名空间导入与导入绑定为快照(见第 4 节) |
204
- | `import.meta` | 可解析但无取值 |
205
+ | `import.meta` | ✓ `import.meta.url` / `.dirname` / `.filename`,由可执行文件路径解析 |
205
206
 
206
207
  > 数字格式化、宽松相等、`+` / 关系运算 `ToPrimitive`、整链可选链、`finally` 提前退出、对象键顺序等均已对齐 Node,并由差分测试覆盖,故不再列为偏差。
207
208
 
@@ -227,9 +228,9 @@
227
228
  ```
228
229
  未实现(语句):namespace/module 声明
229
230
 
230
- 未实现(表达式):new.target、import.meta 取值
231
+ 未实现(表达式):new.target(会给出明确的 UnsupportedFeature)
231
232
 
232
- 未实现(函数):`fn.toString()` 源码文本、异步生成器
233
+ 未实现(函数):`fn.toString()` 源码文本、异步生成器(已明确拒绝)
233
234
 
234
235
  未实现(类/面向对象):abstract/implements、访问控制、父子类 #x 同名
235
236
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xbintsc",
3
- "version": "0.3.81",
3
+ "version": "0.3.87",
4
4
  "description": "xbintsc - a TypeScript binary compiler (TypeScript -> LLVM IR -> native binary)",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -41,6 +41,8 @@
41
41
  "runtime:clean": "node -e \"require('fs').rmSync('build/runtime-obj',{recursive:true,force:true});require('fs').rmSync('runtime/lib',{recursive:true,force:true})\"",
42
42
  "xbintsc": "tsx src/cli/main.ts",
43
43
  "typecheck": "tsc -p tsconfig.json --noEmit",
44
+ "check:version": "tsx scripts/check-version.ts",
45
+ "bench": "tsx scripts/bench.ts",
44
46
  "lint": "eslint . && tsx scripts/check-file-length.ts",
45
47
  "test": "vitest run",
46
48
  "test:watch": "vitest",
@@ -25,7 +25,7 @@ static xt_value xt_buffer_from(xt_value value, const char *encoding) {
25
25
  xt_value text = xt_to_string(value);
26
26
  xt_string *string = xt_as_string(text);
27
27
  size_t decoded = 0;
28
- unsigned char *bytes = xt_node_decode(string->data, string->length, encoding, &decoded);
28
+ unsigned char *bytes = xt_node_decode(xt_string_bytes(string), string->length, encoding, &decoded);
29
29
  if (!bytes) return xt_buffer_new(0, NULL);
30
30
  xt_value result = xt_buffer_from_bytes(bytes, decoded);
31
31
  free(bytes);
@@ -49,7 +49,7 @@ static xt_value xt_buffer_alloc(uint32_t size, xt_value fill, const char *encodi
49
49
  xt_value text = xt_to_string(fill);
50
50
  xt_string *string = xt_as_string(text);
51
51
  size_t decoded = 0;
52
- unsigned char *bytes = xt_node_decode(string->data, string->length, encoding, &decoded);
52
+ unsigned char *bytes = xt_node_decode(xt_string_bytes(string), string->length, encoding, &decoded);
53
53
  if (!bytes || decoded == 0) {
54
54
  free(bytes);
55
55
  return buffer;
@@ -93,7 +93,7 @@ static xt_value xt_buffer_method_write(xt_value thisValue, xt_value env, int32_t
93
93
  uint32_t offset = argc > 1 ? (uint32_t)xt_to_number(argv[1]) : 0;
94
94
  const char *encoding = argc > 3 ? xt_node_encoding(argv[3]) : NULL;
95
95
  size_t decoded = 0;
96
- unsigned char *bytes = xt_node_decode(string->data, string->length, encoding, &decoded);
96
+ unsigned char *bytes = xt_node_decode(xt_string_bytes(string), string->length, encoding, &decoded);
97
97
  uint32_t available = xt_buffer_length(thisValue);
98
98
  uint32_t written = 0;
99
99
  for (size_t i = 0; i < decoded && offset + written < available; i++) {
@@ -19,7 +19,7 @@ static const unsigned char *xt_buffer_search_needle(xt_value value, const char *
19
19
  xt_value text = xt_to_string(value);
20
20
  xt_string *string = xt_as_string(text);
21
21
  size_t decoded = 0;
22
- unsigned char *bytes = xt_node_decode(string->data, string->length, encoding, &decoded);
22
+ unsigned char *bytes = xt_node_decode(xt_string_bytes(string), string->length, encoding, &decoded);
23
23
  *length = decoded;
24
24
  return bytes;
25
25
  }
@@ -82,6 +82,27 @@ static char *read_temp(FILE *file) {
82
82
  return buffer;
83
83
  }
84
84
 
85
+ /*
86
+ * MSVC's `_spawnvp` builds the child command line from argv without quoting
87
+ * argv[0]. When the executable path contains a space — the default install
88
+ * location is "C:\Program Files\…" — the child sees the path split into several
89
+ * arguments ("Files\Microsoft", "Visual", …). Quote argv[0] ourselves. Returns
90
+ * NULL when no quoting is needed (the caller keeps the original pointer).
91
+ */
92
+ static char *quote_argv0(char **child_argv) {
93
+ const char *arg = child_argv[0];
94
+ if (arg == NULL || strchr(arg, ' ') == NULL) return NULL;
95
+ size_t length = strlen(arg);
96
+ char *quoted = (char *)malloc(length + 3);
97
+ if (!quoted) return NULL;
98
+ quoted[0] = '"';
99
+ memcpy(quoted + 1, arg, length);
100
+ quoted[length + 1] = '"';
101
+ quoted[length + 2] = 0;
102
+ child_argv[0] = quoted;
103
+ return quoted;
104
+ }
105
+
85
106
  xt_value xt_child_process_spawn_sync(int32_t argc, xt_value *argv) {
86
107
  const char *command = argc > 0 ? xt_string_data(xt_to_string(argv[0])) : NULL;
87
108
  if (!command) command = "";
@@ -91,6 +112,7 @@ xt_value xt_child_process_spawn_sync(int32_t argc, xt_value *argv) {
91
112
  int count = 0;
92
113
  char **child_argv = (char **)build_argv(command, args, &count);
93
114
  if (!child_argv) return make_result(-1, "", "out of memory");
115
+ char *quoted_command = quote_argv0(child_argv);
94
116
 
95
117
  char previous[4096];
96
118
  int had_cwd = cwd && _getcwd(previous, sizeof(previous)) != NULL;
@@ -99,6 +121,7 @@ xt_value xt_child_process_spawn_sync(int32_t argc, xt_value *argv) {
99
121
  if (cwd) _chdir(cwd);
100
122
  intptr_t inherit_rc = _spawnvp(_P_WAIT, command, (const char *const *)child_argv);
101
123
  if (had_cwd) _chdir(previous);
124
+ free(quoted_command);
102
125
  free(child_argv);
103
126
  return make_result(inherit_rc < 0 ? -1 : (double)inherit_rc, "", "");
104
127
  }
@@ -106,6 +129,7 @@ xt_value xt_child_process_spawn_sync(int32_t argc, xt_value *argv) {
106
129
  FILE *out_file = tmpfile();
107
130
  FILE *err_file = tmpfile();
108
131
  if (!out_file || !err_file) {
132
+ free(quoted_command);
109
133
  free(child_argv);
110
134
  if (out_file) fclose(out_file);
111
135
  if (err_file) fclose(err_file);
@@ -136,6 +160,7 @@ xt_value xt_child_process_spawn_sync(int32_t argc, xt_value *argv) {
136
160
  char *err = read_temp(err_file);
137
161
  fclose(out_file);
138
162
  fclose(err_file);
163
+ free(quoted_command);
139
164
  free(child_argv);
140
165
 
141
166
  xt_value result = make_result(rc < 0 ? -1 : (double)rc, out, err);
@@ -76,7 +76,7 @@ static unsigned char *xt_fs_value_bytes(xt_value value, size_t *outLength) {
76
76
  *outLength = 0;
77
77
  return NULL;
78
78
  }
79
- if (length > 0) memcpy(bytes, string->data, length);
79
+ if (length > 0) memcpy(bytes, xt_string_bytes(string), length);
80
80
  *outLength = length;
81
81
  return bytes;
82
82
  }
package/runtime/rt.h CHANGED
@@ -92,7 +92,9 @@ xt_value xt_bool(int b);
92
92
  xt_value xt_number(double d);
93
93
  xt_value xt_string_new(const char *data, size_t len);
94
94
  xt_value xt_string_from_cstr(const char *data);
95
- /** Raw UTF-8 bytes of a string value (NULL for non-strings); not NUL-safe. */
95
+ /** Raw UTF-8 bytes of a string value (NULL for non-strings); not NUL-safe.
96
+ * Materialises a lazily concatenated string, so the returned pointer is stable
97
+ * until the string is collected. */
96
98
  const char *xt_string_data(xt_value value);
97
99
  int32_t xt_string_length_value(xt_value value);
98
100
 
@@ -119,6 +121,8 @@ xt_value xt_function_set_metadata(xt_value fn, xt_value name, int32_t arity);
119
121
  xt_value xt_this(void);
120
122
  xt_value xt_new(xt_value ctor, int32_t argc, xt_value *argv);
121
123
  xt_value xt_instance_of(xt_value value, xt_value ctor);
124
+ /** `value instanceof <built-in constructor>`; id from `BUILTIN_INSTANCEOF`. */
125
+ xt_value xt_instance_of_builtin(xt_value value, int32_t id);
122
126
  xt_value xt_function_set_prototype(xt_value fn, xt_value proto);
123
127
  xt_value xt_function_get_prototype(xt_value fn);
124
128
 
@@ -177,6 +181,8 @@ xt_value xt_object_values(xt_value obj);
177
181
  xt_value xt_object_entries(xt_value obj);
178
182
  xt_value xt_object_assign(int32_t argc, xt_value *argv);
179
183
  xt_value xt_object_spread(xt_value target, xt_value source);
184
+ /** Own enumerable properties of `source` except the keys in `argv` (object rest). */
185
+ xt_value xt_object_rest(xt_value source, int32_t argc, xt_value *argv);
180
186
 
181
187
  /* -- symbols -------------------------------------------------------------- */
182
188
  /** Implements `Symbol(description)`. */
@@ -340,6 +346,16 @@ void xt_throw(xt_value v);
340
346
  void *xt_try_enter(void);
341
347
  xt_value xt_try_exception(void *frame);
342
348
  void xt_try_leave(void *frame);
349
+ /**
350
+ * JavaScript-level shadow stack. Generated code pushes `(name, file, line)` on
351
+ * function entry and pops before returning; `Error` construction walks it to
352
+ * build `.stack`, and a fatal native signal dumps it. The strings are static
353
+ * module data, so the runtime never owns them.
354
+ */
355
+ void xt_stack_push(const char *name, const char *file, int32_t line);
356
+ void xt_stack_pop(void);
357
+ /** Set the innermost frame's line (called just before `new Error(...)`). */
358
+ void xt_stack_set_line(int32_t line);
343
359
  void xt_console_log(int32_t argc, xt_value *argv);
344
360
  void xt_console_info(int32_t argc, xt_value *argv);
345
361
  void xt_console_warn(int32_t argc, xt_value *argv);
@@ -21,8 +21,12 @@
21
21
  * code free of write barriers and makes the first implementation small and
22
22
  * predictable. Replacing it with a precise/conservative collector is an
23
23
  * isolated change behind `xt_alloc` plus root registration.
24
- * * Strings are UTF-8. JavaScript measures string length in UTF-16 code
25
- * units; `xt_string_length` currently reports code points (see README).
24
+ * * Strings are UTF-8. Concatenation is lazy: a `+` between two non-empty
25
+ * strings builds an immutable cons node (`flat == NULL`) and the bytes are
26
+ * materialised once, on first observation, by `xt_string_bytes`. This keeps
27
+ * `s += x` in a loop linear instead of copying the accumulated prefix every
28
+ * iteration. JavaScript measures string length in UTF-16 code units;
29
+ * `xt_string_length` currently reports code points (see README).
26
30
  * * Objects use a small linear property list. Fine for the language subset
27
31
  * xbintsc targets today; swap for a hash map when the profile demands it.
28
32
  */
@@ -64,11 +68,19 @@ typedef struct xt_header {
64
68
 
65
69
  #define XT_GC_MARK 0x01u
66
70
 
67
- typedef struct {
71
+ /* A string is either *flat* (`flat` points at `capacity` bytes, the last one
72
+ * NUL) or a *cons* node (`flat == NULL`, `left`/`right` name the operands).
73
+ * `length` is always the total byte length, so `.length`, comparisons and the
74
+ * GC never need to force a cons node. `xt_string_bytes` turns a cons node into
75
+ * a flat one in place; treat the result as immutable and never write through a
76
+ * pointer obtained from it. */
77
+ typedef struct xt_string {
68
78
  xt_header header;
69
79
  uint32_t length;
70
80
  uint32_t capacity;
71
- char *data;
81
+ char *flat;
82
+ struct xt_string *left;
83
+ struct xt_string *right;
72
84
  } xt_string;
73
85
 
74
86
  /* Sign-magnitude arbitrary-precision integer; see xt_bigint.c. */
@@ -99,6 +111,10 @@ typedef struct {
99
111
  uint8_t frozen;
100
112
  uint8_t is_array_base;
101
113
  uint16_t reserved2;
114
+ /* Captured `Error.stack` for XT_OBJECT_KIND_ERROR, XT_UNDEFINED otherwise.
115
+ * Deliberately not a property: `Object.keys`, `for...in` and JSON.stringify
116
+ * must not see it, while `error.stack` returns it through `xt_get`. */
117
+ xt_value error_stack;
102
118
  } xt_object;
103
119
 
104
120
  typedef struct {
@@ -191,6 +207,12 @@ xt_string *xt_string_alloc(size_t length);
191
207
  xt_string *xt_as_string(xt_value value);
192
208
  int32_t xt_string_length(xt_string *s);
193
209
  int xt_string_equals(xt_string *a, xt_string *b);
210
+ /* Contiguous UTF-8 bytes of a string, flattening a cons node in place on first
211
+ * use. The result is NUL-terminated at `s->length`; never write through it. */
212
+ const char *xt_string_bytes(xt_string *s);
213
+ /* Mutable bytes of a string just created with `xt_string_alloc` (never a cons
214
+ * node). Used by the constructors that fill a buffer before publishing it. */
215
+ char *xt_string_mutable(xt_string *s);
194
216
  int32_t xt_to_int32(xt_value v);
195
217
 
196
218
  /* ECMAScript number formatting (xt_values.c). */
@@ -284,6 +306,10 @@ typedef struct xt_try_frame {
284
306
  jmp_buf buf;
285
307
  struct xt_try_frame *prev;
286
308
  xt_value exception;
309
+ /* Shadow-stack depth when the protected region was entered; restored when
310
+ * the region is left (normally or through a throw) so frames from unwound
311
+ * calls do not leak into later stack traces. */
312
+ int32_t stack_depth;
287
313
  } xt_try_frame;
288
314
  /* Save a try frame with clang's `_setjmp` built-in.
289
315
  *
@@ -300,6 +326,23 @@ typedef struct xt_try_frame {
300
326
  void *xt_try_mark(void);
301
327
  void xt_try_restore(void *mark);
302
328
 
329
+ /* -- shadow call stack and diagnostics (xt_io/exception.inc) --------------- */
330
+ /* Generated code pushes a frame on entry and pops it before returning, so an
331
+ * Error can capture a JavaScript-level stack without any debug metadata. Frame
332
+ * strings live in the module's static data and are never freed. */
333
+ void xt_stack_push(const char *name, const char *file, int32_t line);
334
+ void xt_stack_pop(void);
335
+ int32_t xt_stack_depth(void);
336
+ void xt_stack_set_line(int32_t line);
337
+ /* Restore the depth saved by a try frame after unwinding inner calls. */
338
+ void xt_stack_trim(int32_t depth);
339
+ /* Format `Name: message` plus the current frames as an `Error.stack` string. */
340
+ xt_value xt_stack_capture(const char *name, const char *message);
341
+ /* The captured stack of an error object, or XT_UNDEFINED. */
342
+ xt_value xt_error_stack(xt_value error);
343
+ /* Best-effort stack dump on a fatal native signal / unhandled exception. */
344
+ void xt_install_crash_handler(void);
345
+
303
346
  /* -- collection / date / regexp representations --------------------------- */
304
347
  typedef struct {
305
348
  xt_header header;
@@ -210,6 +210,9 @@ void xt_gc_init(void) {
210
210
  g_gc_threshold_pinned = 1;
211
211
  }
212
212
  }
213
+ /* A crash is the one case where the JavaScript-level shadow stack is the
214
+ * most useful thing to print; install the handler once per process. */
215
+ xt_install_crash_handler();
213
216
  }
214
217
 
215
218
  void xt_gc_arm(void) { g_gc_armed = 1; }
@@ -328,7 +331,16 @@ static void xt_gc_process(xt_header *header) {
328
331
  const void *base = (const char *)header + sizeof(xt_header);
329
332
 
330
333
  switch (header->kind) {
331
- case XT_OBJECT_KIND_STRING:
334
+ case XT_OBJECT_KIND_STRING: {
335
+ /* A flat string holds bytes only. A cons node is an immutable rope whose
336
+ * children must stay alive until `xt_string_bytes` materialises it. */
337
+ xt_string *string = (xt_string *)header;
338
+ if (string->flat == NULL) {
339
+ if (string->left) xt_gc_gray_push(&string->left->header);
340
+ if (string->right) xt_gc_gray_push(&string->right->header);
341
+ }
342
+ return;
343
+ }
332
344
  case XT_OBJECT_KIND_BIGINT:
333
345
  case XT_OBJECT_KIND_DATE:
334
346
  /* Numbers/bytes only: no references to follow. */
@@ -392,7 +404,7 @@ static void xt_gc_process(xt_header *header) {
392
404
  static void xt_gc_release(xt_header *header) {
393
405
  switch (header->kind) {
394
406
  case XT_OBJECT_KIND_STRING:
395
- free(((xt_string *)header)->data);
407
+ free(((xt_string *)header)->flat);
396
408
  break;
397
409
  case XT_OBJECT_KIND_BIGINT:
398
410
  free(((xt_bigint *)header)->limbs);
@@ -41,7 +41,7 @@ static xt_value bi_from_xt_value(xt_value value) {
41
41
  if (XT_IS_NUMBER(value)) return xt_bigint_from_double(xt_to_double(value));
42
42
  if (XT_IS_STRING(value)) {
43
43
  xt_string *s = xt_as_string(value);
44
- return xt_bigint_from_string(s->data, s->length);
44
+ return xt_bigint_from_string(xt_string_bytes(s), s->length);
45
45
  }
46
46
  if (value == XT_TRUE) return xt_bigint_from_i64(1);
47
47
  if (value == XT_FALSE) return xt_bigint_from_i64(0);