xbintsc 0.3.35 → 0.3.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +95 -0
- package/README.md +31 -3
- package/README.zh-CN.md +23 -0
- package/dist/src/cli/hints.d.ts +54 -0
- package/dist/src/cli/hints.js +165 -0
- package/dist/src/cli/hints.js.map +1 -0
- package/dist/src/cli/main.js +73 -9
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/codegen/generator/module.js +10 -0
- package/dist/src/codegen/generator/module.js.map +1 -1
- package/dist/src/codegen/generator/state.js +4 -0
- package/dist/src/codegen/generator/state.js.map +1 -1
- package/dist/src/codegen/generator/tables.d.ts +26 -0
- package/dist/src/codegen/generator/tables.js +64 -12
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/diagnostics/source-text.d.ts +22 -0
- package/dist/src/diagnostics/source-text.js +76 -0
- package/dist/src/diagnostics/source-text.js.map +1 -0
- package/dist/src/driver/bundler/graph.js +2 -1
- package/dist/src/driver/bundler/graph.js.map +1 -1
- package/dist/src/driver/compiler.js +3 -2
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/lexer/scanner/strings.js +16 -3
- package/dist/src/lexer/scanner/strings.js.map +1 -1
- package/dist/tests/cli/hints.test.d.ts +9 -0
- package/dist/tests/cli/hints.test.js +143 -0
- package/dist/tests/cli/hints.test.js.map +1 -0
- package/dist/tests/cli/main.test.js +6 -4
- package/dist/tests/cli/main.test.js.map +1 -1
- package/dist/tests/codegen/llvm.test.js +17 -2
- package/dist/tests/codegen/llvm.test.js.map +1 -1
- package/dist/tests/e2e/gc.test.d.ts +1 -0
- package/dist/tests/e2e/gc.test.js +168 -0
- package/dist/tests/e2e/gc.test.js.map +1 -0
- package/dist/tests/e2e/harness.d.ts +2 -0
- package/dist/tests/e2e/harness.js +1 -0
- package/dist/tests/e2e/harness.js.map +1 -1
- package/dist/tests/helpers.js +3 -2
- package/dist/tests/helpers.js.map +1 -1
- package/dist/tests/lexer/strings.test.js +14 -2
- package/dist/tests/lexer/strings.test.js.map +1 -1
- package/doc/DESIGN.md +117 -0
- package/doc/ai/README.md +63 -0
- package/doc/ai/build-recipe.md +137 -0
- package/doc/ai/cli.md +142 -0
- package/doc/ai/contributing.md +196 -0
- package/doc/ai/extensions.md +148 -0
- package/doc/ai/language-support.md +152 -0
- package/doc/ai/troubleshooting.md +163 -0
- package/doc/ai/zh-CN/README.md +56 -0
- package/doc/ai/zh-CN/build-recipe.md +132 -0
- package/doc/ai/zh-CN/cli.md +127 -0
- package/doc/ai/zh-CN/contributing.md +173 -0
- package/doc/ai/zh-CN/extensions.md +139 -0
- package/doc/ai/zh-CN/language-support.md +147 -0
- package/doc/ai/zh-CN/troubleshooting.md +150 -0
- package/doc/gui-scripts.md +350 -0
- package/doc/gui.md +646 -0
- package/doc/icon.md +265 -0
- package/doc/implemented.md +373 -0
- package/doc/node-implemented.md +588 -0
- package/doc/node-unimplemented.md +167 -0
- package/doc/post/announce.md +43 -0
- package/doc/requirements.md +145 -0
- package/doc/unimplemented.md +286 -0
- package/doc/xbintsc.config.schema.json +67 -0
- package/doc/zh-CN/DESIGN.md +104 -0
- package/doc/zh-CN/gui-scripts.md +329 -0
- package/doc/zh-CN/gui.md +588 -0
- package/doc/zh-CN/icon.md +241 -0
- package/doc/zh-CN/implemented.md +365 -0
- package/doc/zh-CN/node-implemented.md +533 -0
- package/doc/zh-CN/node-unimplemented.md +141 -0
- package/doc/zh-CN/plan-require-node-modules.md +284 -0
- package/doc/zh-CN/post/announce.md +47 -0
- package/doc/zh-CN/requirements.md +134 -0
- package/doc/zh-CN/unimplemented.md +247 -0
- package/llms.txt +45 -0
- package/package.json +6 -2
- package/runtime/ext_gui/dom_api_proto.cpp +5 -0
- package/runtime/ext_gui/gui.cpp +3 -1
- package/runtime/ext_gui/renderer.cpp +13 -11
- package/runtime/ext_gui/renderer_image.cpp +12 -8
- package/runtime/ext_gui/renderer_shaders.h +131 -4
- package/runtime/ext_gui/renderer_shaders_data.h +1809 -0
- package/runtime/ext_gui/renderer_text.cpp +12 -8
- package/runtime/ext_gui/shaders.hlsl +98 -0
- package/runtime/ext_gui/spirv/fill.frag +19 -0
- package/runtime/ext_gui/spirv/fill.vert +42 -0
- package/runtime/ext_gui/spirv/image.frag +16 -0
- package/runtime/ext_gui/spirv/quad.vert +30 -0
- package/runtime/ext_gui/spirv/text.frag +16 -0
- package/runtime/ext_gui/window.cpp +1 -0
- package/runtime/ext_node/buffer/parts/prototype.inc +1 -0
- package/runtime/ext_node/dgram/dgram.c +1 -0
- package/runtime/ext_node/events/events.c +1 -0
- package/runtime/ext_node/fs/fs_ops.c +10 -25
- package/runtime/ext_node/fs/glob.c +13 -30
- package/runtime/ext_node/fs/promises.c +1 -0
- package/runtime/ext_node/http/parts/prototypes.inc +5 -0
- package/runtime/ext_node/net/parts/prototypes.inc +2 -0
- package/runtime/ext_node/process/process.c +9 -7
- package/runtime/ext_node/stream/stream.c +1 -0
- package/runtime/ext_node/util/util.c +6 -6
- package/runtime/rt.h +10 -0
- package/runtime/rt_internal.h +63 -2
- package/runtime/xt_alloc.c +442 -5
- package/runtime/xt_generator.c +95 -1
- package/runtime/xt_loop.c +35 -1
- package/runtime/xt_promise.c +46 -0
- package/runtime/xt_stdlib2/error.inc +1 -0
- package/runtime/xt_stdlib2/regexp-match.inc +8 -7
- package/runtime/xt_symbol.c +2 -0
- package/runtime/xt_typed_array/construction.inc +142 -0
- package/runtime/xt_typed_array/elements.inc +92 -0
- package/runtime/xt_typed_array/methods.inc +329 -0
- package/runtime/xt_typed_array.c +6 -548
- package/runtime/xt_values/number-format.inc +26 -0
- package/scripts/build-gui-shaders.mjs +204 -0
- package/scripts/build-gui.ts +35 -0
- package/scripts/check-file-length.ts +89 -0
- package/src/cli/hints.ts +194 -0
- package/src/cli/main.ts +82 -9
- package/src/codegen/generator/module.ts +10 -0
- package/src/codegen/generator/state.ts +4 -0
- package/src/codegen/generator/tables.ts +60 -14
- package/src/diagnostics/source-text.ts +78 -0
- package/src/driver/bundler/graph.ts +2 -1
- package/src/driver/compiler.ts +3 -2
- package/src/lexer/scanner/strings.ts +16 -3
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# 排错指南
|
|
2
|
+
|
|
3
|
+
先跑这条命令,看 xbintsc 到底解析到了什么:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
xbintsc doctor
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
它会打印平台、将要使用的工具链(以及来源)、编译器版本、运行时目录、运行时库目录
|
|
10
|
+
与图标资源编译器。绝大多数"跑不起来"的答案都在这里。
|
|
11
|
+
|
|
12
|
+
## 首先确认:clang 够新吗?
|
|
13
|
+
|
|
14
|
+
**需要 clang/LLVM 16 或更新。** 更老的 clang 会以有类型指针(typed pointer)错误
|
|
15
|
+
拒绝生成的 IR:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
D:\...\build\app.ll:382:37: error: '@.str.0' defined with type '[6 x i8]*' but expected 'i8*'
|
|
19
|
+
%r0 = call i64 @xt_string_new(i8* @.str.0, i64 5)
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
编译器生成的 LLVM IR 依赖不透明指针(opaque pointers),而有类型指针在 LLVM 16
|
|
23
|
+
中被移除([LLVM 发布说明](https://github.com/llvm/llvm-project/blob/release/15.x/llvm/docs/ReleaseNotes.rst#changes-to-the-llvm-ir))。
|
|
24
|
+
用 `clang --version` 确认;如果机器上的系统 clang 太旧,装一个新版 LLVM 并让 xbintsc
|
|
25
|
+
用它:
|
|
26
|
+
|
|
27
|
+
```powershell
|
|
28
|
+
$env:xbintsc_CLANG = "C:\Program Files\LLVM\bin\clang.exe"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
export xbintsc_CLANG=/usr/lib/llvm-18/bin/clang
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
注意:只做前端工作(`xbintsc emit`)完全不需要 clang,所以在旧工具链上它照常可用。
|
|
36
|
+
|
|
37
|
+
## 报错信息对照
|
|
38
|
+
|
|
39
|
+
### `module '…' is provided by the 'node' extension; pass --ext node`
|
|
40
|
+
|
|
41
|
+
程序导入了 Node 模块但扩展没开。加上参数,或写进 `xbintsc.config.json` 让它始终生效:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
xbintsc run app.ts --ext node
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### `module '<pkg>' is not supported: xbintsc can import built-in platform modules, relative '.ts' files and ESM packages under node_modules; CommonJS packages are not supported`
|
|
48
|
+
|
|
49
|
+
一个没人认领、也链不进来的裸第三方导入。要么该包是 CommonJS(不支持),要么它是
|
|
50
|
+
打包失败的 ESM 包。优先改用 `node` 扩展提供的 Node 内置模块;可打包的范围见
|
|
51
|
+
[extensions.md](./extensions.md)。
|
|
52
|
+
|
|
53
|
+
### ``CommonJS `require()` is not supported``
|
|
54
|
+
|
|
55
|
+
改写为 ESM:`const fs = require("fs")` 变成 `import fs from "fs"`(并加
|
|
56
|
+
`--ext node`)。`node_modules` 包内部的 `require` 由打包器处理;只有你自己的源码里
|
|
57
|
+
会被拒绝。`require(<非常量表达式>)` 永远不支持。
|
|
58
|
+
|
|
59
|
+
### `error TS4005: xbintsc does not yet support this <construct>`
|
|
60
|
+
|
|
61
|
+
语法解析通过了,但代码生成没实现。查 [language-support.md](./language-support.md) 与
|
|
62
|
+
[../../zh-CN/unimplemented.md](../../zh-CN/unimplemented.md),然后改写该构造(或者
|
|
63
|
+
贡献实现)。常见的是 `namespace` 声明与 `new.target`。
|
|
64
|
+
|
|
65
|
+
### `error TS2xxx`(语法分析)或 `error TS1xxx`(词法分析)
|
|
66
|
+
|
|
67
|
+
源码语法错误。诊断会打印文件、行、列、出错的那一行以及插入符。注意 xbintsc 的
|
|
68
|
+
解析器接受绝大多数 TypeScript,所以语法错误通常意味着语法真的坏了,而不是"不支持"。
|
|
69
|
+
|
|
70
|
+
### `error TS6001: Cannot resolve module './x' from '<file>'` / `Cannot find module`
|
|
71
|
+
|
|
72
|
+
相对导入解析不到。`./helper.js` 说明符会映射到 `helper.ts`,所以要按你实际写的
|
|
73
|
+
TypeScript 运行时路径导入。
|
|
74
|
+
|
|
75
|
+
### `Cannot resolve module '…' required from '<file>'`
|
|
76
|
+
|
|
77
|
+
被打包的 `node_modules` 包内部的 `require(...)` 无法解析。该包不能直接用;请优先
|
|
78
|
+
使用内置模块或换一个依赖。
|
|
79
|
+
|
|
80
|
+
### `xbintsc: <config path>: invalid JSON (…)` / `Unable to read …`
|
|
81
|
+
|
|
82
|
+
项目配置或原生扩展清单格式错误或不存在。修好 JSON,或用 `--no-config` /
|
|
83
|
+
`--config <path>` 绕开自动发现。
|
|
84
|
+
|
|
85
|
+
### `error TS6003` / `Command failed (N): clang …`
|
|
86
|
+
|
|
87
|
+
clang 自身失败。抛出的消息包含完整的 clang 命令与 stderr——请读 stderr,不要只看
|
|
88
|
+
第一行。常见原因:
|
|
89
|
+
|
|
90
|
+
- 工具链太旧(见上文);
|
|
91
|
+
- Windows 上 clang 因为环境没导入而找不到 MSVC/SDK 头文件或库。请在
|
|
92
|
+
**x64 Native Tools Command Prompt for VS 2022**(ARM64 机器上是 **ARM64 Native
|
|
93
|
+
Tools**)里运行,或先导入环境:
|
|
94
|
+
```powershell
|
|
95
|
+
& "$env:ProgramFiles\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" x64
|
|
96
|
+
```
|
|
97
|
+
- Linux 上链接器不对——设置 `xbintsc_CLANG`,并/或传
|
|
98
|
+
`xbintsc_LINKER_ARGS=-fuse-ld=lld`;
|
|
99
|
+
- `No C compiler found. Set xbintsc_CLANG to a clang binary.` —— `PATH` 里完全没有
|
|
100
|
+
clang。
|
|
101
|
+
|
|
102
|
+
### `GUI native library not found at …`
|
|
103
|
+
|
|
104
|
+
`gui` 扩展需要各平台预编译的 `gui.a`/`gui.lib`,普通源码检出并没有构建它。去掉
|
|
105
|
+
`--ext gui`,或按 [../../zh-CN/gui.md](../../zh-CN/gui.md) 构建该归档。
|
|
106
|
+
|
|
107
|
+
### `Icon file not found` / 图标工具缺失
|
|
108
|
+
|
|
109
|
+
`--icon`/`app.icon` 的路径相对配置文件目录解析。Windows 上嵌入 PE 图标需要
|
|
110
|
+
`llvm-rc` 或 `windres`;`doctor` 会报告找到了哪个,只有在确实请求了图标时缺失才是
|
|
111
|
+
致命错误。
|
|
112
|
+
|
|
113
|
+
## 看起来不对、其实正常的构建行为
|
|
114
|
+
|
|
115
|
+
### 构建打印了 `(cached)` 并什么都没做
|
|
116
|
+
|
|
117
|
+
增量缓存命中了:源码哈希、编译器版本、选项、平台与扩展集合都没变,且所有产物都还在。
|
|
118
|
+
这正是设计目标。要强制重建就用 `--force`。
|
|
119
|
+
|
|
120
|
+
### 改了参数后产物是旧的
|
|
121
|
+
|
|
122
|
+
对象缓存的键刻意不含编译参数,这正是那些会改参数的脚本(如运行时覆盖率)使用独立
|
|
123
|
+
`xbintsc_CACHE_DIR` 的原因。如果怀疑产物过期,传 `--force` 或删掉 `.xbintsc/`。
|
|
124
|
+
|
|
125
|
+
### `run` 返回非零但没有错误信息
|
|
126
|
+
|
|
127
|
+
`run` 透传的是**被运行程序**的退出码,编译器本身成功了。直接跑 `./build/app` 看程序
|
|
128
|
+
自己的输出。
|
|
129
|
+
|
|
130
|
+
### `xbintsc: run requires --emit exe`
|
|
131
|
+
|
|
132
|
+
`run` 只能执行可执行文件。要看 IR 请用 `build --emit ir` 或 `emit`。
|
|
133
|
+
|
|
134
|
+
### 程序输出顺序出乎意料
|
|
135
|
+
|
|
136
|
+
`async`/`await` 是同步微任务模型,且事件循环只在程序主体之后运行,所以定时器和套接字
|
|
137
|
+
回调会晚触发;见 [language-support.md](./language-support.md)。
|
|
138
|
+
|
|
139
|
+
## 该去源码哪里看
|
|
140
|
+
|
|
141
|
+
| 症状 | 源码 |
|
|
142
|
+
| --- | --- |
|
|
143
|
+
| 某条诊断文本或代码 | [../../../src/diagnostics/diagnostic.ts](../../../src/diagnostics/diagnostic.ts),以及 `src/` 下的发出点 |
|
|
144
|
+
| 某个特性缺失 | [../../../src/codegen/](../../../src/codegen/) —— `UnsupportedFeature` 在那里抛出 |
|
|
145
|
+
| 导入解析不了 | [../../../src/driver/bundler/](../../../src/driver/bundler/) |
|
|
146
|
+
| clang 调用、链接参数 | [../../../src/driver/toolchain.ts](../../../src/driver/toolchain.ts)、[../../../src/driver/toolchain-provider.ts](../../../src/driver/toolchain-provider.ts) |
|
|
147
|
+
| 缓存行为 | [../../../src/driver/cache.ts](../../../src/driver/cache.ts) |
|
|
148
|
+
| 运行时崩溃、GC、值 | `runtime/*.c`、`runtime/rt.h` |
|
|
149
|
+
|
|
150
|
+
如果你要改的是编译器本身而不是用它,接着读 [contributing.md](./contributing.md)。
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
# GUI extension — DOM handles, events and AOT `<script>`
|
|
2
|
+
|
|
3
|
+
Status: **design** — this document specifies how the `gui` extension grows from
|
|
4
|
+
read-only HTML/CSS rendering (M7) into a small, *interactive* DOM with
|
|
5
|
+
**compile-time (AOT) scripts**, without adding a JavaScript engine and without
|
|
6
|
+
letting GUI concerns leak into the core compiler.
|
|
7
|
+
|
|
8
|
+
It is the plan behind milestones **M8** (DOM object model + mutation + element
|
|
9
|
+
events) and **M9** (AOT `<script>` pipeline). The locked decisions in
|
|
10
|
+
`doc/gui.md` still hold; this document only refines what "no page JS" means.
|
|
11
|
+
|
|
12
|
+
## Motivation
|
|
13
|
+
|
|
14
|
+
Today the engine renders an HTML/CSS tree and exposes only *window-level*
|
|
15
|
+
events (`win.on("click", …)`). The handler receives `e.target` as a CSS
|
|
16
|
+
descriptor string (`div#main.card`), the DOM is read-only (diagnostics only),
|
|
17
|
+
and the only way to change the UI is `win.loadHTML(...)`, which rebuilds
|
|
18
|
+
everything.
|
|
19
|
+
|
|
20
|
+
That is enough for static demos, but the obvious next question — "can I write
|
|
21
|
+
`<script>` and react to clicks on a specific element?" — has no answer yet.
|
|
22
|
+
xbintsc is a **pure AOT compiler** and has **no runtime interpreter/JIT**, so
|
|
23
|
+
`eval`-style script execution is impossible. The only self-consistent way to
|
|
24
|
+
run scripts is to **compile them ahead of time**.
|
|
25
|
+
|
|
26
|
+
## Goals
|
|
27
|
+
|
|
28
|
+
1. **Element handles** — `document.querySelector(...)` returns an object with
|
|
29
|
+
stable identity (`a === b` for the same element) and readable/writable DOM
|
|
30
|
+
properties (`id`, `className`, `textContent`, `innerHTML`, `style`,
|
|
31
|
+
`classList`, attributes, traversal, geometry).
|
|
32
|
+
2. **Mutation** — `appendChild` / `removeChild` / `insertBefore` /
|
|
33
|
+
`replaceChild` / `textContent=` / `innerHTML=` / `classList.*` /
|
|
34
|
+
`style.setProperty` update the tree; the engine restyles + relayouts lazily
|
|
35
|
+
and repaints.
|
|
36
|
+
3. **Element events** — `el.addEventListener(type, fn, options?)` with capture
|
|
37
|
+
and bubble phases, `removeEventListener`, `dispatchEvent`, `el.click()`,
|
|
38
|
+
event objects with `target` / `currentTarget` / `preventDefault` /
|
|
39
|
+
`stopPropagation`.
|
|
40
|
+
4. **AOT `<script>`** — a `<script>` body written in TypeScript is extracted at
|
|
41
|
+
**compile time**, compiled by the *existing* xbintsc front-end as a normal
|
|
42
|
+
module, and invoked by the engine after the document is parsed.
|
|
43
|
+
5. **Zero core-compiler coupling** — the lexer/parser/binder/codegen learn
|
|
44
|
+
nothing about HTML or the GUI. Extensions contribute only modules/objects
|
|
45
|
+
(and, new in M9, an **asset-loader hook**).
|
|
46
|
+
6. **Backward compatibility** — existing programs and tests keep working:
|
|
47
|
+
`win.on(...)` handlers keep receiving a *string* `e.target`.
|
|
48
|
+
|
|
49
|
+
## Non-goals
|
|
50
|
+
|
|
51
|
+
- **A JS engine** (QuickJS/V8/…). Still explicitly out of scope. There is no
|
|
52
|
+
runtime `eval`, no interpreter, no JIT.
|
|
53
|
+
- **Dynamic scripts at runtime.** Scripts are compile-time assets. HTML fetched
|
|
54
|
+
over the network or produced at runtime does **not** execute its scripts.
|
|
55
|
+
- **A security sandbox / browser semantics.** There is no origin model; a
|
|
56
|
+
script is ordinary native TS and can `import` anything the compiler allows.
|
|
57
|
+
This is documented as "not a browser".
|
|
58
|
+
- **Full DOM/BOM.** We implement the practical subset a UI needs.
|
|
59
|
+
|
|
60
|
+
## Locked decisions (refined)
|
|
61
|
+
|
|
62
|
+
| # | Decision |
|
|
63
|
+
| --- | --- |
|
|
64
|
+
| 1 | **No page JS engine.** Behaviour is native TS. `<script>` bodies are *AOT-compiled* to native code by xbintsc and invoked through `xt_call_with_this` — there is still no interpreter. |
|
|
65
|
+
| 8 | **Scripts are compile-time assets.** The HTML asset loader transforms inline `<script>` bodies into compiled modules and replaces them with `data-xt-id` markers; the runtime only *invokes* already-compiled functions. |
|
|
66
|
+
| 9 | **The DOM is owned by the engine; handles are engine objects.** Handles use the same shared-prototype object model as window handles; hidden fields carry a window reference, a document generation and a node index. |
|
|
67
|
+
| 10 | **`window`/`document` are injected as parameters**, not globals, so no global-object machinery is added to the compiler. |
|
|
68
|
+
|
|
69
|
+
## Architecture
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
<script>…</script> compile time (bundler)
|
|
73
|
+
│ gui .html asset loader
|
|
74
|
+
▼
|
|
75
|
+
┌──────────────────────┐ ┌───────────────────────────┐
|
|
76
|
+
│ transform the HTML │ │ emit __xt_script_<hash> │ a normal
|
|
77
|
+
│ body → function │ │ (window, document) │ xbintsc
|
|
78
|
+
│ <script data-xt-id> │ │ + __registerScript(hash) │ module
|
|
79
|
+
└──────────────────────┘ └───────────────────────────┘
|
|
80
|
+
│ │
|
|
81
|
+
▼ ▼
|
|
82
|
+
runtime HTML string native code linked into the binary
|
|
83
|
+
│ win.loadHTML(html)
|
|
84
|
+
▼
|
|
85
|
+
engine parses HTML, finds `data-xt-id` markers, looks each hash up in the
|
|
86
|
+
script registry and calls `fn(windowHandle, documentHandle)`
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 1. `Extension.assetLoaders` (the only new core hook)
|
|
90
|
+
|
|
91
|
+
The core compiler currently has no way for an extension to influence *how a
|
|
92
|
+
file is loaded*. M9 adds one generic hook to `src/extensions/registry.ts`:
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
interface AssetLoadResult {
|
|
96
|
+
/** Replaces the file contents as seen by the bundler. */
|
|
97
|
+
moduleSource: string;
|
|
98
|
+
/** Additional files this asset depends on (recompiled when they change). */
|
|
99
|
+
dependencies?: string[];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
interface Extension {
|
|
103
|
+
// …existing fields…
|
|
104
|
+
/** Optional: rewrite an imported asset into a TS module. Keyed by extension. */
|
|
105
|
+
assetLoaders?(): Record<string, (path: string, source: string) => AssetLoadResult>;
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The bundler (`src/driver/bundler/graph.ts`) consults the registry immediately
|
|
110
|
+
after `readFileSync`, *before* parsing. The core stays platform-agnostic: it
|
|
111
|
+
only knows "an extension may transform the bytes of `*.foo` into TS".
|
|
112
|
+
The `gui` extension registers an `.html` loader. A plain `import html from
|
|
113
|
+
"./index.html"` therefore yields a **string constant** by default, and (when
|
|
114
|
+
the file contains `<script>`) a **side-effecting module** that registers the
|
|
115
|
+
compiled script bodies and default-exports the rewritten HTML.
|
|
116
|
+
|
|
117
|
+
### 2. The `.html` transform
|
|
118
|
+
|
|
119
|
+
Given:
|
|
120
|
+
|
|
121
|
+
```html
|
|
122
|
+
<button id="b">0</button>
|
|
123
|
+
<script lang="ts">
|
|
124
|
+
const b = document.getElementById("b");
|
|
125
|
+
let n = 0;
|
|
126
|
+
b.addEventListener("click", () => { b.textContent = String(++n); });
|
|
127
|
+
</script>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
the loader emits a module shaped like:
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
// generated
|
|
134
|
+
const __html = "<button id=\"b\">0</button>\n<script data-xt-id=\"a1b2…\"></script>";
|
|
135
|
+
function __xt_script_a1b2(window: any, document: any): void {
|
|
136
|
+
const b = document.getElementById("b");
|
|
137
|
+
let n = 0;
|
|
138
|
+
b.addEventListener("click", () => { b.textContent = String(++n); });
|
|
139
|
+
}
|
|
140
|
+
__registerScript("a1b2…", __xt_script_a1b2);
|
|
141
|
+
export default __html;
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
- The id is a content hash of the body (sha256, first 32 hex chars); the body is
|
|
145
|
+
replaced by an empty marker element so the engine can find it in document
|
|
146
|
+
order.
|
|
147
|
+
- `window` and `document` are **function parameters**, so script globals resolve
|
|
148
|
+
as ordinary locals — **no compiler changes**, no global object.
|
|
149
|
+
- `__registerScript(id, fn)` is a new runtime builtin that stores the closure in
|
|
150
|
+
a global `unordered_map<string, xt_value>`.
|
|
151
|
+
- Deferred scripts (`<script defer>`, or all inline scripts, as decided in M9c)
|
|
152
|
+
run after the full document is parsed and the first layout is computed, in
|
|
153
|
+
document order. `DOMContentLoaded` then `load` are fired on `window`.
|
|
154
|
+
|
|
155
|
+
### 2b. External `<script src>`
|
|
156
|
+
|
|
157
|
+
`<script src="./app.ts">` is read relative to the HTML file, parsed, and split:
|
|
158
|
+
its top-level `import`/`export … from` statements are **hoisted** to the
|
|
159
|
+
generated module (with relative specifiers rewritten so they resolve from the
|
|
160
|
+
HTML file's directory), and the remaining statements are wrapped in the script
|
|
161
|
+
function. This is how an external script still runs at `loadHTML` time while
|
|
162
|
+
being able to `import` other modules.
|
|
163
|
+
|
|
164
|
+
Two limitations are enforced with clear errors rather than silent breakage:
|
|
165
|
+
|
|
166
|
+
- a missing `src` file throws (turned into a bundler diagnostic), and
|
|
167
|
+
- two `src` scripts that import the same local binding name collide; the user
|
|
168
|
+
must alias one of them. (Non-conflicting or identical imports are fine.)
|
|
169
|
+
|
|
170
|
+
`src` URLs (`https://…`, `data:…`, `//host`) are left untouched — they are not
|
|
171
|
+
compile-time assets and never run.
|
|
172
|
+
|
|
173
|
+
```html
|
|
174
|
+
<button id="b">0</button>
|
|
175
|
+
<script src="./counter.ts"></script>
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
// counter.ts
|
|
180
|
+
import { double } from "./helper";
|
|
181
|
+
const b = document.getElementById("b");
|
|
182
|
+
let n = 0;
|
|
183
|
+
b.addEventListener("click", () => { n = double(n) + 1; b.textContent = String(n); });
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### 3. Runtime execution
|
|
187
|
+
|
|
188
|
+
`win.loadHTML(html)`:
|
|
189
|
+
|
|
190
|
+
1. parses the document as today,
|
|
191
|
+
2. scans for `data-xt-id` markers,
|
|
192
|
+
3. for each, calls `registry[id](winHandle, docHandle)`,
|
|
193
|
+
4. triggers the first layout, then fires `DOMContentLoaded` and `load`.
|
|
194
|
+
|
|
195
|
+
Because the calls happen after parsing, `document.getElementById(...)` inside a
|
|
196
|
+
script always finds its element. `innerHTML = …` never executes embedded
|
|
197
|
+
scripts (markers are inert).
|
|
198
|
+
|
|
199
|
+
## Element handle object model (M8)
|
|
200
|
+
|
|
201
|
+
Handles are plain runtime objects (`xt_object_new_with_proto`) sharing a
|
|
202
|
+
prototype per kind, exactly like window handles:
|
|
203
|
+
|
|
204
|
+
| Hidden field | Meaning |
|
|
205
|
+
| --- | --- |
|
|
206
|
+
| `__xt_gui_win` | owning window object (resolved by `xt_gui_window_from_this`) |
|
|
207
|
+
| `__xt_gui_gen` | document generation the handle was created for |
|
|
208
|
+
| `__xt_gui_node` | index into the window's node table (element/text handles) |
|
|
209
|
+
| `__xt_gui_doc` | `true` for the singleton `document` handle |
|
|
210
|
+
|
|
211
|
+
**Identity is stable**: the window keeps `node_order: vector<Node*>` plus
|
|
212
|
+
`node_index`/`node_handles: map<Node*, …>`, so querying the same element twice
|
|
213
|
+
returns the *same* object value (`a === b`).
|
|
214
|
+
|
|
215
|
+
**Detached nodes** stay alive in the document pool, so a handle held across a
|
|
216
|
+
`remove()` still resolves (tombstoned semantics); a generation mismatch makes a
|
|
217
|
+
stale handle a no-op instead of a dangling pointer.
|
|
218
|
+
|
|
219
|
+
### Accessors, not fields
|
|
220
|
+
|
|
221
|
+
Read/write DOM properties are defined with `xt_object_define_getter` /
|
|
222
|
+
`xt_object_define_setter`, which already support prototype inheritance with
|
|
223
|
+
`this` = receiver. So `el.textContent = "x"` and `el.id = "y"` work while the
|
|
224
|
+
values live in the C++ tree. `classList` and `style` are small objects that hold
|
|
225
|
+
a back-reference to the element handle (no closures/env needed; the arena never
|
|
226
|
+
frees, so cycles are harmless).
|
|
227
|
+
|
|
228
|
+
### Implemented surface (M8)
|
|
229
|
+
|
|
230
|
+
- `document`: `querySelector`, `querySelectorAll`, `getElementById`,
|
|
231
|
+
`createElement`, `createTextNode`, `body`, `documentElement`,
|
|
232
|
+
`addEventListener` / `removeEventListener` / `dispatchEvent`.
|
|
233
|
+
- node (element/text/document): `tagName` / `nodeName` / `nodeType`, `id`,
|
|
234
|
+
`className`, `textContent` / `innerText`, `innerHTML`, `parentNode` /
|
|
235
|
+
`parentElement`, `children` / `childNodes` / `childElementCount`,
|
|
236
|
+
`firstElementChild` / `lastElementChild`, `nextElementSibling` /
|
|
237
|
+
`previousElementSibling`, `isConnected`, `classList`, `style`.
|
|
238
|
+
- attributes: `getAttribute` / `setAttribute` / `hasAttribute` /
|
|
239
|
+
`removeAttribute`.
|
|
240
|
+
- queries: `querySelector` / `querySelectorAll` / `matches` (subtree-scoped).
|
|
241
|
+
- mutation: `appendChild` / `insertBefore` / `removeChild` / `replaceChild` /
|
|
242
|
+
`remove` / `cloneNode(deep?)`.
|
|
243
|
+
- geometry/focus: `getBoundingClientRect`, `focus`, `blur`, `click`.
|
|
244
|
+
- events: `addEventListener(type, fn, optionsOrCapture?)`,
|
|
245
|
+
`removeEventListener`, `dispatchEvent`.
|
|
246
|
+
|
|
247
|
+
### Lazy restyle / relayout
|
|
248
|
+
|
|
249
|
+
Every mutation calls `XtDocument::invalidate()` and sets `win->struct_dirty`.
|
|
250
|
+
Before any synchronous read (`computedStyle`, `getBoundingClientRect`,
|
|
251
|
+
`queryCount`, `layoutTree`, `paintList`, `hitTest`, …) and before each frame,
|
|
252
|
+
`xt_gui_flush_dom()` restyles + relayouts **once** if anything is pending. This
|
|
253
|
+
keeps mutations cheap and batched while making reads immediately consistent.
|
|
254
|
+
|
|
255
|
+
## Events and bubbling
|
|
256
|
+
|
|
257
|
+
Element listeners are stored on the window (`node_listeners`), keyed by node
|
|
258
|
+
(not on `Node` itself, so the tree stays layout-focused).
|
|
259
|
+
|
|
260
|
+
Dispatch:
|
|
261
|
+
|
|
262
|
+
1. build the propagation path `target → … → root`,
|
|
263
|
+
2. create an Event object (`type`, `target` = node handle, `currentTarget`,
|
|
264
|
+
`bubbles`, `defaultPrevented`, internal `__stop` / `__stopImmediate`),
|
|
265
|
+
3. **capture** phase: root → target (listeners registered with `capture: true`),
|
|
266
|
+
4. **bubble** phase: target → root,
|
|
267
|
+
5. legacy window-level `win.on(type, fn)` handlers run **last**.
|
|
268
|
+
|
|
269
|
+
`once` listeners are removed before being called; `stopPropagation` ends the
|
|
270
|
+
current phase chain and `stopImmediatePropagation` also skips later listeners on
|
|
271
|
+
the same node. `el.click()` synthesises `click` at that node (DOM only);
|
|
272
|
+
real pointer/wheel/key input dispatches **both** the DOM event and the legacy
|
|
273
|
+
window payload.
|
|
274
|
+
|
|
275
|
+
### Backward compatibility
|
|
276
|
+
|
|
277
|
+
The legacy window payload keeps its **string** `e.target`
|
|
278
|
+
(`tests/e2e/gui-*.test.ts` asserts `e.target === "div#inner"`). Only the *element*
|
|
279
|
+
Event's `target` is a handle, whose descriptor (`toString`) matches the same
|
|
280
|
+
`div#id.class` string. `make_dom_event` deliberately does **not** copy the
|
|
281
|
+
legacy `target` field over the handle.
|
|
282
|
+
|
|
283
|
+
## Milestones
|
|
284
|
+
|
|
285
|
+
- **M8 — DOM object model + mutation + element events** ✅
|
|
286
|
+
- `runtime/ext_gui/dom_api.{h,cpp}`, `document.{h,cpp}`, `dom.{h,cpp}`,
|
|
287
|
+
`gui_engine.h`, `gui.cpp`, `window.cpp`, e2e coverage.
|
|
288
|
+
- No compiler changes; independently testable.
|
|
289
|
+
- **M9 — AOT `<script>`** ✅
|
|
290
|
+
- **M9a** ✅ — `Extension.assetLoaders` in `src/extensions/registry.ts` +
|
|
291
|
+
bundler integration in `src/driver/bundler/{graph,merge}.ts`; unit tests.
|
|
292
|
+
- **M9b** ✅ — gui `.html` loader (`src/extensions/gui/html.ts`), the
|
|
293
|
+
`__registerScript` builtin, the script registry (`runtime/ext_gui/script.cpp`),
|
|
294
|
+
`loadHTML` execution and `DOMContentLoaded`/`load`; e2e coverage.
|
|
295
|
+
- **M9c** ✅ — `<script src>` files are read, their top-level imports are
|
|
296
|
+
hoisted (specifiers rewritten to resolve from the HTML file) and their body
|
|
297
|
+
is wrapped and registered; missing files and import-binding collisions
|
|
298
|
+
surface as diagnostics. All scripts run in document order (effectively
|
|
299
|
+
deferred). e2e coverage.
|
|
300
|
+
- **M9d** *(optional)* — detect inline HTML in template literals passed to
|
|
301
|
+
`win.loadHTML(...)` and transform them too (fragile; deferred).
|
|
302
|
+
- **M10 — polish** ✅ — `requestAnimationFrame`/`cancelAnimationFrame` on the
|
|
303
|
+
window, `offsetWidth`/`offsetHeight`/`contains` on elements, docs.
|
|
304
|
+
|
|
305
|
+
### Landing order
|
|
306
|
+
|
|
307
|
+
1. M8 (this branch) — pure engine work, no compiler impact.
|
|
308
|
+
2. M9a — the generic hook + bundler wiring (unit-testable with a fake loader).
|
|
309
|
+
3. M9b — the minimal end-to-end script path (inline `<script>` only).
|
|
310
|
+
4. M9c — external `<script src>`.
|
|
311
|
+
5. M10 — animation frames and helpers.
|
|
312
|
+
|
|
313
|
+
## Open risks
|
|
314
|
+
|
|
315
|
+
- **Handle lifetime.** Solved with a generation counter + detached pool; stale
|
|
316
|
+
handles no-op. `Node*` addresses are never exposed to TS.
|
|
317
|
+
- **No sandbox.** A `<script>` can `import fs`. Documented as "not a browser";
|
|
318
|
+
a real origin/sandbox model is out of scope.
|
|
319
|
+
- **Dynamic HTML scripts.** Scripts in runtime-created HTML (`innerHTML`,
|
|
320
|
+
network fetches) do not run. Documented.
|
|
321
|
+
- **Template-literal HTML** passed to `win.loadHTML(…)` is not transformed in
|
|
322
|
+
M9b (only imported assets are). M9d addresses this if needed.
|
|
323
|
+
- **Import-binding collisions** between external `<script src>` files. Detected
|
|
324
|
+
and reported (alias the import); a per-script rename with reference rewriting
|
|
325
|
+
is possible later.
|
|
326
|
+
- **Assets.** `<script src>` is resolved relative to the HTML asset; URL `src`
|
|
327
|
+
values are ignored. There is no fetch/network loading.
|
|
328
|
+
|
|
329
|
+
## Testing
|
|
330
|
+
|
|
331
|
+
- **M8**: a new e2e case in `tests/e2e/gui-*.test.ts` covering handle identity,
|
|
332
|
+
traversal, attributes, `classList`, inline `style`, `createElement` +
|
|
333
|
+
`appendChild` + `removeChild`, `getBoundingClientRect`, element bubbling,
|
|
334
|
+
`stopPropagation`, `el.click()` and **window-level `e.target` compatibility**.
|
|
335
|
+
- **M9a**: a unit test with a fake extension loader asserting the bundler
|
|
336
|
+
rewrites `*.foo` and records dependencies.
|
|
337
|
+
- **M9b**: an e2e case with an inline `<script>` incrementing a counter on
|
|
338
|
+
click, asserted through `console.log` from the handler.
|
|
339
|
+
- **M10**: e2e cases for `requestAnimationFrame` (runs each frame, re-queues,
|
|
340
|
+
receives a timestamp, honours `cancelAnimationFrame`) and for
|
|
341
|
+
`offsetWidth`/`offsetHeight`/`contains`.
|
|
342
|
+
|
|
343
|
+
## Docs to update
|
|
344
|
+
|
|
345
|
+
- `doc/gui.md` — Non-goals ("Executing page `<script>`" → "Executing *runtime*
|
|
346
|
+
page scripts; compile-time scripts are AOT-compiled"), locked decision #1,
|
|
347
|
+
TS-facing API, milestones, progress log.
|
|
348
|
+
- `doc/implemented.md` / `doc/unimplemented.md` — move the new DOM/script
|
|
349
|
+
capabilities across.
|
|
350
|
+
- `README.md` — mention the DOM API + AOT scripts.
|