xbintsc 0.3.81 → 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 (188) hide show
  1. package/AGENTS.md +8 -4
  2. package/README.md +16 -5
  3. package/README.zh-CN.md +14 -6
  4. package/dist/src/ast/common.d.ts +23 -0
  5. package/dist/src/cli/main.js +68 -24
  6. package/dist/src/cli/main.js.map +1 -1
  7. package/dist/src/codegen/generator/calls/access.js +6 -2
  8. package/dist/src/codegen/generator/calls/access.js.map +1 -1
  9. package/dist/src/codegen/generator/calls/closures.js +4 -0
  10. package/dist/src/codegen/generator/calls/closures.js.map +1 -1
  11. package/dist/src/codegen/generator/calls/invocation.js +9 -4
  12. package/dist/src/codegen/generator/calls/invocation.js.map +1 -1
  13. package/dist/src/codegen/generator/context.d.ts +50 -0
  14. package/dist/src/codegen/generator/context.js +124 -13
  15. package/dist/src/codegen/generator/context.js.map +1 -1
  16. package/dist/src/codegen/generator/debug.d.ts +33 -0
  17. package/dist/src/codegen/generator/debug.js +97 -0
  18. package/dist/src/codegen/generator/debug.js.map +1 -0
  19. package/dist/src/codegen/generator/expressions/assignment.js +9 -5
  20. package/dist/src/codegen/generator/expressions/assignment.js.map +1 -1
  21. package/dist/src/codegen/generator/expressions/operators.js +35 -9
  22. package/dist/src/codegen/generator/expressions/operators.js.map +1 -1
  23. package/dist/src/codegen/generator/expressions/primary.js +11 -3
  24. package/dist/src/codegen/generator/expressions/primary.js.map +1 -1
  25. package/dist/src/codegen/generator/generator.d.ts +3 -1
  26. package/dist/src/codegen/generator/generator.js +3 -1
  27. package/dist/src/codegen/generator/generator.js.map +1 -1
  28. package/dist/src/codegen/generator/module.d.ts +1 -1
  29. package/dist/src/codegen/generator/module.js +82 -12
  30. package/dist/src/codegen/generator/module.js.map +1 -1
  31. package/dist/src/codegen/generator/numeric.d.ts +26 -0
  32. package/dist/src/codegen/generator/numeric.js +127 -0
  33. package/dist/src/codegen/generator/numeric.js.map +1 -0
  34. package/dist/src/codegen/generator/state.d.ts +17 -0
  35. package/dist/src/codegen/generator/state.js +9 -0
  36. package/dist/src/codegen/generator/state.js.map +1 -1
  37. package/dist/src/codegen/generator/statements/control-flow.js +2 -4
  38. package/dist/src/codegen/generator/statements/control-flow.js.map +1 -1
  39. package/dist/src/codegen/generator/statements/dispatch.js +3 -0
  40. package/dist/src/codegen/generator/statements/dispatch.js.map +1 -1
  41. package/dist/src/codegen/generator/statements/variables.js +8 -2
  42. package/dist/src/codegen/generator/statements/variables.js.map +1 -1
  43. package/dist/src/codegen/generator/tables.d.ts +16 -0
  44. package/dist/src/codegen/generator/tables.js +99 -2
  45. package/dist/src/codegen/generator/tables.js.map +1 -1
  46. package/dist/src/driver/bundler/merge.js +41 -5
  47. package/dist/src/driver/bundler/merge.js.map +1 -1
  48. package/dist/src/driver/compiler.d.ts +23 -4
  49. package/dist/src/driver/compiler.js +46 -57
  50. package/dist/src/driver/compiler.js.map +1 -1
  51. package/dist/src/driver/fingerprint.d.ts +27 -0
  52. package/dist/src/driver/fingerprint.js +88 -0
  53. package/dist/src/driver/fingerprint.js.map +1 -0
  54. package/dist/src/driver/toolchain-provider.d.ts +9 -2
  55. package/dist/src/driver/toolchain-provider.js +15 -15
  56. package/dist/src/driver/toolchain-provider.js.map +1 -1
  57. package/dist/src/driver/toolchain.d.ts +58 -1
  58. package/dist/src/driver/toolchain.js +169 -22
  59. package/dist/src/driver/toolchain.js.map +1 -1
  60. package/dist/src/parser/expressions.js +20 -1
  61. package/dist/src/parser/expressions.js.map +1 -1
  62. package/dist/tests/cli/main.test.js +41 -1
  63. package/dist/tests/cli/main.test.js.map +1 -1
  64. package/dist/tests/codegen/closures.test.js +8 -0
  65. package/dist/tests/codegen/closures.test.js.map +1 -1
  66. package/dist/tests/codegen/expressions.test.js +14 -3
  67. package/dist/tests/codegen/expressions.test.js.map +1 -1
  68. package/dist/tests/codegen/llvm.test.js +23 -0
  69. package/dist/tests/codegen/llvm.test.js.map +1 -1
  70. package/dist/tests/driver/toolchain-provider.test.js +49 -2
  71. package/dist/tests/driver/toolchain-provider.test.js.map +1 -1
  72. package/dist/tests/driver/toolchain.test.js +62 -3
  73. package/dist/tests/driver/toolchain.test.js.map +1 -1
  74. package/dist/tests/e2e/async-await.test.d.ts +10 -0
  75. package/dist/tests/e2e/async-await.test.js +247 -0
  76. package/dist/tests/e2e/async-await.test.js.map +1 -0
  77. package/dist/tests/e2e/child-process.test.d.ts +10 -0
  78. package/dist/tests/e2e/child-process.test.js +59 -0
  79. package/dist/tests/e2e/child-process.test.js.map +1 -0
  80. package/dist/tests/e2e/compile-objects-classes.test.js +47 -0
  81. package/dist/tests/e2e/compile-objects-classes.test.js.map +1 -1
  82. package/dist/tests/e2e/error-stack.test.d.ts +9 -0
  83. package/dist/tests/e2e/error-stack.test.js +94 -0
  84. package/dist/tests/e2e/error-stack.test.js.map +1 -0
  85. package/dist/tests/e2e/runtime-coverage-globals.test.js +46 -0
  86. package/dist/tests/e2e/runtime-coverage-globals.test.js.map +1 -1
  87. package/dist/tests/e2e/string-concat.test.d.ts +9 -0
  88. package/dist/tests/e2e/string-concat.test.js +102 -0
  89. package/dist/tests/e2e/string-concat.test.js.map +1 -0
  90. package/dist/tests/e2e/string-utf16.test.d.ts +6 -0
  91. package/dist/tests/e2e/string-utf16.test.js +133 -0
  92. package/dist/tests/e2e/string-utf16.test.js.map +1 -0
  93. package/dist/tests/helpers.d.ts +9 -0
  94. package/dist/tests/helpers.js +26 -3
  95. package/dist/tests/helpers.js.map +1 -1
  96. package/dist/tests/parser/expressions.test.js +7 -0
  97. package/dist/tests/parser/expressions.test.js.map +1 -1
  98. package/dist/tests/version.test.d.ts +1 -0
  99. package/dist/tests/version.test.js +24 -0
  100. package/dist/tests/version.test.js.map +1 -0
  101. package/doc/DESIGN.md +20 -12
  102. package/doc/ai/cli.md +2 -0
  103. package/doc/ai/contributing.md +16 -8
  104. package/doc/ai/extensions.md +3 -2
  105. package/doc/ai/language-support.md +15 -12
  106. package/doc/ai/troubleshooting.md +43 -5
  107. package/doc/ai/zh-CN/cli.md +2 -0
  108. package/doc/ai/zh-CN/contributing.md +11 -7
  109. package/doc/ai/zh-CN/extensions.md +2 -1
  110. package/doc/ai/zh-CN/language-support.md +15 -12
  111. package/doc/ai/zh-CN/troubleshooting.md +38 -4
  112. package/doc/implemented.md +16 -13
  113. package/doc/requirements.md +13 -4
  114. package/doc/unimplemented.md +20 -18
  115. package/doc/zh-CN/DESIGN.md +15 -9
  116. package/doc/zh-CN/implemented.md +15 -12
  117. package/doc/zh-CN/requirements.md +12 -3
  118. package/doc/zh-CN/unimplemented.md +19 -17
  119. package/package.json +3 -1
  120. package/runtime/ext_node/buffer/parts/construction.inc +2 -2
  121. package/runtime/ext_node/buffer/parts/methods.inc +1 -1
  122. package/runtime/ext_node/buffer/parts/search.inc +1 -1
  123. package/runtime/ext_node/child_process/child_process.c +25 -0
  124. package/runtime/ext_node/fs/fd_ops.c +1 -1
  125. package/runtime/rt.h +49 -1
  126. package/runtime/rt_internal.h +136 -4
  127. package/runtime/xt_alloc.c +20 -2
  128. package/runtime/xt_async.c +197 -0
  129. package/runtime/xt_bigint/conversions.inc +1 -1
  130. package/runtime/xt_builtins.c +169 -43
  131. package/runtime/xt_constructors.c +389 -0
  132. package/runtime/xt_containers/access.inc +61 -24
  133. package/runtime/xt_containers/arrays.inc +26 -2
  134. package/runtime/xt_containers/objects.inc +206 -8
  135. package/runtime/xt_coroutine.c +212 -0
  136. package/runtime/xt_generator.c +31 -162
  137. package/runtime/xt_io/exception.inc +171 -2
  138. package/runtime/xt_io/inspect.inc +48 -13
  139. package/runtime/xt_io.c +5 -0
  140. package/runtime/xt_promise.c +62 -17
  141. package/runtime/xt_stdlib/arrays.inc +2 -2
  142. package/runtime/xt_stdlib/dispatch.inc +20 -4
  143. package/runtime/xt_stdlib/strings.inc +98 -29
  144. package/runtime/xt_stdlib2/array-statics.inc +3 -2
  145. package/runtime/xt_stdlib2/arrays.inc +1 -1
  146. package/runtime/xt_stdlib2/error.inc +33 -6
  147. package/runtime/xt_stdlib2/json.inc +46 -16
  148. package/runtime/xt_stdlib2/number-string-statics.inc +24 -13
  149. package/runtime/xt_stdlib2/objects.inc +1 -1
  150. package/runtime/xt_stdlib2/regexp-match.inc +50 -35
  151. package/runtime/xt_stdlib2/set.inc +6 -0
  152. package/runtime/xt_stdlib2/strings.inc +81 -42
  153. package/runtime/xt_symbol.c +1 -1
  154. package/runtime/xt_typed_array/construction.inc +43 -2
  155. package/runtime/xt_typed_array.c +4 -0
  156. package/runtime/xt_values/comparison.inc +2 -2
  157. package/runtime/xt_values/constructors.inc +18 -5
  158. package/runtime/xt_values/conversions.inc +4 -3
  159. package/runtime/xt_values/strings.inc +94 -4
  160. package/runtime/xt_values/utf16.inc +340 -0
  161. package/runtime/xt_values.c +1 -0
  162. package/scripts/bench.ts +172 -0
  163. package/scripts/build-runtime.ts +2 -1
  164. package/scripts/check-version.ts +53 -0
  165. package/src/ast/common.ts +23 -0
  166. package/src/cli/main.ts +68 -26
  167. package/src/codegen/generator/calls/access.ts +7 -2
  168. package/src/codegen/generator/calls/closures.ts +3 -0
  169. package/src/codegen/generator/calls/invocation.ts +9 -3
  170. package/src/codegen/generator/context.ts +129 -18
  171. package/src/codegen/generator/debug.ts +132 -0
  172. package/src/codegen/generator/expressions/assignment.ts +9 -5
  173. package/src/codegen/generator/expressions/operators.ts +47 -8
  174. package/src/codegen/generator/expressions/primary.ts +11 -3
  175. package/src/codegen/generator/generator.ts +18 -2
  176. package/src/codegen/generator/module.ts +83 -13
  177. package/src/codegen/generator/numeric.ts +158 -0
  178. package/src/codegen/generator/state.ts +26 -0
  179. package/src/codegen/generator/statements/control-flow.ts +2 -3
  180. package/src/codegen/generator/statements/dispatch.ts +3 -0
  181. package/src/codegen/generator/statements/variables.ts +8 -2
  182. package/src/codegen/generator/tables.ts +110 -2
  183. package/src/driver/bundler/merge.ts +39 -5
  184. package/src/driver/compiler.ts +65 -55
  185. package/src/driver/fingerprint.ts +85 -0
  186. package/src/driver/toolchain-provider.ts +17 -16
  187. package/src/driver/toolchain.ts +210 -21
  188. package/src/parser/expressions.ts +20 -1
@@ -0,0 +1,102 @@
1
+ /**
2
+ * End-to-end tests for the lazy (rope/cons) string model.
3
+ *
4
+ * Concatenation above the eager-copy threshold produces an immutable cons node
5
+ * whose bytes are materialised on first observation. These tests exercise every
6
+ * kind of observation (length, slicing, searching, case changes, JSON, property
7
+ * keys) and keep chains reachable across a garbage collection.
8
+ */
9
+ import { expect, it } from "vitest";
10
+ import { describeE2E } from "./harness.js";
11
+ describeE2E("lazy string concatenation", (harness) => {
12
+ const { runProgram, runProgramFull } = harness;
13
+ it("matches Node for append and prepend chains", () => {
14
+ const source = `
15
+ function build(n: number): string {
16
+ let text = "";
17
+ for (let i = 0; i < n; i++) text = text + "abcdefghijklmnopqrstuvwxyz".charAt(i % 26);
18
+ return text;
19
+ }
20
+ const text = build(2000);
21
+ console.log(text.length);
22
+ console.log(text.slice(0, 26));
23
+ console.log(text.slice(-26));
24
+ console.log(text.indexOf("xyz"));
25
+ console.log(text.lastIndexOf("abc"));
26
+ console.log(text.includes("mnop"));
27
+ console.log(text.split("def").length);
28
+ console.log(text.toUpperCase().slice(0, 26));
29
+ console.log(text.replace("abc", "ABC").slice(0, 26));
30
+ console.log(text.replaceAll("a", "!").slice(0, 26));
31
+ console.log(text === build(2000));
32
+ console.log(text.startsWith("abc"), text.endsWith("xyz"));
33
+ console.log(JSON.stringify(text.slice(0, 10)));
34
+ console.log(text.charCodeAt(5), text.at(-1));
35
+
36
+ let prepended = "";
37
+ for (let i = 0; i < 2000; i++) prepended = "x" + prepended;
38
+ console.log(prepended.length, prepended.slice(0, 3), prepended.slice(-3));
39
+
40
+ let mixed = "";
41
+ for (let i = 0; i < 200; i++) mixed += i;
42
+ console.log(mixed.length, mixed.slice(0, 12));
43
+
44
+ let templated = "";
45
+ for (let i = 0; i < 2000; i++) templated = \`\${templated}\${i % 10}\`;
46
+ console.log(templated.length, templated.slice(0, 12));
47
+
48
+ let key = "";
49
+ for (let i = 0; i < 40; i++) key += "k";
50
+ const holder: any = {};
51
+ holder[key] = "v";
52
+ console.log(holder[key], Object.keys(holder)[0].length);
53
+ console.log([text, prepended].join("|").length);
54
+ `;
55
+ harness.expectSameOutputAsNode(source, { name: "string_concat_chains" });
56
+ });
57
+ it("keeps cons nodes reachable across collections", () => {
58
+ const source = `
59
+ let text = "";
60
+ for (let i = 0; i < 2000; i++) text += "z";
61
+ const holder: any = { text: text };
62
+ const list: any[] = [text];
63
+ text = "";
64
+
65
+ /* Allocate and drop many objects to force collections while the rope is
66
+ reachable only through the object property and the array element. */
67
+ let junk: any = {};
68
+ for (let i = 0; i < 40000; i++) junk = { value: i };
69
+ junk = {};
70
+ const trigger = "x" + "y";
71
+
72
+ /* Reuse the freed memory before the rope is ever flattened. */
73
+ const reuse: any[] = [];
74
+ for (let i = 0; i < 5000; i++) reuse.push("a" + "b");
75
+
76
+ console.log(holder.text.length, holder.text === "z".repeat(2000));
77
+ console.log(list[0].length, list[0] === holder.text, reuse.length, trigger);
78
+ console.log(holder.text.slice(0, 2), holder.text.slice(-2), holder.text.toUpperCase().slice(0, 3));
79
+ `;
80
+ const result = runProgramFull(source, {
81
+ name: "string_concat_gc",
82
+ env: { XT_GC_THRESHOLD: "1048576" },
83
+ timeout: 60000,
84
+ });
85
+ expect(result.status).toBe(0);
86
+ expect(result.stdout.trim()).toBe("2000 true\n2000 true 5000 xy\nzz zz ZZZ");
87
+ });
88
+ it("flattens reused chains without mutating earlier results", () => {
89
+ const source = `
90
+ let prefix = "start-";
91
+ for (let i = 0; i < 200; i++) prefix += "p";
92
+ const first = prefix + "-first";
93
+ const second = prefix + "-second";
94
+ const prefixCopy = prefix.slice(0);
95
+ console.log(first.slice(-6), second.slice(-7));
96
+ console.log(prefix.length, first.length, second.length);
97
+ console.log(prefix === prefixCopy, prefix === first.slice(0, prefix.length));
98
+ `;
99
+ expect(runProgram(source)).toBe("-first -second\n206 212 213\ntrue true");
100
+ });
101
+ });
102
+ //# sourceMappingURL=string-concat.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"string-concat.test.js","sourceRoot":"","sources":["../../../tests/e2e/string-concat.test.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,WAAW,CAAC,2BAA2B,EAAE,CAAC,OAAO,EAAE,EAAE;IACnD,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC;IAE/C,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwClB,CAAC;QACE,OAAO,CAAC,sBAAsB,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,sBAAsB,EAAE,CAAC,CAAC;IAC3E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;CAqBlB,CAAC;QACE,MAAM,MAAM,GAAG,cAAc,CAAC,MAAM,EAAE;YACpC,IAAI,EAAE,kBAAkB;YACxB,GAAG,EAAE,EAAE,eAAe,EAAE,SAAS,EAAE;YACnC,OAAO,EAAE,KAAK;SACf,CAAC,CAAC;QACH,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC;IAC/E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACjE,MAAM,MAAM,GAAG;;;;;;;;;CASlB,CAAC;QACE,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC;IAC5E,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -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"}
@@ -18,9 +18,18 @@ export declare function bindSource(text: string): {
18
18
  export declare function compileToIr(text: string, extensions?: ExtensionRegistry, target?: {
19
19
  platform: string;
20
20
  arch: string;
21
+ }, options?: {
22
+ readonly debug?: boolean;
21
23
  }): {
22
24
  ir: string;
23
25
  binding: BindResult;
24
26
  diagnostics: readonly Diagnostic[];
25
27
  };
28
+ /**
29
+ * True when a *supported* clang is available. The end-to-end suites really
30
+ * compile and link native binaries, and the emitted IR needs clang >= 16, so a
31
+ * missing or too-old toolchain skips them instead of failing every case with an
32
+ * opaque-pointer error. `resolveToolchain` applies the same selection (and
33
+ * version gate) the driver uses, so the tests and the compiler agree.
34
+ */
26
35
  export declare function hasClang(): boolean;
@@ -7,7 +7,8 @@ import { Parser } from "../src/parser/parser.js";
7
7
  import { bind } from "../src/binder/binder.js";
8
8
  import { generate } from "../src/codegen/llvm.js";
9
9
  import { createDefaultRegistry } from "../src/extensions/registry.js";
10
- import { realRunner } from "../src/driver/toolchain.js";
10
+ import { MINIMUM_CLANG_MAJOR } from "../src/driver/toolchain.js";
11
+ import { resolveToolchain } from "../src/driver/toolchain-provider.js";
11
12
  export function lex(text) {
12
13
  const source = new SourceFile("test.ts", sourceTextBytes(text));
13
14
  const diagnostics = new DiagnosticBag();
@@ -28,7 +29,7 @@ export function bindSource(text) {
28
29
  const result = bind(file);
29
30
  return { result, diagnostics: bag.diagnostics };
30
31
  }
31
- export function compileToIr(text, extensions, target) {
32
+ export function compileToIr(text, extensions, target, options = {}) {
32
33
  const { file, diagnostics: parseDiagnostics } = parse(text);
33
34
  const diagnostics = new DiagnosticBag();
34
35
  diagnostics.addAll(parseDiagnostics);
@@ -37,10 +38,32 @@ export function compileToIr(text, extensions, target) {
37
38
  builtins: registry.builtins(),
38
39
  modules: registry.modules(),
39
40
  ...(target ? { target } : {}),
41
+ debug: options.debug === true,
40
42
  });
41
43
  return { ir, binding, diagnostics: diagnostics.diagnostics };
42
44
  }
45
+ let cachedClangSupport;
46
+ /**
47
+ * True when a *supported* clang is available. The end-to-end suites really
48
+ * compile and link native binaries, and the emitted IR needs clang >= 16, so a
49
+ * missing or too-old toolchain skips them instead of failing every case with an
50
+ * opaque-pointer error. `resolveToolchain` applies the same selection (and
51
+ * version gate) the driver uses, so the tests and the compiler agree.
52
+ */
43
53
  export function hasClang() {
44
- return realRunner.run(process.env.xbintsc_CLANG ?? "clang", ["--version"]).status === 0;
54
+ if (cachedClangSupport === undefined) {
55
+ try {
56
+ const version = resolveToolchain().version;
57
+ cachedClangSupport = version === undefined || version.major >= MINIMUM_CLANG_MAJOR;
58
+ }
59
+ catch {
60
+ cachedClangSupport = false;
61
+ }
62
+ if (!cachedClangSupport) {
63
+ console.warn(`xbintsc: skipping end-to-end tests — clang ${MINIMUM_CLANG_MAJOR}+ was not found ` +
64
+ "(see doc/requirements.md, or set xbintsc_CLANG)");
65
+ }
66
+ }
67
+ return cachedClangSupport;
45
68
  }
46
69
  //# sourceMappingURL=helpers.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../tests/helpers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../tests/helpers.ts"],"names":[],"mappings":"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"}
@@ -44,4 +44,11 @@ describe("assignment validation", () => {
44
44
  expect(diagnostics.some((d) => d.code === DiagnosticCode.InvalidAssignmentTarget)).toBe(true);
45
45
  });
46
46
  });
47
+ describe("unsupported syntax", () => {
48
+ it("reports new.target as an unsupported feature, not a syntax error", () => {
49
+ const { diagnostics } = parse("function F() { return new.target; }");
50
+ expect(diagnostics.map((d) => d.code)).toContain(DiagnosticCode.UnsupportedFeature);
51
+ expect(diagnostics.map((d) => d.code)).not.toContain(DiagnosticCode.UnexpectedToken);
52
+ });
53
+ });
47
54
  //# sourceMappingURL=expressions.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"expressions.test.js","sourceRoot":"","sources":["../../../tests/parser/expressions.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"expressions.test.js","sourceRoot":"","sources":["../../../tests/parser/expressions.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,24 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { describe, expect, it } from "vitest";
3
+ import { COMPILER_VERSION } from "../src/driver/compiler.js";
4
+ const packageJson = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
5
+ describe("compiler version", () => {
6
+ it("matches package.json", () => {
7
+ expect(COMPILER_VERSION).toBe(packageJson.version);
8
+ });
9
+ it("is part of the incremental cache key", () => {
10
+ const source = readFileSync(new URL("../src/driver/compiler.ts", import.meta.url), "utf8");
11
+ expect(source).toMatch(/hashParts\(\[\s*COMPILER_VERSION,/);
12
+ });
13
+ it("is printed by `xbintsc version`", async () => {
14
+ const { run } = await import("../src/cli/main.js");
15
+ const lines = [];
16
+ const code = run(["version"], {
17
+ stdout: (text) => lines.push(text),
18
+ stderr: (text) => lines.push(text),
19
+ });
20
+ expect(code).toBe(0);
21
+ expect(lines.join("")).toContain(COMPILER_VERSION);
22
+ });
23
+ });
24
+ //# sourceMappingURL=version.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.test.js","sourceRoot":"","sources":["../../tests/version.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAE7D,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAC5B,YAAY,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAC3C,CAAC;AAEzB,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,sBAAsB,EAAE,GAAG,EAAE;QAC9B,MAAM,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,GAAG,CAAC,2BAA2B,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC;QAC3F,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,mCAAmC,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iCAAiC,EAAE,KAAK,IAAI,EAAE;QAC/C,MAAM,EAAE,GAAG,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;QACnD,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,GAAG,CAAC,CAAC,SAAS,CAAC,EAAE;YAC5B,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;YAClC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;SACnC,CAAC,CAAC;QACH,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACrB,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
package/doc/DESIGN.md CHANGED
@@ -41,16 +41,21 @@ xt_value fn(xt_value env, int32_t argc, xt_value *argv);
41
41
 
42
42
  `env` threads captured variables by reference through boxes, so direct calls and
43
43
  closure calls share a single code path. JavaScript semantics that are awkward to
44
- inline (`+` coercion, relational comparison, property access, inspection) are
45
- delegated to `@xt_*` runtime calls.
44
+ inline (`+` coercion, property access, inspection) are delegated to `@xt_*`
45
+ runtime calls. Numeric arithmetic and comparison are the exception: codegen emits
46
+ the double fast path inline (`numeric.ts`) and calls the runtime for the coercing
47
+ cases.
46
48
 
47
49
  ### Runtime
48
50
 
49
- `runtime/xt_runtime.c` implements strings, objects, arrays, closures, arithmetic,
50
- comparison, exceptions and Node-like `console.log` inspection. It uses a
51
- non-moving mark-sweep collector behind `xt_alloc` (explicit roots, subsystem
52
- root providers and a conservative C-stack scan), so the collector's internals
53
- stay isolated from the compiler.
51
+ `runtime/` implements strings, objects, arrays, closures, arithmetic, comparison,
52
+ exceptions and Node-like `console.log` inspection. It is split by concern into
53
+ translation units (`xt_values.c`, `xt_containers.c`, `xt_stdlib.c`,
54
+ `xt_builtins.c`, `xt_io.c`, `xt_alloc.c`, …), each `#include`-ing its
55
+ implementation fragments (`.inc`) so files stay small without changing the link
56
+ surface. It uses a non-moving mark-sweep collector behind `xt_alloc` (explicit
57
+ roots, subsystem root providers and a conservative C-stack scan), so the
58
+ collector's internals stay isolated from the compiler.
54
59
 
55
60
  ### Replacing llc
56
61
 
@@ -103,15 +108,18 @@ found on `PATH` (the MSVC ABI when LLVM + the Visual Studio C++ build tools are
103
108
  installed), and each manifest declares any platform-specific linker flags. See
104
109
  `examples/extensions/` and `runtime/xt_ext.h` / `runtime/xt_ext.rs`.
105
110
 
106
- ## Self-hosting roadmap
111
+ ## Self-hosting
107
112
 
108
- The compiler itself is written in TypeScript and emits IR text, and `runtime` is
109
- already decoupled from the compiler. Later, the runtime can be rewritten in TS
110
- and the compiler can compile itself, gradually reaching self-hosting.
113
+ The compiler compiles itself: `xbintsc build src/cli/main.ts --ext node` produces
114
+ a working `xbintsc` binary, and the emitted IR is a fixpoint from the first
115
+ generation onward. CI builds those binaries for every supported platform and
116
+ checks the fixpoint. The runtime stays in C; rewriting it in TypeScript is an
117
+ option for later, not a prerequisite for self-hosting.
111
118
 
112
119
  ## Tests
113
120
 
114
121
  Organised by module: `tests/lexer`, `tests/parser`, `tests/binder`,
115
122
  `tests/codegen`, `tests/driver`, `tests/extensions`, `tests/cli`, `tests/e2e`.
116
- When `clang` is present, e2e tests really compile and run binaries; otherwise
123
+ When a supported clang (16+, the same version gate `build` applies) is present,
124
+ e2e tests really compile and run binaries; otherwise
117
125
  they are skipped automatically.
package/doc/ai/cli.md CHANGED
@@ -36,6 +36,8 @@ unknown command prints an error plus the help text and exits 1.
36
36
  --app-id <id> macOS bundle identifier (e.g. com.example.demo)
37
37
  --force Ignore the incremental cache
38
38
  --verbose Print progress information
39
+ --time Print per-phase build timings
40
+ --debug Emit debug info (DWARF/CodeView) and keep frame pointers
39
41
  ```
40
42
 
41
43
  Parsing details that occasionally surprise:
@@ -38,6 +38,7 @@ it. Release tags are created by `release.yml`, never by the hook.
38
38
  | `npm run test:watch` | vitest in watch mode |
39
39
  | `npm run coverage` | V8 coverage of the TypeScript compiler (`src/`) |
40
40
  | `npm run coverage:runtime` | LLVM coverage of the C runtime (`runtime/`) |
41
+ | `npm run bench` | build and time the fixed benchmark suite (optionally vs Node) |
41
42
 
42
43
  Unit tests live one directory per module under `tests/` (`lexer`, `parser`,
43
44
  `binder`, `codegen`, `driver`, `extensions`, `cli`, `e2e`) and mirror the source
@@ -75,13 +76,15 @@ nondeterminism (a `Map`/`Set` iteration over insertion-ordered data you did not
75
76
  control, a timestamp, a filesystem-order dependency) into codegen.
76
77
 
77
78
  A second consequence is the string model: the compiler's own source text has to
78
- mean the same thing in every generation. A compiled string is UTF-8 bytes, so the
79
- self-hosted `readFileSync` (`--ext node`) hands the scanner the bytes of a file,
80
- while Node hands it the same file already decoded — read source files through
81
- `decodeUtf8` ([src/diagnostics/utf8.ts](../../src/diagnostics/utf8.ts)) and never
82
- assume a code unit is a byte or a character. Getting this wrong shows up as a
83
- non-ASCII literal (`—` in a hint string) coming out re-encoded in `gen2.ll`, which
84
- 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.
85
88
 
86
89
  Verify locally:
87
90
 
@@ -103,7 +106,12 @@ diff scratch/ref.ll scratch/gen2.ll
103
106
  in [../../src/codegen/values.ts](../../src/codegen/values.ts) and
104
107
  [../../runtime/rt.h](../../runtime/rt.h); change both together or not at all.
105
108
  - Awkward JS semantics (`+` coercion, relational comparison, property access,
106
- inspection) are delegated to `@xt_*` runtime calls rather than inlined.
109
+ inspection) are delegated to `@xt_*` runtime calls. Numeric arithmetic and
110
+ comparison are the exception: codegen inlines the two-double fast path
111
+ (`src/codegen/generator/numeric.ts`) and calls the runtime for everything else.
112
+ - String literals are interned: each distinct literal is materialised once in
113
+ `main`, stored in a GC-rooted global, and loaded at each use. Property names
114
+ therefore never allocate per access.
107
115
  - The GC is a non-moving mark-sweep collector: new heap objects go through
108
116
  `xt_alloc`, and a value must be reachable from an explicit root slot, a
109
117
  registered root provider (the event loop, the microtask queue) or the
@@ -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
 
@@ -45,8 +45,8 @@ Canonical detail (always current, much longer):
45
45
  | Destructuring (bindings, parameters, nested) | ✅ | defaults + rest supported |
46
46
  | `delete`, `in`, `instanceof`, `typeof` | ✅ | |
47
47
  | Comma operator, `void` | ✅ | |
48
- | `new.target` | 🚫 | not implemented |
49
- | `import.meta` | 🚫 | parsed but has no value |
48
+ | `new.target` | 🚫 | parsed, then rejected with a clear `UnsupportedFeature` |
49
+ | `import.meta` | ✅ | `url`, `dirname`, `filename` (resolved from the executable path) |
50
50
  | `super` | ⚠️ | single-level inheritance correct; depth > 1 may be inaccurate |
51
51
 
52
52
  ## Functions, classes, objects
@@ -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
- | `Error.stack` capture | 🚫 (not captured) |
107
- | `Object.getPrototypeOf({})` | ⚠️ returns `undefined`, not `Object.prototype` |
109
+ | `Error.stack` capture | ⚠️ function-level frames (name/file/line), no columns; generator bodies are omitted |
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
 
@@ -122,13 +124,14 @@ See section 3 of [../unimplemented.md](../unimplemented.md) for the full list.
122
124
  | `require()` / CommonJS | 🚫 | rejected with a hint to use `import` |
123
125
  | Circular dependencies | 🚫 | |
124
126
  | Live bindings | ⚠️ | imports and namespace members are snapshots |
125
- | `import.meta`, `__dirname`, `__filename` | 🚫 | |
127
+ | `__dirname`, `__filename` | 🚫 | use `import.meta.dirname` / `import.meta.filename` |
126
128
 
127
129
  ## Strings, numbers, memory
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 |