xbintsc 0.3.3 → 0.3.5

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 (215) hide show
  1. package/dist/src/ast/expressions.d.ts +8 -0
  2. package/dist/src/binder/binder.js +1 -0
  3. package/dist/src/binder/binder.js.map +1 -1
  4. package/dist/src/binder/references.d.ts +1 -1
  5. package/dist/src/binder/references.js +34 -5
  6. package/dist/src/binder/references.js.map +1 -1
  7. package/dist/src/binder/types.d.ts +4 -0
  8. package/dist/src/binder/types.js.map +1 -1
  9. package/dist/src/cli/main.js.map +1 -1
  10. package/dist/src/codegen/generator/calls/access.d.ts +9 -1
  11. package/dist/src/codegen/generator/calls/access.js +146 -13
  12. package/dist/src/codegen/generator/calls/access.js.map +1 -1
  13. package/dist/src/codegen/generator/calls/closures.d.ts +1 -0
  14. package/dist/src/codegen/generator/calls/closures.js +25 -0
  15. package/dist/src/codegen/generator/calls/closures.js.map +1 -1
  16. package/dist/src/codegen/generator/calls/invocation.js +12 -1
  17. package/dist/src/codegen/generator/calls/invocation.js.map +1 -1
  18. package/dist/src/codegen/generator/calls/literals.js +12 -4
  19. package/dist/src/codegen/generator/calls/literals.js.map +1 -1
  20. package/dist/src/codegen/generator/calls/super.js +4 -7
  21. package/dist/src/codegen/generator/calls/super.js.map +1 -1
  22. package/dist/src/codegen/generator/context.d.ts +21 -2
  23. package/dist/src/codegen/generator/context.js +42 -5
  24. package/dist/src/codegen/generator/context.js.map +1 -1
  25. package/dist/src/codegen/generator/expressions/assignment.js +1 -3
  26. package/dist/src/codegen/generator/expressions/assignment.js.map +1 -1
  27. package/dist/src/codegen/generator/expressions/primary.d.ts +3 -1
  28. package/dist/src/codegen/generator/expressions/primary.js +99 -1
  29. package/dist/src/codegen/generator/expressions/primary.js.map +1 -1
  30. package/dist/src/codegen/generator/generator.js.map +1 -1
  31. package/dist/src/codegen/generator/module.js +25 -15
  32. package/dist/src/codegen/generator/module.js.map +1 -1
  33. package/dist/src/codegen/generator/state.d.ts +15 -0
  34. package/dist/src/codegen/generator/state.js +28 -1
  35. package/dist/src/codegen/generator/state.js.map +1 -1
  36. package/dist/src/codegen/generator/statements/control-flow.d.ts +3 -1
  37. package/dist/src/codegen/generator/statements/control-flow.js +85 -0
  38. package/dist/src/codegen/generator/statements/control-flow.js.map +1 -1
  39. package/dist/src/codegen/generator/statements/dispatch.js +15 -6
  40. package/dist/src/codegen/generator/statements/dispatch.js.map +1 -1
  41. package/dist/src/codegen/generator/statements/loops.d.ts +3 -0
  42. package/dist/src/codegen/generator/statements/loops.js +25 -11
  43. package/dist/src/codegen/generator/statements/loops.js.map +1 -1
  44. package/dist/src/codegen/generator/tables.d.ts +7 -0
  45. package/dist/src/codegen/generator/tables.js +41 -1
  46. package/dist/src/codegen/generator/tables.js.map +1 -1
  47. package/dist/src/driver/compiler.d.ts +1 -1
  48. package/dist/src/driver/compiler.js +33 -1
  49. package/dist/src/driver/compiler.js.map +1 -1
  50. package/dist/src/driver/modules.d.ts +4 -3
  51. package/dist/src/driver/modules.js +61 -4
  52. package/dist/src/driver/modules.js.map +1 -1
  53. package/dist/src/driver/toolchain.js +1 -1
  54. package/dist/src/driver/toolchain.js.map +1 -1
  55. package/dist/src/lexer/scanner.js.map +1 -1
  56. package/dist/src/parser/declarations/classes.js +4 -0
  57. package/dist/src/parser/declarations/classes.js.map +1 -1
  58. package/dist/src/parser/declarations/type-members.js.map +1 -1
  59. package/dist/src/parser/expressions.d.ts +2 -1
  60. package/dist/src/parser/expressions.js +10 -1
  61. package/dist/src/parser/expressions.js.map +1 -1
  62. package/dist/src/parser/helpers.js +2 -2
  63. package/dist/src/parser/helpers.js.map +1 -1
  64. package/dist/src/parser/literals.js +79 -6
  65. package/dist/src/parser/literals.js.map +1 -1
  66. package/dist/src/parser/modules.js.map +1 -1
  67. package/dist/src/parser/parser.js +0 -1
  68. package/dist/src/parser/parser.js.map +1 -1
  69. package/dist/src/parser/statements.d.ts +1 -0
  70. package/dist/src/parser/statements.js +13 -1
  71. package/dist/src/parser/statements.js.map +1 -1
  72. package/dist/src/parser/types.js +2 -2
  73. package/dist/src/parser/types.js.map +1 -1
  74. package/dist/tests/ast/factory.test.d.ts +1 -0
  75. package/dist/tests/ast/factory.test.js +35 -0
  76. package/dist/tests/ast/factory.test.js.map +1 -0
  77. package/dist/tests/ast/visitor.test.d.ts +1 -0
  78. package/dist/tests/ast/visitor.test.js +53 -0
  79. package/dist/tests/ast/visitor.test.js.map +1 -0
  80. package/dist/tests/cli/main.test.js +109 -9
  81. package/dist/tests/cli/main.test.js.map +1 -1
  82. package/dist/tests/codegen/closures.test.d.ts +1 -0
  83. package/dist/tests/codegen/closures.test.js +32 -0
  84. package/dist/tests/codegen/closures.test.js.map +1 -0
  85. package/dist/tests/codegen/expressions.test.d.ts +1 -0
  86. package/dist/tests/codegen/expressions.test.js +149 -0
  87. package/dist/tests/codegen/expressions.test.js.map +1 -0
  88. package/dist/tests/codegen/llvm.test.js +14 -0
  89. package/dist/tests/codegen/llvm.test.js.map +1 -1
  90. package/dist/tests/codegen/values.test.d.ts +1 -0
  91. package/dist/tests/codegen/values.test.js +48 -0
  92. package/dist/tests/codegen/values.test.js.map +1 -0
  93. package/dist/tests/diagnostics/diagnostic.test.d.ts +1 -0
  94. package/dist/tests/diagnostics/diagnostic.test.js +117 -0
  95. package/dist/tests/diagnostics/diagnostic.test.js.map +1 -0
  96. package/dist/tests/diagnostics/source.test.d.ts +1 -0
  97. package/dist/tests/diagnostics/source.test.js +58 -0
  98. package/dist/tests/diagnostics/source.test.js.map +1 -0
  99. package/dist/tests/driver/modules.test.d.ts +1 -0
  100. package/dist/tests/driver/modules.test.js +140 -0
  101. package/dist/tests/driver/modules.test.js.map +1 -0
  102. package/dist/tests/driver/paths.test.js +17 -1
  103. package/dist/tests/driver/paths.test.js.map +1 -1
  104. package/dist/tests/driver/runtime-lib.test.d.ts +1 -0
  105. package/dist/tests/driver/runtime-lib.test.js +19 -0
  106. package/dist/tests/driver/runtime-lib.test.js.map +1 -0
  107. package/dist/tests/driver/toolchain-provider.test.js +91 -0
  108. package/dist/tests/driver/toolchain-provider.test.js.map +1 -1
  109. package/dist/tests/driver/toolchain.test.js +11 -1
  110. package/dist/tests/driver/toolchain.test.js.map +1 -1
  111. package/dist/tests/e2e/compile-node.test.js +14 -1
  112. package/dist/tests/e2e/compile-node.test.js.map +1 -1
  113. package/dist/tests/e2e/compile.test.js +465 -2
  114. package/dist/tests/e2e/compile.test.js.map +1 -1
  115. package/dist/tests/e2e/differential.test.d.ts +7 -0
  116. package/dist/tests/e2e/differential.test.js +581 -0
  117. package/dist/tests/e2e/differential.test.js.map +1 -0
  118. package/dist/tests/e2e/harness.d.ts +21 -0
  119. package/dist/tests/e2e/harness.js +26 -0
  120. package/dist/tests/e2e/harness.js.map +1 -1
  121. package/dist/tests/extensions/node.test.d.ts +1 -0
  122. package/dist/tests/extensions/node.test.js +32 -0
  123. package/dist/tests/extensions/node.test.js.map +1 -0
  124. package/dist/tests/lexer/identifiers.test.d.ts +1 -0
  125. package/dist/tests/lexer/identifiers.test.js +13 -0
  126. package/dist/tests/lexer/identifiers.test.js.map +1 -0
  127. package/dist/tests/lexer/numbers.test.d.ts +1 -0
  128. package/dist/tests/lexer/numbers.test.js +23 -0
  129. package/dist/tests/lexer/numbers.test.js.map +1 -0
  130. package/dist/tests/lexer/strings.test.d.ts +1 -0
  131. package/dist/tests/lexer/strings.test.js +72 -0
  132. package/dist/tests/lexer/strings.test.js.map +1 -0
  133. package/dist/tests/parser/declarations.test.d.ts +1 -0
  134. package/dist/tests/parser/declarations.test.js +70 -0
  135. package/dist/tests/parser/declarations.test.js.map +1 -0
  136. package/dist/tests/parser/expressions.test.d.ts +1 -0
  137. package/dist/tests/parser/expressions.test.js +47 -0
  138. package/dist/tests/parser/expressions.test.js.map +1 -0
  139. package/dist/tests/parser/helpers.test.d.ts +1 -0
  140. package/dist/tests/parser/helpers.test.js +58 -0
  141. package/dist/tests/parser/helpers.test.js.map +1 -0
  142. package/dist/tests/parser/literals.test.d.ts +1 -0
  143. package/dist/tests/parser/literals.test.js +70 -0
  144. package/dist/tests/parser/literals.test.js.map +1 -0
  145. package/dist/tests/parser/module-syntax.test.d.ts +1 -0
  146. package/dist/tests/parser/module-syntax.test.js +91 -0
  147. package/dist/tests/parser/module-syntax.test.js.map +1 -0
  148. package/dist/tests/parser/statements.test.d.ts +1 -0
  149. package/dist/tests/parser/statements.test.js +85 -0
  150. package/dist/tests/parser/statements.test.js.map +1 -0
  151. package/dist/tests/parser/type-members.test.d.ts +1 -0
  152. package/dist/tests/parser/type-members.test.js +86 -0
  153. package/dist/tests/parser/type-members.test.js.map +1 -0
  154. package/dist/tests/parser/types.test.d.ts +1 -0
  155. package/dist/tests/parser/types.test.js +143 -0
  156. package/dist/tests/parser/types.test.js.map +1 -0
  157. package/package.json +6 -1
  158. package/runtime/ext_node/fs/fs_ops.c +36 -1
  159. package/runtime/rt.h +17 -0
  160. package/runtime/rt_internal.h +88 -2
  161. package/runtime/xt_builtins.c +174 -6
  162. package/runtime/xt_containers.c +234 -42
  163. package/runtime/xt_generator.c +304 -0
  164. package/runtime/xt_io.c +170 -30
  165. package/runtime/xt_promise.c +6 -1
  166. package/runtime/xt_regex.h +105 -35
  167. package/runtime/xt_stdlib/dispatch.inc +255 -4
  168. package/runtime/xt_stdlib/strings.inc +17 -13
  169. package/runtime/xt_stdlib2/arrays.inc +126 -19
  170. package/runtime/xt_stdlib2/date.inc +9 -2
  171. package/runtime/xt_stdlib2/error.inc +162 -12
  172. package/runtime/xt_stdlib2/helpers.inc +4 -0
  173. package/runtime/xt_stdlib2/json.inc +19 -6
  174. package/runtime/xt_stdlib2/number-string-statics.inc +1 -1
  175. package/runtime/xt_stdlib2/numbers.inc +4 -5
  176. package/runtime/xt_stdlib2/objects.inc +29 -0
  177. package/runtime/xt_stdlib2/regexp-match.inc +293 -51
  178. package/runtime/xt_stdlib2/set.inc +58 -2
  179. package/runtime/xt_stdlib2/strings.inc +38 -0
  180. package/runtime/xt_symbol.c +149 -0
  181. package/runtime/xt_values.c +221 -15
  182. package/scripts/build-runtime.ts +1 -1
  183. package/src/ast/expressions.ts +8 -0
  184. package/src/binder/binder.ts +1 -1
  185. package/src/binder/references.ts +47 -4
  186. package/src/binder/types.ts +4 -0
  187. package/src/cli/main.ts +1 -2
  188. package/src/codegen/generator/calls/access.ts +158 -13
  189. package/src/codegen/generator/calls/closures.ts +24 -0
  190. package/src/codegen/generator/calls/invocation.ts +11 -1
  191. package/src/codegen/generator/calls/literals.ts +10 -4
  192. package/src/codegen/generator/calls/super.ts +6 -9
  193. package/src/codegen/generator/context.ts +44 -7
  194. package/src/codegen/generator/expressions/assignment.ts +1 -3
  195. package/src/codegen/generator/expressions/primary.ts +108 -2
  196. package/src/codegen/generator/generator.ts +0 -1
  197. package/src/codegen/generator/module.ts +24 -18
  198. package/src/codegen/generator/state.ts +44 -1
  199. package/src/codegen/generator/statements/control-flow.ts +86 -0
  200. package/src/codegen/generator/statements/dispatch.ts +18 -6
  201. package/src/codegen/generator/statements/loops.ts +29 -11
  202. package/src/codegen/generator/tables.ts +44 -1
  203. package/src/driver/compiler.ts +32 -2
  204. package/src/driver/modules.ts +64 -4
  205. package/src/driver/toolchain.ts +1 -1
  206. package/src/lexer/scanner.ts +0 -1
  207. package/src/parser/declarations/classes.ts +4 -0
  208. package/src/parser/declarations/type-members.ts +0 -2
  209. package/src/parser/expressions.ts +13 -1
  210. package/src/parser/helpers.ts +2 -2
  211. package/src/parser/literals.ts +82 -7
  212. package/src/parser/modules.ts +0 -1
  213. package/src/parser/parser.ts +0 -1
  214. package/src/parser/statements.ts +14 -0
  215. package/src/parser/types.ts +2 -2
@@ -0,0 +1,581 @@
1
+ /**
2
+ * Differential tests: compile a program with xbintsc, run it, and compare the
3
+ * output byte-for-byte with the same program executed by Node. Every program
4
+ * prints canonical `JSON.stringify` lines so console formatting never masks a
5
+ * real semantic difference.
6
+ */
7
+ import { it } from "vitest";
8
+ import { describeE2E } from "./harness.js";
9
+ describeE2E("differential vs Node", (harness) => {
10
+ /** Register one differential case from a complete program source. */
11
+ const diff = (name, source) => {
12
+ it(name, () => {
13
+ harness.expectSameOutputAsNode(source, { name: `diff_${name.replace(/\W+/g, "_")}` });
14
+ });
15
+ };
16
+ const printAll = (values) => `const values: unknown[] = [\n${values.map((v) => ` ${v},`).join("\n")}\n];\n` +
17
+ `for (const v of values) console.log(JSON.stringify(v));\n`;
18
+ diff("number formatting and coercion", printAll([
19
+ "0",
20
+ "-0",
21
+ "1.5",
22
+ "0.1",
23
+ "1 / 3",
24
+ "1e21",
25
+ "1e-7",
26
+ "1e-6",
27
+ "123456789012345680000",
28
+ "Number.MAX_SAFE_INTEGER",
29
+ "Number.MIN_SAFE_INTEGER",
30
+ "Number.EPSILON",
31
+ "Infinity",
32
+ "-Infinity",
33
+ "NaN",
34
+ "(1234.5678).toFixed(2)",
35
+ "(0.000001).toFixed(7)",
36
+ "(1e21).toFixed(2)",
37
+ "(2.5).toFixed(0)",
38
+ "(-2.5).toFixed(0)",
39
+ "(123.456).toPrecision(5)",
40
+ "(123.4).toPrecision(10)",
41
+ "(0.0001234).toPrecision(2)",
42
+ "(12345).toExponential()",
43
+ "(12345).toExponential(2)",
44
+ "(0.000123).toExponential(3)",
45
+ "(255).toString(16)",
46
+ "(255).toString(2)",
47
+ "String(42)",
48
+ "String(1e21)",
49
+ "String(1e-7)",
50
+ ]));
51
+ diff("number parsing and predicates", printAll([
52
+ 'Number(" 42 ")',
53
+ 'Number("")',
54
+ 'Number("0x1f")',
55
+ 'Number("0b101")',
56
+ 'Number("0o17")',
57
+ 'Number("abc")',
58
+ 'parseInt("42px")',
59
+ 'parseFloat("3.14abc")',
60
+ "isNaN(NaN)",
61
+ "isFinite(3)",
62
+ "Number.isInteger(3)",
63
+ "Number.isInteger(3.5)",
64
+ "Number.isNaN(NaN)",
65
+ "Number.isFinite(Infinity)",
66
+ "Number.isSafeInteger(2 ** 53)",
67
+ ]));
68
+ diff("abstract equality and coercion", printAll([
69
+ '1 == "1"',
70
+ '1 === "1"',
71
+ "0 == false",
72
+ '"" == false',
73
+ "null == undefined",
74
+ "null === undefined",
75
+ "[] == false",
76
+ "[] == 0",
77
+ "[1] == 1",
78
+ '[1, 2] == "1,2"',
79
+ '{} == "[object Object]"',
80
+ "NaN == NaN",
81
+ '"1" == true',
82
+ '"0" == false',
83
+ ]));
84
+ diff("arithmetic and relational operators", printAll([
85
+ "[] + []",
86
+ "[1] + [2]",
87
+ "[1, 2] + [3]",
88
+ '{} + ""',
89
+ '1 + "2"',
90
+ '"3" - 1',
91
+ '"3" * "2"',
92
+ "true + 1",
93
+ "null + 1",
94
+ "undefined + 1",
95
+ '+ "42"',
96
+ '+ "0x1f"',
97
+ '+ ""',
98
+ '+ " 12 "',
99
+ '+ "abc"',
100
+ "[2] < 3",
101
+ '[1, 2] < "1,3"',
102
+ '"10" < "9"',
103
+ "10 < 9",
104
+ "2 ** 10",
105
+ "7 % 3",
106
+ "-7 % 3",
107
+ "1 / 0",
108
+ "0 / 0",
109
+ ]));
110
+ diff("logical operators, bitwise and typeof", printAll([
111
+ '0 || "x"',
112
+ '"" || "y"',
113
+ 'null ?? "z"',
114
+ 'undefined ?? "w"',
115
+ "0 ?? 5",
116
+ "5 & 3",
117
+ "5 | 2",
118
+ "5 ^ 1",
119
+ "~5",
120
+ "1 << 4",
121
+ "-8 >> 1",
122
+ "-8 >>> 1",
123
+ "typeof 1",
124
+ 'typeof "a"',
125
+ "typeof true",
126
+ "typeof undefined",
127
+ "typeof null",
128
+ "typeof {}",
129
+ "typeof []",
130
+ "typeof (() => 1)",
131
+ ]));
132
+ diff("string methods", printAll([
133
+ '"Hello, World".length',
134
+ '"Hello, World".toUpperCase()',
135
+ '"Hello, World".toLowerCase()',
136
+ '"Hello, World".indexOf("o")',
137
+ '"Hello, World".lastIndexOf("o")',
138
+ '"Hello, World".includes("World")',
139
+ '"Hello, World".slice(7)',
140
+ '"Hello, World".slice(-5)',
141
+ '"Hello, World".substring(0, 5)',
142
+ '"Hello, World".substr(7, 3)',
143
+ '"a, b, c".split(", ")',
144
+ '"Hello".replace("l", "L")',
145
+ '"Hello".replaceAll("l", "L")',
146
+ '"ab".repeat(3)',
147
+ '"Hello".charAt(1)',
148
+ '"Hello".charCodeAt(0)',
149
+ '" pad ".trim()',
150
+ '"ab".padStart(5, "-")',
151
+ '"ab".padEnd(5, "-")',
152
+ '"Hello".concat("!" )',
153
+ '"a1b2c3".match(/\\d/g)',
154
+ '"a1b2c3".replace(/\\d/g, "#")',
155
+ '"abc".at(-1)',
156
+ '"Hello".localeCompare("World")',
157
+ ]));
158
+ diff("array methods", printAll([
159
+ "[3, 1, 2].sort()",
160
+ '["banana", "apple", "cherry"].sort()',
161
+ "[10, 9, 100].sort()",
162
+ "[3, 1, 2].sort((a, b) => a - b)",
163
+ "[1, 2, 3].map((x) => x * 2)",
164
+ "[1, 2, 3, 4].filter((x) => x % 2 === 0)",
165
+ "[1, 2, 3].reduce((a, b) => a + b, 0)",
166
+ "[1, 2, 3].reverse()",
167
+ '[1, 2, 3].join("-")',
168
+ "[1, [2, [3, [4]]]].flat(2)",
169
+ "[1, 2, 3].flatMap((x) => [x, x])",
170
+ "[1, 2, 3, 4].slice(1, 3)",
171
+ "[1, 2, 3].concat([4, 5])",
172
+ "[1, 2, 3].indexOf(2)",
173
+ "[1, 2, 3].includes(4)",
174
+ "[5, 3, 8].find((x) => x > 4)",
175
+ "[1, 2, 3].some((x) => x > 2)",
176
+ "[1, 2, 3].every((x) => x > 0)",
177
+ "Array.isArray([])",
178
+ "Array.from([1, 2, 3], (x) => x + 1)",
179
+ "Array.of(1, 2, 3)",
180
+ "[1, 2, 3].fill(0, 1)",
181
+ "[1, 2, 3, 4].copyWithin(0, 2)",
182
+ "[1, 2, 3].at(-1)",
183
+ "[1, 2, 3].splice(1, 1)",
184
+ ]));
185
+ diff("objects, key order and JSON", `const o = { b: 2, a: 1, "10": "ten", "2": "two", c: 3 };
186
+ ${printAll([
187
+ "Object.keys(o)",
188
+ "Object.values(o)",
189
+ "Object.entries(o)",
190
+ "JSON.stringify(o)",
191
+ "JSON.stringify({ x: undefined, y: null, z: () => 1 })",
192
+ "JSON.stringify([undefined, null, () => 1])",
193
+ "JSON.stringify({ a: { b: [1, 2, { c: 3 }] } })",
194
+ 'JSON.stringify("hi")',
195
+ "JSON.stringify(5)",
196
+ "JSON.stringify(true)",
197
+ "JSON.stringify(null)",
198
+ "JSON.stringify(NaN)",
199
+ "JSON.stringify(Infinity)",
200
+ "JSON.stringify(undefined)",
201
+ "Object.assign({}, o, { d: 4 })",
202
+ "{ ...o, e: 5 }",
203
+ '"a" in o',
204
+ 'o.hasOwnProperty("a")',
205
+ ])}`);
206
+ diff("control flow, closures and classes", `function fib(n: number): number { return n < 2 ? n : fib(n - 1) + fib(n - 2); }
207
+ function classify(x: number): string {
208
+ if (x < 0) return "neg";
209
+ else if (x === 0) return "zero";
210
+ return "pos";
211
+ }
212
+ function sum(...xs: number[]): number { return xs.reduce((a, b) => a + b, 0); }
213
+ function defaults(a: number, b = 10, c = a + b): number { return a + b + c; }
214
+ function safeDiv(a: number, b: number): string {
215
+ try {
216
+ if (b === 0) throw new Error("divide by zero");
217
+ return String(a / b);
218
+ } catch (err) {
219
+ return "caught";
220
+ } finally {
221
+ // always runs
222
+ }
223
+ }
224
+ class Animal {
225
+ name: string;
226
+ constructor(name: string) { this.name = name; }
227
+ speak(): string { return this.name + " makes a sound"; }
228
+ static create(n: string): Animal { return new Animal(n); }
229
+ }
230
+ class Dog extends Animal {
231
+ constructor(name: string) { super(name); }
232
+ speak(): string { return this.name + " barks"; }
233
+ }
234
+ const counter = () => { let c = 0; return () => ++c; };
235
+ const mk = counter();
236
+ const results: unknown[] = [
237
+ fib(10), classify(-5), classify(0), classify(5), sum(1, 2, 3),
238
+ defaults(1), defaults(1, 2), defaults(1, 2, 3),
239
+ mk(), mk(), mk(),
240
+ new Animal("Cat").speak(), new Dog("Rex").speak(), Animal.create("Bird").speak(),
241
+ Dog.prototype instanceof Animal,
242
+ safeDiv(10, 2), safeDiv(1, 0),
243
+ (() => { let s = 0; for (let i = 0; i < 5; i++) s += i; return s; })(),
244
+ (() => { let s = ""; for (const ch of ["a", "b", "c"]) s += ch; return s; })(),
245
+ (() => { let i = 0, s = 0; while (i < 3) { s += i; i++; } return s; })(),
246
+ (() => { let i = 0; do { i++; } while (i < 3); return i; })(),
247
+ (() => { switch (2) { case 1: return "one"; case 2: return "two"; default: return "other"; } })(),
248
+ ];
249
+ for (const v of results) console.log(JSON.stringify(v));`);
250
+ diff("Math, collections and dates", printAll([
251
+ "Math.max(1, 2, 3)",
252
+ "Math.min()",
253
+ "Math.abs(-3)",
254
+ "Math.floor(1.9)",
255
+ "Math.ceil(1.1)",
256
+ "Math.round(2.5)",
257
+ "Math.round(-2.5)",
258
+ "Math.trunc(-1.9)",
259
+ "Math.sign(-5)",
260
+ "Math.sqrt(16)",
261
+ "Math.pow(2, 8)",
262
+ "Math.PI",
263
+ "new Date(0).toISOString()",
264
+ "new Date(1000).getTime()",
265
+ "new Date(2020, 0, 1).getUTCFullYear()",
266
+ "new Date(Date.UTC(2020, 5, 15)).toISOString()",
267
+ "[...new Set([1, 2, 2, 3])]",
268
+ "[...new Map([[\"a\", 1], [\"b\", 2]]).keys()]",
269
+ ]));
270
+ diff("regexp capture groups and string splitting", printAll([
271
+ '"12-34".match(/(\\d+)-(\\d+)/)',
272
+ '"a1b2c3".split(/(\\d)/)',
273
+ '"a1b2c3".split(/\\d/)',
274
+ '"2020-01-02".replace(/(\\d+)-(\\d+)-(\\d+)/, "$3/$2/$1")',
275
+ '"a1b2".replace(/(\\d)/g, "[$1]")',
276
+ '"a.b.c".search(/\\./)',
277
+ '"abc".split(/(?:)/)',
278
+ '"hello world".match(/(\\w+) (\\w+)/)',
279
+ '"aaa".split("a", 2)',
280
+ '"a,b;c".split(/[,;]/)',
281
+ ]));
282
+ diff("immutable array methods and Object statics", printAll([
283
+ "[3, 1, 2].toSorted((x, y) => x - y)",
284
+ "[3, 1, 2].toReversed()",
285
+ "[3, 1, 2].toSpliced(1, 1, 9, 9)",
286
+ "[3, 1, 2].with(1, 9)",
287
+ "[3, 1, 2].with(-1, 9)",
288
+ "Object.getOwnPropertyNames({ b: 1, a: 2 })",
289
+ "Object.getOwnPropertyNames([1, 2])",
290
+ "Object.groupBy([1, 2, 3, 4], (n) => (n % 2 === 0 ? \"even\" : \"odd\"))",
291
+ "Array.from({ length: 3 }, (_v, i) => i * 2)",
292
+ ]));
293
+ diff("tagged templates and String.raw", `function tag(strings: TemplateStringsArray, ...values: unknown[]): string {
294
+ let out = strings[0];
295
+ for (let i = 0; i < values.length; i++) out += "[" + String(values[i]) + "]" + strings[i + 1];
296
+ return out;
297
+ }
298
+ const obj = {
299
+ prefix: "<",
300
+ tag(strings: TemplateStringsArray, ...values: unknown[]): string {
301
+ return this.prefix + strings.join("|") + "/" + JSON.stringify(strings.raw) + "/" + values.join(",");
302
+ },
303
+ };
304
+ const name = "world";
305
+ ${printAll([
306
+ 'tag`hello ${name} and ${1 + 1}!`',
307
+ 'tag`no substitutions`',
308
+ 'obj.tag`a\\n${name}b${2}`',
309
+ 'String.raw`a\\nb\\t${name}c`',
310
+ 'String.raw`plain`',
311
+ 'tag`${name}`',
312
+ ])}`);
313
+ diff("Function.prototype.call, apply and bind", `function add(a: number, b: number): number { return a + b; }
314
+ const obj = {
315
+ base: 10,
316
+ sum(a: number, b: number): number { return this.base + a + b; },
317
+ };
318
+ class Greeter {
319
+ prefix = "hi";
320
+ greet(name: string): string { return this.prefix + " " + name; }
321
+ }
322
+ const g = new Greeter();
323
+ const bound = obj.sum.bind(obj, 1);
324
+ const bound2 = add.bind(null, 10);
325
+ const mk = (x: number) => x * 2;
326
+ ${printAll([
327
+ "add.call(null, 1, 2)",
328
+ "obj.sum.call(obj, 1, 2)",
329
+ "obj.sum.apply(obj, [3, 4])",
330
+ "add.apply(null, [5, 6])",
331
+ "bound(2)",
332
+ "bound2(5)",
333
+ 'g.greet.call(g, "ada")',
334
+ 'g.greet.apply(g, ["bob"])',
335
+ 'g.greet.bind(g)("cy")',
336
+ "mk.call(null, 21)",
337
+ "mk.apply(null, [3])",
338
+ ])}`);
339
+ diff("first-class built-in method values", `const array = [1, 2, 3];
340
+ const text = "abc";
341
+ const fn = function (x: number): number { return x; };
342
+ const date = new Date(0);
343
+ const map = new Map<string, number>();
344
+ map.set("a", 1);
345
+ const set = new Set<number>([1, 2]);
346
+ const regexp = /a(b)c/;
347
+ const object = { base: 10, add(n: number): number { return this.base + n; } };
348
+ type Holder = { base: number; go?: (n: number) => number };
349
+ const holder: Holder = { base: 10, go(n: number): number { return this.base + n; } };
350
+ const empty: { go?: (n: number) => number } = {};
351
+ const results: unknown[] = [
352
+ typeof array.map,
353
+ typeof array.push,
354
+ typeof text.slice,
355
+ typeof fn.call,
356
+ typeof date.getTime,
357
+ typeof map.get,
358
+ typeof set.add,
359
+ typeof regexp.test,
360
+ array.map.name,
361
+ array.map.length,
362
+ text.slice.length,
363
+ array.map(function (x: number): number { return x * 2; }),
364
+ array.map?.((x: number): number => x + 1),
365
+ text.split?.(""),
366
+ object.add(5),
367
+ holder.go?.(3),
368
+ empty.go?.(3),
369
+ ];
370
+ for (const value of results) console.log(JSON.stringify(value));
371
+ try {
372
+ const detached = array.map;
373
+ (detached as unknown as (cb: (x: number) => number) => number[])((x: number): number => x);
374
+ console.log(JSON.stringify("no throw"));
375
+ } catch (caught) {
376
+ console.log(JSON.stringify("threw"));
377
+ }
378
+ `);
379
+ diff("object-literal getters and setters", `const obj = {
380
+ _x: 0,
381
+ get x(): number { return this._x; },
382
+ set x(v: number) { this._x = v * 2; },
383
+ get double(): number { return this._x * 2; },
384
+ method(a: number, b: number): number { return a + b; },
385
+ };
386
+ obj.x = 5;
387
+ const base = 7;
388
+ const o2 = { base, get next() { return base + 1; } };
389
+ const o3 = { get ["dyn" + "amic"]() { return 42; } };
390
+ ${printAll([
391
+ "obj.x",
392
+ "obj.double",
393
+ "obj.method(2, 3)",
394
+ "Object.keys(obj)",
395
+ "o2.base",
396
+ "o2.next",
397
+ "o3.dynamic",
398
+ ])}`);
399
+ diff("Error family and AggregateError", `
400
+ const e = new Error("boom");
401
+ class MyErr extends Error {
402
+ constructor(m: string) { super(m); this.name = "MyErr"; }
403
+ }
404
+ class CustomRange extends RangeError {}
405
+ const m = new MyErr("m");
406
+ const r = new CustomRange("too big");
407
+ const agg = new AggregateError([1, 2], "all failed");
408
+ ${printAll([
409
+ "typeof Error",
410
+ "typeof TypeError",
411
+ "typeof AggregateError",
412
+ "e instanceof Error",
413
+ "new TypeError('b') instanceof TypeError",
414
+ "new TypeError('b') instanceof Error",
415
+ "new RangeError('c') instanceof TypeError",
416
+ "String(e)",
417
+ "e.toString()",
418
+ "e.name",
419
+ "e.message",
420
+ "m.name",
421
+ "m.message",
422
+ "m instanceof MyErr",
423
+ "m instanceof Error",
424
+ "m instanceof TypeError",
425
+ "r.name",
426
+ "r.message",
427
+ "r instanceof RangeError",
428
+ "r instanceof Error",
429
+ "agg.name",
430
+ "agg.message",
431
+ "agg.errors",
432
+ "agg instanceof AggregateError",
433
+ "agg instanceof Error",
434
+ "String(agg)",
435
+ "typeof e.stack",
436
+ ])}`);
437
+ diff("Promise.any rejects with an AggregateError", `
438
+ Promise.any([Promise.reject("a"), Promise.reject("b"), Promise.resolve(42)])
439
+ .then((value) => console.log(JSON.stringify(["fulfilled", value])))
440
+ .catch((reason) => {
441
+ const err = reason as { name: string; message: string; errors: unknown[] };
442
+ console.log(JSON.stringify([err.name, err.message, err.errors]));
443
+ });
444
+ Promise.any([Promise.reject("x"), Promise.reject("y")]).catch((reason) => {
445
+ const err = reason as { name: string; errors: unknown[] };
446
+ console.log(JSON.stringify([err.name, err.errors, reason instanceof AggregateError]));
447
+ });
448
+ `);
449
+ diff("generators", `
450
+ function* nums() { yield 1; yield 2; yield 3; }
451
+ const g = nums();
452
+ console.log(JSON.stringify([typeof nums, g.next(), g.next(), g.next(), g.next()]));
453
+
454
+ function* counter(start: number) {
455
+ let x = start;
456
+ while (true) {
457
+ const step = yield x;
458
+ x = step === undefined ? x + 1 : step;
459
+ }
460
+ }
461
+ const c = counter(10);
462
+ console.log(JSON.stringify([c.next().value, c.next().value, c.next(100).value, c.next().value]));
463
+
464
+ function* inner() { yield "a"; return "done"; }
465
+ function* outer() { const r = yield* inner(); yield r; yield* [1, 2]; }
466
+ console.log(JSON.stringify([...outer()]));
467
+
468
+ function* echo() { const a = yield "first"; const b = yield a; return b; }
469
+ const e = echo();
470
+ console.log(JSON.stringify([e.next(), e.next("A"), e.next("B")]));
471
+
472
+ function* guarded() {
473
+ try { yield "x"; } catch (err) { yield "caught:" + (err as Error).message; }
474
+ yield "end";
475
+ }
476
+ const t = guarded();
477
+ console.log(JSON.stringify([t.next(), t.throw(new Error("E")), t.next(), t.next()]));
478
+
479
+ function* naturals() { let i = 0; while (true) yield i++; }
480
+ const seen: number[] = [];
481
+ for (const v of naturals()) { if (v >= 3) break; seen.push(v); }
482
+ console.log(JSON.stringify(seen));
483
+
484
+ class Box {
485
+ private n = 0;
486
+ *gen(): Generator<number> { yield this.n++; yield this.n++; }
487
+ }
488
+ console.log(JSON.stringify([...new Box().gen()]));
489
+
490
+ const obj = {
491
+ base: 5,
492
+ *gen() { yield this.base; yield this.base * 2; },
493
+ };
494
+ console.log(JSON.stringify([...obj.gen(), Math.max(...obj.gen())]));
495
+ `);
496
+ it("implements symbols and the well-known symbol protocol", () => {
497
+ harness.expectSameOutputAsNode(`const a = Symbol("desc");
498
+ const b = Symbol("desc");
499
+ const lines: unknown[] = [
500
+ typeof Symbol,
501
+ typeof a,
502
+ a === a,
503
+ a === b,
504
+ a.toString(),
505
+ String(a),
506
+ a.description,
507
+ Symbol().description,
508
+ String(Symbol()),
509
+ String(Symbol.iterator),
510
+ Symbol.iterator === Symbol.iterator,
511
+ Symbol.for("k") === Symbol.for("k"),
512
+ Symbol.keyFor(Symbol.for("k")),
513
+ Symbol.keyFor(Symbol("k")),
514
+ ];
515
+ for (const line of lines) console.log(JSON.stringify(line));
516
+
517
+ const holder: Record<string, unknown> = {};
518
+ (holder as any)[a] = 1;
519
+ (holder as any)[b] = 2;
520
+ holder.x = 3;
521
+ console.log(JSON.stringify(Object.keys(holder)));
522
+ console.log(JSON.stringify(Object.getOwnPropertySymbols(holder).length));
523
+ console.log(
524
+ JSON.stringify([(holder as any)[a], (holder as any)[b], (holder as any)[Symbol("desc")]]),
525
+ );
526
+ console.log(JSON.stringify(holder));
527
+ console.log(JSON.stringify(a));
528
+ console.log(JSON.stringify([a, 1]));
529
+
530
+ class Range {
531
+ start: number;
532
+ end: number;
533
+ constructor(start: number, end: number) {
534
+ this.start = start;
535
+ this.end = end;
536
+ }
537
+ [Symbol.iterator]() {
538
+ let i = this.start;
539
+ const end = this.end;
540
+ return {
541
+ next() {
542
+ return i < end ? { value: i++, done: false } : { value: undefined, done: true };
543
+ },
544
+ };
545
+ }
546
+ }
547
+ console.log(JSON.stringify([...new Range(1, 4)]));
548
+ const walked: number[] = [];
549
+ for (const x of new Range(10, 13)) walked.push(x);
550
+ console.log(JSON.stringify(walked));
551
+
552
+ function* delegates() { yield* new Range(5, 7); }
553
+ console.log(JSON.stringify([...delegates()]));
554
+ `, { name: "diff_symbol" });
555
+ });
556
+ it("binds namespace imports to every export", () => {
557
+ harness.expectSameOutputAsNode(`import * as util from "./util.ts";
558
+ import { add } from "./util.ts";
559
+ const values: unknown[] = [
560
+ util.greeting,
561
+ util.add(2, 3),
562
+ add(20, 22),
563
+ new util.default().value,
564
+ Object.keys(util).sort(),
565
+ typeof util.add,
566
+ ];
567
+ for (const v of values) console.log(JSON.stringify(v));
568
+ `, {
569
+ name: "diff_namespace_import",
570
+ files: {
571
+ "util.ts": [
572
+ 'export const greeting = "hello";',
573
+ "export function add(a: number, b: number): number { return a + b; }",
574
+ "export default class Thing { value = 42; }",
575
+ "export const ignored = 1;\n",
576
+ ].join("\n"),
577
+ },
578
+ });
579
+ });
580
+ });
581
+ //# sourceMappingURL=differential.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"differential.test.js","sourceRoot":"","sources":["../../../tests/e2e/differential.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,WAAW,CAAC,sBAAsB,EAAE,CAAC,OAAO,EAAE,EAAE;IAC9C,qEAAqE;IACrE,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,MAAc,EAAQ,EAAE;QAClD,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE;YACZ,OAAO,CAAC,sBAAsB,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;QACxF,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,MAAM,QAAQ,GAAG,CAAC,MAAgB,EAAU,EAAE,CAC5C,gCAAgC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ;QAC/E,2DAA2D,CAAC;IAE9D,IAAI,CACF,gCAAgC,EAChC,QAAQ,CAAC;QACP,GAAG;QACH,IAAI;QACJ,KAAK;QACL,KAAK;QACL,OAAO;QACP,MAAM;QACN,MAAM;QACN,MAAM;QACN,uBAAuB;QACvB,yBAAyB;QACzB,yBAAyB;QACzB,gBAAgB;QAChB,UAAU;QACV,WAAW;QACX,KAAK;QACL,wBAAwB;QACxB,uBAAuB;QACvB,mBAAmB;QACnB,kBAAkB;QAClB,mBAAmB;QACnB,0BAA0B;QAC1B,yBAAyB;QACzB,4BAA4B;QAC5B,yBAAyB;QACzB,0BAA0B;QAC1B,6BAA6B;QAC7B,oBAAoB;QACpB,mBAAmB;QACnB,YAAY;QACZ,cAAc;QACd,cAAc;KACf,CAAC,CACH,CAAC;IAEF,IAAI,CACF,+BAA+B,EAC/B,QAAQ,CAAC;QACP,kBAAkB;QAClB,YAAY;QACZ,gBAAgB;QAChB,iBAAiB;QACjB,gBAAgB;QAChB,eAAe;QACf,kBAAkB;QAClB,uBAAuB;QACvB,YAAY;QACZ,aAAa;QACb,qBAAqB;QACrB,uBAAuB;QACvB,mBAAmB;QACnB,2BAA2B;QAC3B,+BAA+B;KAChC,CAAC,CACH,CAAC;IAEF,IAAI,CACF,gCAAgC,EAChC,QAAQ,CAAC;QACP,UAAU;QACV,WAAW;QACX,YAAY;QACZ,aAAa;QACb,mBAAmB;QACnB,oBAAoB;QACpB,aAAa;QACb,SAAS;QACT,UAAU;QACV,iBAAiB;QACjB,yBAAyB;QACzB,YAAY;QACZ,aAAa;QACb,cAAc;KACf,CAAC,CACH,CAAC;IAEF,IAAI,CACF,qCAAqC,EACrC,QAAQ,CAAC;QACP,SAAS;QACT,WAAW;QACX,cAAc;QACd,SAAS;QACT,SAAS;QACT,SAAS;QACT,WAAW;QACX,UAAU;QACV,UAAU;QACV,eAAe;QACf,QAAQ;QACR,UAAU;QACV,MAAM;QACN,YAAY;QACZ,SAAS;QACT,SAAS;QACT,gBAAgB;QAChB,YAAY;QACZ,QAAQ;QACR,SAAS;QACT,OAAO;QACP,QAAQ;QACR,OAAO;QACP,OAAO;KACR,CAAC,CACH,CAAC;IAEF,IAAI,CACF,uCAAuC,EACvC,QAAQ,CAAC;QACP,UAAU;QACV,WAAW;QACX,aAAa;QACb,kBAAkB;QAClB,QAAQ;QACR,OAAO;QACP,OAAO;QACP,OAAO;QACP,IAAI;QACJ,QAAQ;QACR,SAAS;QACT,UAAU;QACV,UAAU;QACV,YAAY;QACZ,aAAa;QACb,kBAAkB;QAClB,aAAa;QACb,WAAW;QACX,WAAW;QACX,kBAAkB;KACnB,CAAC,CACH,CAAC;IAEF,IAAI,CACF,gBAAgB,EAChB,QAAQ,CAAC;QACP,uBAAuB;QACvB,8BAA8B;QAC9B,8BAA8B;QAC9B,6BAA6B;QAC7B,iCAAiC;QACjC,kCAAkC;QAClC,yBAAyB;QACzB,0BAA0B;QAC1B,gCAAgC;QAChC,6BAA6B;QAC7B,uBAAuB;QACvB,2BAA2B;QAC3B,8BAA8B;QAC9B,gBAAgB;QAChB,mBAAmB;QACnB,uBAAuB;QACvB,kBAAkB;QAClB,uBAAuB;QACvB,qBAAqB;QACrB,sBAAsB;QACtB,wBAAwB;QACxB,+BAA+B;QAC/B,cAAc;QACd,gCAAgC;KACjC,CAAC,CACH,CAAC;IAEF,IAAI,CACF,eAAe,EACf,QAAQ,CAAC;QACP,kBAAkB;QAClB,sCAAsC;QACtC,qBAAqB;QACrB,iCAAiC;QACjC,6BAA6B;QAC7B,yCAAyC;QACzC,sCAAsC;QACtC,qBAAqB;QACrB,qBAAqB;QACrB,4BAA4B;QAC5B,kCAAkC;QAClC,0BAA0B;QAC1B,0BAA0B;QAC1B,sBAAsB;QACtB,uBAAuB;QACvB,8BAA8B;QAC9B,8BAA8B;QAC9B,+BAA+B;QAC/B,mBAAmB;QACnB,qCAAqC;QACrC,mBAAmB;QACnB,sBAAsB;QACtB,+BAA+B;QAC/B,kBAAkB;QAClB,wBAAwB;KACzB,CAAC,CACH,CAAC;IAEF,IAAI,CACF,6BAA6B,EAC7B;EACF,QAAQ,CAAC;QACT,gBAAgB;QAChB,kBAAkB;QAClB,mBAAmB;QACnB,mBAAmB;QACnB,uDAAuD;QACvD,4CAA4C;QAC5C,gDAAgD;QAChD,sBAAsB;QACtB,mBAAmB;QACnB,sBAAsB;QACtB,sBAAsB;QACtB,qBAAqB;QACrB,0BAA0B;QAC1B,2BAA2B;QAC3B,gCAAgC;QAChC,gBAAgB;QAChB,UAAU;QACV,uBAAuB;KACxB,CAAC,EAAE,CACD,CAAC;IAEF,IAAI,CACF,oCAAoC,EACpC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yDA2CqD,CACtD,CAAC;IAEF,IAAI,CACF,6BAA6B,EAC7B,QAAQ,CAAC;QACP,mBAAmB;QACnB,YAAY;QACZ,cAAc;QACd,iBAAiB;QACjB,gBAAgB;QAChB,iBAAiB;QACjB,kBAAkB;QAClB,kBAAkB;QAClB,eAAe;QACf,eAAe;QACf,gBAAgB;QAChB,SAAS;QACT,2BAA2B;QAC3B,0BAA0B;QAC1B,uCAAuC;QACvC,+CAA+C;QAC/C,4BAA4B;QAC5B,+CAA+C;KAChD,CAAC,CACH,CAAC;IAEF,IAAI,CACF,4CAA4C,EAC5C,QAAQ,CAAC;QACP,gCAAgC;QAChC,yBAAyB;QACzB,uBAAuB;QACvB,0DAA0D;QAC1D,kCAAkC;QAClC,uBAAuB;QACvB,qBAAqB;QACrB,sCAAsC;QACtC,qBAAqB;QACrB,uBAAuB;KACxB,CAAC,CACH,CAAC;IAEF,IAAI,CACF,4CAA4C,EAC5C,QAAQ,CAAC;QACP,qCAAqC;QACrC,wBAAwB;QACxB,iCAAiC;QACjC,sBAAsB;QACtB,uBAAuB;QACvB,4CAA4C;QAC5C,oCAAoC;QACpC,yEAAyE;QACzE,6CAA6C;KAC9C,CAAC,CACH,CAAC;IAEF,IAAI,CACF,iCAAiC,EACjC;;;;;;;;;;;;EAYF,QAAQ,CAAC;QACT,kCAAkC;QAClC,uBAAuB;QACvB,2BAA2B;QAC3B,8BAA8B;QAC9B,mBAAmB;QACnB,cAAc;KACf,CAAC,EAAE,CACD,CAAC;IAEF,IAAI,CACF,yCAAyC,EACzC;;;;;;;;;;;;;EAaF,QAAQ,CAAC;QACT,sBAAsB;QACtB,yBAAyB;QACzB,4BAA4B;QAC5B,yBAAyB;QACzB,UAAU;QACV,WAAW;QACX,wBAAwB;QACxB,2BAA2B;QAC3B,uBAAuB;QACvB,mBAAmB;QACnB,qBAAqB;KACtB,CAAC,EAAE,CACD,CAAC;IAEF,IAAI,CACF,oCAAoC,EACpC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuCH,CACE,CAAC;IAEF,IAAI,CACF,oCAAoC,EACpC;;;;;;;;;;;EAWF,QAAQ,CAAC;QACT,OAAO;QACP,YAAY;QACZ,kBAAkB;QAClB,kBAAkB;QAClB,SAAS;QACT,SAAS;QACT,YAAY;KACb,CAAC,EAAE,CACD,CAAC;IAEF,IAAI,CACF,iCAAiC,EACjC;;;;;;;;;EASF,QAAQ,CAAC;QACT,cAAc;QACd,kBAAkB;QAClB,uBAAuB;QACvB,oBAAoB;QACpB,yCAAyC;QACzC,qCAAqC;QACrC,0CAA0C;QAC1C,WAAW;QACX,cAAc;QACd,QAAQ;QACR,WAAW;QACX,QAAQ;QACR,WAAW;QACX,oBAAoB;QACpB,oBAAoB;QACpB,wBAAwB;QACxB,QAAQ;QACR,WAAW;QACX,yBAAyB;QACzB,oBAAoB;QACpB,UAAU;QACV,aAAa;QACb,YAAY;QACZ,+BAA+B;QAC/B,sBAAsB;QACtB,aAAa;QACb,gBAAgB;KACjB,CAAC,EAAE,CACD,CAAC;IAEF,IAAI,CACF,4CAA4C,EAC5C;;;;;;;;;;;CAWH,CACE,CAAC;IAEF,IAAI,CACF,YAAY,EACZ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8CH,CACE,CAAC;IAEF,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAC/D,OAAO,CAAC,sBAAsB,CAC5B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyDL,EACK,EAAE,IAAI,EAAE,aAAa,EAAE,CACxB,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,OAAO,CAAC,sBAAsB,CAC5B;;;;;;;;;;;CAWL,EACK;YACE,IAAI,EAAE,uBAAuB;YAC7B,KAAK,EAAE;gBACL,SAAS,EAAE;oBACT,kCAAkC;oBAClC,qEAAqE;oBACrE,4CAA4C;oBAC5C,6BAA6B;iBAC9B,CAAC,IAAI,CAAC,IAAI,CAAC;aACb;SACF,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -12,12 +12,22 @@ export interface RunOptions {
12
12
  force?: boolean;
13
13
  /** Child-process timeout in milliseconds. */
14
14
  timeout?: number;
15
+ /** Extra modules written next to the entry file, keyed by relative path. */
16
+ files?: Record<string, string>;
15
17
  }
16
18
  export interface RunResult {
17
19
  stdout: string;
18
20
  stderr: string;
19
21
  status: number | null;
20
22
  }
23
+ export interface NodeRunOptions {
24
+ /** Entry file base name; defaults to a random name. */
25
+ name?: string;
26
+ /** Child-process timeout in milliseconds. */
27
+ timeout?: number;
28
+ /** Extra modules written next to the entry file, keyed by relative path. */
29
+ files?: Record<string, string>;
30
+ }
21
31
  export interface E2EHarness {
22
32
  /** Scratch directory shared by the tests in the calling suite. */
23
33
  readonly workdir: string;
@@ -25,6 +35,17 @@ export interface E2EHarness {
25
35
  runProgram(source: string, options?: RunOptions): string;
26
36
  /** Compile and run `source` without asserting the exit status. */
27
37
  runProgramFull(source: string, options?: RunOptions): RunResult;
38
+ /**
39
+ * Run `source` under Node (through the `tsx` loader) and return its trimmed
40
+ * stdout. Differential cases should print canonical `JSON.stringify` output
41
+ * so console formatting never masks a real difference.
42
+ */
43
+ runNodeProgram(source: string, options?: NodeRunOptions): string;
44
+ /**
45
+ * Compile `source` with xbintsc and run it under Node, asserting both produce
46
+ * byte-identical stdout. This is the core differential-testing primitive.
47
+ */
48
+ expectSameOutputAsNode(source: string, options?: RunOptions): void;
28
49
  }
29
50
  /**
30
51
  * Declare a suite of end-to-end compilation tests. The `define` callback runs
@@ -31,6 +31,9 @@ export function describeE2E(name, define, options = {}) {
31
31
  });
32
32
  const runProgramFull = (source, runOptions = {}) => {
33
33
  const entry = join(workdir, `${runOptions.name ?? `program_${Math.random().toString(36).slice(2)}`}.ts`);
34
+ for (const [relative, contents] of Object.entries(runOptions.files ?? {})) {
35
+ writeFileSync(join(workdir, relative), contents);
36
+ }
34
37
  writeFileSync(entry, source);
35
38
  const extensions = runOptions.extensions ? createDefaultRegistry().register(nodeExtension) : undefined;
36
39
  const result = build(entry, {
@@ -47,6 +50,19 @@ export function describeE2E(name, define, options = {}) {
47
50
  });
48
51
  return { stdout: executed.stdout ?? "", stderr: executed.stderr ?? "", status: executed.status };
49
52
  };
53
+ const runNodeProgram = (source, runOptions = {}) => {
54
+ const entry = join(workdir, `${runOptions.name ?? `node_${Math.random().toString(36).slice(2)}`}.ts`);
55
+ for (const [relative, contents] of Object.entries(runOptions.files ?? {})) {
56
+ writeFileSync(join(workdir, relative), contents);
57
+ }
58
+ writeFileSync(entry, source);
59
+ const executed = spawnSync(process.execPath, ["--import", "tsx", entry], {
60
+ encoding: "utf8",
61
+ ...(runOptions.timeout ? { timeout: runOptions.timeout } : {}),
62
+ });
63
+ expect(executed.status).toBe(0);
64
+ return (executed.stdout ?? "").trim();
65
+ };
50
66
  const harness = {
51
67
  get workdir() {
52
68
  return workdir;
@@ -57,6 +73,16 @@ export function describeE2E(name, define, options = {}) {
57
73
  return result.stdout.trim();
58
74
  },
59
75
  runProgramFull,
76
+ runNodeProgram,
77
+ expectSameOutputAsNode(source, runOptions = {}) {
78
+ const ours = harness.runProgram(source, runOptions);
79
+ const theirs = runNodeProgram(source, {
80
+ ...(runOptions.name ? { name: runOptions.name } : {}),
81
+ ...(runOptions.timeout ? { timeout: runOptions.timeout } : {}),
82
+ ...(runOptions.files ? { files: runOptions.files } : {}),
83
+ });
84
+ expect(ours).toBe(theirs);
85
+ },
60
86
  };
61
87
  define(harness);
62
88
  });