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/doc/unimplemented.md
CHANGED
|
@@ -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`
|
|
72
|
-
>
|
|
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` | ✗ |
|
|
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
|
|
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` | ✗ |
|
|
173
|
-
| Generators / iterators / `yield` | ✓ implemented | `function*`, `yield`, `yield*`, `.next/.throw/.return`; `async` generators
|
|
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` |
|
|
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` |
|
|
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
|
|
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
|
package/doc/zh-CN/DESIGN.md
CHANGED
|
@@ -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 语义(`+`
|
|
42
|
+
不易内联的 JS 语义(`+` 隐式转换、属性访问、打印)委托给 `@xt_*` 运行时调用。
|
|
43
|
+
数值算术与比较是例外:codegen 内联 double 快路径(`numeric.ts`),需要隐式转换时
|
|
44
|
+
才调用运行时。
|
|
43
45
|
|
|
44
46
|
### 运行时
|
|
45
47
|
|
|
46
|
-
`runtime
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
98
|
-
|
|
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
|
|
104
|
-
`
|
|
109
|
+
`tests/driver`、`tests/extensions`、`tests/cli`、`tests/e2e`。存在受支持的 clang
|
|
110
|
+
(16+,与 `build` 使用的版本门槛一致)时,e2e 会真正编译并运行二进制,否则自动跳过。
|
package/doc/zh-CN/implemented.md
CHANGED
|
@@ -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
|
|
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
|
-
|
|
23
|
-
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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);
|
package/runtime/rt_internal.h
CHANGED
|
@@ -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.
|
|
25
|
-
*
|
|
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
|
-
|
|
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 *
|
|
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;
|
package/runtime/xt_alloc.c
CHANGED
|
@@ -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)->
|
|
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
|
|
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);
|