xbintsc 0.3.14 → 0.3.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -22
- package/README.zh-CN.md +20 -19
- package/bin/xbintsc.js +1 -2
- package/dist/src/cli/main.js +8 -5
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/codegen/generator/state.js +8 -0
- package/dist/src/codegen/generator/state.js.map +1 -1
- package/dist/src/codegen/generator/tables.d.ts +5 -6
- package/dist/src/codegen/generator/tables.js +13 -6
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/driver/bundler/graph.d.ts +8 -0
- package/dist/src/driver/bundler/graph.js +85 -0
- package/dist/src/driver/bundler/graph.js.map +1 -0
- package/dist/src/driver/bundler/merge.d.ts +11 -0
- package/dist/src/driver/bundler/merge.js +288 -0
- package/dist/src/driver/bundler/merge.js.map +1 -0
- package/dist/src/driver/bundler/resolve.d.ts +12 -0
- package/dist/src/driver/bundler/resolve.js +246 -0
- package/dist/src/driver/bundler/resolve.js.map +1 -0
- package/dist/src/driver/bundler/symbols.d.ts +15 -0
- package/dist/src/driver/bundler/symbols.js +51 -0
- package/dist/src/driver/bundler/symbols.js.map +1 -0
- package/dist/src/driver/bundler/types.d.ts +36 -0
- package/dist/src/driver/bundler/types.js +5 -0
- package/dist/src/driver/bundler/types.js.map +1 -0
- package/dist/src/driver/index.d.ts +1 -2
- package/dist/src/driver/index.js +1 -2
- package/dist/src/driver/index.js.map +1 -1
- package/dist/src/driver/modules.d.ts +9 -16
- package/dist/src/driver/modules.js +8 -644
- package/dist/src/driver/modules.js.map +1 -1
- package/dist/src/driver/paths.d.ts +0 -9
- package/dist/src/driver/paths.js +0 -31
- package/dist/src/driver/paths.js.map +1 -1
- package/dist/src/driver/toolchain-provider.d.ts +10 -10
- package/dist/src/driver/toolchain-provider.js +9 -38
- package/dist/src/driver/toolchain-provider.js.map +1 -1
- package/dist/src/driver/toolchain.js +8 -2
- package/dist/src/driver/toolchain.js.map +1 -1
- package/dist/src/extensions/catalog.js +2 -1
- package/dist/src/extensions/catalog.js.map +1 -1
- package/dist/src/extensions/gui/index.d.ts +24 -0
- package/dist/src/extensions/gui/index.js +101 -0
- package/dist/src/extensions/gui/index.js.map +1 -0
- package/dist/src/extensions/native.d.ts +2 -3
- package/dist/src/extensions/native.js +2 -3
- package/dist/src/extensions/native.js.map +1 -1
- package/dist/tests/driver/paths.test.js +1 -9
- package/dist/tests/driver/paths.test.js.map +1 -1
- package/dist/tests/driver/toolchain-provider.test.js +12 -79
- package/dist/tests/driver/toolchain-provider.test.js.map +1 -1
- package/dist/tests/e2e/compile-basics.test.d.ts +4 -0
- package/dist/tests/e2e/compile-basics.test.js +134 -0
- package/dist/tests/e2e/compile-basics.test.js.map +1 -0
- package/dist/tests/e2e/compile-builtins.test.d.ts +4 -0
- package/dist/tests/e2e/compile-builtins.test.js +191 -0
- package/dist/tests/e2e/compile-builtins.test.js.map +1 -0
- package/dist/tests/e2e/compile-control-flow.test.d.ts +4 -0
- package/dist/tests/e2e/compile-control-flow.test.js +190 -0
- package/dist/tests/e2e/compile-control-flow.test.js.map +1 -0
- package/dist/tests/e2e/compile-modules.test.d.ts +4 -0
- package/dist/tests/e2e/compile-modules.test.js +48 -0
- package/dist/tests/e2e/compile-modules.test.js.map +1 -0
- package/dist/tests/e2e/compile-objects-classes.test.d.ts +4 -0
- package/dist/tests/e2e/compile-objects-classes.test.js +362 -0
- package/dist/tests/e2e/compile-objects-classes.test.js.map +1 -0
- package/dist/tests/e2e/differential-helpers.d.ts +12 -0
- package/dist/tests/e2e/differential-helpers.js +16 -0
- package/dist/tests/e2e/differential-helpers.js.map +1 -0
- package/dist/tests/e2e/differential-language.test.d.ts +4 -0
- package/dist/tests/e2e/{differential.test.js → differential-language.test.js} +5 -287
- package/dist/tests/e2e/differential-language.test.js.map +1 -0
- package/dist/tests/e2e/differential-symbols.test.d.ts +4 -0
- package/dist/tests/e2e/differential-symbols.test.js +92 -0
- package/dist/tests/e2e/differential-symbols.test.js.map +1 -0
- package/dist/tests/e2e/differential-values.test.d.ts +4 -0
- package/dist/tests/e2e/differential-values.test.js +197 -0
- package/dist/tests/e2e/differential-values.test.js.map +1 -0
- package/dist/tests/e2e/gui.test.d.ts +12 -0
- package/dist/tests/e2e/gui.test.js +493 -0
- package/dist/tests/e2e/gui.test.js.map +1 -0
- package/dist/tests/e2e/native-extensions.test.d.ts +4 -5
- package/dist/tests/e2e/native-extensions.test.js +6 -7
- package/dist/tests/e2e/native-extensions.test.js.map +1 -1
- package/dist/tests/e2e/runtime-coverage-buffer.test.d.ts +4 -0
- package/dist/tests/e2e/runtime-coverage-buffer.test.js +97 -0
- package/dist/tests/e2e/runtime-coverage-buffer.test.js.map +1 -0
- package/dist/tests/e2e/runtime-coverage-globals.test.d.ts +4 -0
- package/dist/tests/e2e/runtime-coverage-globals.test.js +149 -0
- package/dist/tests/e2e/runtime-coverage-globals.test.js.map +1 -0
- package/dist/tests/e2e/runtime-coverage-node-modules.test.d.ts +4 -0
- package/dist/tests/e2e/runtime-coverage-node-modules.test.js +177 -0
- package/dist/tests/e2e/runtime-coverage-node-modules.test.js.map +1 -0
- package/dist/tests/e2e/runtime-coverage-streams.test.d.ts +4 -0
- package/dist/tests/e2e/runtime-coverage-streams.test.js +161 -0
- package/dist/tests/e2e/runtime-coverage-streams.test.js.map +1 -0
- package/dist/tests/e2e/runtime-coverage-system.test.d.ts +4 -0
- package/dist/tests/e2e/runtime-coverage-system.test.js +91 -0
- package/dist/tests/e2e/runtime-coverage-system.test.js.map +1 -0
- package/dist/tests/e2e/timers.test.d.ts +7 -0
- package/dist/tests/e2e/timers.test.js +63 -0
- package/dist/tests/e2e/timers.test.js.map +1 -0
- package/package.json +2 -2
- package/runtime/ext_gui/css.cpp +743 -0
- package/runtime/ext_gui/css.h +98 -0
- package/runtime/ext_gui/document.cpp +264 -0
- package/runtime/ext_gui/document.h +89 -0
- package/runtime/ext_gui/dom.cpp +472 -0
- package/runtime/ext_gui/dom.h +71 -0
- package/runtime/ext_gui/gui.cpp +457 -0
- package/runtime/ext_gui/gui_engine.h +81 -0
- package/runtime/ext_gui/image.cpp +125 -0
- package/runtime/ext_gui/image.h +42 -0
- package/runtime/ext_gui/layout.cpp +748 -0
- package/runtime/ext_gui/layout.h +93 -0
- package/runtime/ext_gui/paint.cpp +137 -0
- package/runtime/ext_gui/paint.h +75 -0
- package/runtime/ext_gui/renderer.cpp +959 -0
- package/runtime/ext_gui/renderer.h +68 -0
- package/runtime/ext_gui/stb_image.h +7988 -0
- package/runtime/ext_gui/style.cpp +771 -0
- package/runtime/ext_gui/style.h +117 -0
- package/runtime/ext_gui/text.cpp +378 -0
- package/runtime/ext_gui/text.h +78 -0
- package/runtime/ext_gui/window.cpp +405 -0
- package/runtime/ext_node/fs/fs_common.h +13 -527
- package/runtime/ext_node/fs/meta_ops.c +3 -3
- package/runtime/ext_node/fs/parts/buffer-bridge.inc +6 -0
- package/runtime/ext_node/fs/parts/builtins.inc +69 -0
- package/runtime/ext_node/fs/parts/dir.inc +47 -0
- package/runtime/ext_node/fs/parts/encoding.inc +186 -0
- package/runtime/ext_node/fs/parts/errors.inc +66 -0
- package/runtime/ext_node/fs/parts/flags.inc +39 -0
- package/runtime/ext_node/fs/parts/platform.inc +86 -0
- package/runtime/ext_node/fs/parts/promisify.inc +15 -0
- package/runtime/ext_node/fs/parts/time.inc +9 -0
- package/runtime/rt.h +36 -4
- package/runtime/rt_internal.h +10 -23
- package/runtime/xt_builtins.c +4 -0
- package/runtime/xt_containers/access.inc +168 -0
- package/runtime/xt_containers/arrays.inc +108 -0
- package/runtime/xt_containers/boxes.inc +11 -0
- package/runtime/xt_containers/objects.inc +326 -0
- package/runtime/xt_containers.c +8 -618
- package/runtime/xt_ext.h +0 -1
- package/runtime/xt_io/base64.inc +87 -0
- package/runtime/xt_io/console.inc +119 -0
- package/runtime/xt_io/exception.inc +142 -0
- package/runtime/xt_io/inspect.inc +212 -0
- package/runtime/xt_io.c +11 -556
- package/runtime/xt_loop.c +262 -24
- package/runtime/xt_regex/api.inc +58 -0
- package/runtime/xt_regex/helpers.inc +68 -0
- package/runtime/xt_regex/matcher.inc +248 -0
- package/runtime/xt_regex/parser.inc +236 -0
- package/runtime/xt_regex/types.inc +58 -0
- package/runtime/xt_regex.h +9 -672
- package/runtime/xt_stdlib2/helpers.inc +2 -4
- package/runtime/xt_values/arithmetic.inc +150 -0
- package/runtime/xt_values/comparison.inc +112 -0
- package/runtime/xt_values/constructors.inc +41 -0
- package/runtime/xt_values/conversions.inc +115 -0
- package/runtime/xt_values/number-format.inc +180 -0
- package/runtime/xt_values/strings.inc +22 -0
- package/runtime/xt_values.c +10 -626
- package/scripts/build-gui.ts +317 -0
- package/scripts/build-runtime.ts +7 -8
- package/scripts/coverage-runtime.ts +4 -4
- package/scripts/package-release.ts +12 -84
- package/src/cli/main.ts +8 -4
- package/src/codegen/generator/state.ts +8 -0
- package/src/codegen/generator/tables.ts +13 -6
- package/src/driver/bundler/graph.ts +95 -0
- package/src/driver/bundler/merge.ts +312 -0
- package/src/driver/bundler/resolve.ts +240 -0
- package/src/driver/bundler/symbols.ts +55 -0
- package/src/driver/bundler/types.ts +37 -0
- package/src/driver/index.ts +1 -9
- package/src/driver/modules.ts +9 -693
- package/src/driver/paths.ts +0 -31
- package/src/driver/toolchain-provider.ts +12 -45
- package/src/driver/toolchain.ts +7 -2
- package/src/extensions/catalog.ts +2 -1
- package/src/extensions/gui/index.ts +107 -0
- package/src/extensions/native.ts +2 -3
- package/dist/src/driver/toolchain-download.d.ts +0 -50
- package/dist/src/driver/toolchain-download.js +0 -102
- package/dist/src/driver/toolchain-download.js.map +0 -1
- package/dist/tests/driver/toolchain-download.test.d.ts +0 -1
- package/dist/tests/driver/toolchain-download.test.js +0 -53
- package/dist/tests/driver/toolchain-download.test.js.map +0 -1
- package/dist/tests/e2e/compile.test.d.ts +0 -8
- package/dist/tests/e2e/compile.test.js +0 -893
- package/dist/tests/e2e/compile.test.js.map +0 -1
- package/dist/tests/e2e/differential.test.d.ts +0 -7
- package/dist/tests/e2e/differential.test.js.map +0 -1
- package/dist/tests/e2e/runtime-coverage.test.d.ts +0 -11
- package/dist/tests/e2e/runtime-coverage.test.js +0 -646
- package/dist/tests/e2e/runtime-coverage.test.js.map +0 -1
- package/scripts/fetch-toolchain.ts +0 -101
- package/src/driver/toolchain-download.ts +0 -135
|
@@ -1,893 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* End-to-end tests: compile TypeScript all the way to a native binary and run
|
|
3
|
-
* it. Each program is compiled and executed through the shared
|
|
4
|
-
* {@link describeE2E} harness, so the suite is skipped when no
|
|
5
|
-
* clang-compatible compiler is available. Node extension coverage lives in
|
|
6
|
-
* `compile-node.test.ts`.
|
|
7
|
-
*/
|
|
8
|
-
import { writeFileSync } from "node:fs";
|
|
9
|
-
import { join } from "node:path";
|
|
10
|
-
import { expect, it } from "vitest";
|
|
11
|
-
import { describeE2E } from "./harness.js";
|
|
12
|
-
describeE2E("end-to-end compilation", (harness) => {
|
|
13
|
-
const { runProgram, runProgramFull } = harness;
|
|
14
|
-
it("evaluates arithmetic and prints results", () => {
|
|
15
|
-
expect(runProgram('console.log("sum", 2 + 3 * 4);')).toBe("sum 14");
|
|
16
|
-
});
|
|
17
|
-
it("supports arbitrary-precision BigInt values", () => {
|
|
18
|
-
const source = `
|
|
19
|
-
const a = 123456789012345678901234567890n;
|
|
20
|
-
console.log(a * a);
|
|
21
|
-
console.log((2n ** 100n).toString(16));
|
|
22
|
-
console.log(-7n / 3n, -7n % 3n);
|
|
23
|
-
console.log(~5n, 1n << 130n);
|
|
24
|
-
console.log(typeof 1n, BigInt.asIntN(8, 0xFFn), BigInt.asUintN(8, 0xFFn));
|
|
25
|
-
console.log("x" + 5n, 2n < 3n, 2n === 2);
|
|
26
|
-
`;
|
|
27
|
-
expect(runProgram(source)).toBe([
|
|
28
|
-
"15241578753238836750495351562536198787501905199875019052100n",
|
|
29
|
-
"10000000000000000000000000",
|
|
30
|
-
"-2n -1n",
|
|
31
|
-
"-6n 1361129467683753853853498429727072845824n",
|
|
32
|
-
"bigint -1n 255n",
|
|
33
|
-
"x5 true false",
|
|
34
|
-
].join("\n"));
|
|
35
|
-
});
|
|
36
|
-
it("interpolates template literals", () => {
|
|
37
|
-
const source = `
|
|
38
|
-
const name = "world";
|
|
39
|
-
console.log(\`Hello, \${name}!\`);
|
|
40
|
-
console.log(\`\${1 + 1}-\${2 + 2}\`, \`plain\`, "[" + \`\` + "]");
|
|
41
|
-
`;
|
|
42
|
-
expect(runProgram(source)).toBe("Hello, world!\n2-4 plain []");
|
|
43
|
-
});
|
|
44
|
-
it("runs recursive functions", () => {
|
|
45
|
-
const source = `
|
|
46
|
-
function fib(n: number): number {
|
|
47
|
-
if (n < 2) return n;
|
|
48
|
-
return fib(n - 1) + fib(n - 2);
|
|
49
|
-
}
|
|
50
|
-
console.log(fib(10));
|
|
51
|
-
`;
|
|
52
|
-
expect(runProgram(source)).toBe("55");
|
|
53
|
-
});
|
|
54
|
-
it("supports loops, arrays and string concatenation", () => {
|
|
55
|
-
const source = `
|
|
56
|
-
const numbers = [1, 2, 3, 4];
|
|
57
|
-
let total = 0;
|
|
58
|
-
for (const n of numbers) {
|
|
59
|
-
total += n;
|
|
60
|
-
}
|
|
61
|
-
console.log("total = " + total);
|
|
62
|
-
`;
|
|
63
|
-
expect(runProgram(source)).toBe("total = 10");
|
|
64
|
-
});
|
|
65
|
-
it("implements closures with by-reference captures", () => {
|
|
66
|
-
const source = `
|
|
67
|
-
const makeCounter = () => {
|
|
68
|
-
let count = 0;
|
|
69
|
-
return () => {
|
|
70
|
-
count += 1;
|
|
71
|
-
return count;
|
|
72
|
-
};
|
|
73
|
-
};
|
|
74
|
-
const next = makeCounter();
|
|
75
|
-
console.log(next(), next(), next());
|
|
76
|
-
`;
|
|
77
|
-
expect(runProgram(source)).toBe("1 2 3");
|
|
78
|
-
});
|
|
79
|
-
it("prints objects and arrays with JS-like formatting", () => {
|
|
80
|
-
const source = `
|
|
81
|
-
const user = { name: "ada", tags: ["math", "code"], age: 36 };
|
|
82
|
-
console.log(user);
|
|
83
|
-
`;
|
|
84
|
-
expect(runProgram(source)).toBe("{ name: 'ada', tags: [ 'math', 'code' ], age: 36 }");
|
|
85
|
-
});
|
|
86
|
-
it("orders object keys like JavaScript", () => {
|
|
87
|
-
const source = `
|
|
88
|
-
console.log(JSON.stringify({ b: 1, a: 2, 10: 3, 2: 4 }));
|
|
89
|
-
const o: any = {};
|
|
90
|
-
o.z = 1; o[5] = 2; o.a = 3; o[1] = 4; o["01"] = 5; o[100] = 6;
|
|
91
|
-
console.log(Object.keys(o).join(" "));
|
|
92
|
-
let seen = "";
|
|
93
|
-
for (const k in o) seen += k + ",";
|
|
94
|
-
console.log(seen);
|
|
95
|
-
console.log(JSON.stringify(Object.fromEntries([["b", 1], ["2", 2], ["a", 3]])));
|
|
96
|
-
`;
|
|
97
|
-
expect(runProgram(source)).toBe([
|
|
98
|
-
'{"2":4,"10":3,"b":1,"a":2}',
|
|
99
|
-
"1 5 100 z a 01",
|
|
100
|
-
"1,5,100,z,a,01,",
|
|
101
|
-
'{"2":2,"b":1,"a":3}',
|
|
102
|
-
].join("\n"));
|
|
103
|
-
});
|
|
104
|
-
it("supports switch statements with fall-through", () => {
|
|
105
|
-
const source = `
|
|
106
|
-
function size(n: number): string {
|
|
107
|
-
switch (n) {
|
|
108
|
-
case 0:
|
|
109
|
-
return "none";
|
|
110
|
-
case 1:
|
|
111
|
-
case 2:
|
|
112
|
-
return "few";
|
|
113
|
-
default:
|
|
114
|
-
return "many";
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
console.log(size(0), size(1), size(2), size(7));
|
|
118
|
-
`;
|
|
119
|
-
expect(runProgram(source)).toBe("none few few many");
|
|
120
|
-
});
|
|
121
|
-
it("provides common array and string methods", () => {
|
|
122
|
-
const source = `
|
|
123
|
-
const xs = [3, 1, 2];
|
|
124
|
-
xs.push(4);
|
|
125
|
-
const doubled = xs.map((x) => x * 2).filter((x) => x > 4);
|
|
126
|
-
const total = xs.reduce((a, x) => a + x, 0);
|
|
127
|
-
console.log(xs.join("-"), doubled.join(","), total, xs.includes(2), xs.indexOf(1));
|
|
128
|
-
const s = " Hello World ";
|
|
129
|
-
console.log(s.trim().toUpperCase(), s.trim().slice(0, 5), "a,b,c".split(",").join("|"));
|
|
130
|
-
`;
|
|
131
|
-
expect(runProgram(source)).toBe("3-1-2-4 6,8 10 true 1\nHELLO WORLD Hello a|b|c");
|
|
132
|
-
});
|
|
133
|
-
it("supports Math, global functions and console levels", () => {
|
|
134
|
-
const source = `
|
|
135
|
-
console.log(Math.max(1, 9, 4), Math.abs(-3), Math.floor(2.9), Math.pow(2, 8), Math.PI > 3.14);
|
|
136
|
-
console.log(parseInt("42px"), parseFloat("2.5x"), isNaN(NaN), isFinite(1), Number("7"), String(9), Boolean(0));
|
|
137
|
-
console.warn("warned");
|
|
138
|
-
console.error("failed");
|
|
139
|
-
`;
|
|
140
|
-
const result = runProgramFull(source);
|
|
141
|
-
expect(result.status).toBe(0);
|
|
142
|
-
expect(result.stdout.trim()).toBe("9 3 2 256 true\n42 2.5 true true 7 9 false");
|
|
143
|
-
expect(result.stderr.trim()).toBe("warned\nfailed");
|
|
144
|
-
});
|
|
145
|
-
it("parses large integer literals without 32-bit clamping", () => {
|
|
146
|
-
// Regression: `parseInt` used to go through `strtol`, whose `long` is only
|
|
147
|
-
// 32 bits on Windows, so hex literals above INT32_MAX (used by the
|
|
148
|
-
// compiler's own `hashText`) were clamped to 2147483647.
|
|
149
|
-
const source = `
|
|
150
|
-
console.log(parseInt("cbf29ce4", 16), parseInt("80000000", 16), parseInt("100000000", 16));
|
|
151
|
-
console.log(parseInt("0xcbf29ce4"), parseInt("0x80000000"), parseInt("4503599627370496"));
|
|
152
|
-
console.log(parseInt("-42"), parseInt("0x10", 10), parseInt("42px"));
|
|
153
|
-
console.log(isNaN(parseInt("xyz")));
|
|
154
|
-
`;
|
|
155
|
-
expect(runProgram(source)).toBe("3421674724 2147483648 4294967296\n3421674724 2147483648 4503599627370496\n-42 0 42\ntrue");
|
|
156
|
-
});
|
|
157
|
-
it("supports default parameters, rest parameters and arguments", () => {
|
|
158
|
-
const source = `
|
|
159
|
-
function greet(name: string = "world", mark: string = "!"): string {
|
|
160
|
-
return "hi " + name + mark;
|
|
161
|
-
}
|
|
162
|
-
function total(...nums: number[]): number {
|
|
163
|
-
let sum = 0;
|
|
164
|
-
for (const n of nums) sum += n;
|
|
165
|
-
return sum;
|
|
166
|
-
}
|
|
167
|
-
function countArgs(): number {
|
|
168
|
-
return arguments.length;
|
|
169
|
-
}
|
|
170
|
-
console.log(greet(), greet("ada"), total(1, 2, 3), countArgs(1, 2, 3, 4));
|
|
171
|
-
`;
|
|
172
|
-
expect(runProgram(source)).toBe("hi world! hi ada! 6 4");
|
|
173
|
-
});
|
|
174
|
-
it("supports Object helpers, spread, in and delete", () => {
|
|
175
|
-
const source = `
|
|
176
|
-
const base = { a: 1, b: 2 };
|
|
177
|
-
const merged = Object.assign({}, base, { c: 3 });
|
|
178
|
-
const copy = { ...base, a: 9 };
|
|
179
|
-
console.log(Object.keys(base).join(","), Object.values(base).join(","));
|
|
180
|
-
console.log(Object.entries(base).map((e) => e[0] + "=" + e[1]).join(","));
|
|
181
|
-
console.log(merged.a, merged.b, merged.c, copy.a, "a" in base, "z" in base);
|
|
182
|
-
delete base.a;
|
|
183
|
-
console.log("a" in base, Object.keys(base).join(","));
|
|
184
|
-
`;
|
|
185
|
-
expect(runProgram(source)).toBe("a,b 1,2\na=1,b=2\n1 2 3 9 true false\nfalse b");
|
|
186
|
-
});
|
|
187
|
-
it("iterates object keys with for...in", () => {
|
|
188
|
-
const source = `
|
|
189
|
-
const obj = { x: 1, y: 2 };
|
|
190
|
-
let out = "";
|
|
191
|
-
for (const key in obj) out += key + "=" + obj[key] + ";";
|
|
192
|
-
console.log(out);
|
|
193
|
-
`;
|
|
194
|
-
expect(runProgram(source)).toBe("x=1;y=2;");
|
|
195
|
-
});
|
|
196
|
-
it("supports try/catch/finally including returns and loop breaks", () => {
|
|
197
|
-
const source = `
|
|
198
|
-
function safe(n: number): number {
|
|
199
|
-
try {
|
|
200
|
-
if (n < 0) throw "negative";
|
|
201
|
-
return n * 2;
|
|
202
|
-
} catch (err) {
|
|
203
|
-
return -1;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
function cleanup(): string {
|
|
207
|
-
let log = "";
|
|
208
|
-
try {
|
|
209
|
-
log += "try;";
|
|
210
|
-
throw "boom";
|
|
211
|
-
} catch (e) {
|
|
212
|
-
log += "catch:" + e + ";";
|
|
213
|
-
} finally {
|
|
214
|
-
log += "finally";
|
|
215
|
-
}
|
|
216
|
-
return log;
|
|
217
|
-
}
|
|
218
|
-
console.log(safe(5), safe(-2), cleanup());
|
|
219
|
-
`;
|
|
220
|
-
expect(runProgram(source)).toBe("10 -1 try;catch:boom;finally");
|
|
221
|
-
});
|
|
222
|
-
it("runs finally blocks on early return, break and continue", () => {
|
|
223
|
-
const source = `
|
|
224
|
-
const events: string[] = [];
|
|
225
|
-
function f(n: number): string {
|
|
226
|
-
try {
|
|
227
|
-
events.push("try" + n);
|
|
228
|
-
if (n === 1) return "ret1";
|
|
229
|
-
if (n === 2) throw "boom" + n;
|
|
230
|
-
} catch (e) {
|
|
231
|
-
events.push("catch" + e);
|
|
232
|
-
if (n === 3) return "ret3";
|
|
233
|
-
} finally {
|
|
234
|
-
events.push("finally" + n);
|
|
235
|
-
}
|
|
236
|
-
return "done" + n;
|
|
237
|
-
}
|
|
238
|
-
console.log(f(0), f(1), f(2), f(3));
|
|
239
|
-
console.log(events.join("|"));
|
|
240
|
-
const loop: string[] = [];
|
|
241
|
-
for (let i = 0; i < 5; i++) {
|
|
242
|
-
try {
|
|
243
|
-
if (i === 2) break;
|
|
244
|
-
if (i === 4) continue;
|
|
245
|
-
loop.push("i" + i);
|
|
246
|
-
} finally {
|
|
247
|
-
loop.push("F" + i);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
console.log(loop.join("|"));
|
|
251
|
-
function nested(): string {
|
|
252
|
-
try {
|
|
253
|
-
try {
|
|
254
|
-
return "inner";
|
|
255
|
-
} finally {
|
|
256
|
-
events.push("innerFinally");
|
|
257
|
-
}
|
|
258
|
-
} finally {
|
|
259
|
-
events.push("outerFinally");
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
console.log(nested(), events[events.length - 2], events[events.length - 1]);
|
|
263
|
-
`;
|
|
264
|
-
expect(runProgram(source)).toBe([
|
|
265
|
-
"done0 ret1 done2 done3",
|
|
266
|
-
"try0|finally0|try1|finally1|try2|catchboom2|finally2|try3|finally3",
|
|
267
|
-
"i0|F0|i1|F1|F2",
|
|
268
|
-
"inner innerFinally outerFinally",
|
|
269
|
-
].join("\n"));
|
|
270
|
-
});
|
|
271
|
-
it("implements abstract equality with ToPrimitive", () => {
|
|
272
|
-
const source = `
|
|
273
|
-
console.log(([] as any) == 0, ([] as any) == "", ([1] as any) == 1, ([1, 2] as any) == "1,2");
|
|
274
|
-
console.log(({} as any) == "[object Object]");
|
|
275
|
-
console.log(([1] as any) == [1], ([] as any) == false, ([] as any) == null);
|
|
276
|
-
console.log(0 == "0", 0 == false, "" == false, null == undefined, null == 0, undefined == 0);
|
|
277
|
-
console.log(NaN == NaN, (1 as any) == "1");
|
|
278
|
-
`;
|
|
279
|
-
expect(runProgram(source)).toBe([
|
|
280
|
-
"true true true true",
|
|
281
|
-
"true",
|
|
282
|
-
"false true false",
|
|
283
|
-
"true true true true false false",
|
|
284
|
-
"false true",
|
|
285
|
-
].join("\n"));
|
|
286
|
-
});
|
|
287
|
-
it("supports labeled break and continue across nested loops", () => {
|
|
288
|
-
const source = `
|
|
289
|
-
const out: string[] = [];
|
|
290
|
-
outer: for (let i = 0; i < 3; i++) {
|
|
291
|
-
inner: for (let j = 0; j < 3; j++) {
|
|
292
|
-
if (j === 1) continue outer;
|
|
293
|
-
if (i === 2) break outer;
|
|
294
|
-
out.push(i + "," + j);
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
block: {
|
|
298
|
-
out.push("before");
|
|
299
|
-
if (out.length > 0) break block;
|
|
300
|
-
out.push("unreachable");
|
|
301
|
-
}
|
|
302
|
-
out.push("after");
|
|
303
|
-
function f(): number {
|
|
304
|
-
let total = 0;
|
|
305
|
-
loop: for (let i = 0; i < 5; i++) {
|
|
306
|
-
try {
|
|
307
|
-
if (i === 2) continue loop;
|
|
308
|
-
if (i === 4) break loop;
|
|
309
|
-
total += i;
|
|
310
|
-
} finally {
|
|
311
|
-
total += 100;
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
return total;
|
|
315
|
-
}
|
|
316
|
-
console.log(out.join("|"), f());
|
|
317
|
-
`;
|
|
318
|
-
expect(runProgram(source)).toBe("0,0|1,0|before|after 504");
|
|
319
|
-
});
|
|
320
|
-
it("supports #private fields, methods and static members", () => {
|
|
321
|
-
const source = `
|
|
322
|
-
class Counter {
|
|
323
|
-
#count = 0;
|
|
324
|
-
static #instances = 0;
|
|
325
|
-
constructor() { Counter.#bumpStatic(); }
|
|
326
|
-
#bump(by: number): void { this.#count += by; }
|
|
327
|
-
static #bumpStatic(): void { Counter.#instances++; }
|
|
328
|
-
inc(by: number): number { this.#bump(by); return this.#count; }
|
|
329
|
-
get count(): number { return this.#count; }
|
|
330
|
-
set count(v: number) { this.#count = v; }
|
|
331
|
-
static instances(): number { return Counter.#instances; }
|
|
332
|
-
}
|
|
333
|
-
class Sub extends Counter {
|
|
334
|
-
#label = "sub";
|
|
335
|
-
constructor() { super(); this.inc(5); }
|
|
336
|
-
describe(): string { return this.#label + ":" + this.count; }
|
|
337
|
-
}
|
|
338
|
-
const a = new Counter();
|
|
339
|
-
const b = new Counter();
|
|
340
|
-
a.inc(3);
|
|
341
|
-
a.count = 10;
|
|
342
|
-
console.log(a.count, b.count, Counter.instances());
|
|
343
|
-
console.log(new Sub().describe());
|
|
344
|
-
`;
|
|
345
|
-
expect(runProgram(source)).toBe("10 0 2\nsub:5");
|
|
346
|
-
});
|
|
347
|
-
it("encodes and decodes URIs like the specification", () => {
|
|
348
|
-
const source = `
|
|
349
|
-
console.log(encodeURI("a/b?c=d&e#f"), encodeURIComponent("a/b?c=d&e#f"));
|
|
350
|
-
console.log(encodeURI("café"), encodeURIComponent("😀 中文"));
|
|
351
|
-
console.log(decodeURI("%2f%2F"), decodeURIComponent("a%23b"));
|
|
352
|
-
console.log(decodeURIComponent("%C3%A9"), decodeURIComponent("%F0%9F%98%80"));
|
|
353
|
-
console.log(typeof encodeURIComponent, typeof decodeURI);
|
|
354
|
-
try { decodeURIComponent("%E0%80%80"); } catch (e) { console.log(String(e)); }
|
|
355
|
-
`;
|
|
356
|
-
expect(runProgram(source)).toBe([
|
|
357
|
-
"a/b?c=d&e#f a%2Fb%3Fc%3Dd%26e%23f",
|
|
358
|
-
"caf%C3%A9 %F0%9F%98%80%20%E4%B8%AD%E6%96%87",
|
|
359
|
-
"%2f%2F a#b",
|
|
360
|
-
"é 😀",
|
|
361
|
-
"function function",
|
|
362
|
-
"URIError: URI malformed",
|
|
363
|
-
].join("\n"));
|
|
364
|
-
});
|
|
365
|
-
it("exposes RegExp capture groups in exec, match, replace and split", () => {
|
|
366
|
-
const source = `
|
|
367
|
-
const m = "12-34".match(/(\\d+)-(\\d+)/)!;
|
|
368
|
-
console.log(JSON.stringify([m[0], m[1], m[2], m.index]), Array.isArray(m), m.length);
|
|
369
|
-
console.log(JSON.stringify("a1b2c3".split(/(\\d)/)));
|
|
370
|
-
console.log(JSON.stringify("a1b2c3".split(/\\d/)));
|
|
371
|
-
console.log("2020-01-02".replace(/(\\d+)-(\\d+)-(\\d+)/, "$3/$2/$1"));
|
|
372
|
-
console.log("a1b2".replace(/(\\d)/g, "[$1]"));
|
|
373
|
-
console.log("a1b2".replace(/\\d/g, (x: string) => "<" + x + ">"));
|
|
374
|
-
console.log("a.b.c".search(/\\./));
|
|
375
|
-
console.log(JSON.stringify("abc".split(/(?:)/)));
|
|
376
|
-
`;
|
|
377
|
-
expect(runProgram(source)).toBe([
|
|
378
|
-
'["12-34","12","34",0] true 3',
|
|
379
|
-
'["a","1","b","2","c","3",""]',
|
|
380
|
-
'["a","b","c",""]',
|
|
381
|
-
"02/01/2020",
|
|
382
|
-
"a[1]b[2]",
|
|
383
|
-
"a<1>b<2>",
|
|
384
|
-
"1",
|
|
385
|
-
'["a","b","c"]',
|
|
386
|
-
].join("\n"));
|
|
387
|
-
});
|
|
388
|
-
it("supports the immutable array methods and Object statics", () => {
|
|
389
|
-
const source = `
|
|
390
|
-
const a = [3, 1, 2];
|
|
391
|
-
console.log(JSON.stringify(a.toSorted((x, y) => x - y)), JSON.stringify(a));
|
|
392
|
-
console.log(JSON.stringify(a.toReversed()), JSON.stringify(a));
|
|
393
|
-
console.log(JSON.stringify(a.toSpliced(1, 1, 9, 9)), JSON.stringify(a));
|
|
394
|
-
console.log(JSON.stringify(a.with(1, 9)), JSON.stringify(a.with(-1, 9)));
|
|
395
|
-
console.log(JSON.stringify(Object.getOwnPropertyNames({ b: 1, a: 2 })));
|
|
396
|
-
console.log(JSON.stringify(Object.getOwnPropertyNames([1, 2])));
|
|
397
|
-
console.log(JSON.stringify(Object.groupBy([1, 2, 3, 4], (n: number) => (n % 2 === 0 ? "even" : "odd"))));
|
|
398
|
-
`;
|
|
399
|
-
expect(runProgram(source)).toBe([
|
|
400
|
-
"[1,2,3] [3,1,2]",
|
|
401
|
-
"[2,1,3] [3,1,2]",
|
|
402
|
-
"[3,9,9,2] [3,1,2]",
|
|
403
|
-
"[3,9,2] [3,1,9]",
|
|
404
|
-
'["b","a"]',
|
|
405
|
-
'["0","1","length"]',
|
|
406
|
-
'{"odd":[1,3],"even":[2,4]}',
|
|
407
|
-
].join("\n"));
|
|
408
|
-
});
|
|
409
|
-
it("supports optional chaining", () => {
|
|
410
|
-
const source = `
|
|
411
|
-
const obj = { a: { b: 5 }, m: (x: number) => x + 1 };
|
|
412
|
-
const empty: any = null;
|
|
413
|
-
console.log(obj?.a?.b, empty?.a?.b, obj?.m?.(10), empty?.m?.(10));
|
|
414
|
-
const arr = [1, 2, 3];
|
|
415
|
-
console.log(arr?.length, arr?.map((x) => x * 2).join(","), empty?.length);
|
|
416
|
-
`;
|
|
417
|
-
expect(runProgram(source)).toBe("5 undefined 11 undefined\n3 2,4,6 undefined");
|
|
418
|
-
});
|
|
419
|
-
it("short-circuits a whole optional chain but throws past the guard", () => {
|
|
420
|
-
const source = `
|
|
421
|
-
const empty: any = null;
|
|
422
|
-
const nested: any = { a: { b: null } };
|
|
423
|
-
// A \`?.\` short-circuits every later member, so nothing past it is evaluated.
|
|
424
|
-
console.log("A", empty?.a.b.c);
|
|
425
|
-
console.log("B", (nested?.a)?.b);
|
|
426
|
-
console.log("C", nested.a?.b?.c);
|
|
427
|
-
// Once the chain has started, a non-optional member on null throws.
|
|
428
|
-
try {
|
|
429
|
-
console.log(nested?.b.c);
|
|
430
|
-
console.log("D-ok");
|
|
431
|
-
} catch (e) {
|
|
432
|
-
console.log("D-threw");
|
|
433
|
-
}
|
|
434
|
-
try {
|
|
435
|
-
console.log(nested.a?.b.c);
|
|
436
|
-
console.log("E-ok");
|
|
437
|
-
} catch (e) {
|
|
438
|
-
console.log("E-threw");
|
|
439
|
-
}
|
|
440
|
-
`;
|
|
441
|
-
expect(runProgram(source)).toBe("A undefined\nB null\nC undefined\nD-threw\nE-threw");
|
|
442
|
-
});
|
|
443
|
-
it("defaults Array.sort to string order and reveals circular references", () => {
|
|
444
|
-
const source = `
|
|
445
|
-
console.log([10, 1, 2, 20].sort().join(","));
|
|
446
|
-
console.log(["banana", "apple", "cherry"].sort().join(","));
|
|
447
|
-
console.log(JSON.stringify([3, undefined, 1, 2, undefined].sort()));
|
|
448
|
-
console.log([5, 3, 8, 1].sort((a, b) => a - b).join(","));
|
|
449
|
-
const o: any = { name: "x" };
|
|
450
|
-
o.self = o;
|
|
451
|
-
o.child = { parent: o };
|
|
452
|
-
console.log(o);
|
|
453
|
-
const arr: any = [1, 2];
|
|
454
|
-
arr.push(arr);
|
|
455
|
-
console.log(arr);
|
|
456
|
-
`;
|
|
457
|
-
expect(runProgram(source)).toBe([
|
|
458
|
-
"1,10,2,20",
|
|
459
|
-
"apple,banana,cherry",
|
|
460
|
-
"[1,2,3,null,null]",
|
|
461
|
-
"1,3,5,8",
|
|
462
|
-
"{ name: 'x', self: [Circular *1], child: { parent: [Circular *1] } }",
|
|
463
|
-
"[ 1, 2, [Circular *1] ]",
|
|
464
|
-
].join("\n"));
|
|
465
|
-
});
|
|
466
|
-
it("supports classes with new, this, statics, inheritance and instanceof", () => {
|
|
467
|
-
const source = `
|
|
468
|
-
class Animal {
|
|
469
|
-
name: string;
|
|
470
|
-
constructor(name: string) {
|
|
471
|
-
this.name = name;
|
|
472
|
-
}
|
|
473
|
-
speak(): string {
|
|
474
|
-
return this.name + " makes a sound";
|
|
475
|
-
}
|
|
476
|
-
static kind(): string {
|
|
477
|
-
return "animal";
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
class Dog extends Animal {
|
|
481
|
-
constructor(name: string) {
|
|
482
|
-
super(name);
|
|
483
|
-
}
|
|
484
|
-
speak(): string {
|
|
485
|
-
return super.speak() + " (woof)";
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
const dog = new Dog("Rex");
|
|
489
|
-
console.log(dog.speak());
|
|
490
|
-
console.log(Animal.kind(), dog instanceof Dog, dog instanceof Animal);
|
|
491
|
-
`;
|
|
492
|
-
expect(runProgram(source)).toBe("Rex makes a sound (woof)\nanimal true true");
|
|
493
|
-
});
|
|
494
|
-
it("supports async/await and promises", () => {
|
|
495
|
-
const source = `
|
|
496
|
-
async function greet(name: string): Promise<string> {
|
|
497
|
-
return "hello " + name;
|
|
498
|
-
}
|
|
499
|
-
async function main(): Promise<void> {
|
|
500
|
-
const message = await greet("world");
|
|
501
|
-
console.log(message);
|
|
502
|
-
const all = await Promise.all([Promise.resolve(1), Promise.resolve(2)]);
|
|
503
|
-
console.log(all[0] + all[1]);
|
|
504
|
-
const settled = await Promise.allSettled([Promise.resolve(1), Promise.reject("no")]);
|
|
505
|
-
console.log(settled[0].status, settled[1].status);
|
|
506
|
-
try {
|
|
507
|
-
await Promise.reject("boom");
|
|
508
|
-
} catch (err) {
|
|
509
|
-
console.log("caught", err);
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
main();
|
|
513
|
-
Promise.resolve(10).then((x) => console.log("then", x + 1));
|
|
514
|
-
`;
|
|
515
|
-
expect(runProgram(source)).toBe("hello world\n3\nfulfilled rejected\ncaught boom\nthen 11");
|
|
516
|
-
});
|
|
517
|
-
it("supports Map, Set, JSON and extended standard library methods", () => {
|
|
518
|
-
const source = `
|
|
519
|
-
const map = new Map<string, number>();
|
|
520
|
-
map.set("a", 1);
|
|
521
|
-
map.set("b", 2);
|
|
522
|
-
const nums = new Set<number>();
|
|
523
|
-
nums.add(3);
|
|
524
|
-
nums.add(3);
|
|
525
|
-
console.log(map.get("a"), map.size, nums.has(3), nums.size);
|
|
526
|
-
const obj = { x: 1, y: [2, 3] };
|
|
527
|
-
console.log(JSON.stringify(obj));
|
|
528
|
-
console.log(JSON.parse('{"k":5}').k);
|
|
529
|
-
console.log([3, 1, 2].sort().join(","), [1, 2, 3].find((n) => n > 1));
|
|
530
|
-
console.log("abc".padStart(5, "*"), (255).toString(16), (3.14159).toFixed(2));
|
|
531
|
-
console.log(Number.isInteger(3), Array.isArray([1]), Math.imul(2, 3));
|
|
532
|
-
`;
|
|
533
|
-
expect(runProgram(source)).toBe("1 2 true 1\n{\"x\":1,\"y\":[2,3]}\n5\n1,2,3 2\n**abc ff 3.14\ntrue true 6");
|
|
534
|
-
});
|
|
535
|
-
it("assigns array.length and spreads iterables (Map/Set/string)", () => {
|
|
536
|
-
const source = `
|
|
537
|
-
const a = [10, 20, 30];
|
|
538
|
-
a.length = 1;
|
|
539
|
-
console.log(a.length, a[0], a[1]);
|
|
540
|
-
a.length = 3;
|
|
541
|
-
console.log(a.length, a[1], a[2]);
|
|
542
|
-
const set = new Set<string>();
|
|
543
|
-
set.add("x");
|
|
544
|
-
set.add("y");
|
|
545
|
-
set.add("x");
|
|
546
|
-
console.log([...set].length, [...set].join(","), ["a", ...set, "b"].join(","));
|
|
547
|
-
const map = new Map<string, number>();
|
|
548
|
-
map.set("k", 1);
|
|
549
|
-
map.set("v", 2);
|
|
550
|
-
console.log([...map].length, [...map].map((p) => p[0] + ":" + p[1]).join(","));
|
|
551
|
-
console.log([..."abc"].length, [..."abc"].join("-"));
|
|
552
|
-
`;
|
|
553
|
-
expect(runProgram(source)).toBe("1 10 undefined\n3 undefined undefined\n2 x,y a,x,y,b\n2 k:1,v:2\n3 a-b-c");
|
|
554
|
-
});
|
|
555
|
-
it("iterates Map and Set with for...of", () => {
|
|
556
|
-
const source = `
|
|
557
|
-
const map = new Map<string, number>();
|
|
558
|
-
map.set("a", 1);
|
|
559
|
-
map.set("b", 2);
|
|
560
|
-
let out = "";
|
|
561
|
-
for (const [k, v] of map) out += k + "=" + v + ";";
|
|
562
|
-
const set = new Set<number>();
|
|
563
|
-
set.add(10);
|
|
564
|
-
set.add(20);
|
|
565
|
-
let sum = 0;
|
|
566
|
-
for (const n of set) sum += n;
|
|
567
|
-
console.log(out, sum);
|
|
568
|
-
`;
|
|
569
|
-
expect(runProgram(source)).toBe("a=1;b=2; 30");
|
|
570
|
-
});
|
|
571
|
-
it("supports import and export across modules", () => {
|
|
572
|
-
writeFileSync(join(harness.workdir, "math_dep.ts"), `
|
|
573
|
-
export const PI = 3.14;
|
|
574
|
-
export function add(a: number, b: number): number {
|
|
575
|
-
return a + b;
|
|
576
|
-
}
|
|
577
|
-
export default function greet(name: string): string {
|
|
578
|
-
return "hi " + name;
|
|
579
|
-
}
|
|
580
|
-
`);
|
|
581
|
-
const source = `
|
|
582
|
-
import greet, { add, PI } from "./math_dep";
|
|
583
|
-
console.log(add(2, 3), PI);
|
|
584
|
-
console.log(greet("ada"));
|
|
585
|
-
`;
|
|
586
|
-
expect(runProgram(source)).toBe("5 3.14\nhi ada");
|
|
587
|
-
});
|
|
588
|
-
it("resolves `.js` specifiers to their TypeScript sources", () => {
|
|
589
|
-
writeFileSync(join(harness.workdir, "esm_dep.ts"), `export const value = 42;`);
|
|
590
|
-
const source = `
|
|
591
|
-
import { value } from "./esm_dep.js";
|
|
592
|
-
console.log(value);
|
|
593
|
-
`;
|
|
594
|
-
expect(runProgram(source)).toBe("42");
|
|
595
|
-
});
|
|
596
|
-
it("bundles JavaScript modules through extension-less imports", () => {
|
|
597
|
-
const source = `
|
|
598
|
-
import { add } from "./js_dep";
|
|
599
|
-
console.log(add(2, 3));
|
|
600
|
-
`;
|
|
601
|
-
expect(runProgram(source, {
|
|
602
|
-
files: {
|
|
603
|
-
"js_dep.js": 'export { add } from "./js_math";',
|
|
604
|
-
"js_math.js": "export function add(a, b) { return a + b; }",
|
|
605
|
-
},
|
|
606
|
-
})).toBe("5");
|
|
607
|
-
});
|
|
608
|
-
it("supports destructuring bindings, enums, regexes and Error", () => {
|
|
609
|
-
const source = `
|
|
610
|
-
const [a, b = 7, ...rest] = [1, undefined, 3, 4];
|
|
611
|
-
const { x, y: z = 9 } = { x: 5 };
|
|
612
|
-
enum Color { Red, Green = 5, Blue }
|
|
613
|
-
const re = /^-[0-9]+/;
|
|
614
|
-
console.log(a, b, rest.join(","), x, z);
|
|
615
|
-
console.log(Color.Red, Color.Green, Color.Blue, Color[5]);
|
|
616
|
-
console.log(re.test("-42abc"), re.test("nope"));
|
|
617
|
-
try { throw new Error("boom"); } catch (e) { console.log(e.name + ": " + e.message); }
|
|
618
|
-
`;
|
|
619
|
-
expect(runProgram(source)).toBe("1 7 3,4 5 9\n0 5 6 Green\ntrue false\nError: boom");
|
|
620
|
-
});
|
|
621
|
-
it("supports destructuring assignments", () => {
|
|
622
|
-
const source = `
|
|
623
|
-
let a = 1, b = 2;
|
|
624
|
-
[a, b] = [b, a];
|
|
625
|
-
console.log(a, b);
|
|
626
|
-
|
|
627
|
-
let c = 0, d = 0, rest: number[] = [];
|
|
628
|
-
[c, d = 5, ...rest] = [10, undefined, 30, 40];
|
|
629
|
-
console.log(c, d, rest.join(","));
|
|
630
|
-
|
|
631
|
-
let x = 0, y = 0;
|
|
632
|
-
[[x], [y]] = [[100], [200]];
|
|
633
|
-
let p = 0, q = 0, r = 0;
|
|
634
|
-
({ p, q = 9, p: r } = { q: 7, p: 8 });
|
|
635
|
-
console.log(x, y, p, q, r);
|
|
636
|
-
|
|
637
|
-
const target = { v: 0 } as any;
|
|
638
|
-
const list = [0, 0];
|
|
639
|
-
[target.v, list[0]] = [5, 6];
|
|
640
|
-
console.log(target.v, list[0]);
|
|
641
|
-
|
|
642
|
-
const pairs = [[1, 2], [3, 4]];
|
|
643
|
-
let u = 0, v = 0;
|
|
644
|
-
for ([u, v] of pairs) console.log(u, v);
|
|
645
|
-
for (const [f, s] of pairs) console.log(f + s);
|
|
646
|
-
`;
|
|
647
|
-
expect(runProgram(source)).toBe("2 1\n10 5 30,40\n100 200 8 7 8\n5 6\n1 2\n3 4\n3\n7");
|
|
648
|
-
});
|
|
649
|
-
it("supports destructuring parameters", () => {
|
|
650
|
-
const source = `
|
|
651
|
-
const pairs = [["a", 1], ["b", 2]];
|
|
652
|
-
const mapped = pairs.map(([name, value]) => [name, { value }]);
|
|
653
|
-
console.log(mapped[0][0], mapped[1][0]);
|
|
654
|
-
console.log(mapped[0][1].value, mapped[1][1].value);
|
|
655
|
-
|
|
656
|
-
function sum([a, b]: [number, number]) { return a + b; }
|
|
657
|
-
console.log(sum([3, 4]));
|
|
658
|
-
|
|
659
|
-
function greet({ name, greeting = "Hello" }: { name: string; greeting?: string }) {
|
|
660
|
-
return greeting + ", " + name;
|
|
661
|
-
}
|
|
662
|
-
console.log(greet({ name: "Ada" }), greet({ name: "Bob", greeting: "Hi" }));
|
|
663
|
-
|
|
664
|
-
const withDefault = ([a, b] = [10, 20]) => a + b;
|
|
665
|
-
console.log(withDefault(), withDefault([1, 2]));
|
|
666
|
-
`;
|
|
667
|
-
expect(runProgram(source)).toBe("a b\n1 2\n7\nHello, Ada Hi, Bob\n30 3");
|
|
668
|
-
});
|
|
669
|
-
it("supports Function.prototype.call, apply and bind", () => {
|
|
670
|
-
const source = `
|
|
671
|
-
function add(a: number, b: number): number { return a + b; }
|
|
672
|
-
console.log(add.call(null, 1, 2), add.apply(null, [5, 6]));
|
|
673
|
-
|
|
674
|
-
const obj = {
|
|
675
|
-
base: 10,
|
|
676
|
-
sum(a: number, b: number): number { return this.base + a + b; },
|
|
677
|
-
};
|
|
678
|
-
console.log(obj.sum.call(obj, 1, 2), obj.sum.apply(obj, [3, 4]));
|
|
679
|
-
|
|
680
|
-
const bound = obj.sum.bind(obj, 1);
|
|
681
|
-
console.log(bound(2));
|
|
682
|
-
const bound2 = add.bind(null, 10);
|
|
683
|
-
console.log(bound2(5));
|
|
684
|
-
|
|
685
|
-
class Greeter {
|
|
686
|
-
prefix = "hi";
|
|
687
|
-
greet(name: string): string { return this.prefix + " " + name; }
|
|
688
|
-
}
|
|
689
|
-
const g = new Greeter();
|
|
690
|
-
console.log(g.greet.call(g, "ada"), g.greet.bind(g)("cy"));
|
|
691
|
-
`;
|
|
692
|
-
expect(runProgram(source)).toBe("3 11\n13 17\n13\n15\nhi ada hi cy");
|
|
693
|
-
});
|
|
694
|
-
it("supports object-literal getters and setters", () => {
|
|
695
|
-
const source = `
|
|
696
|
-
const obj = {
|
|
697
|
-
_x: 0,
|
|
698
|
-
get x(): number { return this._x; },
|
|
699
|
-
set x(v: number) { this._x = v * 2; },
|
|
700
|
-
get double(): number { return this._x * 2; },
|
|
701
|
-
method(a: number, b: number) { return a + b; },
|
|
702
|
-
};
|
|
703
|
-
obj.x = 5;
|
|
704
|
-
console.log(obj.x, obj.double);
|
|
705
|
-
console.log(obj.method(2, 3));
|
|
706
|
-
console.log(Object.keys(obj).join(","));
|
|
707
|
-
|
|
708
|
-
const base = 7;
|
|
709
|
-
const o2 = { base, get next() { return base + 1; } };
|
|
710
|
-
console.log(o2.base, o2.next);
|
|
711
|
-
|
|
712
|
-
const o3 = { get ["dyn" + "amic"]() { return 42; } };
|
|
713
|
-
console.log(o3.dynamic);
|
|
714
|
-
`;
|
|
715
|
-
expect(runProgram(source)).toBe("10 20\n5\n_x,x,double,method\n7 8\n42");
|
|
716
|
-
});
|
|
717
|
-
it("supports first-class built-in method values", () => {
|
|
718
|
-
const source = `
|
|
719
|
-
const arr = [1, 2, 3];
|
|
720
|
-
console.log(typeof arr.map, arr.map.name, arr.map.length);
|
|
721
|
-
console.log(arr.map?.((x: number) => x * 2).join(","));
|
|
722
|
-
|
|
723
|
-
const text = "abc";
|
|
724
|
-
console.log(typeof text.slice, text.slice(1), text.split?.("").join("-"));
|
|
725
|
-
|
|
726
|
-
const obj = { base: 10, add(n: number) { return this.base + n; } };
|
|
727
|
-
console.log(obj.add?.(5));
|
|
728
|
-
|
|
729
|
-
const detached = arr.map;
|
|
730
|
-
try {
|
|
731
|
-
(detached as any)((x: number) => x);
|
|
732
|
-
console.log("no throw");
|
|
733
|
-
} catch (e) {
|
|
734
|
-
console.log("threw");
|
|
735
|
-
}
|
|
736
|
-
`;
|
|
737
|
-
expect(runProgram(source)).toBe("function map 1\n2,4,6\nfunction bc a-b-c\n15\nthrew");
|
|
738
|
-
});
|
|
739
|
-
it("calls functions through computed and indexed element access", () => {
|
|
740
|
-
const source = `
|
|
741
|
-
const fns = [(n: number) => n + 1, (n: number) => n * 2];
|
|
742
|
-
console.log(fns[0](10), fns[1](10));
|
|
743
|
-
let total = 0;
|
|
744
|
-
for (let i = 0; i < fns.length; i++) total += fns[i](3);
|
|
745
|
-
const table: any = { a: () => "A", b: () => "B" };
|
|
746
|
-
const key = "b";
|
|
747
|
-
console.log(total, table[key]());
|
|
748
|
-
`;
|
|
749
|
-
expect(runProgram(source)).toBe("11 20\n10 B");
|
|
750
|
-
});
|
|
751
|
-
it("supports the Error family as first-class constructors", () => {
|
|
752
|
-
const source = `
|
|
753
|
-
console.log(typeof Error, typeof TypeError, typeof AggregateError);
|
|
754
|
-
console.log(new Error("a") instanceof Error);
|
|
755
|
-
console.log(new TypeError("b") instanceof TypeError);
|
|
756
|
-
console.log(new TypeError("b") instanceof Error);
|
|
757
|
-
console.log(new RangeError("c") instanceof TypeError);
|
|
758
|
-
const e = new Error("boom");
|
|
759
|
-
console.log(String(e), e.toString(), e.name, e.message);
|
|
760
|
-
|
|
761
|
-
class MyErr extends Error {
|
|
762
|
-
constructor(m: string) { super(m); this.name = "MyErr"; }
|
|
763
|
-
}
|
|
764
|
-
const m = new MyErr("m");
|
|
765
|
-
console.log(m.name, m.message, m instanceof MyErr, m instanceof Error);
|
|
766
|
-
|
|
767
|
-
class CustomRange extends RangeError {}
|
|
768
|
-
const r = new CustomRange("too big");
|
|
769
|
-
console.log(r.name, r.message, r instanceof RangeError, r instanceof Error);
|
|
770
|
-
|
|
771
|
-
const agg = new AggregateError([1, 2], "all failed");
|
|
772
|
-
console.log(agg.name, agg.message, JSON.stringify(agg.errors), agg instanceof Error);
|
|
773
|
-
|
|
774
|
-
Promise.any([Promise.reject("x"), Promise.reject("y")]).catch((reason) => {
|
|
775
|
-
const err = reason as { name: string; errors: unknown[] };
|
|
776
|
-
console.log(err.name, JSON.stringify(err.errors));
|
|
777
|
-
});
|
|
778
|
-
`;
|
|
779
|
-
expect(runProgram(source)).toBe("function function function\ntrue\ntrue\ntrue\nfalse\n" +
|
|
780
|
-
"Error: boom Error: boom Error boom\n" +
|
|
781
|
-
"MyErr m true true\nRangeError too big true true\n" +
|
|
782
|
-
"AggregateError all failed [1,2] true\nAggregateError [\"x\",\"y\"]");
|
|
783
|
-
});
|
|
784
|
-
it("supports generator functions", () => {
|
|
785
|
-
const source = `
|
|
786
|
-
function* nums() { yield 1; yield 2; yield 3; }
|
|
787
|
-
const g = nums();
|
|
788
|
-
console.log(typeof nums, g.next().value, g.next().value, g.next().value);
|
|
789
|
-
console.log(JSON.stringify(g.next()));
|
|
790
|
-
|
|
791
|
-
function* counter(start: number) {
|
|
792
|
-
let x = start;
|
|
793
|
-
while (true) {
|
|
794
|
-
const step = yield x;
|
|
795
|
-
x = step === undefined ? x + 1 : step;
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
const c = counter(10);
|
|
799
|
-
console.log(c.next().value, c.next().value, c.next(100).value, c.next().value);
|
|
800
|
-
|
|
801
|
-
class Box {
|
|
802
|
-
private n = 0;
|
|
803
|
-
*gen(): Generator<number> { yield this.n++; yield this.n++; }
|
|
804
|
-
}
|
|
805
|
-
const box = new Box();
|
|
806
|
-
console.log([...box.gen()].join(","), [...box.gen()].join(","));
|
|
807
|
-
|
|
808
|
-
function* inner() { yield 1; yield 2; }
|
|
809
|
-
function* outer() { yield* inner(); yield 9; }
|
|
810
|
-
console.log([...outer()].join("-"), [...outer()].join("-"));
|
|
811
|
-
|
|
812
|
-
function* echo() { const a = yield "first"; const b = yield a; return b; }
|
|
813
|
-
const e = echo();
|
|
814
|
-
console.log(e.next().value, e.next("A").value, JSON.stringify(e.next("B")));
|
|
815
|
-
|
|
816
|
-
function* guarded() {
|
|
817
|
-
try { yield "x"; } catch (err) { yield "caught:" + (err as Error).message; }
|
|
818
|
-
yield "end";
|
|
819
|
-
}
|
|
820
|
-
const t = guarded();
|
|
821
|
-
console.log(t.next().value, t.throw(new Error("E")).value, t.next().value);
|
|
822
|
-
|
|
823
|
-
function* once() { yield 1; }
|
|
824
|
-
const o = once();
|
|
825
|
-
o.next();
|
|
826
|
-
console.log(JSON.stringify(o.next()), typeof o);
|
|
827
|
-
|
|
828
|
-
function* naturals() { let i = 0; while (true) yield i++; }
|
|
829
|
-
const seen: number[] = [];
|
|
830
|
-
for (const v of naturals()) { if (v >= 3) break; seen.push(v); }
|
|
831
|
-
console.log(seen.join(","));
|
|
832
|
-
`;
|
|
833
|
-
expect(runProgram(source)).toBe("function 1 2 3\n{\"done\":true}\n10 11 100 101\n0,1 2,3\n" +
|
|
834
|
-
"1-2-9 1-2-9\nfirst A {\"value\":\"B\",\"done\":true}\n" +
|
|
835
|
-
"x caught:E end\n{\"done\":true} object\n0,1,2");
|
|
836
|
-
});
|
|
837
|
-
it("supports symbols and the well-known symbol protocol", () => {
|
|
838
|
-
const source = `
|
|
839
|
-
const a = Symbol("desc");
|
|
840
|
-
const b = Symbol("desc");
|
|
841
|
-
console.log(typeof Symbol, typeof a, a === a, a === b);
|
|
842
|
-
console.log(a.toString(), String(a), a.description, String(Symbol()));
|
|
843
|
-
console.log(String(Symbol.iterator), typeof Symbol.iterator);
|
|
844
|
-
|
|
845
|
-
const holder: Record<string, unknown> = {};
|
|
846
|
-
(holder as any)[a] = 1;
|
|
847
|
-
(holder as any)[b] = 2;
|
|
848
|
-
holder.x = 3;
|
|
849
|
-
console.log(Object.keys(holder).join(","));
|
|
850
|
-
console.log(Object.getOwnPropertySymbols(holder).length);
|
|
851
|
-
console.log((holder as any)[a], (holder as any)[b], (holder as any)[Symbol("desc")]);
|
|
852
|
-
console.log(JSON.stringify(holder), JSON.stringify(a), JSON.stringify([a, 1]));
|
|
853
|
-
console.log(holder);
|
|
854
|
-
|
|
855
|
-
const reg = Symbol.for("shared");
|
|
856
|
-
console.log(reg === Symbol.for("shared"), Symbol.keyFor(reg), Symbol.keyFor(a));
|
|
857
|
-
|
|
858
|
-
class Range {
|
|
859
|
-
start: number;
|
|
860
|
-
end: number;
|
|
861
|
-
constructor(start: number, end: number) {
|
|
862
|
-
this.start = start;
|
|
863
|
-
this.end = end;
|
|
864
|
-
}
|
|
865
|
-
[Symbol.iterator]() {
|
|
866
|
-
let i = this.start;
|
|
867
|
-
const end = this.end;
|
|
868
|
-
return {
|
|
869
|
-
next() {
|
|
870
|
-
return i < end ? { value: i++, done: false } : { value: undefined, done: true };
|
|
871
|
-
},
|
|
872
|
-
};
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
console.log([...new Range(1, 4)].join(","));
|
|
876
|
-
const walked: number[] = [];
|
|
877
|
-
for (const x of new Range(10, 13)) walked.push(x);
|
|
878
|
-
console.log(walked.join(","));
|
|
879
|
-
|
|
880
|
-
function* delegates() { yield* new Range(5, 7); }
|
|
881
|
-
console.log([...delegates()].join(","));
|
|
882
|
-
`;
|
|
883
|
-
expect(runProgram(source)).toBe("function symbol true false\n" +
|
|
884
|
-
"Symbol(desc) Symbol(desc) desc Symbol()\n" +
|
|
885
|
-
"Symbol(Symbol.iterator) symbol\n" +
|
|
886
|
-
"x\n2\n1 2 undefined\n" +
|
|
887
|
-
'{"x":3} undefined [null,1]\n' +
|
|
888
|
-
"{ x: 3, Symbol(desc): 1, Symbol(desc): 2 }\n" +
|
|
889
|
-
"true shared undefined\n" +
|
|
890
|
-
"1,2,3\n10,11,12\n5,6");
|
|
891
|
-
});
|
|
892
|
-
});
|
|
893
|
-
//# sourceMappingURL=compile.test.js.map
|