xbintsc 0.3.87 → 0.5.6

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 (85) hide show
  1. package/AGENTS.md +8 -4
  2. package/README.md +4 -3
  3. package/README.zh-CN.md +2 -2
  4. package/dist/src/codegen/generator/calls/access.js +6 -2
  5. package/dist/src/codegen/generator/calls/access.js.map +1 -1
  6. package/dist/src/codegen/generator/calls/closures.js +4 -0
  7. package/dist/src/codegen/generator/calls/closures.js.map +1 -1
  8. package/dist/src/codegen/generator/calls/invocation.js +4 -3
  9. package/dist/src/codegen/generator/calls/invocation.js.map +1 -1
  10. package/dist/src/codegen/generator/expressions/primary.js +6 -1
  11. package/dist/src/codegen/generator/expressions/primary.js.map +1 -1
  12. package/dist/src/codegen/generator/module.d.ts +0 -1
  13. package/dist/src/codegen/generator/module.js +10 -10
  14. package/dist/src/codegen/generator/module.js.map +1 -1
  15. package/dist/src/codegen/generator/state.js +4 -0
  16. package/dist/src/codegen/generator/state.js.map +1 -1
  17. package/dist/src/codegen/generator/statements/control-flow.js +1 -3
  18. package/dist/src/codegen/generator/statements/control-flow.js.map +1 -1
  19. package/dist/src/codegen/generator/tables.d.ts +9 -0
  20. package/dist/src/codegen/generator/tables.js +74 -2
  21. package/dist/src/codegen/generator/tables.js.map +1 -1
  22. package/dist/src/driver/compiler.d.ts +2 -2
  23. package/dist/src/driver/compiler.js +4 -1
  24. package/dist/src/driver/compiler.js.map +1 -1
  25. package/dist/tests/codegen/closures.test.js +8 -0
  26. package/dist/tests/codegen/closures.test.js.map +1 -1
  27. package/dist/tests/e2e/async-await.test.d.ts +10 -0
  28. package/dist/tests/e2e/async-await.test.js +247 -0
  29. package/dist/tests/e2e/async-await.test.js.map +1 -0
  30. package/dist/tests/e2e/runtime-coverage-globals.test.js +46 -0
  31. package/dist/tests/e2e/runtime-coverage-globals.test.js.map +1 -1
  32. package/dist/tests/e2e/string-utf16.test.d.ts +6 -0
  33. package/dist/tests/e2e/string-utf16.test.js +133 -0
  34. package/dist/tests/e2e/string-utf16.test.js.map +1 -0
  35. package/doc/ai/contributing.md +9 -7
  36. package/doc/ai/extensions.md +3 -2
  37. package/doc/ai/language-support.md +11 -8
  38. package/doc/ai/troubleshooting.md +4 -3
  39. package/doc/ai/zh-CN/contributing.md +5 -5
  40. package/doc/ai/zh-CN/extensions.md +2 -1
  41. package/doc/ai/zh-CN/language-support.md +11 -8
  42. package/doc/ai/zh-CN/troubleshooting.md +3 -2
  43. package/doc/implemented.md +15 -12
  44. package/doc/unimplemented.md +11 -10
  45. package/doc/zh-CN/implemented.md +14 -11
  46. package/doc/zh-CN/unimplemented.md +11 -10
  47. package/package.json +1 -1
  48. package/runtime/rt.h +32 -0
  49. package/runtime/rt_internal.h +99 -10
  50. package/runtime/xt_alloc.c +6 -0
  51. package/runtime/xt_async.c +197 -0
  52. package/runtime/xt_builtins.c +40 -36
  53. package/runtime/xt_constructors.c +389 -0
  54. package/runtime/xt_containers/access.inc +47 -11
  55. package/runtime/xt_containers/arrays.inc +26 -2
  56. package/runtime/xt_containers/objects.inc +202 -5
  57. package/runtime/xt_coroutine.c +212 -0
  58. package/runtime/xt_generator.c +31 -162
  59. package/runtime/xt_io/inspect.inc +35 -2
  60. package/runtime/xt_promise.c +62 -17
  61. package/runtime/xt_stdlib/dispatch.inc +18 -2
  62. package/runtime/xt_stdlib/strings.inc +73 -16
  63. package/runtime/xt_stdlib2/array-statics.inc +3 -3
  64. package/runtime/xt_stdlib2/error.inc +11 -0
  65. package/runtime/xt_stdlib2/json.inc +44 -15
  66. package/runtime/xt_stdlib2/number-string-statics.inc +24 -15
  67. package/runtime/xt_stdlib2/objects.inc +1 -1
  68. package/runtime/xt_stdlib2/regexp-match.inc +2 -2
  69. package/runtime/xt_stdlib2/set.inc +6 -0
  70. package/runtime/xt_stdlib2/strings.inc +70 -40
  71. package/runtime/xt_typed_array/construction.inc +43 -2
  72. package/runtime/xt_typed_array.c +4 -0
  73. package/runtime/xt_values/constructors.inc +8 -0
  74. package/runtime/xt_values/strings.inc +19 -3
  75. package/runtime/xt_values/utf16.inc +340 -0
  76. package/runtime/xt_values.c +1 -0
  77. package/src/codegen/generator/calls/access.ts +7 -2
  78. package/src/codegen/generator/calls/closures.ts +3 -0
  79. package/src/codegen/generator/calls/invocation.ts +4 -3
  80. package/src/codegen/generator/expressions/primary.ts +6 -1
  81. package/src/codegen/generator/module.ts +10 -12
  82. package/src/codegen/generator/state.ts +4 -0
  83. package/src/codegen/generator/statements/control-flow.ts +1 -2
  84. package/src/codegen/generator/tables.ts +84 -2
  85. package/src/driver/compiler.ts +4 -1
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Differential coverage for JavaScript's UTF-16 string view over the runtime's
3
+ * UTF-8 storage: `length`, indexing, slicing, searching, splitting, padding,
4
+ * iteration, surrogate round-trips, JSON and RegExp offsets.
5
+ */
6
+ export {};
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Differential coverage for JavaScript's UTF-16 string view over the runtime's
3
+ * UTF-8 storage: `length`, indexing, slicing, searching, splitting, padding,
4
+ * iteration, surrogate round-trips, JSON and RegExp offsets.
5
+ */
6
+ import { expect, it } from "vitest";
7
+ import { describeE2E } from "./harness.js";
8
+ describeE2E("UTF-16 string semantics", (harness) => {
9
+ const { runProgram, expectSameOutputAsNode } = harness;
10
+ it("matches Node for length, indexing, slicing and searching", () => {
11
+ const source = `
12
+ const s = "中文😀aé";
13
+ const show = (v: unknown) => console.log(JSON.stringify(v));
14
+ show(s.length);
15
+ for (let i = 0; i < s.length; i++) show(s[i]);
16
+ for (let i = 0; i <= s.length; i++) show(s.codePointAt(i));
17
+ show(s.slice(0, 4));
18
+ show(s.slice(1, 5));
19
+ show(s.slice(2, 3));
20
+ show(s.slice(3, 4));
21
+ show(s.slice(-3));
22
+ show(s.substring(3, 1));
23
+ show(s.substring(1, 100));
24
+ show(s.substr(2, 2));
25
+ show(s.substr(-2));
26
+ show(s.charAt(2));
27
+ show(s.charCodeAt(2));
28
+ show(s.at(-1));
29
+ show(s.at(-5));
30
+ show(s.indexOf("😀"));
31
+ show(s.indexOf("a", 4));
32
+ show(s.indexOf("", 100));
33
+ show(s.lastIndexOf("中"));
34
+ show(s.lastIndexOf("😀", 3));
35
+ show(s.lastIndexOf("a", 2));
36
+ show(s.includes("😀"));
37
+ show(s.includes("文", 2));
38
+ show(s.startsWith("中文"));
39
+ show(s.startsWith("😀", 2));
40
+ show(s.endsWith("aé"));
41
+ show(s.endsWith("😀", 4));
42
+ show(s.split("😀"));
43
+ show(s.split("", 4));
44
+ show(s.split("a"));
45
+ show("😀".repeat(3));
46
+ show("x".padStart(4, "😀"));
47
+ show("x".padEnd(4, "😀"));
48
+ show("中".padStart(3, "ab"));
49
+ show("a😀b中".startsWith("😀", 2));
50
+ show("a😀b中".indexOf("😀", 2));
51
+ show("a😀b中".endsWith("a", 2));
52
+ show("a😀b中".lastIndexOf("😀", 2));
53
+ show("a😀b中".lastIndexOf("\\uDE00", 2));
54
+ show(s.length === 6);
55
+ `;
56
+ expectSameOutputAsNode(source, { name: "utf16_indexing" });
57
+ });
58
+ it("matches Node for iteration, keys, surrogate round-trips and JSON", () => {
59
+ const source = `
60
+ const s = "中文😀aé";
61
+ const show = (v: unknown) => console.log(JSON.stringify(v));
62
+ show([...s]);
63
+ show(Array.from(s));
64
+ show(Object.keys(s));
65
+ show(Object.values("😀"));
66
+ show(Object.entries("😀"));
67
+ const high = "\\uD83D";
68
+ const low = "\\uDE00";
69
+ show(high + low);
70
+ show((high + low).length);
71
+ show((high + low) === "😀");
72
+ show(String.fromCharCode(0x4e2d, 0xd83d, 0xde00));
73
+ show(String.fromCharCode(0x10000));
74
+ show(String.fromCodePoint(0x1f600));
75
+ show(String.fromCodePoint(0x4e2d));
76
+ show(JSON.stringify("\\uD83D"));
77
+ show(JSON.stringify("\\uDE00"));
78
+ show(JSON.stringify("\\uD83D\\uDE00"));
79
+ show(JSON.stringify("\\u{1F600}"));
80
+ show("\\u{1F600}".length);
81
+ show("\\u4e2d".length);
82
+ show(JSON.stringify(JSON.parse('"\\\\u4e2d\\\\ud83d\\\\ude00"')));
83
+ show(JSON.parse('"\\\\ud83d\\\\ude00"').length);
84
+ show(JSON.parse('"\\\\ud83d"').length);
85
+ let built = "";
86
+ for (const ch of s) built += ch;
87
+ show(built);
88
+ show(built === s);
89
+ let keys = "";
90
+ for (const key in "😀") keys += key;
91
+ show(keys);
92
+ show(JSON.stringify({ ..."😀" }));
93
+ show("a😀".replaceAll("", "-"));
94
+ show("a😀".replaceAll("😀", "中"));
95
+ show("a😀b".replace("", "-"));
96
+ `;
97
+ expectSameOutputAsNode(source, { name: "utf16_iteration_json" });
98
+ });
99
+ it("reports RegExp offsets in UTF-16 code units", () => {
100
+ const source = `
101
+ const show = (v: unknown) => console.log(JSON.stringify(v));
102
+ show("中😀x1".match(/1/)!.index);
103
+ show("中😀x".search(/x/));
104
+ show("中1".replace(/1/, (m: string, at: number) => String(at)));
105
+ show("a1b2".replace(/\\d/g, (m: string, at: number) => String(at)));
106
+ show("中1".replace(/1/, "#"));
107
+ show(JSON.stringify("中😀中".split(/😀/)));
108
+ `;
109
+ expectSameOutputAsNode(source, { name: "utf16_regex_offsets" });
110
+ });
111
+ it("normalizes surrogate pairs across lazy concatenation boundaries", () => {
112
+ const source = `
113
+ const prefix = "x".repeat(40);
114
+ const high = "\\uD83D";
115
+ const low = "\\uDE00";
116
+ const s = prefix + high + low;
117
+ const show = (v: unknown) => console.log(JSON.stringify(v));
118
+ show(s.length);
119
+ show(s.slice(-2));
120
+ show(s === prefix + "😀");
121
+ show(s.charCodeAt(40));
122
+ show(s.charCodeAt(41));
123
+ show(s.codePointAt(40));
124
+ show(s.endsWith("😀"));
125
+ show(s.indexOf("😀"));
126
+ `;
127
+ expectSameOutputAsNode(source, { name: "utf16_lazy_boundary" });
128
+ });
129
+ it("prints a lone surrogate as the replacement character", () => {
130
+ expect(runProgram('console.log("😀"[0], "中".length);', { name: "utf16_console" })).toBe("� 1");
131
+ });
132
+ });
133
+ //# sourceMappingURL=string-utf16.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"string-utf16.test.js","sourceRoot":"","sources":["../../../tests/e2e/string-utf16.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,WAAW,CAAC,yBAAyB,EAAE,CAAC,OAAO,EAAE,EAAE;IACjD,MAAM,EAAE,UAAU,EAAE,sBAAsB,EAAE,GAAG,OAAO,CAAC;IAEvD,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4ClB,CAAC;QACE,sBAAsB,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC1E,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqClB,CAAC;QACE,sBAAsB,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,sBAAsB,EAAE,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,MAAM,GAAG;;;;;;;;CAQlB,CAAC;QACE,sBAAsB,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,qBAAqB,EAAE,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iEAAiE,EAAE,GAAG,EAAE;QACzE,MAAM,MAAM,GAAG;;;;;;;;;;;;;;CAclB,CAAC;QACE,sBAAsB,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,qBAAqB,EAAE,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,MAAM,CAAC,UAAU,CAAC,mCAAmC,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACjG,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -76,13 +76,15 @@ nondeterminism (a `Map`/`Set` iteration over insertion-ordered data you did not
76
76
  control, a timestamp, a filesystem-order dependency) into codegen.
77
77
 
78
78
  A second consequence is the string model: the compiler's own source text has to
79
- mean the same thing in every generation. A compiled string is UTF-8 bytes, so the
80
- self-hosted `readFileSync` (`--ext node`) hands the scanner the bytes of a file,
81
- while Node hands it the same file already decoded — read source files through
82
- `decodeUtf8` ([src/diagnostics/utf8.ts](../../src/diagnostics/utf8.ts)) and never
83
- assume a code unit is a byte or a character. Getting this wrong shows up as a
84
- non-ASCII literal (`—` in a hint string) coming out re-encoded in `gen2.ll`, which
85
- is why the emitted IR is compared byte-for-byte rather than line-by-line.
79
+ mean the same thing in every generation. A compiled string stores UTF-8 bytes
80
+ but indexes them in UTF-16 code units, so the self-hosted `readFileSync`
81
+ (`--ext node`) hands the scanner a byte string (one byte per code unit, all
82
+ `<= 0xFF`), while Node hands it the same file already decoded — read source
83
+ files through `decodeUtf8` ([src/diagnostics/utf8.ts](../../src/diagnostics/utf8.ts))
84
+ and never assume a code unit is a byte or a character. Getting this wrong shows
85
+ up as a non-ASCII literal (`—` in a hint string) coming out re-encoded in
86
+ `gen2.ll`, which is why the emitted IR is compared byte-for-byte rather than
87
+ line-by-line.
86
88
 
87
89
  Verify locally:
88
90
 
@@ -54,8 +54,9 @@ Coverage per module (exact functions and options) is in
54
54
  [../node-unimplemented.md](../node-unimplemented.md).
55
55
 
56
56
  Remember the runtime model before building on `net`/`http`: the event loop runs
57
- **after** the program body, and `async`/`await` is a synchronous microtask
58
- model. See [language-support.md](./language-support.md).
57
+ **after** the program body, but `async`/`await` now suspends on pending promises,
58
+ so a promise settled from a socket callback can be awaited. See
59
+ [language-support.md](./language-support.md).
59
60
 
60
61
  ### `gui` — HTML/CSS window
61
62
 
@@ -31,7 +31,7 @@ Canonical detail (always current, much longer):
31
31
  | `return`, `throw` as expressions' operands | ✅ | |
32
32
  | `namespace` / `module` declaration | 🚫 | parse ✓, codegen ✗ |
33
33
  | `with`, `debugger` | 🚫 | not supported |
34
- | Top-level `await` | 🚫 | wrap it in an `async` function |
34
+ | Top-level `await` | ⚠️ | works by blocking on the event loop until the promise settles (it does not yield the module body like an ES module); prefer wrapping it in an `async` function |
35
35
 
36
36
  ## Expressions and operators
37
37
 
@@ -71,10 +71,11 @@ Canonical detail (always current, much longer):
71
71
 
72
72
  | Feature | Status | Notes |
73
73
  | --- | --- | --- |
74
- | `async` / `await`, `Promise`, `Promise.all` | ⚠️ | **synchronous microtask model** — `await` on a settled promise continues synchronously |
75
- | `setTimeout` and callbacks | ⚠️ | the event loop runs **after** the program body, so a promise settled from a timer cannot be awaited |
76
- | Sockets / servers (`http`, `net`, `dgram`) | ⚠️ | same model: callbacks run on the event loop after the program body |
77
- | A real async event loop, worker threads | 🚫 | not implemented |
74
+ | `async` / `await`, `Promise`, `Promise.all` | ✅ | real suspension: an `async` body runs on a stackful coroutine, and awaiting a **pending** promise pauses it until the promise settles. Awaiting an already-settled value still continues synchronously |
75
+ | `setTimeout` and callbacks | ✅ | timers run on the core event loop; `await new Promise(r => setTimeout(r, ms))` works |
76
+ | Sockets / servers (`http`, `net`, `dgram`) | ✅ | callbacks run on the event loop and can settle a promise you await (`net` / `http` examples included in the tests) |
77
+ | `fs/promises` | ⚠️ | awaitable, but each call performs its I/O synchronously before settling, so it does not yield while the I/O runs |
78
+ | `async` generators, worker threads | 🚫 | `async function*` is rejected; workers are child processes |
78
79
 
79
80
  ## Types: parsed, then erased
80
81
 
@@ -98,13 +99,14 @@ Implemented: `Math`, `JSON`, `Date`, `RegExp`, `Map`, `Set`, `WeakMap`/`WeakSet`
98
99
  `Symbol`, `Error` family, `BigInt`, `Array`/`String`/`Number`/`Object` methods,
99
100
  typed arrays, `String.prototype.match`/`split`/`replace` with capture groups,
100
101
  immutable array helpers (`toReversed`, `toSorted`, `toSpliced`, `with`), global
101
- URI functions.
102
+ URI functions, first-class built-in constructors (`const A = Array; new A(...)`,
103
+ `A.isArray`, `x instanceof A`) and `Object.prototype`
104
+ (`Object.getPrototypeOf({}) === Object.prototype`).
102
105
 
103
106
  | Missing or different | Status |
104
107
  | --- | --- |
105
108
  | `String.prototype.normalize`, `structuredClone` | 🚫 |
106
109
  | `Error.stack` capture | ⚠️ function-level frames (name/file/line), no columns; generator bodies are omitted |
107
- | `Object.getPrototypeOf({})` | ⚠️ returns `undefined`, not `Object.prototype` |
108
110
  | Global RegExp `lastIndex` | ⚠️ `test`/`exec` ignore it for `/g` and `/y` |
109
111
  | Sparse array holes, array out-of-bounds | ⚠️ holes are not tracked distinctly; out-of-bounds reads give `undefined` |
110
112
 
@@ -128,7 +130,8 @@ See section 3 of [../unimplemented.md](../unimplemented.md) for the full list.
128
130
 
129
131
  | Item | Behaviour |
130
132
  | --- | --- |
131
- | `String.prototype.length` | ⚠️ **UTF-8 bytes**, not UTF-16 units: `"é".length === 1`, `"😀".length === 4`; `codePointAt` differs accordingly |
133
+ | `String.prototype.length` | ✅ UTF-16 code units (`"中文😀".length === 4`); indexing, `slice`, `indexOf`, `split`, `padStart` and `codePointAt` use the same model. Storage stays UTF-8 |
134
+ | RegExp Unicode matching | ⚠️ the engine is byte-oriented: a literal `中` still matches, but `.` and character classes consume one byte per repetition, and `\d`/`\w` stay ASCII. Match offsets (`match.index`, `search`, replacement callbacks) are reported in UTF-16 code units |
132
135
  | Number formatting and coercion | ✅ matches Node (`toFixed`, `toPrecision`, hex/octal/binary parsing) |
133
136
  | `BigInt` | ✅ implemented |
134
137
  | Garbage collection | ✅ non-moving mark-sweep; explicit roots plus a conservative C-stack scan; single-threaded and stop-the-world; no weak references |
@@ -181,9 +181,10 @@ directly (`./build/app`) to see the program's own output.
181
181
 
182
182
  ### Program output appears in an unexpected order
183
183
 
184
- `async`/`await` is a synchronous microtask model and the event loop only runs
185
- after the program body. Timer and socket callbacks therefore fire late; see
186
- [language-support.md](./language-support.md).
184
+ `async`/`await` suspends only on a **pending** promise; awaiting an already
185
+ settled value continues synchronously instead of queueing a microtask the way
186
+ Node does. The event loop starts after the program body, so timer and socket
187
+ callbacks still fire after it; see [language-support.md](./language-support.md).
187
188
 
188
189
  ## Where to look in the code
189
190
 
@@ -67,11 +67,11 @@ gen2 --emit--> gen3.ll # 必须等于 ref.ll
67
67
  必须逐字节确定——包括迭代顺序、生成的符号名与编号。绝不要把不确定性(未受控的
68
68
  `Map`/`Set` 迭代顺序、时间戳、依赖文件系统顺序的东西)引入代码生成。
69
69
 
70
- 第二个推论是字符串模型:编译器自身源码文本在各代中必须含义一致。编译后的字符串是
71
- UTF-8 字节,所以自举后的 `readFileSync`(`--ext node`)交给扫描器的是文件字节,而
72
- Node 交给它的是同一个文件已解码后的文本——读取源码文件一律走 `decodeUtf8`
73
- ([src/diagnostics/utf8.ts](../../../src/diagnostics/utf8.ts)),不要假设一个码元就是
74
- 一个字节或一个字符。弄错的表现是:`gen2.ll` 里非 ASCII 字面量(比如提示语里的 `—`)
70
+ 第二个推论是字符串模型:编译器自身源码文本在各代中必须含义一致。编译后的字符串以 UTF-8
71
+ 存储,但索引按 UTF-16 码元进行,所以自举后的 `readFileSync`(`--ext node`)交给扫描器的是
72
+ 字节串(一个码元一个字节,全部 `<= 0xFF`),而 Node 交给它的是同一个文件已解码后的文本——
73
+ 读取源码文件一律走 `decodeUtf8`([src/diagnostics/utf8.ts](../../../src/diagnostics/utf8.ts)),
74
+ 不要假设一个码元就是一个字节或一个字符。弄错的表现是:`gen2.ll` 里非 ASCII 字面量(比如提示语里的 `—`)
75
75
  被重新编码,这也正是这里逐字节比较 IR 而不是逐行比较的原因。
76
76
 
77
77
  本地验证:
@@ -53,7 +53,8 @@ xbintsc run app.ts --ext node
53
53
  [../../zh-CN/node-unimplemented.md](../../zh-CN/node-unimplemented.md)。
54
54
 
55
55
  在 `net`/`http` 上搭东西之前,先记住运行时模型:事件循环在**程序主体之后**才运行,
56
- 而 `async`/`await` 是同步微任务模型。见 [language-support.md](./language-support.md)。
56
+ 但 `async`/`await` 现在会在 pending Promise 上挂起,因此由套接字回调 settle 的 Promise
57
+ 可以被 await。见 [language-support.md](./language-support.md)。
57
58
 
58
59
  ### `gui` —— HTML/CSS 窗口
59
60
 
@@ -28,7 +28,7 @@ xbintsc 编译 TypeScript 的一个**实用子集**。本页用来判断你能
28
28
  | `function`、箭头函数、默认参数/剩余参数 | ✅ | |
29
29
  | `namespace`/`module` 声明 | 🚫 | 解析 ✓,代码生成 ✗ |
30
30
  | `with`、`debugger` | 🚫 | 不支持 |
31
- | 顶层 `await` | 🚫 | 请包进 `async` 函数 |
31
+ | 顶层 `await` | ⚠️ | 会在事件循环上阻塞直到 Promise settle(不会像 ES module 那样让出模块主体);建议包进 `async` 函数 |
32
32
 
33
33
  ## 表达式与运算符
34
34
 
@@ -68,10 +68,11 @@ xbintsc 编译 TypeScript 的一个**实用子集**。本页用来判断你能
68
68
 
69
69
  | 特性 | 状态 | 说明 |
70
70
  | --- | --- | --- |
71
- | `async`/`await`、`Promise`、`Promise.all` | ⚠️ | **同步微任务模型**——`await` 一个已敲定的 Promise 会同步继续 |
72
- | `setTimeout` 与回调 | ⚠️ | 事件循环在**程序主体之后**才运行,因此定时器里敲定的 Promise 无法被 await |
73
- | 套接字/服务端(`http`、`net`、`dgram`) | ⚠️ | 同一模型:回调在程序主体之后的循环里执行 |
74
- | 真正的异步事件循环、worker 线程 | 🚫 | 未实现 |
71
+ | `async`/`await`、`Promise`、`Promise.all` | ✅ | 真实挂起:async 函数体运行在栈式协程上,等待**未 settle** 的 Promise 时暂停,settle 后恢复。等待已 settle 的值仍同步继续 |
72
+ | `setTimeout` 与回调 | ✅ | 定时器运行在核心事件循环上;`await new Promise(r => setTimeout(r, ms))` 可用 |
73
+ | 套接字/服务端(`http`、`net`、`dgram`) | ✅ | 回调在事件循环上执行,可以 settle 你正在 await 的 Promise(测试含 `net`/`http` 示例) |
74
+ | `fs/promises` | ⚠️ | 可以 await,但每次调用仍先同步完成 I/O 再 settle,I/O 期间不会让出 |
75
+ | `async` 生成器、worker 线程 | 🚫 | `async function*` 被拒绝;worker 是子进程 |
75
76
 
76
77
  ## 类型:解析后擦除
77
78
 
@@ -93,13 +94,14 @@ xbintsc 编译 TypeScript 的一个**实用子集**。本页用来判断你能
93
94
  已实现:`Math`、`JSON`、`Date`、`RegExp`、`Map`、`Set`、`WeakMap`/`WeakSet`、
94
95
  `Symbol`、`Error` 家族、`BigInt`、`Array`/`String`/`Number`/`Object` 方法、TypedArray、
95
96
  带捕获组的 `String.prototype.match`/`split`/`replace`、不可变数组方法
96
- (`toReversed`、`toSorted`、`toSpliced`、`with`)、全局 URI 函数。
97
+ (`toReversed`、`toSorted`、`toSpliced`、`with`)、全局 URI 函数、
98
+ 一等公民内建构造器(`const A = Array; new A(...)`、`A.isArray`、`x instanceof A`)
99
+ 与 `Object.prototype`(`Object.getPrototypeOf({}) === Object.prototype`)。
97
100
 
98
101
  | 缺失或行为不同 | 状态 |
99
102
  | --- | --- |
100
103
  | `String.prototype.normalize`、`structuredClone` | 🚫 |
101
104
  | `Error.stack` 采集 | ⚠️ 函数级帧(名称/文件/行号),没有列号;生成器函数体不产生帧 |
102
- | `Object.getPrototypeOf({})` | ⚠️ 返回 `undefined`,而非 `Object.prototype` |
103
105
  | 全局 RegExp `lastIndex` | ⚠️ `/g`、`/y` 下 `test`/`exec` 忽略它 |
104
106
  | 稀疏数组空洞、数组越界 | ⚠️ 不区分空洞;越界读得 `undefined` |
105
107
 
@@ -123,7 +125,8 @@ xbintsc 编译 TypeScript 的一个**实用子集**。本页用来判断你能
123
125
 
124
126
  | 项目 | 行为 |
125
127
  | --- | --- |
126
- | `String.prototype.length` | ⚠️ 按 **UTF-8 字节**计,不是 UTF-16 码元:`"é".length === 1`,`"😀".length === 4`;`codePointAt` 相应不同 |
128
+ | `String.prototype.length` | ✅ UTF-16 码元(`"中文😀".length === 4`);索引、`slice`、`indexOf`、`split`、`padStart`、`codePointAt` 使用同一模型。底层仍以 UTF-8 存储 |
129
+ | 正则 Unicode 匹配 | ⚠️ 引擎按字节工作:字面量 `中` 仍能匹配,但 `.` 与字符类每次只消费一个字节,`\d`/`\w` 仍是 ASCII。匹配偏移(`match.index`、`search`、替换回调)按 UTF-16 码元报告 |
127
130
  | 数字格式化与强转 | ✅ 与 Node 一致(`toFixed`、`toPrecision`、十六/八/二进制解析) |
128
131
  | `BigInt` | ✅ 已实现 |
129
132
  | 垃圾回收 | ✅ 非移动标记清扫;显式根 + C 栈保守扫描;单线程、stop-the-world;无弱引用 |
@@ -166,8 +166,9 @@ gdb ./build/app # 或 lldb ./build/app,Windows 用 WinDbg 打开 .exe
166
166
 
167
167
  ### 程序输出顺序出乎意料
168
168
 
169
- `async`/`await` 是同步微任务模型,且事件循环只在程序主体之后运行,所以定时器和套接字
170
- 回调会晚触发;见 [language-support.md](./language-support.md)。
169
+ `async`/`await` 只在 **pending** Promise 上挂起;等待已 settle 的值会同步继续,而 Node
170
+ 会排入微任务,因此顺序可能不同。事件循环仍在程序主体之后启动,定时器与套接字回调依然
171
+ 在主体之后触发;见 [language-support.md](./language-support.md)。
171
172
 
172
173
  ## 该去源码哪里看
173
174
 
@@ -198,7 +198,7 @@ xt_value fn(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv);
198
198
 
199
199
  - `thisValue` is threaded as the first ABI parameter (exception-safe, supports nesting); arrow functions inherit `this` lexically from an extra environment slot.
200
200
  - Closures thread captured variables through `env` (by reference, boxed); direct calls and closure calls share one code path.
201
- - `xt_object` carries a prototype field, and property lookup walks the prototype chain; `xt_function` carries a `prototype` and a property bag (static members).
201
+ - `xt_object` carries a prototype field, and property lookup walks the prototype chain; `xt_function` carries a `prototype` and a property bag (static members). `xt_array` also carries a `[[Prototype]]` (Array.prototype). `Object.prototype` is the root of ordinary objects and provides `hasOwnProperty` / `toString` / `valueOf` / `isPrototypeOf` / `propertyIsEnumerable` / `__proto__`; `Object.getPrototypeOf({}) === Object.prototype`.
202
202
 
203
203
  ---
204
204
 
@@ -219,16 +219,17 @@ Location: `src/codegen/llvm.ts`
219
219
  - Array literals (including spread `[...]`), object literals (including shorthand / methods / object spread `{...obj}`)
220
220
  - Typed arrays (`Uint8Array`, `Int8Array`, `Uint8ClampedArray`, `Uint16Array`, `Int16Array`, `Uint32Array`, `Int32Array`, `Float32Array`, `Float64Array`): construction (`new X(n)` / `new X(iterable)` / `X.from` / `X.of`), `length` / `byteLength` / `byteOffset` / `BYTES_PER_ELEMENT`, element reads/writes with per-type coercion (`ToIntN` / `ToUintN`, `Uint8ClampedArray` rounding, `Float32` single-precision rounding) and the prototype methods `fill` / `set` / `slice` / `subarray` / `join` / `toString` / `indexOf` / `lastIndexOf` / `includes` / `forEach` / `map` / `filter` / `every` / `some` / `find` / `findIndex` / `reduce` / `reverse` / `sort` / `copyWithin` / `at` / `keys` / `values` / `entries`. See the unimplemented document for the deliberate divergences (`subarray` copies, iterator methods return arrays, no `ArrayBuffer`/`DataView`).
221
221
  - Property access (with a `length` special case, `Math` constants), element access, calls
222
+ - First-class built-in constructors (`const A = Array`, `const M = Map`, `const E = TypeError`, typed arrays): a bare reference lowers to `xt_builtin_constructor`, giving a cached function with `name` / `length`, a `prototype`, and its statics (`A.isArray`, `A.from`, `M.prototype`, ...). Calling or `new`-ing an alias dispatches to the same runtime constructor, and `x instanceof A` walks the shared prototype chain.
222
223
  - Optional chaining `?.` / `?.[]` / `?.()`: nullish check short-circuits to `undefined`
223
224
  - `delete`, `in`, `instanceof`
224
225
  - `this` (saved to the function's `%saved.this`; arrow functions read from an environment slot), `super` (`super.x` / `super(...)`), `new`, `await`
225
226
  - Closures (arrow functions / function expressions) and capture environment construction
226
227
  - Classes: constructor closure + prototype object, stored in an LLVM global (`@class.<id>`); instance fields are initialized before the constructor body; `static` members live in the constructor's property bag; `extends` sets up the prototype chain; `super(...)` is invoked through a hidden `__ctor` on the prototype.
227
- - `async`: wrapped with `xt_promise_resolve` before returning; `await` calls `xt_await` (drives the microtask queue and raises an exception on rejection).
228
+ - `async`: closures are marked with `xt_function_set_async`; calling one runs the body on a stackful coroutine (`xt_coroutine.c`, `xt_async.c`) and immediately returns a promise. The body runs synchronously until the first `await` of a pending promise, then suspends; settlement queues a microtask that resumes the coroutine. An exception that escapes the body rejects that promise instead of throwing synchronously. `await` on a non-promise or settled promise keeps the synchronous fast path and raises on rejection.
228
229
  - Standard library calls: `console.*`, `Math.*`, `Object.*`, array / string methods all go through `xt_call_method` / `xt_math_call` / `xt_object_*`; `JSON`/`Date`/`Map`/`Set`/`RegExp`/`Promise` statics and constructors go through their `xt_*` functions; global functions (`parseInt`, `setTimeout`, etc.) go through `xt_parse_int`, `xt_set_timeout`, etc.
229
230
  - Global string pool (`@.str.N` private constants, UTF-8 escaped).
230
231
  - Builtin calls: `console.log` / `info` / `warn` / `error`, `Math.*`, `Object.*`, array / string methods, global functions, extension builtins (uniform `(argc, argv)` ABI).
231
- - `main` entry point (returns 0, calls the module function, and runs `xt_drain_microtasks` before returning).
232
+ - `main` entry point (returns 0, calls the module function, then drains microtasks and runs the event loop until no timers or descriptors remain).
232
233
  - Unsupported nodes uniformly report `UnsupportedFeature`; they never crash.
233
234
 
234
235
  ---
@@ -236,18 +237,19 @@ Location: `src/codegen/llvm.ts`
236
237
  ## 8. C runtime (Runtime, implemented)
237
238
 
238
239
  Location: `runtime/xt_alloc.c`, `runtime/xt_values.c`, `runtime/xt_containers.c`,
239
- `runtime/xt_stdlib.c`, `runtime/xt_stdlib2.c`, `runtime/xt_promise.c`, `runtime/xt_builtins.c`,
240
+ `runtime/xt_stdlib.c`, `runtime/xt_stdlib2.c`, `runtime/xt_promise.c`, `runtime/xt_async.c`,
241
+ `runtime/xt_coroutine.c`, `runtime/xt_builtins.c`,
240
242
  `runtime/xt_io.c`, sharing the private header `runtime/rt_internal.h`; the public ABI is in `runtime/rt.h`.
241
243
 
242
244
  - Allocator: non-moving mark-sweep collector behind `xt_alloc` (explicit roots, subsystem root providers and a conservative C-stack scan).
243
245
  - Value construction: `xt_undefined/xt_null/xt_bool/xt_number/xt_string_new/xt_string_from_cstr`.
244
- - Strings: UTF-8 storage, lazy cons-node concatenation (bytes are flattened on first observation, so append loops stay linear), equality comparison, formatted number-to-string.
246
+ - Strings: UTF-8 storage exposed through JavaScript's UTF-16 view (`length`, indexing, `slice`, `indexOf`, `split`, `padStart` and `codePointAt` count code units; `for...of` and `Array.from` count code points; lone surrogate halves are kept as WTF-8 and recombined on concatenation), lazy cons-node concatenation (bytes are flattened on first observation, so append loops stay linear), equality comparison, formatted number-to-string. `xt_string_length` reports bytes for I/O; `xt_string_utf16_length` reports the JavaScript length.
245
247
  - Type conversion: `xt_truthy`, `xt_to_number`, `xt_to_string`, `xt_typeof`.
246
248
  - Arithmetic: `add/sub/mul/div/mod/pow/neg/pos`.
247
249
  - Bitwise: `and/or/xor/not/shl/shr/ushr` (including `ToInt32` semantics).
248
250
  - Comparison: `lt/le/gt/ge`, loose / strict equality, `not`, `is_nullish`.
249
251
  - Objects: linear property list, `object_new/get/set/has/keys/values/entries/assign/spread`.
250
- - Arrays: `array_new/get/set/push/length/spread`; assigning `arr.length` truncates or extends (matching JS); `iter_length` / `iter_value` expose a uniform iteration view over arrays, strings, `Map` and `Set`.
252
+ - Arrays: `array_new/get/set/push/length/spread`; assigning `arr.length` truncates or extends (matching JS); `iter_length` / `iter_value` expose a uniform iteration view over arrays, strings (by code point), `Map` and `Set`.
251
253
  - Typed arrays (`xt_typed_array.c`): typed arrays as property-bag objects with one prototype per element kind; element reads return the stored value and writes are coerced to the element type; `from` / `of` statics plus the full prototype method set; `subarray` copies.
252
254
  - Symbols (`xt_symbol.c`): `Symbol(description)` primitives (`XT_OBJECT_KIND_SYMBOL`), the 13 well-known symbols (`Symbol.iterator`, `Symbol.asyncIterator`, `Symbol.match`, …), `Symbol.for` / `Symbol.keyFor` global registry, `symbol.description` / `toString()` / `valueOf()`; symbols are valid property keys (`Object.getOwnPropertySymbols`, symbol keyed `get`/`set`/`in`/`delete`), are skipped by `Object.keys` / `values` / `entries` / `for...in` / `JSON.stringify`, and print as `Symbol(desc)`.
253
255
  - Generic member access: `xt_get` / `xt_set` (dispatch over arrays / objects / strings).
@@ -257,10 +259,11 @@ Location: `runtime/xt_alloc.c`, `runtime/xt_values.c`, `runtime/xt_containers.c`
257
259
  - Functions and closures: `arg`, `closure_new/call/env/arity`.
258
260
  - Exceptions: `xt_try_enter` / `xt_try_exception` / `xt_try_leave` maintain the `_setjmp` frame stack; `xt_throw` long-jumps to the nearest `try` when a frame exists, otherwise prints `Uncaught ...` and exits. Codegen also maintains a shadow call stack (function name, source file and line): `Error.stack` captures it, and a fatal-signal handler dumps the same frames on a native crash. `--debug` additionally emits `!DICompileUnit` / `!DISubprogram` / `!DILocation` metadata and compiles with `-g -fno-omit-frame-pointer`, so gdb/lldb/WinDbg map instructions back to source lines.
259
261
  - Output: `xt_print/xt_println/xt_console_log/info/warn/error` (Node-style inspect: arrays `[ a, b ]`, objects `{ k: v }`; `info`/`log` to stdout, `warn`/`error` to stderr), plus `dir/trace/assert/count/countReset/group/groupEnd/table/time/timeEnd/timeLog`.
260
- - Objects / functions: `xt_object` with a prototype chain, `xt_function` with a property bag (static members and `prototype`); `xt_new` (instantiation), `xt_instance_of` (prototype chain), `xt_object_freeze/is_frozen/from_entries`.
262
+ - Objects / functions: `xt_object` with a prototype chain, `xt_function` with a property bag (static members and `prototype`); `xt_new` (instantiation), `xt_instance_of` (prototype chain), `xt_object_freeze/is_frozen/from_entries`; `xt_constructors.c` holds the first-class constructor cache, constructor prototypes and static-value dispatch.
261
263
  - Standard library extensions (`xt_stdlib2.c`): extended array / string / number / object methods; `Object` / `Array` / `Number` / `String` statics; `JSON.parse` / `JSON.stringify`; `Map` / `Set`; `Date` (`gmtime_r`); `RegExp` (POSIX ERE `regcomp`/`regexec` `test`/`exec`).
262
- - Promise (`xt_promise.c`): synchronous microtask queue (`xt_microtasks`); `xt_promise_ctor/resolve/reject/static`, instance `then/catch/finally`; `xt_await` drives the queue until settle, and rejection raises via `xt_throw`; `xt_drain_microtasks` empties the queue at program exit.
263
- - Fetch (`xt_fetch.c`): global `fetch(input, init)` is a blocking HTTP/1.1 client whose promise is already settled (the runtime has no event loop). It accepts `http://` URLs only — `https://` rejects with a `TypeError` because no TLS backend is linked — and supports `method`, `headers` (plain object or `Headers`), `body` and `redirect` (`follow` / `manual` / `error`), following up to 20 redirects and switching POST to GET on 301/302/303. The resolved `Response`-shaped object exposes `ok` / `status` / `statusText` / `url` / `headers` plus `text()` / `json()` / `arrayBuffer()` / `bytes()` / `clone()`, and `Headers` is case-insensitive with `get` / `has` / `set` / `append` / `delete` / `keys` / `values` / `entries` / `forEach` / `getSetCookie`. Invalid URLs and network failures reject with a `TypeError`.
264
+ - Promise (`xt_promise.c`): microtask queue (`xt_microtasks`); `xt_promise_ctor/resolve/reject/static`, instance `then/catch/finally`; `xt_drain_microtasks` empties the queue at program exit and the event loop drains it between callbacks. `xt_await` (in `xt_async.c`) returns a settled value synchronously, or suspends the running async coroutine and registers a waiter when the promise is pending.
265
+ - Async coroutines (`xt_async.c`) on the shared stackful-coroutine runtime (`xt_coroutine.c`, POSIX `ucontext` / Windows fibers): each `async` call owns a private stack, swaps its `try` frame stack and GC stack region, and survives `await` across `try`/`finally`, loops and closures. A promise settled from a timer, a socket callback or another coroutine resumes the suspended body on the microtask queue.
266
+ - Fetch (`xt_fetch.c`): global `fetch(input, init)` is a blocking HTTP/1.1 client whose promise is already settled (the exchange happens synchronously; it is not scheduled on the reactor). It accepts `http://` URLs only — `https://` rejects with a `TypeError` because no TLS backend is linked — and supports `method`, `headers` (plain object or `Headers`), `body` and `redirect` (`follow` / `manual` / `error`), following up to 20 redirects and switching POST to GET on 301/302/303. The resolved `Response`-shaped object exposes `ok` / `status` / `statusText` / `url` / `headers` plus `text()` / `json()` / `arrayBuffer()` / `bytes()` / `clone()`, and `Headers` is case-insensitive with `get` / `has` / `set` / `append` / `delete` / `keys` / `values` / `entries` / `forEach` / `getSetCookie`. Invalid URLs and network failures reject with a `TypeError`.
264
267
 
265
268
  ---
266
269
 
@@ -346,7 +349,7 @@ const result = build("program.ts", { emit: "exe", outDir: "build" });
346
349
  Location: `tests/` (`lexer` / `parser` / `binder` / `codegen` / `driver` / `extensions` / `cli` / `e2e`)
347
350
 
348
351
  - Per-module unit tests; when `clang` is present, e2e really compiles and runs binaries, otherwise it is skipped automatically.
349
- - e2e coverage: arithmetic and printing, recursive functions, loops / arrays / string concatenation, closures capturing by reference, JS-style printing of objects / arrays, the Node `fs` extension via `import`, `switch` fall-through, array / string methods, `Math` and global functions and all `console` levels, default / rest parameters and `arguments`, `Object` helpers and spread and `in`/`delete`, `for...in` object key enumeration, `try/catch/finally`, optional chaining, classes and `new`/`this`/`static`/`extends`/`super`/`instanceof`, `async`/`await` and `Promise`, `Map`/`Set`/`JSON` and extended standard library, `for...of` over `Map`/`Set`, array `length` assignment and iterable spread, multi-file `import`/`export` (including `.js` specifiers and plain JavaScript sources), destructuring bindings and assignments (including object rest), built-in `instanceof`, `enum`/`const enum`, regular-expression literals and `new Error(...)`, the `Error` family and `AggregateError`, generators and `Symbol` with custom `Symbol.iterator` iterables, typed arrays (element coercion, `from`/`of`, iteration and the prototype methods), and the global `fetch` (HTTP GET/POST, headers, redirects, JSON/text/binary bodies and `TypeError` rejection).
352
+ - e2e coverage: arithmetic and printing, recursive functions, loops / arrays / string concatenation, closures capturing by reference, JS-style printing of objects / arrays, the Node `fs` extension via `import`, `switch` fall-through, array / string methods, `Math` and global functions and all `console` levels, default / rest parameters and `arguments`, `Object` helpers and spread and `in`/`delete`, `for...in` object key enumeration, `try/catch/finally`, optional chaining, classes and `new`/`this`/`static`/`extends`/`super`/`instanceof`, `async`/`await` and `Promise`, real await suspension (timers, TCP sockets and `fs/promises` across event-loop turns, rejection and `finally` after resuming), `Map`/`Set`/`JSON` and extended standard library, `for...of` over `Map`/`Set`, array `length` assignment and iterable spread, multi-file `import`/`export` (including `.js` specifiers and plain JavaScript sources), destructuring bindings and assignments (including object rest), built-in `instanceof`, `enum`/`const enum`, regular-expression literals and `new Error(...)`, the `Error` family and `AggregateError`, generators and `Symbol` with custom `Symbol.iterator` iterables, typed arrays (element coercion, `from`/`of`, iteration and the prototype methods), first-class built-in constructors and `Object.prototype` (aliases, statics, `getPrototypeOf`, `hasOwnProperty.call`), and the global `fetch` (HTTP GET/POST, headers, redirects, JSON/text/binary bodies and `TypeError` rejection).
350
353
 
351
354
  ---
352
355
 
@@ -360,11 +363,11 @@ Location: `tests/` (`lexer` / `parser` / `binder` / `codegen` / `driver` / `exte
360
363
  | Operators | Arithmetic, comparison, equality, logical, bitwise, shift, unary (including `typeof`/`void`), prefix/postfix increment-decrement, compound assignment, logical assignment, `in`, `delete`, `instanceof` |
361
364
  | Functions | Default parameters, rest parameters, capturing closures, `this` binding, lexical `this` in arrow functions, `call`/`apply`/`bind`, `name`/`length` |
362
365
  | Classes / OO | Constructors, instance fields, methods, `static`, inheritance `extends`/`super`, prototype chain, `instanceof` |
363
- | Async | `async`/`await`, `Promise` (`then/catch/finally`, `resolve/reject/all/allSettled/race`), synchronous microtask queue |
366
+ | Async | `async`/`await` with real stackful-coroutine suspension (a pending promise pauses the body until a timer/socket/coroutine settles it), `Promise` (`then/catch/finally`, `resolve/reject/all/allSettled/race`) |
364
367
  | Modules | `import`/`export` (named / default / re-export / `export *` / `export type`), multi-file bundling over relative paths (`.js`-family specifiers resolve to their `.ts` sources, and TypeScript suffixes are preferred over JavaScript siblings when the specifier omits the extension; plain `.js` / `.jsx` / `.mjs` / `.cjs` sources are bundled directly too) and `node_modules` packages (`exports` / `module` / `main`, scoped packages and subpaths), bare specifiers resolved to extension modules; CommonJS packages under `node_modules` are lowered (`require` / `module.exports` / `exports`), while `require()` in user code is rejected with an `import` hint |
365
368
  | Standard library | Array / string / number / object extension methods, typed arrays, `Math`, `JSON`, `Date`, `RegExp`, `Map`, `Set`, `Symbol`, `Error` family, `Object/Array/Number/String/Symbol` statics, `console.*` |
366
369
  | Value model | 64-bit NaN-boxing, uniform function ABI (including `this`), closure environments, object prototype chains |
367
- | Runtime | Strings / objects / arrays / typed arrays / closures / arithmetic / comparison / catchable exceptions / Promise / collections / symbols / generators / `fetch` / `console` |
370
+ | Runtime | Strings / objects / arrays / typed arrays / closures / arithmetic / comparison / catchable exceptions / Promise / async coroutines / collections / symbols / generators / `fetch` / `console` |
368
371
  | Extensions | Extension registry, `core` (print), `node` (fs / path / os / process / buffer / stream / net / dgram / http imported by specifier) |
369
372
  | GUI (`gui` extension) | Self-hosted HTML/CSS renderer window (SDL3 + SDL_GPU): DOM/CSS/layout, CSS `transform`/`transform-origin`, `<img>`/SVG, `<video>`/`<audio>`, `<canvas>` 2D, events, transitions (including `transform`) |
370
373
  | Toolchain | clang compiles IR/C, linking, incremental cache |
@@ -101,6 +101,7 @@ None known at the expression level.
101
101
  - `JSON.parse` / `JSON.stringify`
102
102
  - `Date`, `RegExp` (POSIX ERE subset with capture groups), `Map`, `Set`, `Promise`, `BigInt`
103
103
  - `Array` statics (`isArray/of/from`), `Object` statics, `Number` statics, `String` statics (`fromCharCode` / `fromCodePoint` / `raw`)
104
+ - Built-in constructors as first-class values (`const A = Array`, `new A(...)`, `A.isArray`, `x instanceof A`) and `Object.prototype` (`hasOwnProperty`, `toString`, `valueOf`, `isPrototypeOf`, `propertyIsEnumerable`, `__proto__`), with `Object.getPrototypeOf({}) === Object.prototype`
104
105
  - `Error` family (`Error`, `TypeError`, `RangeError`, `SyntaxError`, `ReferenceError`, `EvalError`, `URIError`, `AggregateError`): `new`, `instanceof`, `extends`, `String(err)` / `err.toString()` and `.stack`
105
106
 
106
107
  ### 3.3 Still unimplemented
@@ -114,7 +115,7 @@ None known at the expression level.
114
115
  | `AggregateError` constructor | ✓ `new AggregateError(errors, message)`; `Promise.any` now rejects with one |
115
116
  | Iterator protocol / `Symbol.iterator` / custom `for...of` iterables | ✓ arrays, strings, `Map`, `Set`, generators and any object exposing `[Symbol.iterator]()` are iterable in `for...of` / spread |
116
117
  | Generators (`function*`, `yield`, `yield*`, `next`/`throw`/`return`) | ✓ implemented with stackful coroutines; `for...of`, spread and delegation supported. `return()` completes without running `finally` |
117
- | Timers | ✓ `setTimeout` / `clearTimeout` / `setInterval` / `clearInterval` on the core event loop (numeric ids, no `Timeout` object; no `setImmediate` / `queueMicrotask`) |
118
+ | Timers | ✓ `setTimeout` / `clearTimeout` / `setInterval` / `clearInterval` on the core event loop (numeric ids, no `Timeout` object; no `setImmediate` / `queueMicrotask`). A promise settled from a timer can be awaited: the async body suspends on the pending promise and resumes when the timer fires |
118
119
  | I/O / process and other host APIs | only via extensions (e.g. Node `fs`) |
119
120
 
120
121
  ### 3.4 Typed arrays
@@ -133,7 +134,7 @@ element writes, and `fill` / `set` / `slice` / `subarray` / `join` / `toString`
133
134
  | `keys` / `values` / `entries` | ~ return arrays, not iterator objects |
134
135
  | `ArrayBuffer` / `DataView` / `.buffer` / `.byteOffset` semantics | ✗ no backing buffer type; `.byteOffset` is always 0 |
135
136
  | `BigInt64Array` / `BigUint64Array` | ✗ not implemented |
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) |
137
+ | `instanceof` built-in constructors | ✓ `instanceof Array` / `Object` / `Map` / `Set` / `Date` / `RegExp` / `Promise` / `Error` family / typed arrays are recognised by name **and through a first-class constructor value** (`const A = Array; x instanceof A`); constructors can be stored, passed, aliased, called and used with `new` |
137
138
  | Typed-array views over a `Buffer` / `ArrayBuffer` | ✗ constructors copy from a length / array-like / typed array only |
138
139
 
139
140
  ---
@@ -169,12 +170,13 @@ element writes, and `fill` / `set` / `slice` / `subarray` / `join` / `toString`
169
170
  | Destructuring parameters / bindings | ✓ | ✓ implemented |
170
171
  | `arguments` object | ✓ (implicit) | ✓ implemented (arrow functions get their own parameters, not the outer function's `arguments`, unlike JS) |
171
172
  | `this` binding / method call semantics | ✓ | ✓ implemented (`this` is threaded as the first ABI parameter; arrow functions inherit lexically) |
172
- | `async` / `await` / Promise | ✓ | ✓ implemented (synchronous microtask model) |
173
+ | `async` / `await` / Promise | ✓ | ✓ implemented with stackful-coroutine suspension: awaiting a pending promise pauses the body and the event loop resumes it (see the deviations table for the remaining differences) |
173
174
  | `new.target` | ✗ | parsed, then rejected with `UnsupportedFeature` (no `new.target` semantics) |
174
175
  | Generators / iterators / `yield` | ✓ implemented | `function*`, `yield`, `yield*`, `.next/.throw/.return`; `async` generators are rejected with `UnsupportedFeature` |
175
176
  | Closure `arity` | — | `xt_closure_arity` is always -1, never filled in |
176
177
  | `fn.call` / `fn.apply` / `fn.bind` | ✓ | ✓ implemented (the bound closure does not track partial-argument `length`) |
177
178
  | `fn.name` / `fn.length` | ✓ | ✓ implemented (inferred from the declaration / assignment / property key; bound functions use `"bound ..."` and adjusted arity) |
179
+ | First-class built-in constructors (`const A = Array`, `new A(...)`, `typeof Map`) | ✓ | ✓ constructors are ordinary function values with `name`/`length`, a `prototype`, their statics (`A.isArray`) and the full `instanceof` protocol |
178
180
  | First-class built-in methods (`typeof arr.map`, `const f = arr.push`, `obj.method?.()`) | ✓ | ✓ implemented as *unbound* method values: reading `arr.map` yields a function, `arr.map?.(cb)` and `obj.method?.()` bind the receiver as `this`, and a detached call (`const f = arr.map; f(cb)`) throws exactly like JavaScript. Method `name`/`length` are exposed |
179
181
  | `fn.toString()` | ~ | returns a native-style placeholder (`function name() { [native code] }`) instead of the original source text |
180
182
 
@@ -222,8 +224,7 @@ These features **compile and run**, but the result does not fully match ECMAScri
222
224
 
223
225
  | Item | Deviation |
224
226
  | --- | --- |
225
- | String `length` | Counted in UTF-8 bytes, not UTF-16 code units (`"\u00e9".length` reports 1 instead of 2; `"\u{1F600}".length` reports 4 instead of 2; `codePointAt` likewise differs) |
226
- | `Object.getPrototypeOf({})` | Returns `undefined` instead of the `Object.prototype` object |
227
+ | RegExp byte-oriented matching | The engine matches UTF-8 bytes: literal multi-byte text works, but `.` and character classes consume one byte per repetition (`\d` / `\w` stay ASCII). Offsets returned by `exec` / `search` / replacement callbacks are converted to UTF-16 code units |
227
228
  | Global RegExp `lastIndex` | `test` / `exec` do not advance or honour a caller-set `lastIndex` for `/g` / `/y` regexes |
228
229
  | String `normalize` / `structuredClone` | Not implemented |
229
230
  | First-class built-in methods | Implemented as unbound method values (see section 5); `fn.toString()` returns a placeholder rather than source text |
@@ -231,9 +232,9 @@ These features **compile and run**, but the result does not fully match ECMAScri
231
232
  | Array out-of-bounds / sparse | Out-of-bounds access returns `undefined`; assigning `arr.length` truncates / extends, but sparse holes are not tracked distinctly |
232
233
  | Memory management | Non-moving mark-sweep collector behind `xt_alloc`: explicit roots, subsystem root providers and a conservative C-stack scan. Single-threaded and stop-the-world; no moving/compaction or generational mode |
233
234
  | Function `arity` / argument count | No argument count validation; `fn.length` reports the declared arity but calls are never checked against it |
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 |
235
+ | `async` / `await` | Real suspension on a stackful coroutine, but `await` on a value that is **already settled** continues synchronously instead of queueing a microtask as the specification requires, so interleaving with pending `.then` callbacks can differ. Top-level `await` blocks on the event loop rather than yielding the module body. `fs/promises` still performs the I/O synchronously before settling; `fetch` is blocking; `async` generators are unsupported |
235
236
  | `super` | `super.x` / `super(...)` takes the prototype of `this`'s prototype; single-level inheritance is correct, but depth > 1 may be inaccurate |
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) |
237
+ | `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 / `async` bodies (they switch stacks) |
237
238
  | Module live bindings | Namespace imports and imported bindings are snapshots (see section 4) |
238
239
  | `import.meta` | ✓ `import.meta.url` / `.dirname` / `.filename`, resolved from the executable path |
239
240
 
@@ -253,7 +254,7 @@ These features **compile and run**, but the result does not fully match ECMAScri
253
254
  | Type checker | ✗ only diagnostic codes are defined; no checker |
254
255
  | Full standard library | partial: Math / JSON / Date / Map / Set / RegExp / `Error` / `BigInt` / `Symbol` implemented; String.normalize / structuredClone missing |
255
256
  | Multi-file module bundling | partial: relative-path `.ts` bundling, ESM `node_modules` packages, namespace imports and bare-specifier extension module imports implemented; circular dependencies / CommonJS / live bindings not implemented |
256
- | A real async runtime / event loop | ✗ (Promise is a synchronous microtask model) |
257
+ | Real async runtime | ✓ `async`/`await` suspends on pending promises on the `select(2)` reactor: timers, TCP sockets and promise callbacks resume the coroutine. Still missing: truly asynchronous I/O scheduling (`fs/promises` blocks before settling), a non-blocking `fetch` and worker threads (workers are child processes) |
257
258
  | Windows binary artifact verification | adapted at the build layer (`.exe` suffix, link flag branch), verified in CI |
258
259
  | Precise ECMAScript number / string / comparison semantics | partial, see section 8 |
259
260
 
@@ -280,8 +281,8 @@ Unimplemented (modules): circular dependencies, live bindings, dynamic
280
281
  Unimplemented (type system): type checking, generic instantiation, assertion
281
282
  semantics, optional-chaining narrowing
282
283
 
283
- Unimplemented (runtime): a real async event loop, UTF-16 string length,
284
- Object.prototype identity, global-regex lastIndex
284
+ Unimplemented (runtime): asynchronous I/O scheduling,
285
+ global-regex lastIndex
285
286
 
286
287
  Unimplemented (engineering): type checker
287
288
  ```