├── .editorconfig
├── .github
└── workflows
│ └── ci_tests.yml
├── .gitignore
├── .gitmodules
├── .node-version
├── .npm-version
├── .npmignore
├── .nvmrc
├── .prettierignore
├── LICENSE
├── README.md
├── examples
├── contexts.js
├── hello.js
├── logging.js
├── pi_bigdecimal.js
├── pi_bigfloat.js
├── pi_bigint.js
└── print-stdin-bytes.js
├── jest.config.js
├── meta
├── build.sh
├── ci.sh
├── clean.sh
├── docker
│ ├── aarch64-unknown-linux-musl
│ │ ├── Dockerfile
│ │ └── cmd.sh
│ ├── build-all.sh
│ ├── build-images.sh
│ ├── images.sh
│ ├── multi-apple-darwin
│ │ ├── Dockerfile
│ │ └── cmd.sh
│ ├── multi-unknown-linux-gnu
│ │ ├── Dockerfile
│ │ ├── cmd.sh
│ │ └── fixup-gcc-libs.sh
│ ├── x86_64-pc-windows-static
│ │ ├── Dockerfile
│ │ └── cmd.sh
│ └── x86_64-unknown-linux-musl
│ │ ├── Dockerfile
│ │ └── cmd.sh
├── microbench.js
├── ninja
│ ├── defs.ninja.js
│ ├── envs
│ │ ├── host
│ │ │ ├── darwin.ninja.js
│ │ │ ├── freebsd.ninja.js
│ │ │ ├── linux-static.ninja.js
│ │ │ ├── linux.ninja.js
│ │ │ └── other.ninja.js
│ │ └── target
│ │ │ ├── cosmo.ninja.js
│ │ │ ├── cross-darwin-arm64.ninja.js
│ │ │ ├── cross-darwin-x86_64.ninja.js
│ │ │ ├── cross-linux-aarch64.ninja.js
│ │ │ ├── cross-linux-x86_64.ninja.js
│ │ │ ├── cross-windows-x86_64.ninja.js
│ │ │ ├── darwin.ninja.js
│ │ │ ├── freebsd.ninja.js
│ │ │ ├── linux-static.ninja.js
│ │ │ ├── linux.ninja.js
│ │ │ └── other.ninja.js
│ ├── generate.js
│ └── rules.ninja.js
└── tsconfig.json
├── npm
├── cli
│ ├── qjs.js
│ ├── qjsc.js
│ └── quickjs-run.js
├── index.js
├── platforms.json
└── run-binary.js
├── package-lock.json
├── package.json
├── src
├── archives
│ ├── core.ninja.js
│ ├── full.ninja.js
│ ├── quickjs-full-init.c
│ └── quickjs-full-init.h
├── builtin-modules
│ ├── quickjs-bytecode
│ │ ├── quickjs-bytecode.c
│ │ ├── quickjs-bytecode.d.ts
│ │ ├── quickjs-bytecode.h
│ │ └── quickjs-bytecode.ninja.js
│ ├── quickjs-context
│ │ ├── quickjs-context.c
│ │ ├── quickjs-context.d.ts
│ │ ├── quickjs-context.h
│ │ └── quickjs-context.ninja.js
│ ├── quickjs-encoding
│ │ ├── quickjs-encoding.c
│ │ ├── quickjs-encoding.d.ts
│ │ ├── quickjs-encoding.h
│ │ └── quickjs-encoding.ninja.js
│ ├── quickjs-engine
│ │ ├── quickjs-engine.c
│ │ ├── quickjs-engine.d.ts
│ │ ├── quickjs-engine.h
│ │ └── quickjs-engine.ninja.js
│ ├── quickjs-libc
│ │ ├── quickjs-libc.c
│ │ ├── quickjs-libc.d.ts
│ │ ├── quickjs-libc.h
│ │ └── quickjs-libc.ninja.js
│ └── quickjs-pointer
│ │ ├── quickjs-pointer.c
│ │ ├── quickjs-pointer.d.ts
│ │ ├── quickjs-pointer.h
│ │ └── quickjs-pointer.ninja.js
├── globals
│ ├── quickjs-inspect
│ │ ├── inspect.js
│ │ ├── quickjs-inspect.c
│ │ ├── quickjs-inspect.d.ts
│ │ ├── quickjs-inspect.h
│ │ └── quickjs-inspect.ninja.js
│ ├── quickjs-intervals
│ │ ├── intervals.js
│ │ ├── quickjs-intervals.c
│ │ ├── quickjs-intervals.d.ts
│ │ ├── quickjs-intervals.h
│ │ └── quickjs-intervals.ninja.js
│ └── quickjs-print
│ │ ├── quickjs-print.c
│ │ ├── quickjs-print.d.ts
│ │ ├── quickjs-print.h
│ │ └── quickjs-print.ninja.js
├── lib
│ ├── cutils
│ │ ├── cutils.c
│ │ ├── cutils.h
│ │ └── cutils.ninja.js
│ ├── debugprint
│ │ └── debugprint.h
│ ├── execpath
│ │ ├── execpath.c
│ │ ├── execpath.h
│ │ └── execpath.ninja.js
│ ├── libbf
│ │ ├── libbf.c
│ │ ├── libbf.h
│ │ └── libbf.ninja.js
│ ├── libregexp
│ │ ├── libregexp-opcode.h
│ │ ├── libregexp.c
│ │ ├── libregexp.h
│ │ └── libregexp.ninja.js
│ ├── libunicode
│ │ ├── downloaded
│ │ │ ├── CaseFolding.txt
│ │ │ ├── CompositionExclusions.txt
│ │ │ ├── DerivedCoreProperties.txt
│ │ │ ├── DerivedNormalizationProps.txt
│ │ │ ├── NormalizationTest.txt
│ │ │ ├── PropList.txt
│ │ │ ├── PropertyValueAliases.txt
│ │ │ ├── README.md
│ │ │ ├── ScriptExtensions.txt
│ │ │ ├── Scripts.txt
│ │ │ ├── SpecialCasing.txt
│ │ │ ├── UnicodeData.txt
│ │ │ ├── emoji-data.txt
│ │ │ └── unicode_download.sh
│ │ ├── libunicode.c
│ │ ├── libunicode.h
│ │ ├── libunicode.ninja.js
│ │ ├── unicode_gen.c
│ │ └── unicode_gen_def.h
│ ├── list
│ │ └── list.h
│ └── quickjs-utils
│ │ ├── quickjs-utils.c
│ │ ├── quickjs-utils.h
│ │ └── quickjs-utils.ninja.js
├── programs
│ ├── file-to-bytecode
│ │ ├── file-to-bytecode.js
│ │ └── file-to-bytecode.ninja.js
│ ├── log-argv
│ │ ├── indirect-argv0.js
│ │ ├── log-argv.c
│ │ └── log-argv.js
│ ├── qjs
│ │ ├── qjs.c
│ │ └── qjs.ninja.js
│ ├── qjsbootstrap
│ │ ├── README.md
│ │ ├── is-stdin-a-tty.js
│ │ ├── qjsbootstrap.c
│ │ ├── qjsbootstrap.ninja.js
│ │ └── size_check.js
│ ├── qjsc
│ │ ├── qjsc.c
│ │ └── qjsc.ninja.js
│ ├── qjscalc
│ │ ├── qjscalc.js
│ │ └── qjscalc.ninja.js
│ ├── quickjs-run
│ │ ├── quickjs-run.c
│ │ └── quickjs-run.ninja.js
│ ├── repl
│ │ ├── repl.js
│ │ └── repl.ninja.js
│ ├── sample-program
│ │ ├── sample-program.ninja.js
│ │ └── sum.js
│ └── stack-limit-test
│ │ ├── loop.js
│ │ ├── main.c
│ │ └── stack-limit-test.ninja.js
├── quickjs-modulesys
│ ├── module-impl-stub.c
│ ├── module-impl.js
│ ├── quickjs-modulesys.c
│ ├── quickjs-modulesys.d.ts
│ ├── quickjs-modulesys.h
│ └── quickjs-modulesys.ninja.js
├── quickjs
│ ├── quickjs-atom.h
│ ├── quickjs-opcode.h
│ ├── quickjs.c
│ ├── quickjs.d.ts
│ ├── quickjs.h
│ └── quickjs.ninja.js
├── run-test262
│ ├── .gitignore
│ ├── run-test262.c
│ ├── run-test262.ninja.js
│ ├── run.sh
│ ├── runo.sh
│ ├── test262.conf
│ ├── test262.patch
│ ├── test262_errors.txt
│ ├── test262o.conf
│ └── test262o_errors.txt
├── shared-library-modules
│ ├── example-fib
│ │ ├── fib.c
│ │ ├── fib.ninja.js
│ │ └── test_fib.js
│ └── example-point
│ │ ├── point.c
│ │ ├── point.ninja.js
│ │ └── test_point.js
└── tsconfig.json
├── tests
├── _utils.ts
├── error-cause.test.ts
├── error-properties.test.ts
├── exit-code-stuff.test.ts
├── file-to-bytecode.test.ts
├── fixtures
│ ├── ah.txt
│ ├── check-module-ns.js
│ ├── exports-five.js
│ ├── import-meta-require.js
│ ├── import-meta-resolve.js
│ ├── load-module-via-urlget.js
│ ├── log-four.js
│ ├── module-detection
│ │ ├── explicit-module.mjs
│ │ ├── implicit-module-with-shebang.js
│ │ ├── implicit-module-with-two-shebangs.js
│ │ ├── implicit-module.js
│ │ └── implicit-script.js
│ ├── module-hooks
│ │ ├── load-txt.js
│ │ ├── something.txt
│ │ ├── txt-compiler.js
│ │ └── txt-search-extension.js
│ ├── module-ns-mutation
│ │ ├── loads-nothing-then-new.js
│ │ └── loads-nothing.js
│ ├── module-throws-error
│ │ └── esm.js
│ ├── regexp-escaped-dash-in-charset.js
│ ├── setting-main-module
│ │ ├── mod1_import.js
│ │ ├── mod1_importModule.js
│ │ ├── mod2.js
│ │ └── mod3.js
│ ├── string-cooked.js
│ ├── subdir
│ │ ├── another-subdir
│ │ │ └── index.js
│ │ ├── imports-std.js
│ │ ├── log-five.js
│ │ ├── reaches-for-folder-with-index.js
│ │ ├── reaches-over.js
│ │ └── reaches-up.js
│ ├── uses-std-and-bytecode.js
│ ├── with-cjs-export.js
│ └── without-cjs-export.js
├── getpwuid.test.ts
├── import-error.test.ts
├── import-meta-require.test.ts
├── import-meta-resolve.test.ts
├── inspect-custom.test.ts
├── inspect.test.ts
├── intervals.test.ts
├── libbytecode.test.ts
├── libcontext.test.ts
├── libencoding.test.ts
├── libmodule.test.ts
├── load-module-via-urlget.test.ts
├── module-define.test.ts
├── module-detect.test.ts
├── module-hooks.test.ts
├── module-read.test.ts
├── module-resolution.test.ts
├── object-toprimitive.test.ts
├── oldtests
│ ├── oldtests.test.ts
│ ├── test_bignum.js
│ ├── test_builtin.js
│ ├── test_closure.js
│ ├── test_language.js
│ ├── test_loop.js
│ ├── test_op_overloading.js
│ ├── test_qjscalc.js
│ ├── test_std.js
│ ├── test_worker.js
│ └── test_worker_module.js
├── pointer.test.ts
├── print.test.ts
├── qjsbootstrap-argv0.test.ts
├── qjsbootstrap-bytecode.test.ts
├── qjsbootstrap-can-access-bytecode-lib.test.ts
├── qjsc.test.ts
├── regexp-escaped-dash-in-charset.test.ts
├── setting-main-module.test.ts
├── shared-library-modules.test.ts
├── skip-shebangs.test.ts
├── string-cooked.test.ts
├── symbol-typeof-value.test.ts
└── workdir
│ └── .gitignore
├── todo.txt
└── tsconfig.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | trim_trailing_whitespace = true
7 | insert_final_newline = true
8 |
--------------------------------------------------------------------------------
/.github/workflows/ci_tests.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 | types: [opened, synchronize]
9 |
10 | jobs:
11 | build-and-test-ubuntu:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v2
15 | - name: Build and Test (Ubuntu)
16 | run: meta/ci.sh
17 |
18 | build-and-test-macos:
19 | # Note: 14 image uses M1 (arm) processor
20 | runs-on: macos-14
21 | steps:
22 | - uses: actions/checkout@v2
23 | - name: Build and Test (macOS)
24 | run: meta/ci.sh
25 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode
2 | .DS_Store
3 | node_modules
4 | build
5 | build.ninja
6 | .ninja_*
7 | microbench.txt
8 | microbench-new.txt
9 | cmake-build-debug
10 | .idea
11 |
12 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "src/run-test262/test262"]
2 | path = src/run-test262/test262
3 | url = https://github.com/tc39/test262.git
4 | [submodule "src/run-test262/test262o"]
5 | path = src/run-test262/test262o
6 | url = https://github.com/tc39/test262.git
7 |
--------------------------------------------------------------------------------
/.node-version:
--------------------------------------------------------------------------------
1 | v22.15.0
2 |
--------------------------------------------------------------------------------
/.npm-version:
--------------------------------------------------------------------------------
1 | 10.9.2
2 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .github
2 | .gitmodules
3 | .nvmrc
4 | examples
5 | meta
6 | node_modules
7 | src
8 | tests
9 | .editorconfig
10 | .gitignore
11 | .node-version
12 | .npm-version
13 | .npmignore
14 | .prettierignore
15 | build.ninja
16 | Changelog
17 | jest.config.js
18 | build/*/intermediate
19 | build/**/.ninja_*
20 | todo.txt
21 |
22 | # non-platform-specific build artifacts (dev builds)
23 | build/bin
24 | build/intermediate
25 | build/lib
26 | build/include
27 | build/extras
28 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | v22.15.0
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | # prettier handles operator precedence wrong for math mode
2 | tests/oldtests/test_qjscalc.js
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | QuickJS Javascript Engine
2 |
3 | Copyright (c) 2017-2021 Fabrice Bellard
4 | Copyright (c) 2017-2021 Charlie Gordon
5 | Copyright (c) 2022-2023 Lily Skye
6 |
7 | Permission is hereby granted, free of charge, to any person obtaining a copy
8 | of this software and associated documentation files (the "Software"), to deal
9 | in the Software without restriction, including without limitation the rights
10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | copies of the Software, and to permit persons to whom the Software is
12 | furnished to do so, subject to the following conditions:
13 |
14 | The above copyright notice and this permission notice shall be included in
15 | all copies or substantial portions of the Software.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | THE SOFTWARE.
24 |
25 | QuickJS also includes npm:@suchipi/inspect@2.1.0, which has the following license:
26 |
27 | Copyright (c) 2016-2022, John Gardner
28 | Copyright (c) 2022 Lily Skye
29 |
30 | Permission to use, copy, modify, and/or distribute this software for any
31 | purpose with or without fee is hereby granted, provided that the above
32 | copyright notice and this permission notice appear in all copies.
33 |
34 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
35 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
36 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
37 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
38 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
39 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
40 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
41 |
--------------------------------------------------------------------------------
/examples/contexts.js:
--------------------------------------------------------------------------------
1 | import { Context } from "quickjs:context";
2 |
3 | console.log("c1");
4 | const c1 = new Context();
5 | console.log(c1.eval("Array") === c1.globalThis.Array); // true
6 | console.log(c1.eval("Array") === globalThis.Array); // false
7 |
8 | console.log("c2");
9 | const c2 = new Context();
10 | console.log(c2.eval("Array") === c2.globalThis.Array); // true
11 | console.log(c2.eval("Array") === c1.globalThis.Array); // false
12 | console.log(c2.eval("Array") === globalThis.Array); // false
13 |
14 | console.log("c2 require");
15 | console.log(c2.globalThis.require);
16 |
17 | console.log("c3");
18 | // all the options:
19 | const c3 = new Context({
20 | // All of these options default to true
21 | date: false,
22 | eval: false,
23 | stringNormalize: false,
24 | regExp: false,
25 | json: false,
26 | proxy: false,
27 | mapSet: false,
28 | typedArrays: false,
29 | promise: false,
30 | bigint: false,
31 | bigfloat: false,
32 | bigdecimal: false,
33 | operators: false,
34 | useMath: false,
35 | inspect: false,
36 | console: false,
37 | print: false,
38 | moduleGlobals: false,
39 | timers: false,
40 | modules: {
41 | "quickjs:std": false,
42 | "quickjs:os": false,
43 | "quickjs:bytecode": false,
44 | "quickjs:context": false,
45 | "quickjs:engine": false,
46 | },
47 | });
48 |
49 | // Not much here
50 | console.log(inspect(c3.globalThis, { all: true, maxDepth: 1 }));
51 |
52 | // the context's 'eval' function still works even if eval is disabled:
53 | console.log(c3.eval("Array") === c3.globalThis.Array);
54 |
55 | try {
56 | // TypeError: eval is not supported
57 | c3.eval("eval('hi')");
58 | } catch (err) {
59 | console.error(err);
60 | }
61 |
62 | try {
63 | // TypeError: 'console' is not defined
64 | c3.eval("console.log(2 + 2)");
65 | } catch (err) {
66 | console.error(err);
67 | }
68 |
69 | try {
70 | // TypeError: eval is not supported
71 | c3.eval("f = new Function('return 2 + 2'); f();");
72 | } catch (err) {
73 | console.error(err);
74 | }
75 |
--------------------------------------------------------------------------------
/examples/hello.js:
--------------------------------------------------------------------------------
1 | console.log("Hello World");
2 |
--------------------------------------------------------------------------------
/examples/logging.js:
--------------------------------------------------------------------------------
1 | print("print", 1, {});
2 | console.log("console.log", 2, {});
3 | console.warn("console.warn", 3, {});
4 | console.error("console.error", 4, {});
5 | console.info("console.info", 5, {});
6 |
--------------------------------------------------------------------------------
/examples/pi_bigdecimal.js:
--------------------------------------------------------------------------------
1 | /*
2 | * PI computation in Javascript using the QuickJS bigdecimal type
3 | * (decimal floating point)
4 | */
5 | "use strict";
6 |
7 | /* compute PI with a precision of 'prec' digits */
8 | function calc_pi(prec) {
9 | const CHUD_A = 13591409m;
10 | const CHUD_B = 545140134m;
11 | const CHUD_C = 640320m;
12 | const CHUD_C3 = 10939058860032000m; /* C^3/24 */
13 | const CHUD_DIGITS_PER_TERM = 14.18164746272548; /* log10(C/12)*3 */
14 |
15 | /* return [P, Q, G] */
16 | function chud_bs(a, b, need_G) {
17 | var c, P, Q, G, P1, Q1, G1, P2, Q2, G2, b1;
18 | if (a == (b - 1n)) {
19 | b1 = BigDecimal(b);
20 | G = (2m * b1 - 1m) * (6m * b1 - 1m) * (6m * b1 - 5m);
21 | P = G * (CHUD_B * b1 + CHUD_A);
22 | if (b & 1n)
23 | P = -P;
24 | G = G;
25 | Q = b1 * b1 * b1 * CHUD_C3;
26 | } else {
27 | c = (a + b) >> 1n;
28 | [P1, Q1, G1] = chud_bs(a, c, true);
29 | [P2, Q2, G2] = chud_bs(c, b, need_G);
30 | P = P1 * Q2 + P2 * G1;
31 | Q = Q1 * Q2;
32 | if (need_G)
33 | G = G1 * G2;
34 | else
35 | G = 0m;
36 | }
37 | return [P, Q, G];
38 | }
39 |
40 | var n, P, Q, G;
41 | /* number of serie terms */
42 | n = BigInt(Math.ceil(prec / CHUD_DIGITS_PER_TERM)) + 10n;
43 | [P, Q, G] = chud_bs(0n, n, false);
44 | Q = BigDecimal.div(Q, (P + Q * CHUD_A),
45 | { roundingMode: "half-even",
46 | maximumSignificantDigits: prec });
47 | G = (CHUD_C / 12m) * BigDecimal.sqrt(CHUD_C,
48 | { roundingMode: "half-even",
49 | maximumSignificantDigits: prec });
50 | return Q * G;
51 | }
52 |
53 | (function() {
54 | var r, n_digits, n_bits;
55 | if (typeof scriptArgs != "undefined") {
56 | if (scriptArgs.length < 2) {
57 | print("usage: pi n_digits");
58 | return;
59 | }
60 | n_digits = scriptArgs[1] | 0;
61 | } else {
62 | n_digits = 1000;
63 | }
64 | /* we add more digits to reduce the probability of bad rounding for
65 | the last digits */
66 | r = calc_pi(n_digits + 20);
67 | print(r.toFixed(n_digits, "down"));
68 | })();
69 |
--------------------------------------------------------------------------------
/examples/pi_bigfloat.js:
--------------------------------------------------------------------------------
1 | /*
2 | * PI computation in Javascript using the QuickJS bigfloat type
3 | * (binary floating point)
4 | */
5 | "use strict";
6 |
7 | /* compute PI with a precision of 'prec' bits */
8 | function calc_pi() {
9 | const CHUD_A = 13591409n;
10 | const CHUD_B = 545140134n;
11 | const CHUD_C = 640320n;
12 | const CHUD_C3 = 10939058860032000n; /* C^3/24 */
13 | const CHUD_BITS_PER_TERM = 47.11041313821584202247; /* log2(C/12)*3 */
14 |
15 | /* return [P, Q, G] */
16 | function chud_bs(a, b, need_G) {
17 | var c, P, Q, G, P1, Q1, G1, P2, Q2, G2;
18 | if (a == (b - 1n)) {
19 | G = (2n * b - 1n) * (6n * b - 1n) * (6n * b - 5n);
20 | P = BigFloat(G * (CHUD_B * b + CHUD_A));
21 | if (b & 1n)
22 | P = -P;
23 | G = BigFloat(G);
24 | Q = BigFloat(b * b * b * CHUD_C3);
25 | } else {
26 | c = (a + b) >> 1n;
27 | [P1, Q1, G1] = chud_bs(a, c, true);
28 | [P2, Q2, G2] = chud_bs(c, b, need_G);
29 | P = P1 * Q2 + P2 * G1;
30 | Q = Q1 * Q2;
31 | if (need_G)
32 | G = G1 * G2;
33 | else
34 | G = 0l;
35 | }
36 | return [P, Q, G];
37 | }
38 |
39 | var n, P, Q, G;
40 | /* number of serie terms */
41 | n = BigInt(Math.ceil(BigFloatEnv.prec / CHUD_BITS_PER_TERM)) + 10n;
42 | [P, Q, G] = chud_bs(0n, n, false);
43 | Q = Q / (P + Q * BigFloat(CHUD_A));
44 | G = BigFloat((CHUD_C / 12n)) * BigFloat.sqrt(BigFloat(CHUD_C));
45 | return Q * G;
46 | }
47 |
48 | (function() {
49 | var r, n_digits, n_bits;
50 | if (typeof scriptArgs != "undefined") {
51 | if (scriptArgs.length < 2) {
52 | print("usage: pi n_digits");
53 | return;
54 | }
55 | n_digits = scriptArgs[1];
56 | } else {
57 | n_digits = 1000;
58 | }
59 | n_bits = Math.ceil(n_digits * Math.log2(10));
60 | /* we add more bits to reduce the probability of bad rounding for
61 | the last digits */
62 | BigFloatEnv.setPrec( () => {
63 | r = calc_pi();
64 | print(r.toFixed(n_digits, BigFloatEnv.RNDZ));
65 | }, n_bits + 32);
66 | })();
67 |
--------------------------------------------------------------------------------
/examples/pi_bigint.js:
--------------------------------------------------------------------------------
1 | /*
2 | * PI computation in Javascript using the BigInt type
3 | */
4 | "use strict";
5 |
6 | /* return floor(log2(a)) for a > 0 and 0 for a = 0 */
7 | function floor_log2(a)
8 | {
9 | var k_max, a1, k, i;
10 | k_max = 0n;
11 | while ((a >> (2n ** k_max)) != 0n) {
12 | k_max++;
13 | }
14 | k = 0n;
15 | a1 = a;
16 | for(i = k_max - 1n; i >= 0n; i--) {
17 | a1 = a >> (2n ** i);
18 | if (a1 != 0n) {
19 | a = a1;
20 | k |= (1n << i);
21 | }
22 | }
23 | return k;
24 | }
25 |
26 | /* return ceil(log2(a)) for a > 0 */
27 | function ceil_log2(a)
28 | {
29 | return floor_log2(a - 1n) + 1n;
30 | }
31 |
32 | /* return floor(sqrt(a)) (not efficient but simple) */
33 | function int_sqrt(a)
34 | {
35 | var l, u, s;
36 | if (a == 0n)
37 | return a;
38 | l = ceil_log2(a);
39 | u = 1n << ((l + 1n) / 2n);
40 | /* u >= floor(sqrt(a)) */
41 | for(;;) {
42 | s = u;
43 | u = ((a / s) + s) / 2n;
44 | if (u >= s)
45 | break;
46 | }
47 | return s;
48 | }
49 |
50 | /* return pi * 2**prec */
51 | function calc_pi(prec) {
52 | const CHUD_A = 13591409n;
53 | const CHUD_B = 545140134n;
54 | const CHUD_C = 640320n;
55 | const CHUD_C3 = 10939058860032000n; /* C^3/24 */
56 | const CHUD_BITS_PER_TERM = 47.11041313821584202247; /* log2(C/12)*3 */
57 |
58 | /* return [P, Q, G] */
59 | function chud_bs(a, b, need_G) {
60 | var c, P, Q, G, P1, Q1, G1, P2, Q2, G2;
61 | if (a == (b - 1n)) {
62 | G = (2n * b - 1n) * (6n * b - 1n) * (6n * b - 5n);
63 | P = G * (CHUD_B * b + CHUD_A);
64 | if (b & 1n)
65 | P = -P;
66 | Q = b * b * b * CHUD_C3;
67 | } else {
68 | c = (a + b) >> 1n;
69 | [P1, Q1, G1] = chud_bs(a, c, true);
70 | [P2, Q2, G2] = chud_bs(c, b, need_G);
71 | P = P1 * Q2 + P2 * G1;
72 | Q = Q1 * Q2;
73 | if (need_G)
74 | G = G1 * G2;
75 | else
76 | G = 0n;
77 | }
78 | return [P, Q, G];
79 | }
80 |
81 | var n, P, Q, G;
82 | /* number of serie terms */
83 | n = BigInt(Math.ceil(Number(prec) / CHUD_BITS_PER_TERM)) + 10n;
84 | [P, Q, G] = chud_bs(0n, n, false);
85 | Q = (CHUD_C / 12n) * (Q << prec) / (P + Q * CHUD_A);
86 | G = int_sqrt(CHUD_C << (2n * prec));
87 | return (Q * G) >> prec;
88 | }
89 |
90 | function main(args) {
91 | var r, n_digits, n_bits, out;
92 | if (args.length < 1) {
93 | print("usage: pi n_digits");
94 | return;
95 | }
96 | n_digits = args[0] | 0;
97 |
98 | /* we add more bits to reduce the probability of bad rounding for
99 | the last digits */
100 | n_bits = BigInt(Math.ceil(n_digits * Math.log2(10))) + 32n;
101 | r = calc_pi(n_bits);
102 | r = ((10n ** BigInt(n_digits)) * r) >> n_bits;
103 | out = r.toString();
104 | print(out[0] + "." + out.slice(1));
105 | }
106 |
107 | var args;
108 | if (typeof scriptArgs != "undefined") {
109 | args = scriptArgs;
110 | args.shift();
111 | } else if (typeof arguments != "undefined") {
112 | args = arguments;
113 | } else {
114 | /* default: 1000 digits */
115 | args=[1000];
116 | }
117 |
118 | main(args);
119 |
--------------------------------------------------------------------------------
/examples/print-stdin-bytes.js:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | import * as std from "quickjs:std";
5 | import * as os from "quickjs:os";
6 |
7 | os.ttySetRaw(std.in.fileno());
8 |
9 | while (!std.in.eof()) {
10 | const byte = std.in.getByte();
11 | console.log(byte.toString(), `(${String.fromCharCode(byte)})`);
12 |
13 | if (byte === 10) {
14 | // newline
15 | break;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 |
3 | /** @type {import('jest').Config} */
4 | const config = {
5 | transform: {
6 | "\\.[jt]sx?$": [
7 | "babel-jest",
8 | {
9 | babelrc: false,
10 | presets: [
11 | "@babel/preset-typescript",
12 | ["@babel/preset-env", { targets: { node: "current" } }],
13 | ],
14 | },
15 | ],
16 | },
17 | watchPathIgnorePatterns: ["node_modules", path.join(__dirname, "build")],
18 | testPathIgnorePatterns: ["run-test262"],
19 | };
20 |
21 | module.exports = config;
22 |
--------------------------------------------------------------------------------
/meta/build.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -euo pipefail
3 | shopt -s globstar
4 |
5 | if [[ "$(uname)" == "Darwin" ]]; then
6 | HOST_OS="darwin"
7 | elif [[ "$(uname)" == "FreeBSD" ]]; then
8 | HOST_OS="freebsd"
9 | elif [[ "$(uname)" == "Linux" ]]; then
10 | HOST_OS="linux"
11 | else
12 | HOST_OS="other"
13 | fi
14 |
15 | if [[ "${HOST:-}" == "" ]]; then
16 | export HOST="$HOST_OS"
17 | fi
18 | if [[ "${TARGET:-}" == "" ]]; then
19 | export TARGET="$HOST_OS"
20 | fi
21 |
22 | echo "HOST: $HOST"
23 | echo "TARGET: $TARGET"
24 |
25 | if [[ "${SKIP_NPM_INSTALL:-}" == "" ]]; then
26 | npm install
27 | fi
28 |
29 | meta/ninja/generate.js src/**/*.ninja.js
30 | ninja
31 |
--------------------------------------------------------------------------------
/meta/ci.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -e
3 |
4 | if [[ "$(uname)" == "Darwin" ]]; then
5 | # Note: we install a newer version of bash because macOS ships with bash 3,
6 | # which doesn't support "shopt -s globstar", which we use.
7 | brew install ninja bash
8 | # install nvm
9 | curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
10 | elif [[ "$(uname)" == "Linux" ]]; then
11 | sudo apt-get install -y ninja-build
12 | fi
13 |
14 | export NVM_DIR="$HOME/.nvm"
15 | # have to unset "e" when sourcing nvm in macOS due to weird issue described
16 | # here: https://github.com/nvm-sh/nvm/issues/1985#issuecomment-456022013
17 | set +e
18 | [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
19 | set -e
20 |
21 | nvm install
22 | nvm use
23 |
24 | set -x
25 |
26 | env QUICKJS_EXTRAS=1 meta/build.sh
27 | npm test
28 |
--------------------------------------------------------------------------------
/meta/clean.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -euo pipefail
3 |
4 | git clean -dfX
5 |
--------------------------------------------------------------------------------
/meta/docker/aarch64-unknown-linux-musl/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM --platform=aarch64 alpine:3.17.2
2 |
3 | RUN apk add \
4 | sudo \
5 | git \
6 | bash \
7 | build-base \
8 | gcc \
9 | ninja \
10 | nodejs \
11 | npm
12 |
13 | ARG UID=1000
14 | ARG GID=1000
15 |
16 | # Make normal user
17 |
18 | # if group with that GID already exists, use it (macOS ubuntu image on at least
19 | # orbstack has group staff with gid 20, which matches a macOS user's group. Not
20 | # sure if Docker Desktop does that)
21 | RUN if ! getent group $GID; then addgroup -g $GID user; fi
22 |
23 | # group name could vary since group with that id might already exist
24 | RUN GROUP_NAME=$(getent group $GID | cut -d: -f1) && \
25 | adduser -s /bin/sh -u $UID -D -G $GROUP_NAME user
26 |
27 | # allow user to use sudo with no password prompt
28 | RUN echo "user ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
29 |
30 | USER user
31 |
32 | # The intent is that you volume-mount the repo into /opt/quickjs.
33 | WORKDIR /opt/quickjs
34 |
--------------------------------------------------------------------------------
/meta/docker/aarch64-unknown-linux-musl/cmd.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -euo pipefail
3 |
4 | mkdir -p build
5 |
6 | echo "----"
7 | echo "---- Building linux-musl (aarch64) ----"
8 | echo "----"
9 | env SKIP_NPM_INSTALL=1 BUILDDIR=build/aarch64-unknown-linux-musl HOST=linux TARGET=linux meta/build.sh
10 |
11 | echo "----"
12 | echo "---- Building linux-static (aarch64) ----"
13 | echo "----"
14 | env SKIP_NPM_INSTALL=1 BUILDDIR=build/aarch64-unknown-linux-static HOST=linux-static TARGET=linux-static meta/build.sh
15 |
--------------------------------------------------------------------------------
/meta/docker/build-all.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -ex
3 |
4 | # move to root dir
5 | cd $(dirname "$BASH_SOURCE")
6 | cd ../..
7 |
8 | meta/clean.sh
9 |
10 | NODE_VERSION=$(cat .node-version)
11 | docker run --rm -it -v $PWD:/workdir -w /workdir node:${NODE_VERSION/v/} \
12 | npm install
13 |
14 | # defines IMAGES
15 | source meta/docker/images.sh
16 |
17 | meta/docker/build-images.sh
18 |
19 | for DIR in "${IMAGES[@]}"; do
20 | docker run --rm \
21 | -v $PWD:/opt/quickjs \
22 | -e QUICKJS_EXTRAS=1 \
23 | "suchipi/quickjs-builder-${DIR}" \
24 | "/opt/quickjs/meta/docker/$DIR/cmd.sh"
25 | done
26 |
27 | cp -r build/*/dts build/
28 | rm -rf build/*/dts
29 |
--------------------------------------------------------------------------------
/meta/docker/build-images.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -ex
3 |
4 | # defines IMAGES
5 | source meta/docker/images.sh
6 |
7 | pushd meta/docker > /dev/null
8 | for DIR in "${IMAGES[@]}"; do
9 | pushd "$DIR" > /dev/null
10 | docker build \
11 | --build-arg UID=$(id -u) \
12 | --build-arg GID=$(id -g) \
13 | -t "suchipi/quickjs-builder-${DIR}" \
14 | .
15 | popd > /dev/null
16 | done
17 | popd > /dev/null
18 |
--------------------------------------------------------------------------------
/meta/docker/images.sh:
--------------------------------------------------------------------------------
1 | # feel free to comment stuff out
2 | IMAGES=(
3 | aarch64-unknown-linux-musl
4 | multi-apple-darwin
5 | multi-unknown-linux-gnu
6 | x86_64-pc-windows-static
7 | x86_64-unknown-linux-musl
8 | )
9 |
--------------------------------------------------------------------------------
/meta/docker/multi-apple-darwin/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:22-bullseye AS node
2 | FROM crazymax/osxcross:11.3-r7-ubuntu AS osxcross
3 |
4 | FROM ubuntu:20.04
5 |
6 | COPY --from=node /usr/local /usr/local
7 | COPY --from=osxcross /osxcross /osxcross
8 |
9 | RUN apt-get update && \
10 | apt-get install -y \
11 | sudo \
12 | build-essential \
13 | git \
14 | clang \
15 | lld \
16 | libc6-dev \
17 | ninja-build
18 |
19 | ARG UID=1000
20 | ARG GID=1000
21 |
22 | # Make normal user
23 |
24 | # if group with that GID already exists, use it (macOS ubuntu image on at least
25 | # orbstack has group staff with gid 20, which matches a macOS user's group. Not
26 | # sure if Docker Desktop does that)
27 | RUN if ! getent group $GID; then groupadd -g $GID user; fi
28 |
29 | RUN useradd -m -s /bin/bash -u $UID -g $GID user
30 |
31 | # allow user to use sudo with no password prompt
32 | RUN echo "user ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
33 |
34 | USER user
35 |
36 | ENV PATH="/osxcross/bin:$PATH"
37 | ENV LD_LIBRARY_PATH="/osxcross/lib:$LD_LIBRARY_PATH"
38 |
39 | # The intent is that you volume-mount the repo into /opt/quickjs.
40 | WORKDIR /opt/quickjs
41 |
--------------------------------------------------------------------------------
/meta/docker/multi-apple-darwin/cmd.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -euo pipefail
3 |
4 | mkdir -p build
5 |
6 | echo "----"
7 | echo "---- Building darwin (x86_64) ----"
8 | echo "----"
9 | env SKIP_NPM_INSTALL=1 BUILDDIR=build/x86_64-apple-darwin HOST=linux TARGET=cross-darwin-x86_64 meta/build.sh
10 |
11 | echo "----"
12 | echo "---- Building darwin (aarch64) ----"
13 | echo "----"
14 | env SKIP_NPM_INSTALL=1 BUILDDIR=build/aarch64-apple-darwin HOST=linux TARGET=cross-darwin-arm64 meta/build.sh
15 |
--------------------------------------------------------------------------------
/meta/docker/multi-unknown-linux-gnu/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:22-bullseye AS node
2 |
3 | FROM ubuntu:20.04
4 |
5 | COPY --from=node /usr/local /usr/local
6 |
7 | RUN apt-get update && \
8 | apt-get install -y \
9 | sudo \
10 | build-essential \
11 | git \
12 | ninja-build
13 |
14 | # Depending on the docker host arch, one of these will be available
15 | # and the other one won't be. So we use `|| true` to ignore failure
16 | # from the command.
17 | RUN apt-get install -y gcc-aarch64-linux-gnu || true
18 | RUN apt-get install -y gcc-x86-64-linux-gnu || true
19 |
20 | ARG UID=1000
21 | ARG GID=1000
22 |
23 | # Make normal user
24 |
25 | # if group with that GID already exists, use it (macOS ubuntu image on at least
26 | # orbstack has group staff with gid 20, which matches a macOS user's group. Not
27 | # sure if Docker Desktop does that)
28 | RUN if ! getent group $GID; then groupadd -g $GID user; fi
29 |
30 | RUN useradd -m -s /bin/bash -u $UID -g $GID user
31 |
32 | # allow user to use sudo with no password prompt
33 | RUN echo "user ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
34 |
35 | USER user
36 |
37 | # The intent is that you volume-mount the repo into /opt/quickjs.
38 | WORKDIR /opt/quickjs
39 |
--------------------------------------------------------------------------------
/meta/docker/multi-unknown-linux-gnu/cmd.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -euo pipefail
3 |
4 | /opt/quickjs/meta/docker/multi-unknown-linux-gnu/fixup-gcc-libs.sh
5 |
6 | mkdir -p build
7 |
8 | echo "----"
9 | echo "---- Building linux (x86_64) ----"
10 | echo "----"
11 | env SKIP_NPM_INSTALL=1 BUILDDIR=build/x86_64-unknown-linux-gnu HOST=linux TARGET=cross-linux-x86_64 meta/build.sh
12 |
13 | echo "----"
14 | echo "---- Building linux (aarch64) ----"
15 | echo "----"
16 | env SKIP_NPM_INSTALL=1 BUILDDIR=build/aarch64-unknown-linux-gnu HOST=linux TARGET=cross-linux-aarch64 meta/build.sh
17 |
--------------------------------------------------------------------------------
/meta/docker/multi-unknown-linux-gnu/fixup-gcc-libs.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | if [[ -e /usr/x86_64-linux-gnu/lib ]]; then
4 | # gcc x86_64 cross-compiler insists on looking for libs in the wrong place...
5 | sudo mkdir -p /usr/lib/x86_64-linux-gnu
6 | for FILE in /usr/x86_64-linux-gnu/lib/*; do
7 | sudo cp $FILE /usr/lib/x86_64-linux-gnu
8 | done
9 | fi
10 |
--------------------------------------------------------------------------------
/meta/docker/x86_64-pc-windows-static/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:22-bullseye AS node
2 |
3 | FROM ubuntu:20.04
4 |
5 | COPY --from=node /usr/local /usr/local
6 |
7 | RUN apt-get update && \
8 | apt-get install -y \
9 | sudo \
10 | build-essential \
11 | git \
12 | gcc-mingw-w64-x86-64 \
13 | ninja-build
14 |
15 | ARG UID=1000
16 | ARG GID=1000
17 |
18 | # Make normal user
19 |
20 | # if group with that GID already exists, use it (macOS ubuntu image on at least
21 | # orbstack has group staff with gid 20, which matches a macOS user's group. Not
22 | # sure if Docker Desktop does that)
23 | RUN if ! getent group $GID; then groupadd -g $GID user; fi
24 |
25 | RUN useradd -m -s /bin/bash -u $UID -g $GID user
26 |
27 | # allow user to use sudo with no password prompt
28 | RUN echo "user ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
29 |
30 | USER user
31 |
32 | # The intent is that you volume-mount the repo into /opt/quickjs.
33 | WORKDIR /opt/quickjs
34 |
--------------------------------------------------------------------------------
/meta/docker/x86_64-pc-windows-static/cmd.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -euo pipefail
3 |
4 | mkdir -p build
5 |
6 | echo "----"
7 | echo "---- Building windows MinGW (x86_64) ----"
8 | echo "----"
9 | env SKIP_NPM_INSTALL=1 BUILDDIR=build/x86_64-pc-windows-static HOST=linux TARGET=cross-windows-x86_64 meta/build.sh
10 |
--------------------------------------------------------------------------------
/meta/docker/x86_64-unknown-linux-musl/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM --platform=x86_64 alpine:3.17.2
2 |
3 | RUN apk add \
4 | sudo \
5 | git \
6 | bash \
7 | build-base \
8 | gcc \
9 | ninja \
10 | nodejs \
11 | npm
12 |
13 | ARG UID=1000
14 | ARG GID=1000
15 |
16 | # Make normal user
17 |
18 | # if group with that GID already exists, use it (macOS ubuntu image on at least
19 | # orbstack has group staff with gid 20, which matches a macOS user's group. Not
20 | # sure if Docker Desktop does that)
21 | RUN if ! getent group $GID; then addgroup -g $GID user; fi
22 |
23 | # group name could vary since group with that id might already exist
24 | RUN GROUP_NAME=$(getent group $GID | cut -d: -f1) && \
25 | adduser -s /bin/sh -u $UID -D -G $GROUP_NAME user
26 |
27 | # allow user to use sudo with no password prompt
28 | RUN echo "user ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
29 |
30 | USER user
31 |
32 | # The intent is that you volume-mount the repo into /opt/quickjs.
33 | WORKDIR /opt/quickjs
34 |
--------------------------------------------------------------------------------
/meta/docker/x86_64-unknown-linux-musl/cmd.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -euo pipefail
3 |
4 | mkdir -p build
5 |
6 | echo "----"
7 | echo "---- Building linux-musl (x86_64) ----"
8 | echo "----"
9 | env SKIP_NPM_INSTALL=1 BUILDDIR=build/x86_64-unknown-linux-musl HOST=linux TARGET=linux meta/build.sh
10 |
11 | echo "----"
12 | echo "---- Building linux-static (x86_64) ----"
13 | echo "----"
14 | env SKIP_NPM_INSTALL=1 BUILDDIR=build/x86_64-unknown-linux-static HOST=linux-static TARGET=linux-static meta/build.sh
15 |
--------------------------------------------------------------------------------
/meta/ninja/defs.ninja.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 |
3 | // PROGRAM_SUFFIX is ".exe" on windows or ".com" when targeting Cosmopolitan Libc
4 | if (getVar("PROGRAM_SUFFIX") == null) {
5 | declare("PROGRAM_SUFFIX", "");
6 | }
7 |
8 | const headerFiles = glob("**/*.h", {
9 | cwd: path.resolve(__dirname, "../.."),
10 | });
11 |
12 | const dirsWithHeaderFiles = Array.from(
13 | new Set(headerFiles.map((file) => path.dirname(file)))
14 | );
15 |
16 | for (const suffix of ["HOST", "TARGET"]) {
17 | // Standard safe compiler optimizations
18 | declareOrAppend(`CFLAGS_${suffix}`, "-O2");
19 |
20 | // Show all warnings.
21 | declareOrAppend(`CFLAGS_${suffix}`, "-Wall");
22 |
23 | // Add include for build dir intermediates (for generated headers)
24 | declareOrAppend(`CFLAGS_${suffix}`, "-I" + builddir("intermediate"));
25 |
26 | // Add includes for all dirs with header files
27 | declareOrAppend(
28 | `CFLAGS_${suffix}`,
29 | dirsWithHeaderFiles.map((dir) => "-I" + dir).join(" ")
30 | );
31 |
32 | // Include source debugging info in the binaries
33 | declareOrAppend(`LDFLAGS_${suffix}`, "-g");
34 |
35 | // math functions and constants.
36 | declareOrAppend(`LIBS_${suffix}`, "-lm");
37 |
38 | // multithreading.
39 | declareOrAppend(`LIBS_${suffix}`, "-lpthread");
40 |
41 | declareOrAppend(
42 | `DEFINES_${suffix}`,
43 | '-DCONFIG_VERSION="\\"suchipi-`git rev-parse --short HEAD`\\""'
44 | );
45 |
46 | // Uncomment to print debug info
47 | // declareOrAppend(`DEFINES_${suffix}`, "-DDEBUG");
48 |
49 | // enable 'use math' and BigFloat
50 | declareOrAppend(`DEFINES_${suffix}`, "-DCONFIG_BIGNUM");
51 |
52 | if (getVar(`LTO_${suffix}`) === "y") {
53 | declareOrAppend(`DEFINES_${suffix}`, "-DCONFIG_LTO");
54 | declareOrAppend(`LDFLAGS_${suffix}`, "-flto");
55 | }
56 |
57 | // qjsc searchdir for quickjs.h
58 | declareOrAppend(`DEFINES_${suffix}`, '-DCONFIG_PREFIX="\\"/usr/local\\""');
59 |
60 | // include full unicode tables
61 | declareOrAppend(`DEFINES_${suffix}`, "-DCONFIG_ALL_UNICODE");
62 |
63 | // always treat chars as unsigned for consistent bytecode
64 | declareOrAppend(`CFLAGS_${suffix}`, "-funsigned-char");
65 |
66 | if (getVar(`LDEXPORT_${suffix}`)?.match(/-rdynamic/)) {
67 | // Enable importing *.so library modules from JS code.
68 | declareOrAppend(`DEFINES_${suffix}`, "-DCONFIG_SHARED_LIBRARY_MODULES");
69 | // the dynamic linker API
70 | declareOrAppend(`LIBS_${suffix}`, "-ldl");
71 | // Position-independent code; needed for shared libraries.
72 | declareOrAppend(`CFLAGS_${suffix}`, "-fPIC");
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/meta/ninja/envs/host/darwin.ninja.js:
--------------------------------------------------------------------------------
1 | // Host: darwin (arch may vary)
2 |
3 | declare("CC_HOST", "clang");
4 | declare("AR_HOST", "ar");
5 | declare("LDEXPORT_HOST", "-rdynamic");
6 | declare("SHARED_LIBRARY_FLAGS_HOST", "-undefined dynamic_lookup");
7 |
--------------------------------------------------------------------------------
/meta/ninja/envs/host/freebsd.ninja.js:
--------------------------------------------------------------------------------
1 | // Host: freebsd (arch may vary)
2 |
3 | declare("CC_HOST", "cc");
4 | declare("AR_HOST", "ar");
5 | declare("LDEXPORT_HOST", "-rdynamic");
6 |
--------------------------------------------------------------------------------
/meta/ninja/envs/host/linux-static.ninja.js:
--------------------------------------------------------------------------------
1 | // Host: linux (arch may vary)
2 |
3 | declare("LTO_HOST", "y");
4 | declare("CC_HOST", "gcc");
5 | declare("AR_HOST", "gcc-ar");
6 | declare("DEFINES_HOST", "-D_GNU_SOURCE");
7 | declare("LDEXPORT_HOST", "-static");
8 |
--------------------------------------------------------------------------------
/meta/ninja/envs/host/linux.ninja.js:
--------------------------------------------------------------------------------
1 | // Host: linux (arch may vary)
2 |
3 | declare("LTO_HOST", "y");
4 | declare("CC_HOST", "gcc");
5 | declare("AR_HOST", "gcc-ar");
6 | declare("DEFINES_HOST", "-D_GNU_SOURCE");
7 | declare("LDEXPORT_HOST", "-rdynamic");
8 |
--------------------------------------------------------------------------------
/meta/ninja/envs/host/other.ninja.js:
--------------------------------------------------------------------------------
1 | // Host: anything (configure stuff yourself via env vars)
2 |
3 | declare("LTO_HOST", env.LTO_HOST || env.LTO || "n");
4 | declare("CC_HOST", env.CC_HOST || env.CC || "cc");
5 | declare("AR_HOST", env.AR_HOST || env.AR || "ar");
6 | declare("DEFINES_HOST", env.DEFINES_HOST || env.DEFINES || "");
7 | declare("CFLAGS_HOST", env.CFLAGS_HOST || env.CFLAGS || "");
8 | declare("LDFLAGS_HOST", env.LDFLAGS_HOST || env.LDFLAGS || "");
9 | declare("LDEXPORT_HOST", env.LDEXPORT_HOST || env.LDEXPORT || "-rdynamic");
10 |
11 | console.error("LTO_HOST", getVar("LTO_HOST"));
12 | console.error("CC_HOST", getVar("CC_HOST"));
13 | console.error("AR_HOST", getVar("AR_HOST"));
14 | console.error("DEFINES_HOST", getVar("DEFINES_HOST"));
15 | console.error("CFLAGS_HOST", getVar("CFLAGS_HOST"));
16 | console.error("LDFLAGS_HOST", getVar("LDFLAGS_HOST"));
17 | console.error("LDEXPORT_HOST", getVar("LDEXPORT_HOST"));
18 |
--------------------------------------------------------------------------------
/meta/ninja/envs/target/cosmo.ninja.js:
--------------------------------------------------------------------------------
1 | // Target: Cosmopolitan Libc (https://github.com/jart/cosmopolitan)
2 |
3 | declare("CC_TARGET", "unknown-unknown-cosmo-cc");
4 | declare("AR_TARGET", "unknown-unknown-cosmo-ar");
5 | declare("DEFINES_TARGET", "-D_GNU_SOURCE -D__COSMO__");
6 | declare("LDEXPORT_TARGET", "-rdynamic");
7 |
8 | declare("PROGRAM_SUFFIX", ".com");
9 |
--------------------------------------------------------------------------------
/meta/ninja/envs/target/cross-darwin-arm64.ninja.js:
--------------------------------------------------------------------------------
1 | // Target: arm64 darwin
2 |
3 | declare("CC_TARGET", "arm64-apple-darwin20.4-clang");
4 | declare("AR_TARGET", "arm64-apple-darwin20.4-ar");
5 | declare("LDEXPORT_TARGET", "-rdynamic");
6 | declare("SHARED_LIBRARY_FLAGS_TARGET", "-undefined dynamic_lookup");
7 |
--------------------------------------------------------------------------------
/meta/ninja/envs/target/cross-darwin-x86_64.ninja.js:
--------------------------------------------------------------------------------
1 | // Target: x86_64 darwin
2 |
3 | declare("CC_TARGET", "x86_64-apple-darwin20.4-clang");
4 | declare("AR_TARGET", "x86_64-apple-darwin20.4-ar");
5 | declare("LDEXPORT_TARGET", "-rdynamic");
6 | declare("SHARED_LIBRARY_FLAGS_TARGET", "-undefined dynamic_lookup");
7 |
--------------------------------------------------------------------------------
/meta/ninja/envs/target/cross-linux-aarch64.ninja.js:
--------------------------------------------------------------------------------
1 | // Target: aarch64 (arm 64-bit) linux
2 | if (process.arch === "arm64") {
3 | // If target arch is the same as host arch, compile natively instead
4 | // of using cross-compiler.
5 | require("./linux.ninja");
6 | } else {
7 | declare("LTO_TARGET", "y");
8 | declare("CC_TARGET", "aarch64-linux-gnu-gcc");
9 | declare("AR_TARGET", "aarch64-linux-gnu-gcc-ar");
10 | declare("DEFINES_TARGET", "-D_GNU_SOURCE");
11 | declare("LDEXPORT_TARGET", "-rdynamic");
12 | }
13 |
--------------------------------------------------------------------------------
/meta/ninja/envs/target/cross-linux-x86_64.ninja.js:
--------------------------------------------------------------------------------
1 | // Target: x86_64 linux
2 | if (process.arch === "x64") {
3 | // If target arch is the same as host arch, compile natively instead
4 | // of using cross-compiler.
5 | require("./linux.ninja");
6 | } else {
7 | declare("LTO_TARGET", "y");
8 | declare("CC_TARGET", "x86_64-linux-gnu-gcc");
9 | declare("AR_TARGET", "x86_64-linux-gnu-gcc-ar");
10 | declare("DEFINES_TARGET", "-D_GNU_SOURCE");
11 | declare("LDEXPORT_TARGET", "-rdynamic");
12 | declare("LDFLAGS_TARGET", "-L/usr/x86_64-linux-gnu/lib/");
13 | }
14 |
--------------------------------------------------------------------------------
/meta/ninja/envs/target/cross-windows-x86_64.ninja.js:
--------------------------------------------------------------------------------
1 | // Target: x86_64 windows
2 | declare("LTO_TARGET", "y");
3 | declare("CC_TARGET", "x86_64-w64-mingw32-gcc");
4 | declare("AR_TARGET", "x86_64-w64-mingw32-gcc-ar");
5 | declare("DEFINES_TARGET", [
6 | "-D_WIN32",
7 | // to get environ
8 | "-D_GNU_SOURCE",
9 | // to get standard snprintf behavior
10 | "-D__USE_MINGW_ANSI_STDIO",
11 | // to get localtime_r
12 | "-D_POSIX_THREAD_SAFE_FUNCTIONS",
13 | ]);
14 | // statically-linked so that it doesn't depend on libwinpthread-1.dll
15 | declare("LDEXPORT_TARGET", "-static");
16 |
17 | declare("PROGRAM_SUFFIX", ".exe");
18 |
19 | declare("SKIP_SHARED_LIBS", true);
20 |
--------------------------------------------------------------------------------
/meta/ninja/envs/target/darwin.ninja.js:
--------------------------------------------------------------------------------
1 | // Target: darwin (same arch as host)
2 |
3 | declare("CC_TARGET", "clang");
4 | declare("AR_TARGET", "ar");
5 | declare("LDEXPORT_TARGET", "-rdynamic");
6 | declare("SHARED_LIBRARY_FLAGS_TARGET", "-undefined dynamic_lookup");
7 |
--------------------------------------------------------------------------------
/meta/ninja/envs/target/freebsd.ninja.js:
--------------------------------------------------------------------------------
1 | // Target: freebsd (arch may vary)
2 |
3 | declare("CC_TARGET", "cc");
4 | declare("AR_TARGET", "ar");
5 | declare("LDEXPORT_TARGET", "-rdynamic");
6 |
--------------------------------------------------------------------------------
/meta/ninja/envs/target/linux-static.ninja.js:
--------------------------------------------------------------------------------
1 | // Target: linux (same arch as host)
2 |
3 | declare("LTO_TARGET", "y");
4 | declare("CC_TARGET", "gcc");
5 | declare("AR_TARGET", "gcc-ar");
6 | declare("DEFINES_TARGET", "-D_GNU_SOURCE");
7 | declare("LDEXPORT_TARGET", "-static");
8 |
--------------------------------------------------------------------------------
/meta/ninja/envs/target/linux.ninja.js:
--------------------------------------------------------------------------------
1 | // Target: linux (same arch as host)
2 |
3 | declare("LTO_TARGET", "y");
4 | declare("CC_TARGET", "gcc");
5 | declare("AR_TARGET", "gcc-ar");
6 | declare("DEFINES_TARGET", "-D_GNU_SOURCE");
7 | declare("LDEXPORT_TARGET", "-rdynamic");
8 |
--------------------------------------------------------------------------------
/meta/ninja/envs/target/other.ninja.js:
--------------------------------------------------------------------------------
1 | // Target: anything (configure stuff yourself via env vars)
2 |
3 | declare("LTO_TARGET", env.LTO_TARGET || env.LTO || "n");
4 | declare("CC_TARGET", env.CC_TARGET || env.CC || "cc");
5 | declare("AR_TARGET", env.AR_TARGET || env.AR || "ar");
6 | declare("DEFINES_TARGET", env.DEFINES_TARGET || env.DEFINES || "");
7 | declare("CFLAGS_TARGET", env.CFLAGS_TARGET || env.CFLAGS || "");
8 | declare("LDFLAGS_TARGET", env.LDFLAGS_TARGET || env.LDFLAGS || "");
9 | declare("LDEXPORT_TARGET", env.LDEXPORT_TARGET || env.LDEXPORT || "-rdynamic");
10 |
11 | console.error("LTO_TARGET", getVar("LTO_TARGET"));
12 | console.error("CC_TARGET", getVar("CC_TARGET"));
13 | console.error("AR_TARGET", getVar("AR_TARGET"));
14 | console.error("DEFINES_TARGET", getVar("DEFINES_TARGET"));
15 | console.error("CFLAGS_TARGET", getVar("CFLAGS_TARGET"));
16 | console.error("LDFLAGS_TARGET", getVar("LDFLAGS_TARGET"));
17 | console.error("LDEXPORT_TARGET", getVar("LDEXPORT_TARGET"));
18 |
--------------------------------------------------------------------------------
/meta/ninja/generate.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | const { HOST, TARGET, QUICKJS_SHINOBI_EXTRA_FILES } = process.env;
4 |
5 | if (!HOST) {
6 | throw new Error("You must define the HOST env var");
7 | }
8 |
9 | if (!TARGET) {
10 | throw new Error("You must define the TARGET env var");
11 | }
12 |
13 | const { Shinobi } = require("@suchipi/shinobi");
14 |
15 | const files = [
16 | `meta/ninja/envs/host/${HOST}.ninja.js`,
17 | `meta/ninja/envs/target/${TARGET}.ninja.js`,
18 | "meta/ninja/defs.ninja.js",
19 | "meta/ninja/rules.ninja.js",
20 | ];
21 |
22 | if (QUICKJS_SHINOBI_EXTRA_FILES) {
23 | const parsed = JSON.parse(QUICKJS_SHINOBI_EXTRA_FILES);
24 | if (!Array.isArray(parsed)) {
25 | throw new Error(
26 | "When present, env var QUICKJS_SHINOBI_EXTRA_FILES must be a JSON array of strings"
27 | );
28 | }
29 | files.push(...parsed);
30 | }
31 |
32 | files.push(...process.argv.slice(2));
33 |
34 | const shinobi = new Shinobi();
35 |
36 | for (const file of files) {
37 | shinobi.load(file);
38 | }
39 |
40 | const result = shinobi.render();
41 |
42 | const fs = require("fs");
43 |
44 | fs.writeFileSync("build.ninja", result);
45 |
--------------------------------------------------------------------------------
/meta/ninja/rules.ninja.js:
--------------------------------------------------------------------------------
1 | // compiles one or more .c files into one .o file.
2 | // takes 1..n inputs, has one output (the .o file).
3 | rule("cc_host", {
4 | command: `$CC_HOST $DEFINES_HOST $CFLAGS_HOST $cc_args -c $in -o $out`,
5 | description: "CC_HOST $out",
6 | });
7 | rule("cc_target", {
8 | command: `$CC_TARGET $DEFINES_TARGET $CFLAGS_TARGET $cc_args -c $in -o $out`,
9 | description: "CC_TARGET $out",
10 | });
11 |
12 | // compiles one or more .c files into one .so file.
13 | // takes 1..n inputs, has one output (the .so file).
14 | if (getVar("SKIP_SHARED_LIBS")) {
15 | rule("shared_lib_host", {
16 | command: `echo Skipping shared library build: $in -> $out`,
17 | description: "SHARED_LIB_HOST $out",
18 | });
19 | rule("shared_lib_target", {
20 | command: `echo Skipping shared library build: $in -> $out`,
21 | description: "SHARED_LIB_TARGET $out",
22 | });
23 | } else {
24 | rule("shared_lib_host", {
25 | command: `$CC_HOST $DEFINES_HOST $CFLAGS_HOST -shared $SHARED_LIBRARY_FLAGS_HOST $in -o $out`,
26 | description: "SHARED_LIB_HOST $out",
27 | });
28 | rule("shared_lib_target", {
29 | command: `$CC_TARGET $DEFINES_TARGET $CFLAGS_TARGET -shared $SHARED_LIBRARY_FLAGS_TARGET $in -o $out`,
30 | description: "SHARED_LIB_TARGET $out",
31 | });
32 | }
33 |
34 | // compiles one or more .o files into one executable file.
35 | // takes 1..n inputs, has one output (the program file).
36 | rule("link_host", {
37 | command: `$CC_HOST $LDFLAGS_HOST $LDEXPORT_HOST $in -o $out $LIBS_HOST`,
38 | description: "LINK_HOST $out",
39 | });
40 | rule("link_target", {
41 | command: `$CC_TARGET $LDFLAGS_TARGET $LDEXPORT_TARGET $in -o $out $LIBS_TARGET`,
42 | description: "LINK_TARGET $out",
43 | });
44 |
45 | // compiles one or more .o files into an .a file.
46 | // takes 1..n inputs, has one output (the .a file).
47 | rule("ar_host", {
48 | command: `$AR_HOST -rcs $out $in`,
49 | description: "AR_HOST $out",
50 | });
51 | rule("ar_target", {
52 | command: `$AR_TARGET -rcs $out $in`,
53 | description: "AR_TARGET $out",
54 | });
55 |
56 | // uses the user-facing "full" qjsc
57 | rule("qjsc", {
58 | command: [builddir("intermediate/qjsc.host"), `$qjsc_args -o $out $in`],
59 | description: "QJSC $out",
60 | implicitInputs: [builddir("intermediate/qjsc.host")],
61 | });
62 |
63 | // this one only has std and os. used to compile the bytecode for other things
64 | rule("qjsc-minimal", {
65 | command: [
66 | builddir("intermediate/qjsc-minimal.host"),
67 | `$qjsc_args -o $out $in`,
68 | ],
69 | description: "QJSC $out",
70 | implicitInputs: [builddir("intermediate/qjsc-minimal.host")],
71 | });
72 |
73 | // copy a file from one place to another
74 | rule("copy", {
75 | command: `cp $in $out`,
76 | description: "COPY $out",
77 | });
78 |
79 | // Append files together
80 | rule("combine", {
81 | command: "cat $in > $out",
82 | description: "COMBINE $out",
83 | });
84 |
85 | // Append files together into something and then mark it as executable
86 | rule("combine_into_executable", {
87 | command: "cat $in > $out && chmod +x $out",
88 | description: "COMBINE $out",
89 | });
90 |
--------------------------------------------------------------------------------
/meta/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": ["../node_modules/@suchipi/shinobi/globals.d.ts"],
3 | "include": ["./**/*.ninja.js"],
4 | "compilerOptions": {
5 | "allowJs": true,
6 | "checkJs": true,
7 | "noEmit": true,
8 | "lib": ["ES2020"],
9 | "strictNullChecks": true,
10 | "types": ["node"],
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/npm/cli/qjs.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | const runBinary = require("../run-binary");
3 |
4 | runBinary("qjs");
5 |
--------------------------------------------------------------------------------
/npm/cli/qjsc.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | const runBinary = require("../run-binary");
3 |
4 | runBinary("qjsc");
5 |
--------------------------------------------------------------------------------
/npm/cli/quickjs-run.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | const runBinary = require("../run-binary");
3 |
4 | runBinary("quickjs-run");
5 |
--------------------------------------------------------------------------------
/npm/index.js:
--------------------------------------------------------------------------------
1 | const fs = require("fs");
2 | const path = require("path");
3 |
4 | function buildArtifactsLocation() {
5 | return path.resolve(__dirname, "..", "build");
6 | }
7 | exports.buildArtifactsLocation = buildArtifactsLocation;
8 |
9 | const platforms = require("./platforms.json");
10 | exports.platforms = platforms;
11 |
12 | function identifyCurrentPlatform() {
13 | const matchingPlatform = platforms.find((platform) => {
14 | return (
15 | platform.os === process.platform &&
16 | platform.architectures.includes(process.arch)
17 | );
18 | });
19 |
20 | if (matchingPlatform) {
21 | return matchingPlatform;
22 | } else {
23 | throw new Error(
24 | `Unsupported platform: ${process.platform}/${process.arch}`
25 | );
26 | }
27 | }
28 | exports.identifyCurrentPlatform = identifyCurrentPlatform;
29 |
--------------------------------------------------------------------------------
/npm/platforms.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name": "aarch64-apple-darwin",
4 | "architectures": ["aarch64", "arm64"],
5 | "os": "darwin",
6 | "abi": "",
7 | "programSuffix": ""
8 | },
9 | {
10 | "name": "aarch64-unknown-linux-static",
11 | "architectures": ["aarch64", "arm64"],
12 | "os": "linux",
13 | "abi": "static",
14 | "programSuffix": ""
15 | },
16 | {
17 | "name": "aarch64-unknown-linux-gnu",
18 | "architectures": ["aarch64", "arm64"],
19 | "os": "linux",
20 | "abi": "gnu",
21 | "programSuffix": ""
22 | },
23 | {
24 | "name": "aarch64-unknown-linux-musl",
25 | "architectures": ["aarch64", "arm64"],
26 | "os": "linux",
27 | "abi": "musl",
28 | "programSuffix": ""
29 | },
30 | {
31 | "name": "x86_64-apple-darwin",
32 | "architectures": ["x86_64", "amd64", "x64"],
33 | "os": "darwin",
34 | "abi": "",
35 | "programSuffix": ""
36 | },
37 | {
38 | "name": "x86_64-pc-windows-static",
39 | "architectures": ["x86_64", "amd64", "x64"],
40 | "os": "win32",
41 | "abi": "",
42 | "programSuffix": ".exe"
43 | },
44 | {
45 | "name": "x86_64-unknown-linux-static",
46 | "architectures": ["x86_64", "amd64", "x64"],
47 | "os": "linux",
48 | "abi": "static",
49 | "programSuffix": ""
50 | },
51 | {
52 | "name": "x86_64-unknown-linux-gnu",
53 | "architectures": ["x86_64", "amd64", "x64"],
54 | "os": "linux",
55 | "abi": "gnu",
56 | "programSuffix": ""
57 | },
58 | {
59 | "name": "x86_64-unknown-linux-musl",
60 | "architectures": ["x86_64", "amd64", "x64"],
61 | "os": "linux",
62 | "abi": "musl",
63 | "programSuffix": ""
64 | }
65 | ]
66 |
--------------------------------------------------------------------------------
/npm/run-binary.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | const path = require("path");
3 | const child_process = require("child_process");
4 | const { identifyCurrentPlatform, buildArtifactsLocation } = require(".");
5 |
6 | module.exports = function runBinary(name) {
7 | const buildDir = buildArtifactsLocation();
8 | const platform = identifyCurrentPlatform();
9 |
10 | const bin = path.resolve(
11 | buildDir,
12 | platform.name,
13 | "bin",
14 | name + platform.programSuffix
15 | );
16 |
17 | const argv = process.argv.slice(2);
18 |
19 | child_process.spawn(bin, argv, { stdio: "inherit" }).on("exit", process.exit);
20 | };
21 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@suchipi/quickjs",
3 | "description": "Suchipi's QuickJS Fork",
4 | "version": "0.10.2",
5 | "main": "./npm/index.js",
6 | "bin": {
7 | "qjs": "npm/cli/qjs.js",
8 | "qjsc": "npm/cli/qjsc.js",
9 | "quickjs-run": "npm/cli/quickjs-run.js"
10 | },
11 | "devDependencies": {
12 | "@babel/core": "^7.26.10",
13 | "@babel/preset-env": "^7.26.9",
14 | "@babel/preset-typescript": "^7.27.0",
15 | "@suchipi/print": "^2.5.0",
16 | "@suchipi/shinobi": "^1.0.1",
17 | "@types/jest": "^29.5.14",
18 | "@types/node": "^22.15.2",
19 | "@types/shelljs": "^0.8.15",
20 | "babel-jest": "^29.7.0",
21 | "first-base": "^1.5.2",
22 | "jest": "^29.7.0",
23 | "path-less-traveled": "^2.1.0",
24 | "prettier": "^2.8.1",
25 | "shelljs": "^0.9.2"
26 | },
27 | "scripts": {
28 | "test": "jest --runInBand"
29 | },
30 | "prettier": {},
31 | "license": "MIT",
32 | "repository": {
33 | "type": "git",
34 | "url": "git+https://github.com/suchipi/quickjs.git"
35 | },
36 | "publishConfig": {
37 | "access": "public"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/archives/core.ninja.js:
--------------------------------------------------------------------------------
1 | const deps_host = [
2 | builddir("intermediate/cutils.host.o"),
3 | builddir("intermediate/libbf.host.o"),
4 | builddir("intermediate/libregexp.host.o"),
5 | builddir("intermediate/libunicode.host.o"),
6 | builddir("intermediate/quickjs.host.o"),
7 | builddir("intermediate/execpath.host.o"),
8 | builddir("intermediate/quickjs-utils.host.o"),
9 | builddir("intermediate/quickjs-modulesys.host.o"),
10 | builddir("intermediate/quickjs-print.host.o"),
11 | builddir("intermediate/quickjs-inspect.host.o"),
12 | builddir("intermediate/quickjs-intervals.host.o"),
13 | ];
14 |
15 | const deps_target = [
16 | builddir("intermediate/cutils.target.o"),
17 | builddir("intermediate/libbf.target.o"),
18 | builddir("intermediate/libregexp.target.o"),
19 | builddir("intermediate/libunicode.target.o"),
20 | builddir("intermediate/quickjs.target.o"),
21 | builddir("intermediate/execpath.target.o"),
22 | builddir("intermediate/quickjs-utils.target.o"),
23 | builddir("intermediate/quickjs-modulesys.target.o"),
24 | builddir("intermediate/quickjs-print.target.o"),
25 | builddir("intermediate/quickjs-inspect.target.o"),
26 | builddir("intermediate/quickjs-intervals.target.o"),
27 | ];
28 |
29 | const core_host = build({
30 | output: builddir("intermediate/quickjs-core.host.a"),
31 | rule: "ar_host",
32 | inputs: [
33 | ...deps_host,
34 | builddir("intermediate/quickjs-modulesys/module-impl-stub.host.o"),
35 | ],
36 | });
37 |
38 | const core_target = build({
39 | output: builddir("intermediate/quickjs-core.target.a"),
40 | rule: "ar_target",
41 | inputs: [
42 | ...deps_target,
43 | builddir("intermediate/quickjs-modulesys/module-impl-stub.target.o"),
44 | ],
45 | });
46 |
47 | const quickjs_core_a = build({
48 | output: builddir("lib/quickjs-core.a"),
49 | rule: "copy",
50 | inputs: [core_target],
51 | });
52 |
53 | module.exports = { deps_host, deps_target };
54 |
--------------------------------------------------------------------------------
/src/archives/full.ninja.js:
--------------------------------------------------------------------------------
1 | const core = require("./core.ninja");
2 |
3 | const quickjs_full_init_target_o = build({
4 | output: builddir("intermediate/quickjs-full-init.target.o"),
5 | rule: "cc_target",
6 | inputs: [rel("quickjs-full-init.c")],
7 | });
8 |
9 | const quickjs_full_init_host_o = build({
10 | output: builddir("intermediate/quickjs-full-init.host.o"),
11 | rule: "cc_host",
12 | inputs: [rel("quickjs-full-init.c")],
13 | });
14 |
15 | const deps_target = [
16 | ...core.deps_target,
17 | quickjs_full_init_target_o,
18 | builddir("intermediate/inspect.target.o"),
19 | builddir("intermediate/intervals.target.o"),
20 | builddir("intermediate/quickjs-libc.target.o"),
21 | builddir("intermediate/quickjs-context.target.o"),
22 | builddir("intermediate/quickjs-bytecode.target.o"),
23 | builddir("intermediate/quickjs-pointer.target.o"),
24 | builddir("intermediate/quickjs-engine.target.o"),
25 | builddir("intermediate/quickjs-encoding.target.o"),
26 | builddir("intermediate/quickjs-modulesys/module-impl.target.o"),
27 | ];
28 |
29 | const full_target = build({
30 | output: builddir("intermediate/quickjs-full.target.a"),
31 | rule: "ar_target",
32 | inputs: deps_target,
33 | });
34 |
35 | const deps_host = [
36 | ...core.deps_host,
37 | quickjs_full_init_host_o,
38 | builddir("intermediate/inspect.host.o"),
39 | builddir("intermediate/intervals.host.o"),
40 | builddir("intermediate/quickjs-libc.host.o"),
41 | builddir("intermediate/quickjs-context.host.o"),
42 | builddir("intermediate/quickjs-bytecode.host.o"),
43 | builddir("intermediate/quickjs-pointer.host.o"),
44 | builddir("intermediate/quickjs-engine.host.o"),
45 | builddir("intermediate/quickjs-encoding.host.o"),
46 | builddir("intermediate/quickjs-modulesys/module-impl.host.o"),
47 | ];
48 |
49 | const full_host = build({
50 | output: builddir("intermediate/quickjs-full.host.a"),
51 | rule: "ar_host",
52 | inputs: deps_host,
53 | });
54 |
55 | const quickjs_full_a = build({
56 | output: builddir("lib/quickjs-full.a"),
57 | rule: "copy",
58 | inputs: [full_target],
59 | });
60 |
61 | build({
62 | output: builddir("include/quickjs-full-init.h"),
63 | rule: "copy",
64 | inputs: [rel("quickjs-full-init.h")],
65 | });
66 |
--------------------------------------------------------------------------------
/src/archives/quickjs-full-init.c:
--------------------------------------------------------------------------------
1 | #include "quickjs-full-init.h"
2 |
3 | /* returns 0 on success, nonzero on failure */
4 | static int quickjs_full_init_modules(JSContext *ctx)
5 | {
6 | if (js_init_module_std(ctx, "quickjs:std") == NULL) {
7 | return -1;
8 | }
9 | if (js_init_module_os(ctx, "quickjs:os") == NULL) {
10 | return -1;
11 | }
12 | if (js_init_module_bytecode(ctx, "quickjs:bytecode") == NULL) {
13 | return -1;
14 | }
15 | if (js_init_module_context(ctx, "quickjs:context") == NULL) {
16 | return -1;
17 | }
18 | if (js_init_module_pointer(ctx, "quickjs:pointer") == NULL) {
19 | return -1;
20 | }
21 | if (js_init_module_engine(ctx, "quickjs:engine") == NULL) {
22 | return -1;
23 | }
24 | if (js_init_module_encoding(ctx, "quickjs:encoding") == NULL) {
25 | return -1;
26 | }
27 |
28 | return 0;
29 | }
30 |
31 | static int quickjs_full_init_globals(JSContext *ctx)
32 | {
33 | if (js_inspect_add_inspect_global(ctx)) {
34 | return -1;
35 | }
36 |
37 | js_print_add_print_global(ctx);
38 | js_print_add_console_global(ctx);
39 |
40 | if (js_intervals_add_setInterval_clearInterval_globals(ctx)) {
41 | return -1;
42 | }
43 |
44 | return 0;
45 | }
46 |
47 | int quickjs_full_init(JSContext *ctx)
48 | {
49 | if (quickjs_full_init_modules(ctx)) {
50 | return -1;
51 | }
52 | if (quickjs_full_init_globals(ctx)) {
53 | return -1;
54 | }
55 |
56 | return 0;
57 | }
58 |
--------------------------------------------------------------------------------
/src/archives/quickjs-full-init.h:
--------------------------------------------------------------------------------
1 | #ifndef QUICKJS_FULL_INIT_H
2 | #define QUICKJS_FULL_INIT_H
3 |
4 | #include "quickjs.h"
5 | #include "quickjs-print.h"
6 | #include "quickjs-inspect.h"
7 | #include "quickjs-intervals.h"
8 | #include "quickjs-libc.h"
9 | #include "quickjs-bytecode.h"
10 | #include "quickjs-context.h"
11 | #include "quickjs-pointer.h"
12 | #include "quickjs-modulesys.h"
13 | #include "quickjs-engine.h"
14 | #include "quickjs-encoding.h"
15 |
16 | /* returns 0 on success, nonzero on failure */
17 | int quickjs_full_init(JSContext *ctx);
18 |
19 | #endif /* ifndef QUICKJS_FULL_INIT_H */
20 |
--------------------------------------------------------------------------------
/src/builtin-modules/quickjs-bytecode/quickjs-bytecode.d.ts:
--------------------------------------------------------------------------------
1 | declare module "quickjs:bytecode" {
2 | /**
3 | * Convert the module or script in the specified file into bytecode.
4 | *
5 | * When converted back to a value, it will be a function.
6 | */
7 | export function fromFile(
8 | path: string,
9 | options?: {
10 | byteSwap?: boolean;
11 | sourceType?: "module" | "script";
12 | encodedFileName?: string;
13 | }
14 | ): ArrayBuffer;
15 |
16 | /**
17 | * Convert the provided value into bytecode. Doesn't work with all values.
18 | */
19 | export function fromValue(
20 | value: any,
21 | options?: { byteSwap?: boolean }
22 | ): ArrayBuffer;
23 |
24 | /**
25 | * Convert the provided bytecode into a value.
26 | */
27 | export function toValue(bytecode: ArrayBuffer): any;
28 | }
29 |
--------------------------------------------------------------------------------
/src/builtin-modules/quickjs-bytecode/quickjs-bytecode.h:
--------------------------------------------------------------------------------
1 | #ifndef QUICKJS_BYTECODE_H
2 | #define QUICKJS_BYTECODE_H
3 |
4 | #include "quickjs.h"
5 |
6 | JSModuleDef *js_init_module_bytecode(JSContext *ctx, const char *module_name);
7 |
8 | #endif /* ifndef QUICKJS_BYTECODE_H */
9 |
--------------------------------------------------------------------------------
/src/builtin-modules/quickjs-bytecode/quickjs-bytecode.ninja.js:
--------------------------------------------------------------------------------
1 | build({
2 | output: builddir("intermediate/quickjs-bytecode.host.o"),
3 | rule: "cc_host",
4 | inputs: [rel("quickjs-bytecode.c")],
5 | });
6 |
7 | build({
8 | output: builddir("intermediate/quickjs-bytecode.target.o"),
9 | rule: "cc_target",
10 | inputs: [rel("quickjs-bytecode.c")],
11 | });
12 |
13 | build({
14 | output: builddir("dts/quickjs-bytecode.d.ts"),
15 | rule: "copy",
16 | inputs: [rel("quickjs-bytecode.d.ts")],
17 | });
18 |
19 | build({
20 | output: builddir("include/quickjs-bytecode.h"),
21 | rule: "copy",
22 | inputs: [rel("quickjs-bytecode.h")],
23 | });
24 |
--------------------------------------------------------------------------------
/src/builtin-modules/quickjs-context/quickjs-context.h:
--------------------------------------------------------------------------------
1 | #ifndef QUICKJS_CONTEXT_H
2 | #define QUICKJS_CONTEXT_H
3 |
4 | #include "quickjs.h"
5 |
6 | JSModuleDef *js_init_module_context(JSContext *ctx, const char *module_name);
7 |
8 | #endif /* ifndef QUICKJS_CONTEXT_H */
9 |
--------------------------------------------------------------------------------
/src/builtin-modules/quickjs-context/quickjs-context.ninja.js:
--------------------------------------------------------------------------------
1 | build({
2 | output: builddir("intermediate/quickjs-context.host.o"),
3 | rule: "cc_host",
4 | inputs: [rel("quickjs-context.c")],
5 | });
6 |
7 | build({
8 | output: builddir("intermediate/quickjs-context.target.o"),
9 | rule: "cc_target",
10 | inputs: [rel("quickjs-context.c")],
11 | });
12 |
13 | build({
14 | output: builddir("dts/quickjs-context.d.ts"),
15 | rule: "copy",
16 | inputs: [rel("quickjs-context.d.ts")],
17 | });
18 |
19 | build({
20 | output: builddir("include/quickjs-context.h"),
21 | rule: "copy",
22 | inputs: [rel("quickjs-context.h")],
23 | });
24 |
--------------------------------------------------------------------------------
/src/builtin-modules/quickjs-encoding/quickjs-encoding.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include "cutils.h"
5 | #include "quickjs-encoding.h"
6 |
7 | static JSValue js_encoding_toUtf8(JSContext *ctx, JSValueConst this_val,
8 | int argc, JSValueConst *argv)
9 | {
10 | size_t len;
11 | JSValue input;
12 | uint8_t *buf;
13 |
14 | input = argv[0];
15 | buf = JS_GetArrayBuffer(ctx, &len, input);
16 | if (buf == NULL) {
17 | return JS_ThrowError(ctx, "input must be an ArrayBuffer");
18 | }
19 |
20 | return JS_NewStringLen(ctx, (char *)buf, len);
21 | }
22 |
23 | static JSValue js_encoding_fromUtf8(JSContext *ctx, JSValueConst this_val,
24 | int argc, JSValueConst *argv)
25 | {
26 | size_t len;
27 | JSValue input;
28 | const char *buf;
29 |
30 | input = argv[0];
31 | buf = JS_ToCStringLen2(ctx, &len, input, 1);
32 | if (buf == NULL) {
33 | return JS_ThrowError(ctx, "input must be a string");
34 | }
35 |
36 | return JS_NewArrayBufferCopy(ctx, (uint8_t*) buf, len);
37 | }
38 |
39 | static const JSCFunctionListEntry js_encoding_funcs[] = {
40 | JS_CFUNC_DEF("toUtf8", 1, js_encoding_toUtf8 ),
41 | JS_CFUNC_DEF("fromUtf8", 1, js_encoding_fromUtf8 ),
42 | };
43 |
44 | static int js_encoding_init(JSContext *ctx, JSModuleDef *m)
45 | {
46 | return JS_SetModuleExportList(ctx, m, js_encoding_funcs,
47 | countof(js_encoding_funcs));
48 | }
49 |
50 | JSModuleDef *js_init_module_encoding(JSContext *ctx, const char *module_name)
51 | {
52 | JSModuleDef *m;
53 | m = JS_NewCModule(ctx, module_name, js_encoding_init, NULL);
54 | if (!m) {
55 | return NULL;
56 | }
57 | JS_AddModuleExportList(ctx, m, js_encoding_funcs, countof(js_encoding_funcs));
58 | return m;
59 | }
60 |
--------------------------------------------------------------------------------
/src/builtin-modules/quickjs-encoding/quickjs-encoding.d.ts:
--------------------------------------------------------------------------------
1 | // WHATWG encoding spec at https://encoding.spec.whatwg.org/ would be better,
2 | // but this is better than nothing
3 | declare module "quickjs:encoding" {
4 | export function toUtf8(input: ArrayBuffer): string;
5 | export function fromUtf8(input: string): ArrayBuffer;
6 | }
7 |
--------------------------------------------------------------------------------
/src/builtin-modules/quickjs-encoding/quickjs-encoding.h:
--------------------------------------------------------------------------------
1 | #ifndef QUICKJS_ENCODING_H
2 | #define QUICKJS_ENCODING_H
3 |
4 | #include "quickjs.h"
5 |
6 | JSModuleDef *js_init_module_encoding(JSContext *ctx, const char *module_name);
7 |
8 | #endif /* ifndef QUICKJS_ENCODING_H */
9 |
--------------------------------------------------------------------------------
/src/builtin-modules/quickjs-encoding/quickjs-encoding.ninja.js:
--------------------------------------------------------------------------------
1 | build({
2 | output: builddir("intermediate/quickjs-encoding.host.o"),
3 | rule: "cc_host",
4 | inputs: [rel("quickjs-encoding.c")],
5 | });
6 |
7 | build({
8 | output: builddir("intermediate/quickjs-encoding.target.o"),
9 | rule: "cc_target",
10 | inputs: [rel("quickjs-encoding.c")],
11 | });
12 |
13 | build({
14 | output: builddir("dts/quickjs-encoding.d.ts"),
15 | rule: "copy",
16 | inputs: [rel("quickjs-encoding.d.ts")],
17 | });
18 |
19 | build({
20 | output: builddir("include/quickjs-encoding.h"),
21 | rule: "copy",
22 | inputs: [rel("quickjs-encoding.h")],
23 | });
24 |
--------------------------------------------------------------------------------
/src/builtin-modules/quickjs-engine/quickjs-engine.h:
--------------------------------------------------------------------------------
1 | #ifndef QUICKJS_ENGINE_H
2 | #define QUICKJS_ENGINE_H
3 |
4 | #include "quickjs.h"
5 |
6 | JSModuleDef *js_init_module_engine(JSContext *ctx, const char *module_name);
7 |
8 | #endif /* ifndef QUICKJS_ENGINE_H */
9 |
--------------------------------------------------------------------------------
/src/builtin-modules/quickjs-engine/quickjs-engine.ninja.js:
--------------------------------------------------------------------------------
1 | build({
2 | output: builddir("intermediate/quickjs-engine.host.o"),
3 | rule: "cc_host",
4 | inputs: [rel("quickjs-engine.c")],
5 | });
6 |
7 | build({
8 | output: builddir("intermediate/quickjs-engine.target.o"),
9 | rule: "cc_target",
10 | inputs: [rel("quickjs-engine.c")],
11 | });
12 |
13 | build({
14 | output: builddir("dts/quickjs-engine.d.ts"),
15 | rule: "copy",
16 | inputs: [rel("quickjs-engine.d.ts")],
17 | });
18 |
19 | build({
20 | output: builddir("include/quickjs-engine.h"),
21 | rule: "copy",
22 | inputs: [rel("quickjs-engine.h")],
23 | });
24 |
--------------------------------------------------------------------------------
/src/builtin-modules/quickjs-libc/quickjs-libc.h:
--------------------------------------------------------------------------------
1 | /*
2 | * QuickJS C library
3 | *
4 | * Copyright (c) 2017-2018 Fabrice Bellard
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 | #ifndef QUICKJS_LIBC_H
25 | #define QUICKJS_LIBC_H
26 |
27 | #include
28 | #include
29 |
30 | #include "quickjs.h"
31 |
32 | #ifdef __cplusplus
33 | extern "C"
34 | {
35 | #endif
36 |
37 | JSModuleDef *js_init_module_std(JSContext *ctx, const char *module_name);
38 | JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name);
39 |
40 | /*
41 | Creates 'scriptArgs' and 'setTimeout'.
42 | */
43 | void js_std_add_helpers(JSContext *ctx, int argc, char **argv);
44 |
45 | /* Creates 'scriptArgs' global */
46 | void js_std_add_scriptArgs(JSContext *ctx, int argc, char **argv);
47 |
48 | /* Creates 'setTimeout' and 'clearTimeout' globals */
49 | void js_std_add_timeout(JSContext *ctx);
50 |
51 | JS_BOOL js_std_is_main_thread(JSRuntime *rt);
52 | void js_std_interrupt_handler_start(JSContext *ctx);
53 | void js_std_interrupt_handler_finish(JSContext *ctx, JSValue ret);
54 |
55 | /* returns the exit status code */
56 | int js_std_loop(JSContext *ctx);
57 | void js_std_init_handlers(JSRuntime *rt);
58 | void js_std_free_handlers(JSRuntime *rt);
59 | void js_std_set_worker_new_context_func(JSContext *(*func)(JSRuntime *rt));
60 |
61 | #ifdef __cplusplus
62 | } /* extern "C" { */
63 | #endif
64 |
65 | #endif /* QUICKJS_LIBC_H */
66 |
--------------------------------------------------------------------------------
/src/builtin-modules/quickjs-libc/quickjs-libc.ninja.js:
--------------------------------------------------------------------------------
1 | const quickjs_libc_host_o = build({
2 | output: builddir("intermediate/quickjs-libc.host.o"),
3 | rule: "cc_host",
4 | inputs: [rel("quickjs-libc.c")],
5 | });
6 |
7 | const quickjs_libc_target_o = build({
8 | output: builddir("intermediate/quickjs-libc.target.o"),
9 | rule: "cc_target",
10 | inputs: [rel("quickjs-libc.c")],
11 | });
12 |
13 | build({
14 | output: builddir("dts/quickjs-libc.d.ts"),
15 | rule: "copy",
16 | inputs: [rel("quickjs-libc.d.ts")],
17 | });
18 |
19 | build({
20 | output: builddir("include/quickjs-libc.h"),
21 | rule: "copy",
22 | inputs: [rel("quickjs-libc.h")],
23 | });
24 |
--------------------------------------------------------------------------------
/src/builtin-modules/quickjs-pointer/quickjs-pointer.c:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include "quickjs-pointer.h"
4 |
5 | static JSClassID js_pointer_class_id;
6 |
7 | static JSClassDef js_pointer_class = {
8 | "Pointer",
9 | };
10 |
11 | static JSValue js_pointer_ctor(JSContext *ctx, JSValueConst this_val,
12 | int argc, JSValueConst *argv)
13 | {
14 | return JS_ThrowError(ctx, "Pointer objects cannot be created by JavaScript code. They must instead be created by native code, by using the function `js_new_pointer`.");
15 | }
16 |
17 | static JSValue js_pointer_Pointer_isPointer(JSContext *ctx, JSValueConst this_val,
18 | int argc, JSValueConst *argv)
19 | {
20 | if (argc != 1) {
21 | return JS_FALSE;
22 | }
23 |
24 | if (JS_IsObject(argv[0]) && JS_VALUE_GET_CLASS_ID(argv[0]) == js_pointer_class_id) {
25 | return JS_TRUE;
26 | } else {
27 | return JS_FALSE;
28 | }
29 | }
30 |
31 | static int js_pointer_init(JSContext *ctx, JSModuleDef *m) {
32 | JSValue pointer, pointer_proto;
33 |
34 | JS_NewClassID(&js_pointer_class_id);
35 | JS_NewClass(JS_GetRuntime(ctx), js_pointer_class_id, &js_pointer_class);
36 |
37 | pointer_proto = JS_NewObject(ctx);
38 | pointer = JS_NewCFunction2(ctx, js_pointer_ctor, "Pointer", 0,
39 | JS_CFUNC_constructor, 0);
40 | JS_SetConstructor(ctx, pointer, pointer_proto);
41 | JS_SetClassProto(ctx, js_pointer_class_id, pointer_proto);
42 |
43 | JS_SetPropertyStr(ctx, pointer, "NULL", js_new_pointer(ctx, NULL));
44 | JS_SetPropertyStr(ctx, pointer, "isPointer", JS_NewCFunction(ctx, js_pointer_Pointer_isPointer, "isPointer", 1));
45 |
46 | JS_SetModuleExport(ctx, m, "Pointer", pointer);
47 |
48 | return 0;
49 | }
50 |
51 | JSModuleDef *js_init_module_pointer(JSContext *ctx, const char *module_name)
52 | {
53 | JSModuleDef *m;
54 | m = JS_NewCModule(ctx, module_name, js_pointer_init, NULL);
55 | if (!m) {
56 | return NULL;
57 | }
58 | JS_AddModuleExport(ctx, m, "Pointer");
59 | return m;
60 | }
61 |
62 | JSValue js_new_pointer(JSContext *ctx, void *value)
63 | {
64 | JSValue ret;
65 |
66 | ret = JS_NewObjectClass(ctx, js_pointer_class_id);
67 | JS_SetOpaque(ret, value);
68 |
69 | // Add _info property, if we can
70 | {
71 | size_t info_len =
72 | sizeof(char)
73 | * sizeof(void *) /* bytes in pointer-to-void */
74 | * 2 /* printed hex chars per byte */
75 | + 2 /* for the 0x at the front */
76 | + 16 /* %p is 'implementation defined' so god I hope they don't do weird stuff, but here's some bonus space in case they do */
77 | ;
78 |
79 | char *buf = js_malloc(ctx, info_len);
80 | if (buf != NULL) {
81 | if (snprintf(buf, info_len, "%p", value) >= 0) {
82 | JSValue ptr_as_str = JS_NewString(ctx, buf);
83 | if (JS_IsException(ptr_as_str)) {
84 | // clear exception
85 | JS_GetException(ctx);
86 | } else {
87 | JS_DefinePropertyValueStr(ctx, ret, "_info", ptr_as_str, JS_PROP_ENUMERABLE);
88 | }
89 | }
90 |
91 |
92 | js_free(ctx, buf);
93 | }
94 | }
95 |
96 | return ret;
97 | }
98 |
--------------------------------------------------------------------------------
/src/builtin-modules/quickjs-pointer/quickjs-pointer.d.ts:
--------------------------------------------------------------------------------
1 | declare module "quickjs:pointer" {
2 | /**
3 | * An opaque pointer value which can be passed around by JS
4 | */
5 | export class Pointer {
6 | static NULL: Pointer;
7 |
8 | /** Returns a boolean indicating whether the provided value is a `Pointer` object. */
9 | static isPointer(value: any): value is Pointer;
10 |
11 | /** For debug logging purposes only; format can vary from platform to platform and is not guaranteed to be present or consistent. */
12 | _info?: string;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/builtin-modules/quickjs-pointer/quickjs-pointer.h:
--------------------------------------------------------------------------------
1 | #ifndef QUICKJS_POINTER_H
2 | #define QUICKJS_POINTER_H
3 |
4 | #include "quickjs.h"
5 |
6 | JSModuleDef *js_init_module_pointer(JSContext *ctx, const char *module_name);
7 | JSValue js_new_pointer(JSContext *ctx, void *value);
8 |
9 | #endif /* ifndef QUICKJS_POINTER_H */
10 |
--------------------------------------------------------------------------------
/src/builtin-modules/quickjs-pointer/quickjs-pointer.ninja.js:
--------------------------------------------------------------------------------
1 | build({
2 | output: builddir("intermediate/quickjs-pointer.host.o"),
3 | rule: "cc_host",
4 | inputs: [rel("quickjs-pointer.c")],
5 | });
6 |
7 | build({
8 | output: builddir("intermediate/quickjs-pointer.target.o"),
9 | rule: "cc_target",
10 | inputs: [rel("quickjs-pointer.c")],
11 | });
12 |
13 | build({
14 | output: builddir("dts/quickjs-pointer.d.ts"),
15 | rule: "copy",
16 | inputs: [rel("quickjs-pointer.d.ts")],
17 | });
18 |
19 | build({
20 | output: builddir("include/quickjs-pointer.h"),
21 | rule: "copy",
22 | inputs: [rel("quickjs-pointer.h")],
23 | });
24 |
--------------------------------------------------------------------------------
/src/globals/quickjs-inspect/quickjs-inspect.c:
--------------------------------------------------------------------------------
1 | #include "quickjs-inspect.h"
2 | #include "quickjs-modulesys.h"
3 |
4 | extern const uint8_t qjsc_inspect[];
5 | extern const uint32_t qjsc_inspect_size;
6 |
7 | int js_inspect_add_inspect_global(JSContext *ctx)
8 | {
9 | return QJMS_EvalBinary(ctx, qjsc_inspect, qjsc_inspect_size, 0);
10 | }
11 |
--------------------------------------------------------------------------------
/src/globals/quickjs-inspect/quickjs-inspect.h:
--------------------------------------------------------------------------------
1 | #ifndef QUICKJS_INSPECT_H
2 | #define QUICKJS_INSPECT_H
3 |
4 | #include "quickjs.h"
5 |
6 | /* Adds 'inspect' global */
7 | int js_inspect_add_inspect_global(JSContext *ctx);
8 |
9 | #endif /* ifndef QUICKJS_INSPECT_H */
10 |
--------------------------------------------------------------------------------
/src/globals/quickjs-inspect/quickjs-inspect.ninja.js:
--------------------------------------------------------------------------------
1 | // The C helper function for adding the global
2 | build({
3 | output: builddir("intermediate/quickjs-inspect.host.o"),
4 | rule: "cc_host",
5 | inputs: [rel("quickjs-inspect.c")],
6 | });
7 |
8 | build({
9 | output: builddir("intermediate/quickjs-inspect.target.o"),
10 | rule: "cc_target",
11 | inputs: [rel("quickjs-inspect.c")],
12 | });
13 |
14 | // C bytecode file generated from js
15 | const inspect_c = build({
16 | output: builddir("intermediate/inspect.c"),
17 | rule: "qjsc-minimal",
18 | inputs: [rel("inspect.js")],
19 | ruleVariables: {
20 | qjsc_args: `-c -m`,
21 | },
22 | });
23 |
24 | // The compiled objects containing the bytecode
25 | build({
26 | output: builddir("intermediate/inspect.host.o"),
27 | rule: "cc_host",
28 | inputs: [inspect_c],
29 | });
30 |
31 | build({
32 | output: builddir("intermediate/inspect.target.o"),
33 | rule: "cc_target",
34 | inputs: [inspect_c],
35 | });
36 |
37 | build({
38 | output: builddir("dts/quickjs-inspect.d.ts"),
39 | rule: "copy",
40 | inputs: [rel("quickjs-inspect.d.ts")],
41 | });
42 |
43 | build({
44 | output: builddir("include/quickjs-inspect.h"),
45 | rule: "copy",
46 | inputs: [rel("quickjs-inspect.h")],
47 | });
48 |
--------------------------------------------------------------------------------
/src/globals/quickjs-intervals/intervals.js:
--------------------------------------------------------------------------------
1 | (() => {
2 | const timerMap = new WeakMap();
3 | class Interval {}
4 |
5 | globalThis.setInterval = function setInterval(fn, ms) {
6 | const interval = new Interval();
7 | const wrappedFn = () => {
8 | try {
9 | fn();
10 | } catch (err) {
11 | let consoleErrorWorked = false;
12 | try {
13 | if (
14 | typeof console === "object" &&
15 | console != null &&
16 | typeof console.error === "function"
17 | ) {
18 | console.error(err);
19 | consoleErrorWorked = true;
20 | }
21 | } catch (err2) {}
22 | if (!consoleErrorWorked) {
23 | try {
24 | if (typeof print === "function") {
25 | print(err);
26 | }
27 | } catch (err3) {}
28 | }
29 | }
30 | const timer = setTimeout(wrappedFn, ms);
31 | timerMap.set(interval, timer);
32 | };
33 |
34 | const timer = setTimeout(wrappedFn, ms);
35 | timerMap.set(interval, timer);
36 |
37 | return interval;
38 | };
39 |
40 | globalThis.clearInterval = function clearInterval(interval) {
41 | if (interval == null) return;
42 | if (typeof interval != "object") return;
43 | const timer = timerMap.get(interval);
44 | if (timer != null) {
45 | clearTimeout(timer);
46 | }
47 | };
48 | })();
49 |
--------------------------------------------------------------------------------
/src/globals/quickjs-intervals/quickjs-intervals.c:
--------------------------------------------------------------------------------
1 | #include "quickjs-intervals.h"
2 | #include "quickjs-modulesys.h"
3 |
4 | extern const uint8_t qjsc_intervals[];
5 | extern const uint32_t qjsc_intervals_size;
6 |
7 | int js_intervals_add_setInterval_clearInterval_globals(JSContext *ctx)
8 | {
9 | return QJMS_EvalBinary(ctx, qjsc_intervals, qjsc_intervals_size, 0);
10 | }
11 |
--------------------------------------------------------------------------------
/src/globals/quickjs-intervals/quickjs-intervals.d.ts:
--------------------------------------------------------------------------------
1 | declare type Interval = { [Symbol.toStringTag]: "Interval" };
2 |
3 | declare function setInterval(func: (...args: any) => any, ms: number): Interval;
4 | declare function clearInterval(interval: Interval): void;
5 |
--------------------------------------------------------------------------------
/src/globals/quickjs-intervals/quickjs-intervals.h:
--------------------------------------------------------------------------------
1 | #ifndef QUICKJS_INTERVALS_H
2 | #define QUICKJS_INTERVALS_H
3 |
4 | #include "quickjs.h"
5 |
6 | /* Adds 'setInterval' and 'clearInterval' globals */
7 | int js_intervals_add_setInterval_clearInterval_globals(JSContext *ctx);
8 |
9 | #endif /* ifndef QUICKJS_INTERVALS_H */
10 |
--------------------------------------------------------------------------------
/src/globals/quickjs-intervals/quickjs-intervals.ninja.js:
--------------------------------------------------------------------------------
1 | // The C helper function for adding the global
2 | build({
3 | output: builddir("intermediate/quickjs-intervals.host.o"),
4 | rule: "cc_host",
5 | inputs: [rel("quickjs-intervals.c")],
6 | });
7 |
8 | build({
9 | output: builddir("intermediate/quickjs-intervals.target.o"),
10 | rule: "cc_target",
11 | inputs: [rel("quickjs-intervals.c")],
12 | });
13 |
14 | // C bytecode file generated from js
15 | const intervals_c = build({
16 | output: builddir("intermediate/intervals.c"),
17 | rule: "qjsc-minimal",
18 | inputs: [rel("intervals.js")],
19 | ruleVariables: {
20 | qjsc_args: `-c -m`,
21 | },
22 | });
23 |
24 | // The compiled objects containing the bytecode
25 | build({
26 | output: builddir("intermediate/intervals.host.o"),
27 | rule: "cc_host",
28 | inputs: [intervals_c],
29 | });
30 |
31 | build({
32 | output: builddir("intermediate/intervals.target.o"),
33 | rule: "cc_target",
34 | inputs: [intervals_c],
35 | });
36 |
37 | build({
38 | output: builddir("dts/quickjs-intervals.d.ts"),
39 | rule: "copy",
40 | inputs: [rel("quickjs-intervals.d.ts")],
41 | });
42 |
43 | build({
44 | output: builddir("include/quickjs-intervals.h"),
45 | rule: "copy",
46 | inputs: [rel("quickjs-intervals.h")],
47 | });
48 |
--------------------------------------------------------------------------------
/src/globals/quickjs-print/quickjs-print.c:
--------------------------------------------------------------------------------
1 | #include "quickjs-print.h"
2 |
3 | static JSValue js_print(JSContext *ctx, JSValueConst this_val,
4 | int argc, JSValueConst *argv, int magic)
5 | {
6 | int i;
7 | const char *str;
8 | size_t len;
9 | FILE *out;
10 |
11 | if (magic == 1) {
12 | out = stdout;
13 | } else if (magic == 2) {
14 | out = stderr;
15 | } else {
16 | return JS_ThrowInternalError(ctx, "js_print called with incorrect 'magic' value. This is a bug in quickjs-print.");
17 | }
18 |
19 | for(i = 0; i < argc; i++) {
20 | if (i != 0) {
21 | putc(' ', out);
22 | }
23 | str = JS_ToCStringLen(ctx, &len, argv[i]);
24 | if (!str)
25 | return JS_EXCEPTION;
26 | fwrite(str, 1, len, out);
27 | JS_FreeCString(ctx, str);
28 | }
29 | putc('\n', out);
30 | return JS_UNDEFINED;
31 | }
32 |
33 | void js_print_add_print_global(JSContext *ctx)
34 | {
35 | JSValue global_obj = JS_GetGlobalObject(ctx);
36 |
37 | JS_SetPropertyStr(ctx, global_obj, "print",
38 | JS_NewCFunctionMagic(ctx, js_print, "print", 1,
39 | JS_CFUNC_generic_magic, 1));
40 |
41 | JS_FreeValue(ctx, global_obj);
42 | }
43 |
44 | void js_print_add_console_global(JSContext *ctx)
45 | {
46 | JSValue global_obj, console;
47 |
48 | global_obj = JS_GetGlobalObject(ctx);
49 |
50 | console = JS_NewObject(ctx);
51 | JS_SetPropertyStr(ctx, console, "log",
52 | JS_NewCFunctionMagic(ctx, js_print, "log", 1,
53 | JS_CFUNC_generic_magic, 1));
54 | JS_SetPropertyStr(ctx, console, "info",
55 | JS_NewCFunctionMagic(ctx, js_print, "info", 1,
56 | JS_CFUNC_generic_magic, 1));
57 | JS_SetPropertyStr(ctx, console, "warn",
58 | JS_NewCFunctionMagic(ctx, js_print, "warn", 1,
59 | JS_CFUNC_generic_magic, 2));
60 | JS_SetPropertyStr(ctx, console, "error",
61 | JS_NewCFunctionMagic(ctx, js_print, "error", 1,
62 | JS_CFUNC_generic_magic, 2));
63 | JS_SetPropertyStr(ctx, global_obj, "console", console);
64 |
65 | JS_FreeValue(ctx, global_obj);
66 | }
67 |
--------------------------------------------------------------------------------
/src/globals/quickjs-print/quickjs-print.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Print the arguments separated by spaces and a trailing newline.
3 | *
4 | * Non-string args are coerced into a string via [ToString](https://tc39.es/ecma262/#sec-tostring).
5 | * Objects can override the default `ToString` behavior by defining a `toString` method.
6 | */
7 | declare var print: (...args: Array) => void;
8 |
9 | /**
10 | * Object that provides functions for logging information.
11 | */
12 | interface Console {
13 | /** Same as {@link print}(). */
14 | log: typeof print;
15 |
16 | /** Same as {@link print}(). */
17 | warn: typeof print;
18 |
19 | /** Same as {@link print}(). */
20 | error: typeof print;
21 |
22 | /** Same as {@link print}(). */
23 | info: typeof print;
24 | }
25 |
26 | declare var console: Console;
27 |
--------------------------------------------------------------------------------
/src/globals/quickjs-print/quickjs-print.h:
--------------------------------------------------------------------------------
1 | #ifndef QUICKJS_PRINT_H
2 | #define QUICKJS_PRINT_H
3 |
4 | #include "quickjs.h"
5 |
6 | void js_print_add_print_global(JSContext *ctx);
7 | void js_print_add_console_global(JSContext *ctx);
8 |
9 | #endif /* ifndef QUICKJS_PRINT_H */
10 |
--------------------------------------------------------------------------------
/src/globals/quickjs-print/quickjs-print.ninja.js:
--------------------------------------------------------------------------------
1 | build({
2 | output: builddir("intermediate/quickjs-print.host.o"),
3 | rule: "cc_host",
4 | inputs: [rel("quickjs-print.c")],
5 | });
6 |
7 | build({
8 | output: builddir("intermediate/quickjs-print.target.o"),
9 | rule: "cc_target",
10 | inputs: [rel("quickjs-print.c")],
11 | });
12 |
13 | build({
14 | output: builddir("dts/quickjs-print.d.ts"),
15 | rule: "copy",
16 | inputs: [rel("quickjs-print.d.ts")],
17 | });
18 |
19 | build({
20 | output: builddir("include/quickjs-print.h"),
21 | rule: "copy",
22 | inputs: [rel("quickjs-print.h")],
23 | });
24 |
--------------------------------------------------------------------------------
/src/lib/cutils/cutils.ninja.js:
--------------------------------------------------------------------------------
1 | build({
2 | output: builddir("intermediate/cutils.host.o"),
3 | rule: "cc_host",
4 | inputs: [rel("cutils.c")],
5 | });
6 |
7 | build({
8 | output: builddir("intermediate/cutils.target.o"),
9 | rule: "cc_target",
10 | inputs: [rel("cutils.c")],
11 | });
12 |
--------------------------------------------------------------------------------
/src/lib/debugprint/debugprint.h:
--------------------------------------------------------------------------------
1 | #ifndef DEBUGPRINT_H
2 | #define DEBUGPRINT_H
3 |
4 | #ifdef DEBUG
5 | #define debugprint(...); printf (__VA_ARGS__);
6 | #else
7 | #define debugprint(...);
8 | #endif
9 |
10 | #endif
11 |
--------------------------------------------------------------------------------
/src/lib/execpath/execpath.h:
--------------------------------------------------------------------------------
1 | char *execpath(char *argv0, char *info_message, char *error_message);
2 |
--------------------------------------------------------------------------------
/src/lib/execpath/execpath.ninja.js:
--------------------------------------------------------------------------------
1 | build({
2 | output: builddir("intermediate/execpath.host.o"),
3 | rule: "cc_host",
4 | inputs: [rel("execpath.c")],
5 | });
6 |
7 | build({
8 | output: builddir("intermediate/execpath.target.o"),
9 | rule: "cc_target",
10 | inputs: [rel("execpath.c")],
11 | });
12 |
--------------------------------------------------------------------------------
/src/lib/libbf/libbf.ninja.js:
--------------------------------------------------------------------------------
1 | build({
2 | output: builddir("intermediate/libbf.host.o"),
3 | rule: "cc_host",
4 | inputs: [rel("libbf.c")],
5 | });
6 |
7 | build({
8 | output: builddir("intermediate/libbf.target.o"),
9 | rule: "cc_target",
10 | inputs: [rel("libbf.c")],
11 | });
12 |
--------------------------------------------------------------------------------
/src/lib/libregexp/libregexp-opcode.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Regular Expression Engine
3 | *
4 | * Copyright (c) 2017-2018 Fabrice Bellard
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | #ifdef DEF
26 |
27 | DEF(invalid, 1) /* never used */
28 | DEF(char, 3)
29 | DEF(char32, 5)
30 | DEF(dot, 1)
31 | DEF(any, 1) /* same as dot but match any character including line terminator */
32 | DEF(line_start, 1)
33 | DEF(line_end, 1)
34 | DEF(goto, 5)
35 | DEF(split_goto_first, 5)
36 | DEF(split_next_first, 5)
37 | DEF(match, 1)
38 | DEF(save_start, 2) /* save start position */
39 | DEF(save_end, 2) /* save end position, must come after saved_start */
40 | DEF(save_reset, 3) /* reset save positions */
41 | DEF(loop, 5) /* decrement the top the stack and goto if != 0 */
42 | DEF(push_i32, 5) /* push integer on the stack */
43 | DEF(drop, 1)
44 | DEF(word_boundary, 1)
45 | DEF(not_word_boundary, 1)
46 | DEF(back_reference, 2)
47 | DEF(backward_back_reference, 2) /* must come after back_reference */
48 | DEF(range, 3) /* variable length */
49 | DEF(range32, 3) /* variable length */
50 | DEF(lookahead, 5)
51 | DEF(negative_lookahead, 5)
52 | DEF(push_char_pos, 1) /* push the character position on the stack */
53 | DEF(bne_char_pos, 5) /* pop one stack element and jump if equal to the character
54 | position */
55 | DEF(prev, 1) /* go to the previous char */
56 | DEF(simple_greedy_quant, 17)
57 |
58 | #else
59 | #error DEF was not defined when libregexp-opcode.h was run.
60 | #endif /* DEF */
61 |
--------------------------------------------------------------------------------
/src/lib/libregexp/libregexp.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Regular Expression Engine
3 | *
4 | * Copyright (c) 2017-2018 Fabrice Bellard
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 | #ifndef LIBREGEXP_H
25 | #define LIBREGEXP_H
26 |
27 | #include
28 |
29 | #include "libunicode.h"
30 |
31 | #define LRE_BOOL int /* for documentation purposes */
32 |
33 | #define LRE_FLAG_GLOBAL (1 << 0)
34 | #define LRE_FLAG_IGNORECASE (1 << 1)
35 | #define LRE_FLAG_MULTILINE (1 << 2)
36 | #define LRE_FLAG_DOTALL (1 << 3)
37 | #define LRE_FLAG_UTF16 (1 << 4)
38 | #define LRE_FLAG_STICKY (1 << 5)
39 |
40 | #define LRE_FLAG_NAMED_GROUPS (1 << 7) /* named groups are present in the regexp */
41 |
42 | uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size,
43 | const char *buf, size_t buf_len, int re_flags,
44 | void *opaque);
45 | int lre_get_capture_count(const uint8_t *bc_buf);
46 | int lre_get_flags(const uint8_t *bc_buf);
47 | const char *lre_get_groupnames(const uint8_t *bc_buf);
48 | int lre_exec(uint8_t **capture,
49 | const uint8_t *bc_buf, const uint8_t *cbuf, int cindex, int clen,
50 | int cbuf_type, void *opaque);
51 |
52 | int lre_parse_escape(const uint8_t **pp, int allow_utf16);
53 | LRE_BOOL lre_is_space(int c);
54 |
55 | /* must be provided by the user */
56 | LRE_BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size);
57 | void *lre_realloc(void *opaque, void *ptr, size_t size);
58 |
59 | /* JS identifier test */
60 | extern uint32_t const lre_id_start_table_ascii[4];
61 | extern uint32_t const lre_id_continue_table_ascii[4];
62 |
63 | static inline int lre_js_is_ident_first(int c)
64 | {
65 | if ((uint32_t)c < 128) {
66 | return (lre_id_start_table_ascii[c >> 5] >> (c & 31)) & 1;
67 | } else {
68 | #ifdef CONFIG_ALL_UNICODE
69 | return lre_is_id_start(c);
70 | #else
71 | return !lre_is_space(c);
72 | #endif
73 | }
74 | }
75 |
76 | static inline int lre_js_is_ident_next(int c)
77 | {
78 | if ((uint32_t)c < 128) {
79 | return (lre_id_continue_table_ascii[c >> 5] >> (c & 31)) & 1;
80 | } else {
81 | /* ZWNJ and ZWJ are accepted in identifiers */
82 | #ifdef CONFIG_ALL_UNICODE
83 | return lre_is_id_continue(c) || c == 0x200C || c == 0x200D;
84 | #else
85 | return !lre_is_space(c) || c == 0x200C || c == 0x200D;
86 | #endif
87 | }
88 | }
89 |
90 | #undef LRE_BOOL
91 |
92 | #endif /* LIBREGEXP_H */
93 |
--------------------------------------------------------------------------------
/src/lib/libregexp/libregexp.ninja.js:
--------------------------------------------------------------------------------
1 | build({
2 | output: builddir("intermediate/libregexp.host.o"),
3 | rule: "cc_host",
4 | inputs: [rel("libregexp.c")],
5 | implicitInputs: [builddir("intermediate/libunicode.host.o")],
6 | });
7 |
8 | build({
9 | output: builddir("intermediate/libregexp.target.o"),
10 | rule: "cc_target",
11 | inputs: [rel("libregexp.c")],
12 | implicitInputs: [builddir("intermediate/libunicode.target.o")],
13 | });
14 |
--------------------------------------------------------------------------------
/src/lib/libunicode/downloaded/README.md:
--------------------------------------------------------------------------------
1 | Run `./unicode_download.sh` in this folder to update these. But you usually won't need to.
2 |
--------------------------------------------------------------------------------
/src/lib/libunicode/downloaded/unicode_download.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 |
4 | URL="ftp://ftp.unicode.org/Public/15.0.0/ucd"
5 |
6 | wget "$URL/CaseFolding.txt" -O "./CaseFolding.txt"
7 | wget "$URL/DerivedNormalizationProps.txt" -O "./DerivedNormalizationProps.txt"
8 | wget "$URL/PropList.txt" -O "./PropList.txt"
9 | wget "$URL/SpecialCasing.txt" -O "./SpecialCasing.txt"
10 | wget "$URL/CompositionExclusions.txt" -O "./CompositionExclusions.txt"
11 | wget "$URL/ScriptExtensions.txt" -O "./ScriptExtensions.txt"
12 | wget "$URL/UnicodeData.txt" -O "./UnicodeData.txt"
13 | wget "$URL/DerivedCoreProperties.txt" -O "./DerivedCoreProperties.txt"
14 | wget "$URL/NormalizationTest.txt" -O "./NormalizationTest.txt"
15 | wget "$URL/Scripts.txt" -O "./Scripts.txt"
16 | wget "$URL/PropertyValueAliases.txt" -O "./PropertyValueAliases.txt"
17 |
18 | # The url path for this one is subtly different
19 | wget "$URL/emoji/emoji-data.txt" -O "./emoji-data.txt"
20 |
--------------------------------------------------------------------------------
/src/lib/libunicode/libunicode.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Unicode utilities
3 | *
4 | * Copyright (c) 2017-2018 Fabrice Bellard
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 | #ifndef LIBUNICODE_H
25 | #define LIBUNICODE_H
26 |
27 | #include
28 |
29 | #define LRE_BOOL int /* for documentation purposes */
30 |
31 | #define LRE_CC_RES_LEN_MAX 3
32 |
33 | typedef enum {
34 | UNICODE_NFC,
35 | UNICODE_NFD,
36 | UNICODE_NFKC,
37 | UNICODE_NFKD,
38 | } UnicodeNormalizationEnum;
39 |
40 | int lre_case_conv(uint32_t *res, uint32_t c, int conv_type);
41 | LRE_BOOL lre_is_cased(uint32_t c);
42 | LRE_BOOL lre_is_case_ignorable(uint32_t c);
43 |
44 | /* char ranges */
45 |
46 | typedef struct {
47 | int len; /* in points, always even */
48 | int size;
49 | uint32_t *points; /* points sorted by increasing value */
50 | void *mem_opaque;
51 | void *(*realloc_func)(void *opaque, void *ptr, size_t size);
52 | } CharRange;
53 |
54 | typedef enum {
55 | CR_OP_UNION,
56 | CR_OP_INTER,
57 | CR_OP_XOR,
58 | } CharRangeOpEnum;
59 |
60 | void cr_init(CharRange *cr, void *mem_opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size));
61 | void cr_free(CharRange *cr);
62 | int cr_realloc(CharRange *cr, int size);
63 | int cr_copy(CharRange *cr, const CharRange *cr1);
64 |
65 | static inline int cr_add_point(CharRange *cr, uint32_t v)
66 | {
67 | if (cr->len >= cr->size) {
68 | if (cr_realloc(cr, cr->len + 1))
69 | return -1;
70 | }
71 | cr->points[cr->len++] = v;
72 | return 0;
73 | }
74 |
75 | static inline int cr_add_interval(CharRange *cr, uint32_t c1, uint32_t c2)
76 | {
77 | if ((cr->len + 2) > cr->size) {
78 | if (cr_realloc(cr, cr->len + 2))
79 | return -1;
80 | }
81 | cr->points[cr->len++] = c1;
82 | cr->points[cr->len++] = c2;
83 | return 0;
84 | }
85 |
86 | int cr_union1(CharRange *cr, const uint32_t *b_pt, int b_len);
87 |
88 | static inline int cr_union_interval(CharRange *cr, uint32_t c1, uint32_t c2)
89 | {
90 | uint32_t b_pt[2];
91 | b_pt[0] = c1;
92 | b_pt[1] = c2 + 1;
93 | return cr_union1(cr, b_pt, 2);
94 | }
95 |
96 | int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len,
97 | const uint32_t *b_pt, int b_len, int op);
98 |
99 | int cr_invert(CharRange *cr);
100 |
101 | #ifdef CONFIG_ALL_UNICODE
102 |
103 | LRE_BOOL lre_is_id_start(uint32_t c);
104 | LRE_BOOL lre_is_id_continue(uint32_t c);
105 |
106 | int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len,
107 | UnicodeNormalizationEnum n_type,
108 | void *opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size));
109 |
110 | /* Unicode character range functions */
111 |
112 | int unicode_script(CharRange *cr,
113 | const char *script_name, LRE_BOOL is_ext);
114 | int unicode_general_category(CharRange *cr, const char *gc_name);
115 | int unicode_prop(CharRange *cr, const char *prop_name);
116 |
117 | #endif /* CONFIG_ALL_UNICODE */
118 |
119 | #undef LRE_BOOL
120 |
121 | #endif /* LIBUNICODE_H */
122 |
--------------------------------------------------------------------------------
/src/lib/libunicode/libunicode.ninja.js:
--------------------------------------------------------------------------------
1 | const unicode_gen_host_o = build({
2 | output: builddir("intermediate/unicode_gen.host.o"),
3 | rule: "cc_host",
4 | inputs: [rel("unicode_gen.c")],
5 | });
6 |
7 | const unicode_gen_host = build({
8 | output: builddir("intermediate/unicode_gen.host"),
9 | rule: "link_host",
10 | inputs: [builddir("intermediate/cutils.host.o"), unicode_gen_host_o],
11 | });
12 |
13 | rule("unicode_gen", {
14 | command: [unicode_gen_host, "$in", "$out"],
15 | description: "UNICODE_GEN $out",
16 | });
17 |
18 | const libunicode_table_h = build({
19 | output: builddir("intermediate/libunicode-table.h"),
20 | rule: "unicode_gen",
21 | inputs: [rel("downloaded")],
22 | implicitInputs: [
23 | unicode_gen_host,
24 |
25 | rel("./downloaded/CaseFolding.txt"),
26 | rel("./downloaded/DerivedNormalizationProps.txt"),
27 | rel("./downloaded/PropList.txt"),
28 | rel("./downloaded/SpecialCasing.txt"),
29 | rel("./downloaded/CompositionExclusions.txt"),
30 | rel("./downloaded/ScriptExtensions.txt"),
31 | rel("./downloaded/UnicodeData.txt"),
32 | rel("./downloaded/DerivedCoreProperties.txt"),
33 | rel("./downloaded/NormalizationTest.txt"),
34 | rel("./downloaded/Scripts.txt"),
35 | rel("./downloaded/PropertyValueAliases.txt"),
36 | rel("./downloaded/emoji-data.txt"),
37 | ],
38 | });
39 |
40 | build({
41 | output: builddir("intermediate/libunicode.host.o"),
42 | rule: "cc_host",
43 | inputs: [rel("libunicode.c")],
44 | implicitInputs: [libunicode_table_h],
45 | });
46 | build({
47 | output: builddir("intermediate/libunicode.target.o"),
48 | rule: "cc_target",
49 | inputs: [rel("libunicode.c")],
50 | implicitInputs: [libunicode_table_h],
51 | });
52 |
--------------------------------------------------------------------------------
/src/lib/list/list.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Linux klist like system
3 | *
4 | * Copyright (c) 2016-2017 Fabrice Bellard
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 | #ifndef LIST_H
25 | #define LIST_H
26 |
27 | #ifndef NULL
28 | #include
29 | #endif
30 |
31 | struct list_head {
32 | struct list_head *prev;
33 | struct list_head *next;
34 | };
35 |
36 | #define LIST_HEAD_INIT(el) { &(el), &(el) }
37 |
38 | /* return the pointer of type 'type *' containing 'el' as field 'member' */
39 | #define list_entry(el, type, member) \
40 | ((type *)((uint8_t *)(el) - offsetof(type, member)))
41 |
42 | static inline void init_list_head(struct list_head *head)
43 | {
44 | head->prev = head;
45 | head->next = head;
46 | }
47 |
48 | /* insert 'el' between 'prev' and 'next' */
49 | static inline void __list_add(struct list_head *el,
50 | struct list_head *prev, struct list_head *next)
51 | {
52 | prev->next = el;
53 | el->prev = prev;
54 | el->next = next;
55 | next->prev = el;
56 | }
57 |
58 | /* add 'el' at the head of the list 'head' (= after element head) */
59 | static inline void list_add(struct list_head *el, struct list_head *head)
60 | {
61 | __list_add(el, head, head->next);
62 | }
63 |
64 | /* add 'el' at the end of the list 'head' (= before element head) */
65 | static inline void list_add_tail(struct list_head *el, struct list_head *head)
66 | {
67 | __list_add(el, head->prev, head);
68 | }
69 |
70 | static inline void list_del(struct list_head *el)
71 | {
72 | struct list_head *prev, *next;
73 | prev = el->prev;
74 | next = el->next;
75 | prev->next = next;
76 | next->prev = prev;
77 | el->prev = NULL; /* fail safe */
78 | el->next = NULL; /* fail safe */
79 | }
80 |
81 | static inline int list_empty(struct list_head *el)
82 | {
83 | return el->next == el;
84 | }
85 |
86 | #define list_for_each(el, head) \
87 | for(el = (head)->next; el != (head); el = el->next)
88 |
89 | #define list_for_each_safe(el, el1, head) \
90 | for(el = (head)->next, el1 = el->next; el != (head); \
91 | el = el1, el1 = el->next)
92 |
93 | #define list_for_each_prev(el, head) \
94 | for(el = (head)->prev; el != (head); el = el->prev)
95 |
96 | #define list_for_each_prev_safe(el, el1, head) \
97 | for(el = (head)->prev, el1 = el->prev; el != (head); \
98 | el = el1, el1 = el->prev)
99 |
100 | #endif /* LIST_H */
101 |
--------------------------------------------------------------------------------
/src/lib/quickjs-utils/quickjs-utils.h:
--------------------------------------------------------------------------------
1 | #ifndef QUICKJS_UTILS_H
2 | #define QUICKJS_UTILS_H
3 |
4 | #include "quickjs.h"
5 |
6 | typedef struct QJUForEachPropertyState
7 | {
8 | uint32_t len;
9 | uint32_t i;
10 | JSPropertyEnum *tab;
11 | JSAtom key;
12 | JSValue val;
13 | } QJUForEachPropertyState;
14 |
15 | /* free using QJU_FreeForEachPropertyState */
16 | QJUForEachPropertyState *QJU_NewForEachPropertyState(JSContext *ctx, JSValue obj, int flags);
17 |
18 | // put a block after this invocation; it will be run once per property
19 | #define QJU_ForEachProperty(ctx, state) \
20 | for (state->i = 0; state->i < state->len; state->i++)
21 |
22 | // fills in `state` with data for the current property.
23 | // return value will be JS_EXCEPTION if an exception was thrown, or JS_UNDEFINED otherwise.
24 | // you must JS_DupValue `state->val` if you wanna keep it around.
25 | JSValue QJU_ForEachProperty_Read(JSContext *ctx, JSValue obj, QJUForEachPropertyState *state);
26 |
27 | void QJU_FreeForEachPropertyState(JSContext *ctx, QJUForEachPropertyState *state);
28 |
29 | /* reads the contents of the file at `filename` into a buffer. */
30 | uint8_t *QJU_ReadFile(JSContext *ctx, size_t *pbuf_len, const char *filename);
31 |
32 | /*
33 | prints `exception_val` to `f`. If `exception_val` is an Error,
34 | it gets printed nicely.
35 | */
36 | void QJU_PrintError(JSContext *ctx, FILE *f, JSValueConst exception_val);
37 |
38 | /*
39 | prints the context's current exception to `f`.
40 | this calls JS_GetException, so the exception will be cleared.
41 | */
42 | void QJU_PrintException(JSContext *ctx, FILE *f);
43 |
44 | /* an unhandled promise rejection handler suitable for passing into JS_SetHostPromiseRejectionTracker */
45 | void QJU_PrintPromiseRejection(JSContext *ctx, JSValueConst promise,
46 | JSValueConst reason, JS_BOOL is_handled,
47 | void *opaque);
48 |
49 | #endif /* ifndef QUICKJS_UTILS_H */
50 |
--------------------------------------------------------------------------------
/src/lib/quickjs-utils/quickjs-utils.ninja.js:
--------------------------------------------------------------------------------
1 | build({
2 | output: builddir("intermediate/quickjs-utils.host.o"),
3 | rule: "cc_host",
4 | inputs: [rel("quickjs-utils.c")],
5 | });
6 |
7 | build({
8 | output: builddir("intermediate/quickjs-utils.target.o"),
9 | rule: "cc_target",
10 | inputs: [rel("quickjs-utils.c")],
11 | });
12 |
13 | build({
14 | output: builddir("include/quickjs-utils.h"),
15 | rule: "copy",
16 | inputs: [rel("quickjs-utils.h")],
17 | });
18 |
--------------------------------------------------------------------------------
/src/programs/file-to-bytecode/file-to-bytecode.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env quickjs-run
2 |
3 | // Simple script which reads in a file, converts it to bytecode, and writes out
4 | // the bytecode to a new file.
5 |
6 | ///
7 | ///
8 | ///
9 |
10 | import * as std from "quickjs:std";
11 | import * as Bytecode from "quickjs:bytecode";
12 |
13 | function main() {
14 | let [_quickjsRun, _thisScript, inputFile, outputFile, encodedFileName] =
15 | scriptArgs;
16 |
17 | if (!(inputFile && outputFile)) {
18 | throw new Error(
19 | "You must pass two positional arguments to this script: the input file path (js) and the output file path (bytecode binary)."
20 | );
21 | }
22 |
23 | if (!encodedFileName) {
24 | encodedFileName = inputFile;
25 | }
26 |
27 | const bytecode = Bytecode.fromFile(inputFile, { encodedFileName });
28 | const out = std.open(outputFile, "wb");
29 | out.write(bytecode, 0, bytecode.byteLength);
30 | }
31 |
32 | main();
33 |
--------------------------------------------------------------------------------
/src/programs/file-to-bytecode/file-to-bytecode.ninja.js:
--------------------------------------------------------------------------------
1 | build({
2 | output: builddir("bin/file-to-bytecode.js"),
3 | rule: "copy",
4 | inputs: [rel("./file-to-bytecode.js")],
5 | });
6 |
--------------------------------------------------------------------------------
/src/programs/log-argv/indirect-argv0.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | const path = require("path");
3 | const child_process = require("child_process");
4 |
5 | const [target, ...args] = process.argv.slice(2);
6 | const argv0 = path.basename(target);
7 |
8 | console.log({ target, args, argv0 });
9 |
10 | child_process.spawnSync(target, args, { argv0, stdio: "inherit" });
11 |
--------------------------------------------------------------------------------
/src/programs/log-argv/log-argv.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include "execpath.h"
6 |
7 | int main(int argc, char *argv[])
8 | {
9 | char *program_path;
10 |
11 | for (int i = 0; i < argc; i++) {
12 | printf("%d: \"%s\"\n", i, argv[i]);
13 | }
14 |
15 | program_path = execpath(argv[0], NULL, NULL);
16 | if (program_path == NULL) {
17 | printf("execpath failed: %s\n", strerror(errno));
18 | } else {
19 | printf("execpath result: %s\n", program_path);
20 | free(program_path);
21 | }
22 |
23 | return 0;
24 | }
25 |
--------------------------------------------------------------------------------
/src/programs/log-argv/log-argv.js:
--------------------------------------------------------------------------------
1 | if (env.QUICKJS_EXTRAS === "1") {
2 | const log_argv_target_o = build({
3 | output: builddir("intermediate/log-argv.target.o"),
4 | rule: "cc_target",
5 | inputs: [rel("log-argv.c")],
6 | });
7 |
8 | build({
9 | output: builddir("extras/log-argv$PROGRAM_SUFFIX"),
10 | rule: "link_target",
11 | inputs: [log_argv_target_o, builddir("intermediate/execpath.target.o")],
12 | });
13 | }
14 |
--------------------------------------------------------------------------------
/src/programs/qjs/qjs.ninja.js:
--------------------------------------------------------------------------------
1 | const qjs_target_o = build({
2 | output: builddir("intermediate/qjs.target.o"),
3 | rule: "cc_target",
4 | inputs: [rel("qjs.c")],
5 | });
6 |
7 | build({
8 | output: builddir("bin/qjs$PROGRAM_SUFFIX"),
9 | rule: "link_target",
10 | inputs: [
11 | qjs_target_o,
12 | builddir("intermediate/qjscalc.target.o"),
13 | builddir("intermediate/repl.target.o"),
14 | builddir("intermediate/quickjs-full.target.a"),
15 | ],
16 | });
17 |
--------------------------------------------------------------------------------
/src/programs/qjsbootstrap/README.md:
--------------------------------------------------------------------------------
1 | # qjsbootstrap
2 |
3 | qjsbootstrap can be used to create cross-platform, distributable, standalone binaries written using JavaScript. Write your code in JavaScript (leveraging the [quickjs-libc APIs](https://github.com/suchipi/quickjs/blob/main/src/quickjs-libc/quickjs-libc.d.ts)), then append it to a copy of the `qjsbootstrap` binary, and now that binary runs your code.
4 |
5 | ```sh
6 | $ ls
7 | qjsbootstrap
8 | $ ./qjsbootstrap
9 | append UTF-8 encoded JavaScript to the end of this binary to change this binary into a program that executes that JavaScript code
10 | $ cp qjsbootstrap my-program
11 | $ echo 'console.log("hello!")' >> my-program
12 | $ ls
13 | qjsbootstrap
14 | my-program
15 | $ ./my-program
16 | hello!
17 | ```
18 |
19 | ## Usage
20 |
21 | First, compile QuickJS and qjsbootstrap:
22 |
23 | - Make sure you have docker installed and running.
24 | - Clone this repo and cd to its folder.
25 | - Run `meta/docker/build-all.sh`
26 |
27 | Then, write your script, and test it using the `qjs` binary for your OS:
28 |
29 | ```sh
30 | $ echo 'console.log("hello!")' >> myscript.js
31 | $ build/linux/bin/qjs myscript.js # replace build/linux with whatever is appropriate for your OS
32 | hello!
33 | ```
34 |
35 | Once you've verified your script works correctly, you can use `qjsbootstrap` to bundle it up.
36 |
37 | Make copies of the `qjsbootstrap` binary for each OS:
38 |
39 | ```sh
40 | $ cp build/darwin-arm64/bin/qjsbootstrap myscript-darwin-arm64
41 | $ cp build/darwin-x86_64/bin/qjsbootstrap myscript-darwin-x86_64
42 | $ cp build/linux-aarch64/bin/qjsbootstrap myscript-linux-aarch64
43 | $ cp build/linux-amd64/bin/qjsbootstrap myscript-linux-amd64
44 | $ cp build/windows-x86_64/bin/qjsbootstrap.exe myscript-windows-x86_64.exe
45 | ```
46 |
47 | And then append your script to each binary:
48 |
49 | ```sh
50 | $ cat myscript.js >> myscript-darwin-arm64
51 | $ cat myscript.js >> myscript-darwin-x86_64
52 | $ cat myscript.js >> myscript-linux-aarch64
53 | $ cat myscript.js >> myscript-linux-amd64
54 | $ cat myscript.js >> myscript-windows-x86_64.exe
55 | ```
56 |
57 | Now test the binary for your platform:
58 |
59 | ```sh
60 | $ ./myscript-linux-amd64 # run whichever one matches the OS you're on
61 | hello!
62 | ```
63 |
64 | If all works well, you're all set, and can distribute those `myscript-*` binaries.
65 |
--------------------------------------------------------------------------------
/src/programs/qjsbootstrap/is-stdin-a-tty.js:
--------------------------------------------------------------------------------
1 | // This is just a simple example demonstrating how to use qjsbootstrap.
2 | // This file is built with the following command:
3 | //
4 | // cat build/bin/qjsbootstrap src/qjsbootstrap/is-stdin-a-tty.js > build/extras/is-stdin-a-tty && chmod +x build/extras/is-stdin-a-tty
5 |
6 | import * as std from "quickjs:std";
7 | import * as os from "quickjs:os";
8 |
9 | console.log("is stdin a tty?");
10 | const istty = os.isatty(std.in.fileno());
11 | console.log(istty);
12 |
13 | if (istty) {
14 | std.exit(0);
15 | } else {
16 | std.exit(1);
17 | }
18 |
--------------------------------------------------------------------------------
/src/programs/qjsbootstrap/qjsbootstrap.ninja.js:
--------------------------------------------------------------------------------
1 | // NOTE: the filenames for the 'zero' version and the 'fill' version
2 | // have to be the same length, so that they have the same filesize, so
3 | // that the filesize of the 'zero' version can be used to create the
4 | // 'fill' version.
5 |
6 | let qjsbootstrapForExample;
7 |
8 | const qjsbootstrap_size_check_rule = rule("qjsbootstrap_size_check", {
9 | command: `${builddir("intermediate/quickjs-run.host$PROGRAM_SUFFIX")} ${rel(
10 | "size_check.js"
11 | )} $in > $out`,
12 | description: "SIZE_CHECK $in",
13 | });
14 | const qjsbootstrap_size_check_rule_implicit_inputs = [
15 | builddir("intermediate/quickjs-run.host$PROGRAM_SUFFIX"),
16 | rel("size_check.js"),
17 | ];
18 |
19 | for (const bytecode of [false, true]) {
20 | const dashByteCode = bytecode ? "-bytecode" : "";
21 |
22 | const qjsbootstrap_zero_target_o = build({
23 | output: builddir(`intermediate/qjsbootstrap-zero${dashByteCode}.target.o`),
24 | rule: "cc_target",
25 | inputs: [rel("qjsbootstrap.c")],
26 | ruleVariables: {
27 | cc_args: [
28 | bytecode ? `-DCONFIG_BYTECODE` : "",
29 | // an arbitrary size that's relatively close and should optimize the same way (hopefully)
30 | "-DBOOTSTRAP_BIN_SIZE=1221712",
31 | ].join(" "),
32 | },
33 | });
34 |
35 | const qjsbootstrap_zero_target = build({
36 | output: builddir(`intermediate/qjsbootstrap-zero${dashByteCode}.target`),
37 | rule: "link_target",
38 | inputs: [
39 | qjsbootstrap_zero_target_o,
40 | builddir("intermediate/quickjs-full.target.a"),
41 | ],
42 | });
43 |
44 | const qjsbootstrap_fill_target_o = build({
45 | output: builddir(`intermediate/qjsbootstrap-fill${dashByteCode}.target.o`),
46 | rule: "cc_target",
47 | inputs: [rel("qjsbootstrap.c")],
48 | implicitInputs: [qjsbootstrap_zero_target],
49 | ruleVariables: {
50 | cc_args: [
51 | bytecode ? `-DCONFIG_BYTECODE` : "",
52 | `-DBOOTSTRAP_BIN_SIZE=$$(wc -c ${qjsbootstrap_zero_target} | awk '{print $$1}')`,
53 | ].join(" "),
54 | },
55 | });
56 |
57 | const qjsbootstrap_fill_target = build({
58 | output: builddir(`intermediate/qjsbootstrap-fill${dashByteCode}.target`),
59 | rule: "link_target",
60 | inputs: [
61 | qjsbootstrap_fill_target_o,
62 | builddir("intermediate/quickjs-full.target.a"),
63 | ],
64 | });
65 |
66 | build({
67 | output: builddir(
68 | `intermediate/qjsbootstrap-size-check${dashByteCode}.target.txt`
69 | ),
70 | rule: qjsbootstrap_size_check_rule,
71 | inputs: [qjsbootstrap_zero_target, qjsbootstrap_fill_target],
72 | implicitInputs: qjsbootstrap_size_check_rule_implicit_inputs,
73 | });
74 |
75 | const qjsbootstrap = build({
76 | output: builddir(`bin/qjsbootstrap${dashByteCode}$PROGRAM_SUFFIX`),
77 | rule: "copy",
78 | inputs: [qjsbootstrap_fill_target],
79 | });
80 |
81 | if (!bytecode) {
82 | qjsbootstrapForExample = qjsbootstrap;
83 | }
84 | }
85 |
86 | if (env.QUICKJS_EXTRAS === "1") {
87 | const is_stdin_a_tty = build({
88 | output: builddir("extras/is-stdin-a-tty$PROGRAM_SUFFIX"),
89 | rule: "combine_into_executable",
90 | inputs: [qjsbootstrapForExample, rel("is-stdin-a-tty.js")],
91 | });
92 | }
93 |
--------------------------------------------------------------------------------
/src/programs/qjsbootstrap/size_check.js:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 | import * as os from "quickjs:os";
4 |
5 | const file1 = scriptArgs[2];
6 | const file2 = scriptArgs[3];
7 |
8 | if (!(file1 && file2)) {
9 | throw new Error("Please pass two filenames to this script");
10 | }
11 |
12 | const stat1 = os.stat(file1);
13 | const stat2 = os.stat(file2);
14 |
15 | if (stat1.size !== stat2.size) {
16 | throw new Error(`Sizes differ!! ${stat1.size} vs ${stat2.size}`);
17 | }
18 |
19 | print(`OK (both files ${stat1.size})`);
20 |
--------------------------------------------------------------------------------
/src/programs/qjsc/qjsc.ninja.js:
--------------------------------------------------------------------------------
1 | const qjsc_minimal_host_o = build({
2 | output: builddir("intermediate/qjsc-minimal.host.o"),
3 | rule: "cc_host",
4 | inputs: [rel("qjsc.c")],
5 | ruleVariables: {
6 | cc_args: "-DQJSC_MINIMAL",
7 | },
8 | });
9 |
10 | const qjsc_minimal_host = build({
11 | output: builddir("intermediate/qjsc-minimal.host"),
12 | rule: "link_host",
13 | inputs: [
14 | qjsc_minimal_host_o,
15 | builddir("intermediate/quickjs-libc.host.o"),
16 | builddir("intermediate/quickjs-core.host.a"),
17 | ],
18 | });
19 |
20 | const qjsc_host_o = build({
21 | output: builddir("intermediate/qjsc.host.o"),
22 | rule: "cc_host",
23 | inputs: [rel("qjsc.c")],
24 | });
25 |
26 | build({
27 | output: builddir("intermediate/qjsc.host"),
28 | rule: "link_host",
29 | inputs: [qjsc_host_o, builddir("intermediate/quickjs-full.host.a")],
30 | });
31 |
32 | const qjsc_target_o = build({
33 | output: builddir("intermediate/qjsc.target.o"),
34 | rule: "cc_target",
35 | inputs: [rel("qjsc.c")],
36 | });
37 |
38 | const qjsc_target = build({
39 | output: builddir("intermediate/qjsc.target"),
40 | rule: "link_target",
41 | inputs: [qjsc_target_o, builddir("intermediate/quickjs-full.target.a")],
42 | });
43 |
44 | build({
45 | output: builddir("bin/qjsc$PROGRAM_SUFFIX"),
46 | rule: "copy",
47 | inputs: [qjsc_target],
48 | });
49 |
--------------------------------------------------------------------------------
/src/programs/qjscalc/qjscalc.ninja.js:
--------------------------------------------------------------------------------
1 | const qjscalc_c = build({
2 | output: builddir("intermediate/qjscalc.c"),
3 | rule: "qjsc-minimal",
4 | inputs: [rel("qjscalc.js")],
5 | ruleVariables: {
6 | qjsc_args: `-fbignum -c`,
7 | },
8 | });
9 |
10 | build({
11 | output: builddir("intermediate/qjscalc.host.o"),
12 | rule: "cc_host",
13 | inputs: [qjscalc_c],
14 | });
15 |
16 | build({
17 | output: builddir("intermediate/qjscalc.target.o"),
18 | rule: "cc_target",
19 | inputs: [qjscalc_c],
20 | });
21 |
--------------------------------------------------------------------------------
/src/programs/quickjs-run/quickjs-run.c:
--------------------------------------------------------------------------------
1 | /*
2 | * QuickJS Run
3 | *
4 | * Built by Lily Skye, using some code generated by qjsc.
5 | *
6 | * Copyright (c) 2022-2023 Lily Skye
7 | * Copyright (c) 2017-2021 Fabrice Bellard
8 | * Copyright (c) 2017-2021 Charlie Gordon
9 | *
10 | * Permission is hereby granted, free of charge, to any person obtaining a copy
11 | * of this software and associated documentation files (the "Software"), to deal
12 | * in the Software without restriction, including without limitation the rights
13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 | * copies of the Software, and to permit persons to whom the Software is
15 | * furnished to do so, subject to the following conditions:
16 | *
17 | * The above copyright notice and this permission notice shall be included in
18 | * all copies or substantial portions of the Software.
19 | *
20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 | * THE SOFTWARE.
27 | */
28 | #include
29 | #include
30 | #include "quickjs-full-init.h"
31 | #include "quickjs-modulesys.h"
32 | #include "cutils.h"
33 |
34 | static JSContext *JS_NewCustomContext(JSRuntime *rt)
35 | {
36 | JSContext *ctx = JS_NewContextRaw(rt);
37 | if (!ctx) {
38 | return NULL;
39 | }
40 |
41 | JS_AddIntrinsicBaseObjects(ctx);
42 | JS_AddIntrinsicDate(ctx);
43 | JS_AddIntrinsicEval(ctx);
44 | JS_AddIntrinsicStringNormalize(ctx);
45 | JS_AddIntrinsicRegExp(ctx);
46 | JS_AddIntrinsicJSON(ctx);
47 | JS_AddIntrinsicProxy(ctx);
48 | JS_AddIntrinsicMapSet(ctx);
49 | JS_AddIntrinsicTypedArrays(ctx);
50 | JS_AddIntrinsicPromise(ctx);
51 | JS_AddIntrinsicBigInt(ctx);
52 | JS_AddIntrinsicBigFloat(ctx);
53 | JS_AddIntrinsicBigDecimal(ctx);
54 | JS_AddIntrinsicOperators(ctx);
55 | JS_EnableBignumExt(ctx, TRUE);
56 |
57 | if (quickjs_full_init(ctx)) {
58 | exit(1);
59 | }
60 |
61 | return ctx;
62 | }
63 |
64 | int main(int argc, char **argv)
65 | {
66 | JSRuntime *rt;
67 | JSContext *ctx;
68 | const char *filename;
69 | int exit_status;
70 |
71 | if (argc < 2) {
72 | printf("You must pass a parameter to this binary: The file to run.\n");
73 | return 2;
74 | }
75 |
76 | filename = argv[1];
77 |
78 | rt = JS_NewRuntime();
79 | js_std_set_worker_new_context_func(JS_NewCustomContext);
80 | js_std_init_handlers(rt);
81 | JS_SetMaxStackSize(rt, 8000000);
82 | QJMS_InitState(rt);
83 | JS_SetCanBlock(rt, TRUE);
84 | ctx = JS_NewCustomContext(rt);
85 | js_std_add_helpers(ctx, argc, argv);
86 | QJMS_InitContext(ctx);
87 |
88 | if (QJMS_EvalFile(ctx, filename, -1)) {
89 | JS_FreeContext(ctx);
90 | JS_FreeRuntime(rt);
91 | return 1;
92 | }
93 |
94 | exit_status = js_std_loop(ctx);
95 | QJMS_FreeState(rt);
96 | js_std_free_handlers(rt);
97 | JS_FreeContext(ctx);
98 | JS_FreeRuntime(rt);
99 |
100 | return exit_status;
101 | }
102 |
--------------------------------------------------------------------------------
/src/programs/quickjs-run/quickjs-run.ninja.js:
--------------------------------------------------------------------------------
1 | const quickjs_run_target_o = build({
2 | output: builddir("intermediate/quickjs-run.target.o"),
3 | rule: "cc_target",
4 | inputs: [rel("quickjs-run.c")],
5 | });
6 |
7 | build({
8 | output: builddir("bin/quickjs-run$PROGRAM_SUFFIX"),
9 | rule: "link_target",
10 | inputs: [
11 | quickjs_run_target_o,
12 | builddir("intermediate/quickjs-full.target.a"),
13 | ],
14 | });
15 |
16 | const quickjs_run_host_o = build({
17 | output: builddir("intermediate/quickjs-run.host.o"),
18 | rule: "cc_host",
19 | inputs: [rel("quickjs-run.c")],
20 | });
21 |
22 | build({
23 | output: builddir("intermediate/quickjs-run.host$PROGRAM_SUFFIX"),
24 | rule: "link_host",
25 | inputs: [quickjs_run_host_o, builddir("intermediate/quickjs-full.host.a")],
26 | });
27 |
--------------------------------------------------------------------------------
/src/programs/repl/repl.ninja.js:
--------------------------------------------------------------------------------
1 | const repl_c = build({
2 | output: builddir("intermediate/repl.c"),
3 | rule: "qjsc-minimal",
4 | inputs: [rel("repl.js")],
5 | ruleVariables: {
6 | qjsc_args: `-c -m`,
7 | },
8 | });
9 |
10 | build({
11 | output: builddir("intermediate/repl.host.o"),
12 | rule: "cc_host",
13 | inputs: [repl_c],
14 | });
15 |
16 | build({
17 | output: builddir("intermediate/repl.target.o"),
18 | rule: "cc_target",
19 | inputs: [repl_c],
20 | });
21 |
--------------------------------------------------------------------------------
/src/programs/sample-program/sample-program.ninja.js:
--------------------------------------------------------------------------------
1 | if (env.QUICKJS_EXTRAS === "1") {
2 | const sum_c = build({
3 | output: builddir("intermediate/sample-program/sum.c"),
4 | rule: "qjsc",
5 | inputs: [rel("sum.js")],
6 | ruleVariables: {
7 | qjsc_args: `-e -m`,
8 | },
9 | });
10 |
11 | const sum_target_o = build({
12 | output: builddir("intermediate/sample-program/sum.target.o"),
13 | rule: "cc_target",
14 | inputs: [sum_c],
15 | });
16 |
17 | build({
18 | output: builddir("extras/sample-program/sum$PROGRAM_SUFFIX"),
19 | rule: "link_target",
20 | inputs: [sum_target_o, builddir("intermediate/quickjs-full.target.a")],
21 | });
22 | }
23 |
--------------------------------------------------------------------------------
/src/programs/sample-program/sum.js:
--------------------------------------------------------------------------------
1 | import * as std from "quickjs:std";
2 |
3 | function sum(nums) {
4 | return nums.reduce((prev, curr) => prev + curr, 0);
5 | }
6 |
7 | const result = sum(
8 | scriptArgs.slice(1).map((arg) => {
9 | const num = parseFloat(arg);
10 | if (Number.isNaN(num)) {
11 | throw new Error("Not a number: " + arg);
12 | }
13 | return num;
14 | })
15 | );
16 |
17 | std.out.puts(result + "\n");
18 |
--------------------------------------------------------------------------------
/src/programs/stack-limit-test/loop.js:
--------------------------------------------------------------------------------
1 | function loop(i = 0) {
2 | try {
3 | console.log(i);
4 | return loop(i + 1);
5 | } catch (e) {
6 | console.error(e.message);
7 | console.log("in catch", i);
8 | }
9 | }
10 |
11 | loop();
12 |
--------------------------------------------------------------------------------
/src/programs/stack-limit-test/main.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include "quickjs.h"
4 | #include "quickjs-libc.h"
5 | #include "quickjs-print.h"
6 | #include "quickjs-modulesys.h"
7 |
8 | extern const uint32_t qjsc_loop_size;
9 | extern const uint8_t qjsc_loop[];
10 |
11 | static JSContext *JS_NewCustomContext(JSRuntime *rt)
12 | {
13 | JSContext *ctx = JS_NewContextRaw(rt);
14 | if (!ctx)
15 | return NULL;
16 | JS_AddIntrinsicBaseObjects(ctx);
17 | JS_AddIntrinsicDate(ctx);
18 | JS_AddIntrinsicEval(ctx);
19 | JS_AddIntrinsicStringNormalize(ctx);
20 | JS_AddIntrinsicRegExp(ctx);
21 | JS_AddIntrinsicJSON(ctx);
22 | JS_AddIntrinsicProxy(ctx);
23 | JS_AddIntrinsicMapSet(ctx);
24 | JS_AddIntrinsicTypedArrays(ctx);
25 | JS_AddIntrinsicPromise(ctx);
26 | JS_AddIntrinsicBigInt(ctx);
27 | js_print_add_print_global(ctx);
28 | js_print_add_console_global(ctx);
29 | js_init_module_os(ctx, "quickjs:os");
30 | js_init_module_std(ctx, "quickjs:std");
31 |
32 | return ctx;
33 | }
34 |
35 | int main(int argc, char **argv)
36 | {
37 | JSRuntime *rt;
38 | JSContext *ctx;
39 | int exit_status;
40 |
41 | if (argc != 3) {
42 | printf("You must provide two positional arguments: the max stack size (or NONE to skip calling JS_SetMaxStackSize), and the memory limit (or NONE to skip calling JS_SetMemoryLimit).\n");
43 | return 1;
44 | }
45 |
46 | rt = JS_NewRuntime();
47 | js_std_set_worker_new_context_func(JS_NewCustomContext);
48 | js_std_init_handlers(rt);
49 |
50 | if (!strcmp(argv[1], "NONE")) {
51 | printf("skipping JS_SetMaxStackSize call\n");
52 | } else {
53 | double max_stack_size = strtod(argv[1], NULL);
54 |
55 | printf("max_stack_size: (double)%f, (size_t)%zu\n", max_stack_size, (size_t)max_stack_size);
56 |
57 | JS_SetMaxStackSize(rt, max_stack_size);
58 | }
59 |
60 | if (!strcmp(argv[2], "NONE")) {
61 | printf("skipping JS_SetMemoryLimit call\n");
62 | } else {
63 | double memory_limit = strtod(argv[1], NULL);
64 |
65 | printf("memory_limit: (double)%f, (size_t)%zu\n", memory_limit, (size_t)memory_limit);
66 |
67 | JS_SetMemoryLimit(rt, memory_limit);
68 | }
69 |
70 | QJMS_InitState(rt);
71 | ctx = JS_NewCustomContext(rt);
72 | js_std_add_helpers(ctx, argc, argv);
73 | QJMS_InitContext(ctx);
74 | QJMS_EvalBinary(ctx, qjsc_loop, qjsc_loop_size, 0);
75 | exit_status = js_std_loop(ctx);
76 | QJMS_FreeState(rt);
77 | JS_FreeContext(ctx);
78 | JS_FreeRuntime(rt);
79 | return exit_status;
80 | }
81 |
--------------------------------------------------------------------------------
/src/programs/stack-limit-test/stack-limit-test.ninja.js:
--------------------------------------------------------------------------------
1 | if (env.QUICKJS_EXTRAS === "1") {
2 | // loop.js
3 |
4 | const loop_c = build({
5 | output: builddir("intermediate/stack-limit-test/loop.c"),
6 | rule: "qjsc",
7 | inputs: [rel("loop.js")],
8 | ruleVariables: {
9 | qjsc_args: `-c -m`,
10 | },
11 | });
12 |
13 | const loop_target_o = build({
14 | output: builddir("intermediate/stack-limit-test/loop.target.o"),
15 | rule: "cc_target",
16 | inputs: [loop_c],
17 | });
18 |
19 | // main.c
20 |
21 | const main_target_o = build({
22 | output: builddir("intermediate/stack-limit-test/main.target.o"),
23 | rule: "cc_target",
24 | inputs: [rel("main.c")],
25 | });
26 |
27 | // program
28 |
29 | build({
30 | output: builddir("extras/stack-limit-test$PROGRAM_SUFFIX"),
31 | rule: "link_target",
32 | inputs: [
33 | main_target_o,
34 | loop_target_o,
35 | builddir("intermediate/quickjs-full.target.a"),
36 | ],
37 | });
38 | }
39 |
--------------------------------------------------------------------------------
/src/quickjs-modulesys/module-impl-stub.c:
--------------------------------------------------------------------------------
1 | // Stub to be used in qjsc and other places where we have to use the quickjs
2 | // 'core' archive instead of the 'full' archive
3 |
4 | #include
5 |
6 | const uint32_t qjsc_module_impl_size = 0;
7 | const uint8_t qjsc_module_impl[0] = {};
8 |
--------------------------------------------------------------------------------
/src/quickjs-modulesys/module-impl.js:
--------------------------------------------------------------------------------
1 | import * as std from "quickjs:std";
2 | import * as os from "quickjs:os";
3 |
4 | (() => {
5 | let ModuleDelegate = __qjms_temp_ModuleDelegate;
6 |
7 | ModuleDelegate.read = (moduleName) => {
8 | if (typeof moduleName !== "string") {
9 | const err = new Error("moduleName must be a string");
10 | err.moduleName = moduleName;
11 | throw err;
12 | }
13 | const matches = moduleName.match(/(\.[^.]+)$/);
14 | const ext = matches ? matches[1] : "";
15 |
16 | const compilers = ModuleDelegate.compilers;
17 | const userCompiler = compilers[ext];
18 |
19 | let fileContent;
20 | try {
21 | fileContent = std.loadFile(moduleName);
22 | } catch (err) {
23 | err.message = "Failed to load module: " + err.message;
24 | throw err;
25 | }
26 |
27 | if (userCompiler) {
28 | if (typeof userCompiler !== "function") {
29 | const err = new Error(
30 | `ModuleDelegate.compilers[${JSON.stringify(ext)}] was not a function`
31 | );
32 | err.compiler = userCompiler;
33 | throw err;
34 | }
35 | const result = userCompiler(moduleName, fileContent);
36 | if (typeof result !== "string") {
37 | const err = new Error(
38 | `ModuleDelegate.compilers[${JSON.stringify(ext)}] returned non-string`
39 | );
40 | err.result = result;
41 | throw err;
42 | }
43 | return result;
44 | } else {
45 | return fileContent;
46 | }
47 | };
48 |
49 | ModuleDelegate.resolve = (name, baseName) => {
50 | try {
51 | if (name.includes(":")) {
52 | // namespaced module
53 | return name;
54 | }
55 |
56 | if (name[0] !== ".") {
57 | // maybe something made with ModuleDelegate.define
58 | return name;
59 | }
60 |
61 | if (!isAbsolute(baseName)) {
62 | try {
63 | baseName = os.realpath(baseName);
64 | } catch (err) {
65 | // ignored
66 | }
67 | }
68 |
69 | const parts = baseName.split("/");
70 | const lastPart = parts[parts.length - 1];
71 | if (lastPart !== "." && lastPart !== "..") {
72 | parts.pop();
73 | }
74 |
75 | const request = [...parts, name].join("/");
76 | if (isValidFile(request)) {
77 | return os.realpath(request);
78 | }
79 |
80 | for (const ext of ModuleDelegate.searchExtensions) {
81 | if (typeof ext !== "string") {
82 | throw new Error(
83 | "ModuleDelegate.searchExtensions contained a non-string"
84 | );
85 | }
86 |
87 | if (isValidFile(request + ext)) {
88 | return os.realpath(request + ext);
89 | }
90 |
91 | if (isValidFile(request + "/index" + ext)) {
92 | return os.realpath(request + "/index" + ext);
93 | }
94 | }
95 |
96 | throw new Error(
97 | `No such file: '${request}' (using search extensions: ${JSON.stringify(
98 | ModuleDelegate.searchExtensions
99 | )})`
100 | );
101 | } catch (err) {
102 | const newErr = new Error(
103 | `Failed to resolve '${name}' from '${baseName}': ${err.message}`
104 | );
105 | newErr.stack = newErr.stack + "Caused by:\n" + err.stack;
106 | throw newErr;
107 | }
108 | };
109 |
110 | ModuleDelegate.builtinModuleNames = [
111 | "quickjs:std",
112 | "quickjs:os",
113 | "quickjs:bytecode",
114 | "quickjs:context",
115 | "quickjs:pointer",
116 | "quickjs:engine",
117 | "quickjs:encoding",
118 | ];
119 |
120 | function isAbsolute(path) {
121 | return path[0] === "/" || /[A-Za-z]:[/\\]/.test(path);
122 | }
123 |
124 | function isValidFile(path) {
125 | try {
126 | os.access(path, os.F_OK);
127 | const stats = os.stat(path);
128 | return (stats.mode & os.S_IFMT) === os.S_IFREG;
129 | } catch (err) {
130 | return false;
131 | }
132 | }
133 | })();
134 |
--------------------------------------------------------------------------------
/src/quickjs-modulesys/quickjs-modulesys.h:
--------------------------------------------------------------------------------
1 | #ifndef QUICKJS_MODULESYS_H
2 | #define QUICKJS_MODULESYS_H
3 |
4 | #include "quickjs.h"
5 |
6 | typedef struct QJMS_State QJMS_State;
7 |
8 | /*
9 | initialize and register the module loader system.
10 | call both this (once per runtime) AND QJMS_InitContext (once per context).
11 | */
12 | void QJMS_InitState(JSRuntime *rt);
13 |
14 | /*
15 | initialize and register the module loader system.
16 | call both this (once per context) AND QJMS_InitState (once per runtime).
17 | */
18 | void QJMS_InitContext(JSContext *ctx);
19 |
20 | /* free resources allocated by the module loader system */
21 | void QJMS_FreeState(JSRuntime *rt);
22 |
23 | /*
24 | Affects the value of import.meta.main.
25 | */
26 | void QJMS_SetMainModule(JSRuntime *rt, const char *module_name);
27 |
28 | /*
29 | Check if import.meta.main would be true for this module.
30 | */
31 | JS_BOOL QJMS_IsMainModule(JSRuntime *rt, const char *module_name);
32 |
33 | /* initializes the import.meta object for the provided module function */
34 | int QJMS_SetModuleImportMeta(JSContext *ctx, JSValueConst func_val);
35 |
36 | /*
37 | returns 0 on success, nonzero on error.
38 | in case of error, prints error to stderr before returning.
39 | */
40 | int QJMS_EvalBuf(JSContext *ctx, const void *buf, int buf_len,
41 | const char *filename, int eval_flags);
42 |
43 | /*
44 | module can be -1 for autodetect.
45 | returns 0 on success, nonzero on error.
46 | in case of error, prints error to stderr before returning.
47 | */
48 | int QJMS_EvalFile(JSContext *ctx, const char *filename, int module);
49 |
50 | /*
51 | returns 0 on success, nonzero on error.
52 | in case of error, prints error to stderr before returning.
53 | */
54 | int QJMS_EvalBinary(JSContext *ctx, const uint8_t *buf, size_t buf_len,
55 | int load_only);
56 |
57 | /* the internal behavior of the 'require' function, exposed as a C API */
58 | JSValue QJMS_Require(JSContext *ctx, JSValueConst specifier);
59 | JSValue QJMS_Require2(JSContext *ctx, JSValueConst specifier, JSValueConst basename);
60 |
61 | /* the internal behavior of the 'require.resolve' function, exposed as a C API */
62 | JSValue QJMS_RequireResolve(JSContext *ctx, JSValueConst specifier_val);
63 | JSValue QJMS_RequireResolve2(JSContext *ctx, JSValueConst specifier_val, JSAtom basename_atom);
64 |
65 | /* returns an object like { require: Function, ModuleDelegate: Object } */
66 | JSValue QJMS_GetModuleLoaderInternals(JSContext *ctx);
67 |
68 | #endif /* ifndef QUICKJS_MODULESYS_H */
69 |
--------------------------------------------------------------------------------
/src/quickjs-modulesys/quickjs-modulesys.ninja.js:
--------------------------------------------------------------------------------
1 | const module_impl_c = build({
2 | output: builddir("intermediate/quickjs-modulesys/module-impl.c"),
3 | rule: "qjsc-minimal",
4 | inputs: [rel("module-impl.js")],
5 | ruleVariables: {
6 | qjsc_args: `-c -m`,
7 | },
8 | });
9 |
10 | const module_impl_host_o = build({
11 | output: builddir("intermediate/quickjs-modulesys/module-impl.host.o"),
12 | rule: "cc_host",
13 | inputs: [module_impl_c],
14 | });
15 |
16 | const module_impl_target_o = build({
17 | output: builddir("intermediate/quickjs-modulesys/module-impl.target.o"),
18 | rule: "cc_target",
19 | inputs: [module_impl_c],
20 | });
21 |
22 | const module_impl_stub_host_o = build({
23 | output: builddir("intermediate/quickjs-modulesys/module-impl-stub.host.o"),
24 | rule: "cc_host",
25 | inputs: [rel("module-impl-stub.c")],
26 | });
27 |
28 | const module_impl_stub_target_o = build({
29 | output: builddir("intermediate/quickjs-modulesys/module-impl-stub.target.o"),
30 | rule: "cc_target",
31 | inputs: [rel("module-impl-stub.c")],
32 | });
33 |
34 | build({
35 | output: builddir("intermediate/quickjs-modulesys.host.o"),
36 | rule: "cc_host",
37 | inputs: [rel("quickjs-modulesys.c")],
38 | });
39 |
40 | build({
41 | output: builddir("intermediate/quickjs-modulesys.target.o"),
42 | rule: "cc_target",
43 | inputs: [rel("quickjs-modulesys.c")],
44 | });
45 |
46 | build({
47 | output: builddir("dts/quickjs-modulesys.d.ts"),
48 | rule: "copy",
49 | inputs: [rel("quickjs-modulesys.d.ts")],
50 | });
51 |
52 | build({
53 | output: builddir("include/quickjs-modulesys.h"),
54 | rule: "copy",
55 | inputs: [rel("quickjs-modulesys.h")],
56 | });
57 |
--------------------------------------------------------------------------------
/src/quickjs/quickjs.ninja.js:
--------------------------------------------------------------------------------
1 | build({
2 | output: builddir("intermediate/quickjs.host.o"),
3 | rule: "cc_host",
4 | inputs: [rel("quickjs.c")],
5 | });
6 |
7 | build({
8 | output: builddir("intermediate/quickjs.target.o"),
9 | rule: "cc_target",
10 | inputs: [rel("quickjs.c")],
11 | });
12 |
13 | build({
14 | output: builddir("dts/quickjs.d.ts"),
15 | rule: "copy",
16 | inputs: [rel("quickjs.d.ts")],
17 | });
18 |
19 | build({
20 | output: builddir("include/quickjs.h"),
21 | rule: "copy",
22 | inputs: [rel("quickjs.h")],
23 | });
24 |
--------------------------------------------------------------------------------
/src/run-test262/.gitignore:
--------------------------------------------------------------------------------
1 | test262_report.txt
2 | test262o_report.txt
3 |
--------------------------------------------------------------------------------
/src/run-test262/run-test262.ninja.js:
--------------------------------------------------------------------------------
1 | if (env.QUICKJS_EXTRAS === "1") {
2 | const run_test262_target_o = build({
3 | output: builddir("intermediate/run-test262.target.o"),
4 | rule: "cc_target",
5 | inputs: [rel("run-test262.c")],
6 | });
7 |
8 | build({
9 | output: builddir("extras/run-test262$PROGRAM_SUFFIX"),
10 | rule: "link_target",
11 | inputs: [
12 | run_test262_target_o,
13 | builddir("intermediate/quickjs-full.target.a"),
14 | ],
15 | });
16 | }
17 |
--------------------------------------------------------------------------------
/src/run-test262/run.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | cd $(dirname $(realpath $BASH_SOURCE))
4 |
5 | ../../build/extras/run-test262 -c ./test262.conf
6 |
--------------------------------------------------------------------------------
/src/run-test262/runo.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | cd $(dirname $(realpath $BASH_SOURCE))
4 |
5 | ../../build/extras/run-test262 -c ./test262o.conf
6 |
--------------------------------------------------------------------------------
/src/run-test262/test262.patch:
--------------------------------------------------------------------------------
1 | diff --git a/harness/atomicsHelper.js b/harness/atomicsHelper.js
2 | index 9c1217351e..3c24755558 100644
3 | --- a/harness/atomicsHelper.js
4 | +++ b/harness/atomicsHelper.js
5 | @@ -227,10 +227,14 @@ $262.agent.waitUntil = function(typedArray, index, expected) {
6 | * }
7 | */
8 | $262.agent.timeouts = {
9 | - yield: 100,
10 | - small: 200,
11 | - long: 1000,
12 | - huge: 10000,
13 | +// yield: 100,
14 | +// small: 200,
15 | +// long: 1000,
16 | +// huge: 10000,
17 | + yield: 20,
18 | + small: 20,
19 | + long: 100,
20 | + huge: 1000,
21 | };
22 |
23 | /**
24 | diff --git a/harness/regExpUtils.js b/harness/regExpUtils.js
25 | index be7039fda0..7b38abf8df 100644
26 | --- a/harness/regExpUtils.js
27 | +++ b/harness/regExpUtils.js
28 | @@ -6,24 +6,27 @@ description: |
29 | defines: [buildString, testPropertyEscapes, matchValidator]
30 | ---*/
31 |
32 | +if ($262 && typeof $262.codePointRange === "function") {
33 | + /* use C function to build the codePointRange (much faster with
34 | + slow JS engines) */
35 | + codePointRange = $262.codePointRange;
36 | +} else {
37 | + codePointRange = function codePointRange(start, end) {
38 | + const codePoints = [];
39 | + let length = 0;
40 | + for (codePoint = start; codePoint < end; codePoint++) {
41 | + codePoints[length++] = codePoint;
42 | + }
43 | + return String.fromCodePoint.apply(null, codePoints);
44 | + }
45 | +}
46 | +
47 | function buildString({ loneCodePoints, ranges }) {
48 | - const CHUNK_SIZE = 10000;
49 | - let result = Reflect.apply(String.fromCodePoint, null, loneCodePoints);
50 | - for (let i = 0; i < ranges.length; i++) {
51 | - const range = ranges[i];
52 | - const start = range[0];
53 | - const end = range[1];
54 | - const codePoints = [];
55 | - for (let length = 0, codePoint = start; codePoint <= end; codePoint++) {
56 | - codePoints[length++] = codePoint;
57 | - if (length === CHUNK_SIZE) {
58 | - result += Reflect.apply(String.fromCodePoint, null, codePoints);
59 | - codePoints.length = length = 0;
60 | - }
61 | + let result = String.fromCodePoint.apply(null, loneCodePoints);
62 | + for (const [start, end] of ranges) {
63 | + result += codePointRange(start, end + 1);
64 | }
65 | - result += Reflect.apply(String.fromCodePoint, null, codePoints);
66 | - }
67 | - return result;
68 | + return result;
69 | }
70 |
71 | function testPropertyEscapes(regex, string, expression) {
72 |
--------------------------------------------------------------------------------
/src/run-test262/test262o_errors.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suchipi/quickjs/e77a3be11a293337eaf2df0630a4a851882747ca/src/run-test262/test262o_errors.txt
--------------------------------------------------------------------------------
/src/shared-library-modules/example-fib/fib.c:
--------------------------------------------------------------------------------
1 | /*
2 | * QuickJS: Example of C module
3 | *
4 | * Copyright (c) 2017-2018 Fabrice Bellard
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 | #include "quickjs.h"
25 |
26 | #define countof(x) (sizeof(x) / sizeof((x)[0]))
27 |
28 | static int fib(int n)
29 | {
30 | if (n <= 0)
31 | return 0;
32 | else if (n == 1)
33 | return 1;
34 | else
35 | return fib(n - 1) + fib(n - 2);
36 | }
37 |
38 | static JSValue js_fib(JSContext *ctx, JSValueConst this_val,
39 | int argc, JSValueConst *argv)
40 | {
41 | int n, res;
42 | if (JS_ToInt32(ctx, &n, argv[0]))
43 | return JS_EXCEPTION;
44 | res = fib(n);
45 | return JS_NewInt32(ctx, res);
46 | }
47 |
48 | static const JSCFunctionListEntry js_fib_funcs[] = {
49 | JS_CFUNC_DEF("fib", 1, js_fib ),
50 | };
51 |
52 | static int js_fib_init(JSContext *ctx, JSModuleDef *m)
53 | {
54 | return JS_SetModuleExportList(ctx, m, js_fib_funcs,
55 | countof(js_fib_funcs));
56 | }
57 |
58 | JSModuleDef *js_init_module(JSContext *ctx, const char *module_name)
59 | {
60 | JSModuleDef *m;
61 | m = JS_NewCModule(ctx, module_name, js_fib_init, NULL);
62 | if (!m)
63 | return NULL;
64 | JS_AddModuleExportList(ctx, m, js_fib_funcs, countof(js_fib_funcs));
65 | return m;
66 | }
67 |
--------------------------------------------------------------------------------
/src/shared-library-modules/example-fib/fib.ninja.js:
--------------------------------------------------------------------------------
1 | if (env.QUICKJS_EXTRAS === "1") {
2 | build({
3 | output: builddir("extras/fib.so"),
4 | rule: "shared_lib_target",
5 | inputs: [rel("fib.c")],
6 | });
7 | }
8 |
--------------------------------------------------------------------------------
/src/shared-library-modules/example-fib/test_fib.js:
--------------------------------------------------------------------------------
1 | /* example of JS module importing a C module */
2 |
3 | import { fib } from "../../../build/extras/fib.so";
4 |
5 | console.log("fib(10)=", fib(10));
6 |
--------------------------------------------------------------------------------
/src/shared-library-modules/example-point/point.ninja.js:
--------------------------------------------------------------------------------
1 | if (env.QUICKJS_EXTRAS === "1") {
2 | build({
3 | output: builddir("extras/point.so"),
4 | rule: "shared_lib_target",
5 | inputs: [rel("point.c")],
6 | });
7 | }
8 |
--------------------------------------------------------------------------------
/src/shared-library-modules/example-point/test_point.js:
--------------------------------------------------------------------------------
1 | import * as std from "quickjs:std";
2 | /* example of JS module importing a C module with a class in it */
3 | import { Point } from "../../../build/extras/point.so";
4 |
5 | function assert(condition) {
6 | if (!condition) {
7 | throw Error("assertion failed");
8 | }
9 | }
10 |
11 | const tests = {
12 | "normal usage": () => {
13 | const point = new Point(2, 3);
14 | assert(point.x === 2);
15 | assert(point.y === 3);
16 | point.x = 4;
17 | assert(point.x === 4);
18 | assert(point.norm() == 5);
19 | },
20 | "subclass usage": () => {
21 | class ColorPoint extends Point {
22 | constructor(x, y, color) {
23 | super(x, y);
24 | this.color = color;
25 | }
26 | getColor() {
27 | return this.color;
28 | }
29 | }
30 |
31 | const point2 = new ColorPoint(2, 3, 0xffffff);
32 | assert(point2.x === 2);
33 | assert(point2.color === 0xffffff);
34 | assert(point2.getColor() === 0xffffff);
35 | },
36 | };
37 |
38 | for (const [name, testFn] of Object.entries(tests)) {
39 | std.out.puts(name + "...");
40 |
41 | try {
42 | testFn();
43 | std.out.puts(" PASS\n");
44 | } catch (err) {
45 | std.out.puts(" FAIL\n");
46 | console.error(err.message + "\n" + err.stack);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": ["../node_modules/@suchipi/shinobi/globals.d.ts"],
3 | "include": ["./**/*.ninja.js"],
4 | "compilerOptions": {
5 | "allowJs": true,
6 | "checkJs": true,
7 | "noEmit": true,
8 | "lib": ["ES2020"],
9 | "strictNullChecks": true,
10 | "types": ["node"],
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/tests/_utils.ts:
--------------------------------------------------------------------------------
1 | import path from "path";
2 | import { pathMarker } from "path-less-traveled";
3 | import type { RunContext } from "first-base";
4 |
5 | export const rootDir = pathMarker(path.resolve(__dirname, ".."));
6 | export const binDir = pathMarker(rootDir("build", "bin"));
7 | export const fixturesDir = pathMarker(rootDir("tests", "fixtures"));
8 | export const testsWorkDir = pathMarker(rootDir("tests", "workdir"));
9 |
10 | export function cleanString(str: string): string {
11 | return str.replaceAll(rootDir(), "");
12 | }
13 |
14 | export function cleanResult(
15 | result: RunContext["result"]
16 | ): RunContext["result"] {
17 | return {
18 | ...result,
19 | stderr: cleanString(result.stderr),
20 | stdout: cleanString(result.stdout),
21 | };
22 | }
23 |
--------------------------------------------------------------------------------
/tests/error-cause.test.ts:
--------------------------------------------------------------------------------
1 | import { spawn } from "first-base";
2 | import { binDir } from "./_utils";
3 |
4 | test("error cause", async () => {
5 | const run = spawn(binDir("qjs"), [
6 | "-e",
7 | `
8 | const err = new Error("hi", { cause: new Error("yeah") });
9 | console.log(inspect(err));
10 | // Note slight deviation from the spec here: property is enumerable. This is intentional.
11 | console.log(inspect(Object.getOwnPropertyDescriptor(err, "cause")));
12 | `,
13 | ]);
14 | await run.completion;
15 | expect(run.result).toMatchInlineSnapshot(`
16 | {
17 | "code": 0,
18 | "error": false,
19 | "stderr": "",
20 | "stdout": "Error {
21 | Error: hi
22 | at (:2)
23 |
24 |
25 | cause: Error {
26 | Error: yeah
27 | at (:2)
28 |
29 | }
30 | }
31 | {
32 | value: Error {
33 | Error: yeah
34 | at (:2)
35 |
36 | }
37 | writable: true
38 | enumerable: true
39 | configurable: true
40 | }
41 | ",
42 | }
43 | `);
44 | });
45 |
--------------------------------------------------------------------------------
/tests/error-properties.test.ts:
--------------------------------------------------------------------------------
1 | import { spawn } from "first-base";
2 | import { binDir } from "./_utils";
3 |
4 | test("error properties - normal Error constructor", async () => {
5 | const run = spawn(binDir("qjs"), [
6 | "-e",
7 | `
8 | const err = new Error("hi", { something: 45, somethingElse: 47 });
9 | console.log(inspect(err), inspect(Object.entries(err)));
10 | `,
11 | ]);
12 | await run.completion;
13 | expect(run.result).toMatchInlineSnapshot(`
14 | {
15 | "code": 0,
16 | "error": false,
17 | "stderr": "",
18 | "stdout": "Error {
19 | Error: hi
20 | at (:2)
21 |
22 |
23 | something: 45
24 | somethingElse: 47
25 | } [
26 | [
27 | "something"
28 | 45
29 | ]
30 | [
31 | "somethingElse"
32 | 47
33 | ]
34 | ]
35 | ",
36 | }
37 | `);
38 | });
39 |
40 | test("error properties - child Error constructor", async () => {
41 | const run = spawn(binDir("qjs"), [
42 | "-e",
43 | `
44 | const err = new TypeError("hi", { something: 45, somethingElse: 47 });
45 | console.log(inspect(err), inspect(Object.entries(err)));
46 | `,
47 | ]);
48 | await run.completion;
49 | expect(run.result).toMatchInlineSnapshot(`
50 | {
51 | "code": 0,
52 | "error": false,
53 | "stderr": "",
54 | "stdout": "TypeError {
55 | TypeError: hi
56 | at (:2)
57 |
58 |
59 | something: 45
60 | somethingElse: 47
61 | } [
62 | [
63 | "something"
64 | 45
65 | ]
66 | [
67 | "somethingElse"
68 | 47
69 | ]
70 | ]
71 | ",
72 | }
73 | `);
74 | });
75 |
76 | test("error properties - AggregateError constructor", async () => {
77 | const run = spawn(binDir("qjs"), [
78 | "-e",
79 | `
80 | const err = new AggregateError([], "hi", { something: 45, somethingElse: 47 });
81 | console.log(inspect(err), inspect(Object.entries(err)));
82 | `,
83 | ]);
84 | await run.completion;
85 | expect(run.result).toMatchInlineSnapshot(`
86 | {
87 | "code": 0,
88 | "error": false,
89 | "stderr": "",
90 | "stdout": "AggregateError {
91 | AggregateError: hi
92 | at (:2)
93 |
94 |
95 | something: 45
96 | somethingElse: 47
97 | } [
98 | [
99 | "something"
100 | 45
101 | ]
102 | [
103 | "somethingElse"
104 | 47
105 | ]
106 | ]
107 | ",
108 | }
109 | `);
110 | });
111 |
--------------------------------------------------------------------------------
/tests/exit-code-stuff.test.ts:
--------------------------------------------------------------------------------
1 | import { spawn } from "first-base";
2 | import { binDir } from "./_utils";
3 |
4 | test("setting exit code and exiting normally", async () => {
5 | const run = spawn(
6 | binDir("qjs"),
7 | [
8 | "-e",
9 | `
10 | const std = require("quickjs:std");
11 |
12 | std.setExitCode(5);
13 | `,
14 | ],
15 | { cwd: __dirname }
16 | );
17 | await run.completion;
18 | expect(run.result).toMatchInlineSnapshot(`
19 | {
20 | "code": 5,
21 | "error": false,
22 | "stderr": "",
23 | "stdout": "",
24 | }
25 | `);
26 | });
27 |
28 | test("setting exit code and exiting with std.exit", async () => {
29 | const run = spawn(
30 | binDir("qjs"),
31 | [
32 | "-e",
33 | `
34 | const std = require("quickjs:std");
35 |
36 | std.setExitCode(5);
37 | std.exit();
38 | `,
39 | ],
40 | { cwd: __dirname }
41 | );
42 | await run.completion;
43 | expect(run.result).toMatchInlineSnapshot(`
44 | {
45 | "code": 5,
46 | "error": false,
47 | "stderr": "",
48 | "stdout": "",
49 | }
50 | `);
51 | });
52 |
53 | test("overriding the set exit code by exiting with std.exit", async () => {
54 | const run = spawn(
55 | binDir("qjs"),
56 | [
57 | "-e",
58 | `
59 | const std = require("quickjs:std");
60 |
61 | std.setExitCode(5);
62 | std.exit(2);
63 | `,
64 | ],
65 | { cwd: __dirname }
66 | );
67 | await run.completion;
68 | expect(run.result).toMatchInlineSnapshot(`
69 | {
70 | "code": 2,
71 | "error": false,
72 | "stderr": "",
73 | "stdout": "",
74 | }
75 | `);
76 | });
77 |
78 | test("overriding the set exit code with unhandled exception", async () => {
79 | const run = spawn(
80 | binDir("qjs"),
81 | [
82 | "-e",
83 | `
84 | const std = require("quickjs:std");
85 |
86 | std.setExitCode(5);
87 | throw new Error("that's bad!");
88 | `,
89 | ],
90 | { cwd: __dirname }
91 | );
92 | await run.completion;
93 | expect(run.result).toMatchInlineSnapshot(`
94 | {
95 | "code": 1,
96 | "error": false,
97 | "stderr": "Error: that's bad!
98 | at (:5)
99 |
100 | ",
101 | "stdout": "",
102 | }
103 | `);
104 | });
105 |
106 | test("getting the set exit code", async () => {
107 | const run = spawn(
108 | binDir("qjs"),
109 | [
110 | "-e",
111 | `
112 | const std = require("quickjs:std");
113 |
114 | std.setExitCode(5);
115 | const code = std.getExitCode();
116 | print(code);
117 | `,
118 | ],
119 | { cwd: __dirname }
120 | );
121 | await run.completion;
122 | expect(run.result).toMatchInlineSnapshot(`
123 | {
124 | "code": 5,
125 | "error": false,
126 | "stderr": "",
127 | "stdout": "5
128 | ",
129 | }
130 | `);
131 | });
132 |
133 | test("setting the exit code multiple times (last wins)", async () => {
134 | const run = spawn(
135 | binDir("qjs"),
136 | [
137 | "-e",
138 | `
139 | const std = require("quickjs:std");
140 |
141 | std.setExitCode(5);
142 | std.setExitCode(4);
143 | std.setExitCode(0);
144 | std.setExitCode(2);
145 | `,
146 | ],
147 | { cwd: __dirname }
148 | );
149 | await run.completion;
150 | expect(run.result).toMatchInlineSnapshot(`
151 | {
152 | "code": 2,
153 | "error": false,
154 | "stderr": "",
155 | "stdout": "",
156 | }
157 | `);
158 | });
159 |
--------------------------------------------------------------------------------
/tests/file-to-bytecode.test.ts:
--------------------------------------------------------------------------------
1 | import fs from "fs";
2 | import inspect from "@suchipi/print";
3 | import { pathMarker } from "path-less-traveled";
4 | import { rm, mkdir } from "shelljs";
5 | import { spawn } from "first-base";
6 | import { binDir, rootDir } from "./_utils";
7 |
8 | const workDir = pathMarker(rootDir("build/tests/file-to-bytecode"));
9 |
10 | beforeEach(() => {
11 | rm("-rf", workDir());
12 | mkdir("-p", workDir());
13 | });
14 |
15 | test("file-to-bytecode.js works", async () => {
16 | const run = spawn(
17 | binDir("quickjs-run"),
18 | [
19 | binDir("file-to-bytecode.js"),
20 | binDir("file-to-bytecode.js"),
21 | workDir("file-to-bytecode.bin"),
22 | "file-to-bytecode.js",
23 | ],
24 | { cwd: __dirname }
25 | );
26 | await run.completion;
27 | expect(run.result).toMatchInlineSnapshot(`
28 | {
29 | "code": 0,
30 | "error": false,
31 | "stderr": "",
32 | "stdout": "",
33 | }
34 | `);
35 |
36 | const bytecodeView = inspect(
37 | fs.readFileSync(workDir("file-to-bytecode.bin"))
38 | );
39 | expect(bytecodeView).toMatchInlineSnapshot(`
40 | "Buffer [
41 | │0x00000000│ 02 14 26 66 69 6C 65 2D 74 6F 2D 62 79 74 65 63
42 | │0x00000010│ 6F 64 65 2E 6A 73 16 71 75 69 63 6B 6A 73 3A 73
43 | │0x00000020│ 74 64 20 71 75 69 63 6B 6A 73 3A 62 79 74 65 63
44 | │0x00000030│ 6F 64 65 06 73 74 64 10 42 79 74 65 63 6F 64 65
45 | │0x00000040│ 08 6D 61 69 6E 16 5F 71 75 69 63 6B 6A 73 52 75
46 | │0x00000050│ 6E 16 5F 74 68 69 73 53 63 72 69 70 74 12 69 6E
47 | │0x00000060│ 70 75 74 46 69 6C 65 14 6F 75 74 70 75 74 46 69
48 | │0x00000070│ 6C 65 1E 65 6E 63 6F 64 65 64 46 69 6C 65 4E 61
49 | │0x00000080│ 6D 65 10 62 79 74 65 63 6F 64 65 06 6F 75 74 14
50 | │0x00000090│ 73 63 72 69 70 74 41 72 67 73 F6 01 59 6F 75 20
51 | │0x000000A0│ 6D 75 73 74 20 70 61 73 73 20 74 77 6F 20 70 6F
52 | │0x000000B0│ 73 69 74 69 6F 6E 61 6C 20 61 72 67 75 6D 65 6E
53 | │0x000000C0│ 74 73 20 74 6F 20 74 68 69 73 20 73 63 72 69 70
54 | │0x000000D0│ 74 3A 20 74 68 65 20 69 6E 70 75 74 20 66 69 6C
55 | │0x000000E0│ 65 20 70 61 74 68 20 28 6A 73 29 20 61 6E 64 20
56 | │0x000000F0│ 74 68 65 20 6F 75 74 70 75 74 20 66 69 6C 65 20
57 | │0x00000100│ 70 61 74 68 20 28 62 79 74 65 63 6F 64 65 20 62
58 | │0x00000110│ 69 6E 61 72 79 29 2E 10 66 72 6F 6D 46 69 6C 65
59 | │0x00000120│ 08 6F 70 65 6E 04 77 62 0A 77 72 69 74 65 14 62
60 | │0x00000130│ 79 74 65 4C 65 6E 67 74 68 0F C6 03 02 C8 03 CA
61 | │0x00000140│ 03 00 00 02 00 FC 01 00 01 FC 01 01 0E 00 06 01
62 | │0x00000150│ A2 01 00 00 00 01 03 01 0A 00 CC 03 00 0D CE 03
63 | │0x00000160│ 01 0D D0 03 00 01 08 EA 05 C0 00 E3 29 DF EE 29
64 | │0x00000170│ C6 03 01 04 01 00 07 3E 0E 43 06 01 D0 03 00 07
65 | │0x00000180│ 00 05 02 00 AB 01 07 D2 03 01 00 20 D4 03 01 01
66 | │0x00000190│ 20 D6 03 01 02 20 D8 03 01 03 20 DA 03 01 04 20
67 | │0x000001A0│ DC 03 01 05 30 DE 03 01 06 30 CE 03 01 0C CC 03
68 | │0x000001B0│ 00 0C 61 06 00 61 05 00 61 04 00 61 03 00 61 02
69 | │0x000001C0│ 00 61 01 00 61 00 00 06 11 F2 EB 1A 7D 80 00 0E
70 | │0x000001D0│ C9 80 00 0E CA 80 00 0E CB 80 00 0E CC 80 00 0E
71 | │0x000001E0│ C3 04 83 EC 09 0E 38 F0 00 00 00 EC E0 62 02 00
72 | │0x000001F0│ 11 EA 05 0E 62 03 00 96 EA 10 38 98 00 00 00 11
73 | │0x00000200│ 04 F1 00 00 00 21 01 00 2F 62 04 00 96 EA 09 62
74 | │0x00000210│ 02 00 11 63 04 00 0E 65 00 00 42 F2 00 00 00 62
75 | │0x00000220│ 02 00 0B 62 04 00 4C ED 00 00 00 24 02 00 C3 05
76 | │0x00000230│ 65 01 00 42 F3 00 00 00 62 03 00 04 F4 00 00 00
77 | │0x00000240│ 24 02 00 C3 06 62 06 00 42 F5 00 00 00 62 05 00
78 | │0x00000250│ B5 62 05 00 41 F6 00 00 00 24 03 00 29 C6 03 0C
79 | │0x00000260│ 0C 6C 9E 27 44 3B 12 09 21 2D 80 6C 76
80 | ]"
81 | `);
82 | });
83 |
--------------------------------------------------------------------------------
/tests/fixtures/ah.txt:
--------------------------------------------------------------------------------
1 | あ
2 |
--------------------------------------------------------------------------------
/tests/fixtures/check-module-ns.js:
--------------------------------------------------------------------------------
1 | import * as engine from "quickjs:engine";
2 | import * as ns from "./exports-five";
3 |
4 | console.log("isModuleNamespace(ns):", engine.isModuleNamespace(ns));
5 | console.log("isModuleNamespace({}):", engine.isModuleNamespace({}));
6 | console.log("isModuleNamespace(null):", engine.isModuleNamespace(null));
7 | console.log("isModuleNamespace(42):", engine.isModuleNamespace(42));
8 |
--------------------------------------------------------------------------------
/tests/fixtures/exports-five.js:
--------------------------------------------------------------------------------
1 | console.log("exporting 5");
2 | export const five = 5;
3 |
--------------------------------------------------------------------------------
/tests/fixtures/import-meta-require.js:
--------------------------------------------------------------------------------
1 | // Force file to be parsed as a module
2 | export {};
3 |
4 | console.log(
5 | inspect({
6 | "import.meta.require": import.meta.require,
7 | "import.meta.require === globalThis.require":
8 | import.meta.require === globalThis.require,
9 | "import.meta.require('quickjs:bytecode')": import.meta.require(
10 | "quickjs:bytecode"
11 | ),
12 | })
13 | );
14 |
--------------------------------------------------------------------------------
/tests/fixtures/import-meta-resolve.js:
--------------------------------------------------------------------------------
1 | // Force file to be parsed as a module
2 | export {};
3 |
4 | console.log(
5 | inspect({
6 | "import.meta.resolve": import.meta.resolve,
7 | "import.meta.resolve === globalThis.require.resolve":
8 | import.meta.resolve === globalThis.require.resolve,
9 | "import.meta.resolve('./log-four')": import.meta.resolve("./log-four"),
10 | })
11 | );
12 |
--------------------------------------------------------------------------------
/tests/fixtures/load-module-via-urlget.js:
--------------------------------------------------------------------------------
1 | import * as std from "quickjs:std";
2 | import { ModuleDelegate } from "quickjs:engine";
3 |
4 | const HTTP_RE = /^https?:\/\//;
5 |
6 | const defaultResolve = ModuleDelegate.resolve;
7 | ModuleDelegate.resolve = (name, fromFile) => {
8 | if (HTTP_RE.test(name)) {
9 | return name;
10 | }
11 | return defaultResolve(name, fromFile);
12 | };
13 |
14 | const defaultRead = ModuleDelegate.read;
15 | ModuleDelegate.read = (moduleName) => {
16 | if (HTTP_RE.test(moduleName)) {
17 | return std.urlGet(moduleName);
18 | }
19 | return defaultRead(moduleName);
20 | };
21 |
22 | // preact tries to do 'self.preact = ...', so we need to make sure there's a
23 | // 'self' for it to assign onto
24 | globalThis.self = globalThis;
25 |
26 | require("https://unpkg.com/preact@10.11.3/dist/preact.min.js");
27 |
28 | // 'preact' global should now be available
29 | console.log(typeof preact.render);
30 |
--------------------------------------------------------------------------------
/tests/fixtures/log-four.js:
--------------------------------------------------------------------------------
1 | console.log(2 + 2);
2 |
--------------------------------------------------------------------------------
/tests/fixtures/module-detection/explicit-module.mjs:
--------------------------------------------------------------------------------
1 | const isScript = this === globalThis;
2 | print("isScript:", isScript);
3 |
--------------------------------------------------------------------------------
/tests/fixtures/module-detection/implicit-module-with-shebang.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env qjs
2 | import * as std from "quickjs:std";
3 |
4 | const isScript = this === globalThis;
5 | print("isScript:", isScript);
6 |
--------------------------------------------------------------------------------
/tests/fixtures/module-detection/implicit-module-with-two-shebangs.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env qjs
2 | #!/usr/bin/env qjs again
3 | import * as std from "quickjs:std";
4 |
5 | const isScript = this === globalThis;
6 | print("isScript:", isScript);
7 |
--------------------------------------------------------------------------------
/tests/fixtures/module-detection/implicit-module.js:
--------------------------------------------------------------------------------
1 | import * as std from "quickjs:std";
2 |
3 | const isScript = this === globalThis;
4 | print("isScript:", isScript);
5 |
--------------------------------------------------------------------------------
/tests/fixtures/module-detection/implicit-script.js:
--------------------------------------------------------------------------------
1 | const isScript = this === globalThis;
2 | print("isScript:", isScript);
3 |
--------------------------------------------------------------------------------
/tests/fixtures/module-hooks/load-txt.js:
--------------------------------------------------------------------------------
1 | // Have to use require instead of import so that things load in order
2 |
3 | require("./txt-compiler");
4 | require("./txt-search-extension");
5 |
6 | const mod = require("./something");
7 |
8 | console.log("content:", mod.default);
9 |
--------------------------------------------------------------------------------
/tests/fixtures/module-hooks/something.txt:
--------------------------------------------------------------------------------
1 | hello world!
2 |
--------------------------------------------------------------------------------
/tests/fixtures/module-hooks/txt-compiler.js:
--------------------------------------------------------------------------------
1 | import { ModuleDelegate } from "quickjs:engine";
2 |
3 | ModuleDelegate.compilers[".txt"] = (filename, content) => {
4 | return `export default ${JSON.stringify(content)};`;
5 | };
6 |
--------------------------------------------------------------------------------
/tests/fixtures/module-hooks/txt-search-extension.js:
--------------------------------------------------------------------------------
1 | import { ModuleDelegate } from "quickjs:engine";
2 |
3 | ModuleDelegate.searchExtensions.push(".txt");
4 |
--------------------------------------------------------------------------------
/tests/fixtures/module-ns-mutation/loads-nothing-then-new.js:
--------------------------------------------------------------------------------
1 | import "./loads-nothing";
2 | import "./loads-new";
3 |
--------------------------------------------------------------------------------
/tests/fixtures/module-ns-mutation/loads-nothing.js:
--------------------------------------------------------------------------------
1 | import {} from "./subject";
2 |
--------------------------------------------------------------------------------
/tests/fixtures/module-throws-error/esm.js:
--------------------------------------------------------------------------------
1 | export const something = {};
2 |
3 | throw new Error("that ain't good");
4 |
--------------------------------------------------------------------------------
/tests/fixtures/regexp-escaped-dash-in-charset.js:
--------------------------------------------------------------------------------
1 | const regex = /([\w\-:])/suy;
2 | print(regex);
3 |
--------------------------------------------------------------------------------
/tests/fixtures/setting-main-module/mod1_import.js:
--------------------------------------------------------------------------------
1 | import * as engine from "quickjs:engine";
2 |
3 | console.log("hi from mod1", import.meta.main);
4 |
5 | engine.setMainModule(engine.resolveModule("./mod3"));
6 |
7 | import "./mod2";
8 | import "./mod3";
9 |
--------------------------------------------------------------------------------
/tests/fixtures/setting-main-module/mod1_importModule.js:
--------------------------------------------------------------------------------
1 | import * as engine from "quickjs:engine";
2 |
3 | console.log("hi from mod1", import.meta.main);
4 |
5 | engine.setMainModule(engine.resolveModule("./mod3"));
6 |
7 | engine.importModule("./mod2");
8 | engine.importModule("./mod3");
9 |
--------------------------------------------------------------------------------
/tests/fixtures/setting-main-module/mod2.js:
--------------------------------------------------------------------------------
1 | console.log("hi from mod2", import.meta.main);
2 |
--------------------------------------------------------------------------------
/tests/fixtures/setting-main-module/mod3.js:
--------------------------------------------------------------------------------
1 | console.log("hi from mod3", import.meta.main);
2 |
--------------------------------------------------------------------------------
/tests/fixtures/string-cooked.js:
--------------------------------------------------------------------------------
1 | const raw = String.raw`C:\Program Files (x86)\Steam\steamapps\common`;
2 | const cooked = String.cooked`C:\Program Files (x86)\Steam\steamapps\common`;
3 |
4 | function myUpperCaseTag(strings, ...keys) {
5 | return String.cooked(
6 | strings.map((str) => str.toUpperCase()),
7 | ...keys.map((k) => String(k).toUpperCase())
8 | );
9 | }
10 |
11 | const upperCased = myUpperCaseTag`hello \there ${"yeah!!"} yay ${{}} woo`;
12 |
13 | console.log(
14 | inspect({
15 | raw,
16 | cooked,
17 | upperCased,
18 | })
19 | );
20 |
--------------------------------------------------------------------------------
/tests/fixtures/subdir/another-subdir/index.js:
--------------------------------------------------------------------------------
1 | console.log("hello there");
2 |
--------------------------------------------------------------------------------
/tests/fixtures/subdir/imports-std.js:
--------------------------------------------------------------------------------
1 | import * as std from "quickjs:std";
2 |
3 | console.log(typeof std.exit);
4 |
--------------------------------------------------------------------------------
/tests/fixtures/subdir/log-five.js:
--------------------------------------------------------------------------------
1 | console.log(5);
2 |
--------------------------------------------------------------------------------
/tests/fixtures/subdir/reaches-for-folder-with-index.js:
--------------------------------------------------------------------------------
1 | import "./another-subdir";
2 |
--------------------------------------------------------------------------------
/tests/fixtures/subdir/reaches-over.js:
--------------------------------------------------------------------------------
1 | import "./log-five";
2 |
--------------------------------------------------------------------------------
/tests/fixtures/subdir/reaches-up.js:
--------------------------------------------------------------------------------
1 | import "../log-four";
2 |
--------------------------------------------------------------------------------
/tests/fixtures/uses-std-and-bytecode.js:
--------------------------------------------------------------------------------
1 | import * as std from "quickjs:std";
2 | import * as Bytecode from "quickjs:std";
3 |
4 | console.log("typeof std:", typeof std);
5 | console.log("typeof Bytecode:", typeof Bytecode);
6 |
--------------------------------------------------------------------------------
/tests/fixtures/with-cjs-export.js:
--------------------------------------------------------------------------------
1 | export const four = 4;
2 | export const five = 5;
3 |
4 | export default function nine() {
5 | return four + five;
6 | }
7 |
8 | export const __cjsExports = Object.assign(() => nine(), {
9 | four,
10 | five,
11 | });
12 |
--------------------------------------------------------------------------------
/tests/fixtures/without-cjs-export.js:
--------------------------------------------------------------------------------
1 | export const four = 4;
2 | export const five = 5;
3 |
4 | export default function nine() {
5 | return four + five;
6 | }
7 |
--------------------------------------------------------------------------------
/tests/getpwuid.test.ts:
--------------------------------------------------------------------------------
1 | import { spawn } from "first-base";
2 | import { binDir } from "./_utils";
3 |
4 | test("pwuid", async () => {
5 | const run = spawn(binDir("qjs"), [
6 | "-e",
7 | `
8 | const std = require("quickjs:std");
9 |
10 | console.log("arity", std.getpwuid.length);
11 | const pwd = std.getpwuid(std.geteuid());
12 | console.log(JSON.stringify(Object.keys(pwd)));
13 | console.log("name", typeof pwd.name);
14 | console.log("passwd", typeof pwd.passwd);
15 | console.log("uid", typeof pwd.uid);
16 | console.log("gid", typeof pwd.gid);
17 | console.log("gecos", typeof pwd.gecos);
18 | console.log("dir", typeof pwd.dir);
19 | console.log("shell", typeof pwd.shell);
20 | `,
21 | ]);
22 | await run.completion;
23 | expect(run.result).toMatchInlineSnapshot(`
24 | {
25 | "code": 0,
26 | "error": false,
27 | "stderr": "",
28 | "stdout": "arity 1
29 | ["name","passwd","uid","gid","gecos","dir","shell"]
30 | name string
31 | passwd string
32 | uid number
33 | gid number
34 | gecos string
35 | dir string
36 | shell string
37 | ",
38 | }
39 | `);
40 | });
41 |
--------------------------------------------------------------------------------
/tests/import-error.test.ts:
--------------------------------------------------------------------------------
1 | import { spawn } from "first-base";
2 | import { binDir } from "./_utils";
3 |
4 | test("module with error is gc'd properly: esm", async () => {
5 | const run = spawn(
6 | binDir("qjs"),
7 | [
8 | "-e",
9 | `
10 | const std = require("quickjs:std");
11 | try {
12 | require("./fixtures/module-throws-error/esm.js");
13 | } catch (err) {
14 | console.error(err);
15 | }
16 | require("./fixtures/log-four");
17 | `,
18 | ],
19 | { cwd: __dirname }
20 | );
21 | await run.completion;
22 | expect(run.result).toMatchInlineSnapshot(`
23 | {
24 | "code": 0,
25 | "error": false,
26 | "stderr": "Error: that ain't good
27 | ",
28 | "stdout": "4
29 | ",
30 | }
31 | `);
32 | });
33 |
--------------------------------------------------------------------------------
/tests/import-meta-require.test.ts:
--------------------------------------------------------------------------------
1 | import { spawn } from "first-base";
2 | import { binDir, rootDir } from "./_utils";
3 |
4 | const fixture = rootDir("tests/fixtures/import-meta-require.js");
5 |
6 | test("import.meta.require", async () => {
7 | const run = spawn(binDir("quickjs-run"), [fixture]);
8 | await run.completion;
9 | expect(run.result).toMatchInlineSnapshot(`
10 | {
11 | "code": 0,
12 | "error": false,
13 | "stderr": "",
14 | "stdout": "{
15 | import.meta.require: Function "require" {
16 | resolve: Function "require.resolve" {
17 | │1│ function require.resolve() {
18 | │2│ [native code]
19 | │3│ }
20 | }
21 |
22 | │1│ function require() {
23 | │2│ [native code]
24 | │3│ }
25 | }
26 | import.meta.require === globalThis.require: true
27 | import.meta.require('quickjs:bytecode'): {
28 | Null prototype
29 | Sealed
30 |
31 | fromFile: Function "fromFile" {
32 | │1│ function fromFile() {
33 | │2│ [native code]
34 | │3│ }
35 | }
36 | fromValue: Function "fromValue" {
37 | │1│ function fromValue() {
38 | │2│ [native code]
39 | │3│ }
40 | }
41 | toValue: Function "toValue" {
42 | │1│ function toValue() {
43 | │2│ [native code]
44 | │3│ }
45 | }
46 | }
47 | }
48 | ",
49 | }
50 | `);
51 | });
52 |
--------------------------------------------------------------------------------
/tests/import-meta-resolve.test.ts:
--------------------------------------------------------------------------------
1 | import { spawn } from "first-base";
2 | import { binDir, rootDir, cleanResult } from "./_utils";
3 |
4 | const fixture = rootDir("tests/fixtures/import-meta-resolve.js");
5 |
6 | test("import.meta.resolve", async () => {
7 | const run = spawn(binDir("quickjs-run"), [fixture]);
8 | await run.completion;
9 | expect(cleanResult(run.result)).toMatchInlineSnapshot(`
10 | {
11 | "code": 0,
12 | "error": false,
13 | "stderr": "",
14 | "stdout": "{
15 | import.meta.resolve: Function "require.resolve" {
16 | │1│ function require.resolve() {
17 | │2│ [native code]
18 | │3│ }
19 | }
20 | import.meta.resolve === globalThis.require.resolve: true
21 | import.meta.resolve('./log-four'): "/tests/fixtures/log-four.js"
22 | }
23 | ",
24 | }
25 | `);
26 | });
27 |
--------------------------------------------------------------------------------
/tests/inspect-custom.test.ts:
--------------------------------------------------------------------------------
1 | import { spawn } from "first-base";
2 | import { binDir, rootDir } from "./_utils";
3 |
4 | test("inspect.custom", async () => {
5 | const run = spawn(binDir("qjs"), [
6 | "-e",
7 | `
8 | const obj = {
9 | something: {
10 | theSomething: true,
11 | [inspect.custom]: (inputs) => {
12 | inputs.brackets = ["<", ">"];
13 | inputs.linesBefore.push("yup");
14 | inputs.linesAfter.push("yeah!");
15 | }
16 | }
17 | }
18 |
19 | print(inspect(obj));
20 | `,
21 | ]);
22 | await run.completion;
23 | expect(run.result).toMatchInlineSnapshot(`
24 | {
25 | "code": 0,
26 | "error": false,
27 | "stderr": "",
28 | "stdout": "{
29 | something: <
30 | yup
31 |
32 | theSomething: true
33 | Symbol(inspect.custom): Function "[inspect.custom]" {
34 | │1│ (inputs) => {
35 | │2│ inputs.brackets = ["<", ">"];
36 | │3│ inputs.linesBefore.push("yup");
37 | │4│ inputs.linesAfter.push("yeah!");
38 | │5│ }
39 | }
40 |
41 | yeah!
42 | >
43 | }
44 | ",
45 | }
46 | `);
47 | });
48 |
--------------------------------------------------------------------------------
/tests/inspect.test.ts:
--------------------------------------------------------------------------------
1 | import { spawn } from "first-base";
2 | import { binDir, rootDir } from "./_utils";
3 |
4 | test("inspect", async () => {
5 | const run = spawn(binDir("qjs"), [
6 | "-e",
7 | `
8 | const obj = {
9 | something: {
10 | theSomething: true,
11 | }
12 | }
13 |
14 | print(inspect(obj));
15 | `,
16 | ]);
17 | await run.completion;
18 | expect(run.result).toMatchInlineSnapshot(`
19 | {
20 | "code": 0,
21 | "error": false,
22 | "stderr": "",
23 | "stdout": "{
24 | something: {
25 | theSomething: true
26 | }
27 | }
28 | ",
29 | }
30 | `);
31 | });
32 |
--------------------------------------------------------------------------------
/tests/libbytecode.test.ts:
--------------------------------------------------------------------------------
1 | import { spawn } from "first-base";
2 | import { binDir, rootDir } from "./_utils";
3 |
4 | test("bytecode - normal values", async () => {
5 | const run = spawn(
6 | binDir("qjs"),
7 | [
8 | "-e",
9 | `
10 | const bytecode = require("quickjs:bytecode");
11 | function log(label, arg) { console.log(label, inspect(arg)); }
12 |
13 | const bc1 = bytecode.fromValue(7);
14 | log("fromValue 7", bc1);
15 | log("back toValue", bytecode.toValue(bc1));
16 |
17 | const bc2 = bytecode.fromValue({ a: 5 });
18 | log("fromValue { a: 5 }", bc2);
19 | log("back toValue", bytecode.toValue(bc2));
20 | `,
21 | ],
22 | { cwd: rootDir() }
23 | );
24 | await run.completion;
25 | expect(run.result).toMatchInlineSnapshot(`
26 | {
27 | "code": 0,
28 | "error": false,
29 | "stderr": "",
30 | "stdout": "fromValue 7 ArrayBuffer {
31 | │0x00000000│ 02 00 05 0E
32 | }
33 | back toValue 7
34 | fromValue { a: 5 } ArrayBuffer {
35 | │0x00000000│ 02 01 02 61 08 01 C6 03 05 0A
36 | }
37 | back toValue {
38 | a: 5
39 | }
40 | ",
41 | }
42 | `);
43 | });
44 |
45 | test("bytecode - script file", async () => {
46 | const run = spawn(
47 | binDir("qjs"),
48 | [
49 | "-e",
50 | `
51 | const bytecode = require("quickjs:bytecode");
52 | function log(label, arg) { console.log(label, inspect(arg)); }
53 |
54 | const bc = bytecode.fromFile("tests/fixtures/log-four.js");
55 | log("fromFile", bc);
56 | const reified = bytecode.toValue(bc);
57 | log("toValue", reified);
58 | const result = reified();
59 | log("result", result);
60 | `,
61 | ],
62 | { cwd: rootDir() }
63 | );
64 | await run.completion;
65 | expect(run.result).toMatchInlineSnapshot(`
66 | {
67 | "code": 0,
68 | "error": false,
69 | "stderr": "",
70 | "stdout": "fromFile ArrayBuffer {
71 | │0x00000000│ 02 03 0E 63 6F 6E 73 6F 6C 65 06 6C 6F 67 34 74
72 | │0x00000010│ 65 73 74 73 2F 66 69 78 74 75 72 65 73 2F 6C 6F
73 | │0x00000020│ 67 2D 66 6F 75 72 2E 6A 73 0E 00 06 00 A2 01 00
74 | │0x00000030│ 01 00 04 00 00 12 01 A4 01 00 00 00 38 E3 00 00
75 | │0x00000040│ 00 42 E4 00 00 00 B7 B7 9D 24 01 00 CD 28 CA 03
76 | │0x00000050│ 01 00
77 | }
78 | toValue Function "bound bytecode" {
79 | │1│ function bound bytecode() {
80 | │2│ [native code]
81 | │3│ }
82 | }
83 | 4
84 | result undefined
85 | ",
86 | }
87 | `);
88 | });
89 |
90 | test("bytecode - module file", async () => {
91 | const run = spawn(
92 | binDir("qjs"),
93 | [
94 | "-e",
95 | `
96 | const bytecode = require("quickjs:bytecode");
97 | function log(label, arg) { console.log(label, inspect(arg)); }
98 |
99 | const bc = bytecode.fromFile("tests/fixtures/exports-five.js", { sourceType: "module" });
100 | log("fromFile", bc);
101 | const reified = bytecode.toValue(bc);
102 | log("toValue", reified);
103 | const result = reified();
104 | log("result", result);
105 | `,
106 | ],
107 | { cwd: rootDir() }
108 | );
109 | await run.completion;
110 | expect(run.result).toMatchInlineSnapshot(`
111 | {
112 | "code": 0,
113 | "error": false,
114 | "stderr": "",
115 | "stdout": "fromFile ArrayBuffer {
116 | │0x00000000│ 02 05 3C 74 65 73 74 73 2F 66 69 78 74 75 72 65
117 | │0x00000010│ 73 2F 65 78 70 6F 72 74 73 2D 66 69 76 65 2E 6A
118 | │0x00000020│ 73 08 66 69 76 65 0E 63 6F 6E 73 6F 6C 65 06 6C
119 | │0x00000030│ 6F 67 16 65 78 70 6F 72 74 69 6E 67 20 35 0F C6
120 | │0x00000040│ 03 00 01 00 00 C8 03 00 00 0E 00 06 01 A2 01 00
121 | │0x00000050│ 00 00 03 01 00 1A 00 C8 03 00 0D 08 EA 02 29 38
122 | │0x00000060│ E5 00 00 00 42 E6 00 00 00 04 E7 00 00 00 24 01
123 | │0x00000070│ 00 0E BA E1 29 C6 03 01 03 01 17 62
124 | }
125 | toValue Function "bound bytecode" {
126 | │1│ function bound bytecode() {
127 | │2│ [native code]
128 | │3│ }
129 | }
130 | exporting 5
131 | result undefined
132 | ",
133 | }
134 | `);
135 | });
136 |
--------------------------------------------------------------------------------
/tests/libcontext.test.ts:
--------------------------------------------------------------------------------
1 | import { spawn } from "first-base";
2 | import { binDir, rootDir } from "./_utils";
3 |
4 | test("quickjs:context", async () => {
5 | const run = spawn(binDir("qjs"), [rootDir("examples/contexts.js")]);
6 | await run.completion;
7 | expect(run.result).toMatchInlineSnapshot(`
8 | {
9 | "code": 0,
10 | "error": false,
11 | "stderr": "TypeError: eval is not supported
12 | ReferenceError: 'console' is not defined
13 | TypeError: eval is not supported
14 | ",
15 | "stdout": "c1
16 | true
17 | false
18 | c2
19 | true
20 | false
21 | false
22 | c2 require
23 | function require() {
24 | [native code]
25 | }
26 | c3
27 | Object {
28 | Object: Function "Object" {…}
29 | Function: Function "Function" {…}
30 | Error: Function "Error" {…}
31 | EvalError: Function "EvalError" {…}
32 | RangeError: Function "RangeError" {…}
33 | ReferenceError: Function "ReferenceError" {…}
34 | SyntaxError: Function "SyntaxError" {…}
35 | TypeError: Function "TypeError" {…}
36 | URIError: Function "URIError" {…}
37 | InternalError: Function "InternalError" {…}
38 | AggregateError: Function "AggregateError" {…}
39 | Array: Function "Array" {…}
40 | parseInt: Function "parseInt" {…}
41 | parseFloat: Function "parseFloat" {…}
42 | isNaN: Function "isNaN" {…}
43 | isFinite: Function "isFinite" {…}
44 | decodeURI: Function "decodeURI" {…}
45 | decodeURIComponent: Function "decodeURIComponent" {…}
46 | encodeURI: Function "encodeURI" {…}
47 | encodeURIComponent: Function "encodeURIComponent" {…}
48 | escape: Function "escape" {…}
49 | unescape: Function "unescape" {…}
50 | Infinity: Infinity
51 | NaN: NaN
52 | undefined: undefined
53 | __date_clock: Function "__date_clock" {…}
54 | Number: Function "Number" {…}
55 | Boolean: Function "Boolean" {…}
56 | String: Function "String" {…}
57 | Math: Object {…}
58 | Reflect: Object {…}
59 | Symbol: Function "Symbol" {…}
60 | eval: Function "eval" {…}
61 | globalThis: -> {root}
62 | }
63 | true
64 | ",
65 | }
66 | `);
67 | });
68 |
--------------------------------------------------------------------------------
/tests/libencoding.test.ts:
--------------------------------------------------------------------------------
1 | import { spawn } from "first-base";
2 | import { binDir, fixturesDir } from "./_utils";
3 |
4 | test("quickjs:encoding - basic test", async () => {
5 | const run = spawn(binDir("qjs"), [
6 | "-e",
7 | `
8 | const std = require("quickjs:std");
9 | const enc = require("quickjs:encoding");
10 |
11 | const file = std.open(${JSON.stringify(fixturesDir("ah.txt"))}, "rb");
12 | const buffer = new ArrayBuffer(8);
13 | const result = file.read(buffer, 0, 8);
14 | console.log(inspect({
15 | result,
16 | buffer,
17 | }));
18 |
19 | const asStr = enc.toUtf8(buffer);
20 | console.log(inspect({ asStr }));
21 | `,
22 | ]);
23 | await run.completion;
24 | expect(run.result).toMatchInlineSnapshot(`
25 | {
26 | "code": 0,
27 | "error": false,
28 | "stderr": "",
29 | "stdout": "{
30 | result: 0
31 | buffer: ArrayBuffer {
32 | │0x00000000│ E3 81 82 0A 00 00 00 00
33 | }
34 | }
35 | {
36 | asStr: "あ\\n\\0\\0\\0\\0"
37 | }
38 | ",
39 | }
40 | `);
41 | });
42 |
43 | test("quickjs:encoding - fromUtf8 is inverse of toUtf8", async () => {
44 | const run = spawn(binDir("qjs"), [
45 | "-e",
46 | `
47 | import * as encoding from "quickjs:encoding";
48 |
49 | const input = "hi あ";
50 | const fromUtf8 = encoding.fromUtf8(input);
51 | const toUtf8 = encoding.toUtf8(fromUtf8);
52 | console.log(
53 | inspect({
54 | input,
55 | fromUtf8,
56 | toUtf8,
57 | })
58 | );
59 | `,
60 | ]);
61 | await run.completion;
62 | expect(run.result).toMatchInlineSnapshot(`
63 | {
64 | "code": 0,
65 | "error": false,
66 | "stderr": "",
67 | "stdout": "{
68 | input: "hi あ"
69 | fromUtf8: ArrayBuffer {
70 | │0x00000000│ 68 69 20 E3 81 82
71 | }
72 | toUtf8: "hi あ"
73 | }
74 | ",
75 | }
76 | `);
77 | });
78 |
--------------------------------------------------------------------------------
/tests/libmodule.test.ts:
--------------------------------------------------------------------------------
1 | import { spawn } from "first-base";
2 | import { binDir, rootDir } from "./_utils";
3 |
4 | test("isModuleNamespace", async () => {
5 | const fixture = rootDir("tests/fixtures/check-module-ns.js");
6 |
7 | const run = spawn(binDir("qjs"), [fixture], { cwd: rootDir() });
8 | await run.completion;
9 | expect(run.result).toMatchInlineSnapshot(`
10 | {
11 | "code": 0,
12 | "error": false,
13 | "stderr": "",
14 | "stdout": "exporting 5
15 | isModuleNamespace(ns): true
16 | isModuleNamespace({}): false
17 | isModuleNamespace(null): false
18 | isModuleNamespace(42): false
19 | ",
20 | }
21 | `);
22 | });
23 |
--------------------------------------------------------------------------------
/tests/load-module-via-urlget.test.ts:
--------------------------------------------------------------------------------
1 | import { spawn } from "first-base";
2 | import { binDir } from "./_utils";
3 |
4 | test("using custom Module.resolve and custom Module.read to import stuff via std.urlGet", async () => {
5 | const run = spawn(binDir("qjs"), ["./fixtures/load-module-via-urlget.js"], {
6 | cwd: __dirname,
7 | });
8 | await run.completion;
9 | expect(run.result).toMatchInlineSnapshot(`
10 | {
11 | "code": 0,
12 | "error": false,
13 | "stderr": "",
14 | "stdout": "function
15 | ",
16 | }
17 | `);
18 | });
19 |
--------------------------------------------------------------------------------
/tests/module-define.test.ts:
--------------------------------------------------------------------------------
1 | import { spawn } from "first-base";
2 | import { binDir } from "./_utils";
3 |
4 | test("defineBuiltinModule - basic test", async () => {
5 | const run = spawn(binDir("qjs"), [
6 | "-m",
7 | "-e",
8 | `
9 | import { defineBuiltinModule, ModuleDelegate } from "quickjs:engine";
10 |
11 | console.log("builtins contains mymodule:", ModuleDelegate.builtinModuleNames.includes("mymodule"));
12 |
13 | defineBuiltinModule("mymodule", {
14 | something: 5,
15 | somethingElse: () => 6
16 | });
17 |
18 | console.log("builtins contains mymodule:", ModuleDelegate.builtinModuleNames.includes("mymodule"));
19 |
20 | const mod = require("mymodule");
21 | console.log(mod.something);
22 | console.log(mod.somethingElse());
23 | `,
24 | ]);
25 | await run.completion;
26 | expect(run.result).toMatchInlineSnapshot(`
27 | {
28 | "code": 0,
29 | "error": false,
30 | "stderr": "",
31 | "stdout": "builtins contains mymodule: false
32 | builtins contains mymodule: true
33 | 5
34 | 6
35 | ",
36 | }
37 | `);
38 | });
39 |
40 | test("defineBuiltinModule - never imported", async () => {
41 | const run = spawn(binDir("qjs"), [
42 | "-m",
43 | "-e",
44 | `
45 | import { defineBuiltinModule, ModuleDelegate } from "quickjs:engine";
46 |
47 | console.log("builtins contains mymodule:", ModuleDelegate.builtinModuleNames.includes("mymodule"));
48 |
49 | defineBuiltinModule("mymodule", {
50 | something: 5,
51 | somethingElse: () => 6
52 | });
53 |
54 | console.log("builtins contains mymodule:", ModuleDelegate.builtinModuleNames.includes("mymodule"));
55 | `,
56 | ]);
57 | await run.completion;
58 | expect(run.result).toMatchInlineSnapshot(`
59 | {
60 | "code": 0,
61 | "error": false,
62 | "stderr": "",
63 | "stdout": "builtins contains mymodule: false
64 | builtins contains mymodule: true
65 | ",
66 | }
67 | `);
68 | });
69 |
--------------------------------------------------------------------------------
/tests/module-detect.test.ts:
--------------------------------------------------------------------------------
1 | import { pathMarker } from "path-less-traveled";
2 | import { spawn } from "first-base";
3 | import { binDir, rootDir } from "./_utils";
4 |
5 | const moduleDetectionFixturesDir = pathMarker(
6 | rootDir("tests/fixtures/module-detection")
7 | );
8 |
9 | async function runFile(target: string) {
10 | const run = spawn(binDir("quickjs-run"), [
11 | moduleDetectionFixturesDir(target),
12 | ]);
13 | await run.completion;
14 | return run.result;
15 | }
16 |
17 | test("module detection works as intended", async () => {
18 | expect(await runFile("explicit-module.mjs")).toMatchInlineSnapshot(`
19 | {
20 | "code": 0,
21 | "error": false,
22 | "stderr": "",
23 | "stdout": "isScript: false
24 | ",
25 | }
26 | `);
27 |
28 | expect(await runFile("implicit-module-with-shebang.js"))
29 | .toMatchInlineSnapshot(`
30 | {
31 | "code": 0,
32 | "error": false,
33 | "stderr": "",
34 | "stdout": "isScript: false
35 | ",
36 | }
37 | `);
38 |
39 | expect(await runFile("implicit-module-with-two-shebangs.js"))
40 | .toMatchInlineSnapshot(`
41 | {
42 | "code": 0,
43 | "error": false,
44 | "stderr": "",
45 | "stdout": "isScript: false
46 | ",
47 | }
48 | `);
49 |
50 | expect(await runFile("implicit-module.js")).toMatchInlineSnapshot(`
51 | {
52 | "code": 0,
53 | "error": false,
54 | "stderr": "",
55 | "stdout": "isScript: false
56 | ",
57 | }
58 | `);
59 |
60 | expect(await runFile("implicit-script.js")).toMatchInlineSnapshot(`
61 | {
62 | "code": 0,
63 | "error": false,
64 | "stderr": "",
65 | "stdout": "isScript: true
66 | ",
67 | }
68 | `);
69 | });
70 |
--------------------------------------------------------------------------------
/tests/module-hooks.test.ts:
--------------------------------------------------------------------------------
1 | import { spawn } from "first-base";
2 | import { binDir, rootDir } from "./_utils";
3 |
4 | test("ModuleDelegate.compilers and ModuleDelegate.searchExtensions", async () => {
5 | const run = spawn(
6 | binDir("qjs"),
7 | ["tests/fixtures/module-hooks/load-txt.js"],
8 | { cwd: rootDir() }
9 | );
10 | await run.completion;
11 | expect(run.result).toMatchInlineSnapshot(`
12 | {
13 | "code": 0,
14 | "error": false,
15 | "stderr": "",
16 | "stdout": "content: hello world!
17 |
18 | ",
19 | }
20 | `);
21 | });
22 |
--------------------------------------------------------------------------------
/tests/module-read.test.ts:
--------------------------------------------------------------------------------
1 | import { spawn } from "first-base";
2 | import { binDir } from "./_utils";
3 |
4 | describe("ModuleDelegate.read", () => {
5 | test("default impl returns file content", async () => {
6 | const run = spawn(
7 | binDir("qjs"),
8 | [
9 | "-e",
10 | `
11 | require("./fixtures/log-four.js");
12 | `,
13 | ],
14 | { cwd: __dirname }
15 | );
16 | await run.completion;
17 | expect(run.result).toMatchInlineSnapshot(`
18 | {
19 | "code": 0,
20 | "error": false,
21 | "stderr": "",
22 | "stdout": "4
23 | ",
24 | }
25 | `);
26 | });
27 |
28 | test("custom impl can override", async () => {
29 | const run = spawn(
30 | binDir("qjs"),
31 | [
32 | "-m",
33 | "-e",
34 | `
35 | import { ModuleDelegate } from "quickjs:engine";
36 |
37 | ModuleDelegate.read = (path) => {
38 | console.error(path);
39 | return "console.log(17);"
40 | };
41 |
42 | require("./fixtures/log-four");
43 | `,
44 | ],
45 | { cwd: __dirname }
46 | );
47 | await run.completion;
48 | run.result.stderr = run.result.stderr.replace(__dirname, "<__dirname>");
49 | expect(run.result).toMatchInlineSnapshot(`
50 | {
51 | "code": 0,
52 | "error": false,
53 | "stderr": "<__dirname>/fixtures/log-four.js
54 | ",
55 | "stdout": "17
56 | ",
57 | }
58 | `);
59 | });
60 | });
61 |
--------------------------------------------------------------------------------
/tests/module-resolution.test.ts:
--------------------------------------------------------------------------------
1 | import { spawn } from "first-base";
2 | import { binDir } from "./_utils";
3 |
4 | describe("module resolution", () => {
5 | test("can resolve sibling files", async () => {
6 | const run = spawn(binDir("qjs"), ["./fixtures/subdir/reaches-over.js"], {
7 | cwd: __dirname,
8 | });
9 | await run.completion;
10 | expect(run.result).toMatchInlineSnapshot(`
11 | {
12 | "code": 0,
13 | "error": false,
14 | "stderr": "",
15 | "stdout": "5
16 | ",
17 | }
18 | `);
19 | });
20 |
21 | test("can resolve parent-dir files", async () => {
22 | const run = spawn(binDir("qjs"), ["./fixtures/subdir/reaches-up.js"], {
23 | cwd: __dirname,
24 | });
25 | await run.completion;
26 | expect(run.result).toMatchInlineSnapshot(`
27 | {
28 | "code": 0,
29 | "error": false,
30 | "stderr": "",
31 | "stdout": "4
32 | ",
33 | }
34 | `);
35 | });
36 |
37 | test("can resolve files with an index.js inside", async () => {
38 | const run = spawn(
39 | binDir("qjs"),
40 | ["./fixtures/subdir/reaches-for-folder-with-index.js"],
41 | {
42 | cwd: __dirname,
43 | }
44 | );
45 | await run.completion;
46 | expect(run.result).toMatchInlineSnapshot(`
47 | {
48 | "code": 0,
49 | "error": false,
50 | "stderr": "",
51 | "stdout": "hello there
52 | ",
53 | }
54 | `);
55 | });
56 |
57 | test("can resolve builtin modules", async () => {
58 | const run = spawn(binDir("qjs"), ["./fixtures/subdir/imports-std.js"], {
59 | cwd: __dirname,
60 | });
61 | await run.completion;
62 | expect(run.result).toMatchInlineSnapshot(`
63 | {
64 | "code": 0,
65 | "error": false,
66 | "stderr": "",
67 | "stdout": "function
68 | ",
69 | }
70 | `);
71 | });
72 | });
73 |
--------------------------------------------------------------------------------
/tests/object-toprimitive.test.ts:
--------------------------------------------------------------------------------
1 | import { spawn } from "first-base";
2 | import { binDir } from "./_utils";
3 |
4 | test("Object.toPrimitive - normal object", async () => {
5 | const run = spawn(
6 | binDir("qjs"),
7 | [
8 | "-e",
9 | `
10 | console.log(JSON.stringify([
11 | Object.toPrimitive({}),
12 | Object.toPrimitive({}, "number"),
13 | Object.toPrimitive({}, "string"),
14 | Object.toPrimitive({}, "default"),
15 | ], null, 2));
16 | `,
17 | ],
18 | { cwd: __dirname }
19 | );
20 | await run.completion;
21 | expect(run.result).toMatchInlineSnapshot(`
22 | {
23 | "code": 0,
24 | "error": false,
25 | "stderr": "",
26 | "stdout": "[
27 | "[object Object]",
28 | "[object Object]",
29 | "[object Object]",
30 | "[object Object]"
31 | ]
32 | ",
33 | }
34 | `);
35 | });
36 |
37 | test("Object.toPrimitive - string", async () => {
38 | const run = spawn(
39 | binDir("qjs"),
40 | [
41 | "-e",
42 | `
43 | console.log(JSON.stringify([
44 | Object.toPrimitive("47"),
45 | Object.toPrimitive("47", "number"),
46 | Object.toPrimitive("47", "string"),
47 | Object.toPrimitive("47", "default"),
48 | ], null, 2));
49 | `,
50 | ],
51 | { cwd: __dirname }
52 | );
53 | await run.completion;
54 | expect(run.result).toMatchInlineSnapshot(`
55 | {
56 | "code": 0,
57 | "error": false,
58 | "stderr": "",
59 | "stdout": "[
60 | "47",
61 | "47",
62 | "47",
63 | "47"
64 | ]
65 | ",
66 | }
67 | `);
68 | });
69 |
70 | test("Object.toPrimitive - interaction with Symbol.toPrimitive", async () => {
71 | const run = spawn(
72 | binDir("qjs"),
73 | [
74 | "-e",
75 | `
76 | const obj = {
77 | [Symbol.toPrimitive](hint) {
78 | return "hi - " + hint;
79 | }
80 | }
81 |
82 | console.log(JSON.stringify([
83 | Object.toPrimitive(obj),
84 | Object.toPrimitive(obj, "number"),
85 | Object.toPrimitive(obj, "string"),
86 | Object.toPrimitive(obj, "default"),
87 | ], null, 2));
88 | `,
89 | ],
90 | { cwd: __dirname }
91 | );
92 | await run.completion;
93 | expect(run.result).toMatchInlineSnapshot(`
94 | {
95 | "code": 0,
96 | "error": false,
97 | "stderr": "",
98 | "stdout": "[
99 | "hi - default",
100 | "hi - number",
101 | "hi - string",
102 | "hi - default"
103 | ]
104 | ",
105 | }
106 | `);
107 | });
108 |
--------------------------------------------------------------------------------
/tests/oldtests/oldtests.test.ts:
--------------------------------------------------------------------------------
1 | import { spawn } from "first-base";
2 | import { binDir } from "../_utils";
3 |
4 | async function qjs(args: Array) {
5 | const run = spawn(binDir("qjs"), args, { cwd: __dirname });
6 | await run.completion;
7 | return run.result;
8 | }
9 |
10 | describe("oldtests", () => {
11 | test("test_bignum.js", async () => {
12 | expect(await qjs(["--bignum", "./test_bignum.js"])).toMatchInlineSnapshot(`
13 | {
14 | "code": 0,
15 | "error": false,
16 | "stderr": "",
17 | "stdout": "",
18 | }
19 | `);
20 | });
21 |
22 | test("test_builtin.js", async () => {
23 | expect(await qjs(["./test_builtin.js"])).toMatchInlineSnapshot(`
24 | {
25 | "code": 0,
26 | "error": false,
27 | "stderr": "",
28 | "stdout": "",
29 | }
30 | `);
31 | });
32 |
33 | test("test_closure.js", async () => {
34 | expect(await qjs(["./test_closure.js"])).toMatchInlineSnapshot(`
35 | {
36 | "code": 0,
37 | "error": false,
38 | "stderr": "",
39 | "stdout": "",
40 | }
41 | `);
42 | });
43 |
44 | test("test_language.js", async () => {
45 | expect(await qjs(["./test_language.js"])).toMatchInlineSnapshot(`
46 | {
47 | "code": 0,
48 | "error": false,
49 | "stderr": "",
50 | "stdout": "",
51 | }
52 | `);
53 | });
54 |
55 | test("test_loop.js", async () => {
56 | expect(await qjs(["./test_loop.js"])).toMatchInlineSnapshot(`
57 | {
58 | "code": 0,
59 | "error": false,
60 | "stderr": "",
61 | "stdout": "",
62 | }
63 | `);
64 | });
65 |
66 | test("test_op_overloading.js", async () => {
67 | expect(await qjs(["--bignum", "./test_op_overloading.js"]))
68 | .toMatchInlineSnapshot(`
69 | {
70 | "code": 0,
71 | "error": false,
72 | "stderr": "",
73 | "stdout": "",
74 | }
75 | `);
76 | });
77 |
78 | test("test_qjscalc.js", async () => {
79 | expect(await qjs(["--qjscalc", "./test_qjscalc.js"]))
80 | .toMatchInlineSnapshot(`
81 | {
82 | "code": 0,
83 | "error": false,
84 | "stderr": "",
85 | "stdout": "",
86 | }
87 | `);
88 | });
89 |
90 | test("test_std.js", async () => {
91 | expect(await qjs(["./test_std.js"])).toMatchInlineSnapshot(`
92 | {
93 | "code": 0,
94 | "error": false,
95 | "stderr": "",
96 | "stdout": "",
97 | }
98 | `);
99 | });
100 |
101 | test("test_worker.js", async () => {
102 | expect(await qjs(["./test_worker.js"])).toMatchInlineSnapshot(`
103 | {
104 | "code": 0,
105 | "error": false,
106 | "stderr": "",
107 | "stdout": "",
108 | }
109 | `);
110 | });
111 | });
112 |
--------------------------------------------------------------------------------
/tests/oldtests/test_worker.js:
--------------------------------------------------------------------------------
1 | /* os.Worker API test */
2 | import * as std from "quickjs:std";
3 | import * as os from "quickjs:os";
4 |
5 | function assert(actual, expected, message) {
6 | if (arguments.length == 1)
7 | expected = true;
8 |
9 | if (actual === expected)
10 | return;
11 |
12 | if (actual !== null && expected !== null
13 | && typeof actual == 'object' && typeof expected == 'object'
14 | && actual.toString() === expected.toString())
15 | return;
16 |
17 | throw Error("assertion failed: got |" + actual + "|" +
18 | ", expected |" + expected + "|" +
19 | (message ? " (" + message + ")" : ""));
20 | }
21 |
22 | var worker;
23 |
24 | function test_worker()
25 | {
26 | var counter;
27 |
28 | worker = new os.Worker("./test_worker_module.js");
29 |
30 | counter = 0;
31 | worker.onmessage = function (e) {
32 | var ev = e.data;
33 | // print("recv", JSON.stringify(ev));
34 | switch(ev.type) {
35 | case "num":
36 | assert(ev.num, counter);
37 | counter++;
38 | if (counter == 10) {
39 | /* test SharedArrayBuffer modification */
40 | let sab = new SharedArrayBuffer(10);
41 | let buf = new Uint8Array(sab);
42 | worker.postMessage({ type: "sab", buf: buf });
43 | }
44 | break;
45 | case "sab_done":
46 | {
47 | let buf = ev.buf;
48 | /* check that the SharedArrayBuffer was modified */
49 | assert(buf[2], 10);
50 | worker.postMessage({ type: "abort" });
51 | }
52 | break;
53 | case "done":
54 | /* terminate */
55 | worker.onmessage = null;
56 | break;
57 | }
58 | };
59 | }
60 |
61 |
62 | test_worker();
63 |
--------------------------------------------------------------------------------
/tests/oldtests/test_worker_module.js:
--------------------------------------------------------------------------------
1 | /* Worker code for test_worker.js */
2 | import * as std from "quickjs:std";
3 | import * as os from "quickjs:os";
4 |
5 | var parent = os.Worker.parent;
6 |
7 | function handle_msg(e) {
8 | var ev = e.data;
9 | // print("child_recv", JSON.stringify(ev));
10 | switch(ev.type) {
11 | case "abort":
12 | parent.postMessage({ type: "done" });
13 | break;
14 | case "sab":
15 | /* modify the SharedArrayBuffer */
16 | ev.buf[2] = 10;
17 | parent.postMessage({ type: "sab_done", buf: ev.buf });
18 | break;
19 | }
20 | }
21 |
22 | function worker_main() {
23 | var i;
24 |
25 | parent.onmessage = handle_msg;
26 | for(i = 0; i < 10; i++) {
27 | parent.postMessage({ type: "num", num: i });
28 | }
29 | }
30 |
31 | worker_main();
32 |
--------------------------------------------------------------------------------
/tests/pointer.test.ts:
--------------------------------------------------------------------------------
1 | import { RunContext, spawn } from "first-base";
2 | import { binDir, rootDir } from "./_utils";
3 |
4 | function cleanResult(result: RunContext["result"]): RunContext["result"] {
5 | function cleanStr(str: string): string {
6 | return (
7 | str
8 | // behavior of %p varies with platform, so don't encode it in the test
9 | .replace(/_info: [^\n]+\n/, "_info: \n")
10 |
11 | // whitespace shenanigans are abound because of prettier interacting with the inline snapshot...
12 | .replace(/\t/g, " ")
13 | .split("\n")
14 | .map((part) => {
15 | if (part.trim() === "") {
16 | return "";
17 | } else {
18 | return part;
19 | }
20 | })
21 | .join("\n")
22 | );
23 | }
24 |
25 | return {
26 | ...result,
27 | stderr: cleanStr(result.stderr),
28 | stdout: cleanStr(result.stdout),
29 | };
30 | }
31 |
32 | test("pointer", async () => {
33 | const run = spawn(
34 | binDir("qjs"),
35 | [
36 | "-e",
37 | `
38 | const { Pointer } = require("quickjs:pointer");
39 |
40 | console.log("Pointer:", inspect(Pointer));
41 |
42 | console.log("Pointer.isPointer(42):", Pointer.isPointer(42));
43 | console.log("Pointer.isPointer(Pointer.NULL):", Pointer.isPointer(Pointer.NULL));
44 | `,
45 | ],
46 | { cwd: rootDir() }
47 | );
48 | await run.completion;
49 | expect(cleanResult(run.result)).toMatchInlineSnapshot(`
50 | {
51 | "code": 0,
52 | "error": false,
53 | "stderr": "",
54 | "stdout": "Pointer: Function "Pointer" {
55 | NULL: Pointer {
56 | _info:
57 | }
58 | isPointer: Function "isPointer" {
59 | │1│ function isPointer() {
60 | │2│ [native code]
61 | │3│ }
62 | }
63 |
64 | │1│ function Pointer() {
65 | │2│ [native code]
66 | │3│ }
67 | }
68 | Pointer.isPointer(42): false
69 | Pointer.isPointer(Pointer.NULL): true
70 | ",
71 | }
72 | `);
73 | });
74 |
--------------------------------------------------------------------------------
/tests/print.test.ts:
--------------------------------------------------------------------------------
1 | import { spawn } from "first-base";
2 | import { binDir, rootDir } from "./_utils";
3 |
4 | test("quickjs-print", async () => {
5 | const run = spawn(binDir("qjs"), [rootDir("examples/logging.js")]);
6 | await run.completion;
7 | expect(run.result).toMatchInlineSnapshot(`
8 | {
9 | "code": 0,
10 | "error": false,
11 | "stderr": "console.warn 3 [object Object]
12 | console.error 4 [object Object]
13 | ",
14 | "stdout": "print 1 [object Object]
15 | console.log 2 [object Object]
16 | console.info 5 [object Object]
17 | ",
18 | }
19 | `);
20 | });
21 |
--------------------------------------------------------------------------------
/tests/qjsbootstrap-argv0.test.ts:
--------------------------------------------------------------------------------
1 | import path from "path";
2 | import { cp, rm, mkdir, echo } from "shelljs";
3 | import { spawn } from "first-base";
4 | import { binDir, rootDir } from "./_utils";
5 |
6 | const workdir = rootDir("build/tests/qjsbootstrap-argv0");
7 |
8 | beforeEach(() => {
9 | rm("-rf", workdir);
10 | mkdir("-p", workdir);
11 | });
12 |
13 | test("qjsbootstrap - finds itself properly when argv0 is indirect", async () => {
14 | const run = spawn(binDir("qjsbootstrap"), {
15 | argv0: "something not particularly helpful",
16 | });
17 | await run.completion;
18 | expect(run.result).toMatchInlineSnapshot(`
19 | {
20 | "code": 0,
21 | "error": false,
22 | "stderr": "",
23 | "stdout": "append UTF-8 encoded JavaScript to the end of this binary to change this binary into a program that executes that JavaScript code
24 | ",
25 | }
26 | `);
27 |
28 | const prog = path.join(workdir, "myprog");
29 | cp(binDir("qjsbootstrap"), prog);
30 | echo("console.log(2 + 2)").toEnd(prog);
31 |
32 | const run2 = spawn(prog, {
33 | argv0: "something not particularly helpful",
34 | });
35 | await run2.completion;
36 | expect(run2.result).toMatchInlineSnapshot(`
37 | {
38 | "code": 0,
39 | "error": false,
40 | "stderr": "",
41 | "stdout": "4
42 | ",
43 | }
44 | `);
45 | });
46 |
--------------------------------------------------------------------------------
/tests/qjsbootstrap-bytecode.test.ts:
--------------------------------------------------------------------------------
1 | import fs from "fs";
2 | import path from "path";
3 | import { cp, rm, mkdir } from "shelljs";
4 | import { spawn } from "first-base";
5 | import { binDir, rootDir } from "./_utils";
6 |
7 | // Generated by running this in `qjs --bignum`: require("quickjs:bytecode").fromFile("./tests/fixtures/log-four.js")
8 | const bytecode = Buffer.from([
9 | 2, 3, 14, 99, 111, 110, 115, 111, 108, 101, 6, 108, 111, 103, 56, 46, 47, 116,
10 | 101, 115, 116, 115, 47, 102, 105, 120, 116, 117, 114, 101, 115, 47, 108, 111,
11 | 103, 45, 102, 111, 117, 114, 46, 106, 115, 14, 0, 6, 0, 162, 1, 0, 1, 0, 4, 0,
12 | 0, 18, 1, 164, 1, 0, 0, 0, 56, 227, 0, 0, 0, 66, 228, 0, 0, 0, 183, 183, 157,
13 | 36, 1, 0, 205, 40, 202, 3, 1, 0,
14 | ]);
15 |
16 | const workdir = rootDir("build/tests/qjsbootstrap-bytecode");
17 |
18 | beforeEach(() => {
19 | rm("-rf", workdir);
20 | mkdir("-p", workdir);
21 | });
22 |
23 | test("qjsbootstrap-bytecode - can execute bytecode", async () => {
24 | const prog = path.join(workdir, "myprog");
25 | cp(binDir("qjsbootstrap-bytecode"), prog);
26 | fs.appendFileSync(prog, bytecode);
27 |
28 | const run2 = spawn(prog, { argv0: "myprog" });
29 | await run2.completion;
30 | expect(run2.result).toMatchInlineSnapshot(`
31 | {
32 | "code": 0,
33 | "error": false,
34 | "stderr": "",
35 | "stdout": "4
36 | ",
37 | }
38 | `);
39 | });
40 |
--------------------------------------------------------------------------------
/tests/qjsbootstrap-can-access-bytecode-lib.test.ts:
--------------------------------------------------------------------------------
1 | import fs from "fs";
2 | import path from "path";
3 | import { cp, rm, mkdir } from "shelljs";
4 | import { spawn } from "first-base";
5 | import { binDir, rootDir } from "./_utils";
6 |
7 | const workdir = rootDir(
8 | "build/tests/tests/qjsbootstrap-can-access-bytecode-lib"
9 | );
10 |
11 | beforeEach(() => {
12 | rm("-rf", workdir);
13 | mkdir("-p", workdir);
14 | });
15 |
16 | test("qjsbootstrap - can access bytecode lib", async () => {
17 | const prog = path.join(workdir, "myprog");
18 | cp(binDir("qjsbootstrap"), prog);
19 | fs.appendFileSync(prog, "console.log(inspect(require('quickjs:bytecode')))");
20 |
21 | const run2 = spawn(prog, { argv0: "myprog" });
22 | await run2.completion;
23 | expect(run2.result).toMatchInlineSnapshot(`
24 | {
25 | "code": 0,
26 | "error": false,
27 | "stderr": "",
28 | "stdout": "{
29 | Null prototype
30 | Sealed
31 |
32 | fromFile: Function "fromFile" {
33 | │1│ function fromFile() {
34 | │2│ [native code]
35 | │3│ }
36 | }
37 | fromValue: Function "fromValue" {
38 | │1│ function fromValue() {
39 | │2│ [native code]
40 | │3│ }
41 | }
42 | toValue: Function "toValue" {
43 | │1│ function toValue() {
44 | │2│ [native code]
45 | │3│ }
46 | }
47 | }
48 | ",
49 | }
50 | `);
51 | });
52 |
--------------------------------------------------------------------------------
/tests/qjsc.test.ts:
--------------------------------------------------------------------------------
1 | import * as fs from "node:fs";
2 | import { RunContext, spawn } from "first-base";
3 | import {
4 | binDir,
5 | rootDir,
6 | fixturesDir,
7 | testsWorkDir,
8 | cleanResult,
9 | } from "./_utils";
10 |
11 | const ownWorkDir = testsWorkDir.concat("qjsc");
12 |
13 | beforeEach(() => {
14 | fs.rmSync(ownWorkDir(), { recursive: true, force: true });
15 | fs.mkdirSync(ownWorkDir(), { recursive: true });
16 | });
17 |
18 | test("qjsc-compiled program has access to both std and bytecode", async () => {
19 | const run = spawn(
20 | binDir("qjsc"),
21 | [
22 | "-e",
23 | "-m",
24 | "-o",
25 | ownWorkDir("uses-std-and-bytecode.c"),
26 | fixturesDir("uses-std-and-bytecode.js"),
27 | ],
28 | { cwd: rootDir() }
29 | );
30 | await run.completion;
31 | expect(cleanResult(run.result)).toMatchInlineSnapshot(`
32 | {
33 | "code": 0,
34 | "error": false,
35 | "stderr": "",
36 | "stdout": "",
37 | }
38 | `);
39 |
40 | const run2 = spawn("cc", [
41 | `-I${rootDir("build/include")}`,
42 | "-o",
43 | ownWorkDir("uses-std-and-bytecode"),
44 | ownWorkDir("uses-std-and-bytecode.c"),
45 | rootDir("build/lib/quickjs-full.a"),
46 | "-lm",
47 | ]);
48 | await run2.completion;
49 | expect(cleanResult(run2.result)).toMatchInlineSnapshot(`
50 | {
51 | "code": 0,
52 | "error": false,
53 | "stderr": "",
54 | "stdout": "",
55 | }
56 | `);
57 |
58 | const run3 = spawn(ownWorkDir("uses-std-and-bytecode"));
59 | await run3.completion;
60 | expect(cleanResult(run3.result)).toMatchInlineSnapshot(`
61 | {
62 | "code": 0,
63 | "error": false,
64 | "stderr": "",
65 | "stdout": "typeof std: object
66 | typeof Bytecode: object
67 | ",
68 | }
69 | `);
70 | });
71 |
--------------------------------------------------------------------------------
/tests/regexp-escaped-dash-in-charset.test.ts:
--------------------------------------------------------------------------------
1 | import { spawn } from "first-base";
2 | import { binDir, rootDir } from "./_utils";
3 |
4 | test("RegExp: escaped dash in character set", async () => {
5 | const run = spawn(
6 | binDir("qjs"),
7 | ["tests/fixtures/regexp-escaped-dash-in-charset.js"],
8 | { cwd: rootDir() }
9 | );
10 | await run.completion;
11 | expect(run.result).toMatchInlineSnapshot(`
12 | {
13 | "code": 0,
14 | "error": false,
15 | "stderr": "",
16 | "stdout": "/([\\w\\-:])/suy
17 | ",
18 | }
19 | `);
20 | });
21 |
--------------------------------------------------------------------------------
/tests/setting-main-module.test.ts:
--------------------------------------------------------------------------------
1 | import { spawn } from "first-base";
2 | import { binDir, fixturesDir } from "./_utils";
3 |
4 | const importFixturePath = fixturesDir("setting-main-module/mod1_import.js");
5 | const importModuleFixturePath = fixturesDir(
6 | "setting-main-module/mod1_importModule.js"
7 | );
8 |
9 | test("setting the main module - import syntax", async () => {
10 | const run = spawn(binDir("quickjs-run"), [importFixturePath], {
11 | cwd: __dirname,
12 | });
13 | await run.completion;
14 | expect(run.result).toMatchInlineSnapshot(`
15 | {
16 | "code": 0,
17 | "error": false,
18 | "stderr": "",
19 | "stdout": "hi from mod2 false
20 | hi from mod3 false
21 | hi from mod1 false
22 | ",
23 | }
24 | `);
25 | });
26 |
27 | test("setting the main module - std.importModule", async () => {
28 | const run = spawn(binDir("quickjs-run"), [importModuleFixturePath], {
29 | cwd: __dirname,
30 | });
31 | await run.completion;
32 | expect(run.result).toMatchInlineSnapshot(`
33 | {
34 | "code": 0,
35 | "error": false,
36 | "stderr": "",
37 | "stdout": "hi from mod1 false
38 | hi from mod2 false
39 | hi from mod3 true
40 | ",
41 | }
42 | `);
43 | });
44 |
--------------------------------------------------------------------------------
/tests/shared-library-modules.test.ts:
--------------------------------------------------------------------------------
1 | import { spawn } from "first-base";
2 | import { binDir, rootDir } from "./_utils";
3 |
4 | describe("shared library modules (examples)", () => {
5 | test("fib", async () => {
6 | const run = spawn(
7 | binDir("qjs"),
8 | ["src/shared-library-modules/example-fib/test_fib.js"],
9 | { cwd: rootDir() }
10 | );
11 | await run.completion;
12 | expect(run.result).toMatchInlineSnapshot(`
13 | {
14 | "code": 0,
15 | "error": false,
16 | "stderr": "",
17 | "stdout": "fib(10)= 55
18 | ",
19 | }
20 | `);
21 | });
22 |
23 | test("point", async () => {
24 | const run = spawn(
25 | binDir("qjs"),
26 | ["src/shared-library-modules/example-point/test_point.js"],
27 | { cwd: rootDir() }
28 | );
29 | await run.completion;
30 | expect(run.result).toMatchInlineSnapshot(`
31 | {
32 | "code": 0,
33 | "error": false,
34 | "stderr": "",
35 | "stdout": "normal usage... PASS
36 | subclass usage... PASS
37 | ",
38 | }
39 | `);
40 | });
41 | });
42 |
--------------------------------------------------------------------------------
/tests/skip-shebangs.test.ts:
--------------------------------------------------------------------------------
1 | import fs from "fs";
2 | import path from "path";
3 | import { rm, mkdir } from "shelljs";
4 | import { spawn } from "first-base";
5 | import { binDir, rootDir } from "./_utils";
6 |
7 | const workdir = rootDir("build/tests/skip-shebangs");
8 |
9 | beforeEach(() => {
10 | rm("-rf", workdir);
11 | mkdir("-p", workdir);
12 | });
13 |
14 | async function runFile(target: string) {
15 | const run = spawn(binDir("qjs"), [target]);
16 | await run.completion;
17 | return run.result;
18 | }
19 |
20 | test("files with 0, 1, or 2 shebangs all load properly", async () => {
21 | const noShebang = path.join(workdir, "no-shebang.js");
22 | fs.writeFileSync(noShebang, `console.log(2 + 2);`);
23 | expect(await runFile(noShebang)).toMatchInlineSnapshot(`
24 | {
25 | "code": 0,
26 | "error": false,
27 | "stderr": "",
28 | "stdout": "4
29 | ",
30 | }
31 | `);
32 |
33 | const oneShebang = path.join(workdir, "one-shebang.js");
34 | fs.writeFileSync(oneShebang, `#!/usr/bin/env node\nconsole.log(2 + 2);`);
35 | expect(await runFile(oneShebang)).toMatchInlineSnapshot(`
36 | {
37 | "code": 0,
38 | "error": false,
39 | "stderr": "",
40 | "stdout": "4
41 | ",
42 | }
43 | `);
44 |
45 | const twoShebangs = path.join(workdir, "two-shebangs.js");
46 | fs.writeFileSync(
47 | twoShebangs,
48 | `#!/usr/bin/env node\n#!/usr/bin/env node\nconsole.log(2 + 2);`
49 | );
50 | expect(await runFile(twoShebangs)).toMatchInlineSnapshot(`
51 | {
52 | "code": 0,
53 | "error": false,
54 | "stderr": "",
55 | "stdout": "4
56 | ",
57 | }
58 | `);
59 | });
60 |
--------------------------------------------------------------------------------
/tests/string-cooked.test.ts:
--------------------------------------------------------------------------------
1 | import { spawn } from "first-base";
2 | import { binDir, rootDir } from "./_utils";
3 |
4 | test("String.cooked", async () => {
5 | const run = spawn(binDir("qjs"), ["tests/fixtures/string-cooked.js"], {
6 | cwd: rootDir(),
7 | });
8 | await run.completion;
9 | expect(run.result).toMatchInlineSnapshot(`
10 | {
11 | "code": 0,
12 | "error": false,
13 | "stderr": "",
14 | "stdout": "{
15 | raw: "C:\\\\Program Files (x86)\\\\Steam\\\\steamapps\\\\common"
16 | cooked: "C:Program Files (x86)Steamsteamappscommon"
17 | upperCased: "HELLO \\tHERE YEAH!! YAY [OBJECT OBJECT] WOO"
18 | }
19 | ",
20 | }
21 | `);
22 | });
23 |
--------------------------------------------------------------------------------
/tests/workdir/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 |
--------------------------------------------------------------------------------
/todo.txt:
--------------------------------------------------------------------------------
1 | - [ ] pull in upstream changes: https://github.com/suchipi/quickjs/compare/main...bellard%3Aquickjs%3Amaster
2 | - [x] seems module detection (via quickjs-run) always assumes script when there's a shebang line in the file
3 | - [x] qjsbootstrap binaries are differing in size on linux aarch64
4 | - [x] move "Module" global into "quickjs:module"
5 | - [x] mostly done, but module-impl is still in QJMS. Maybe that's fine?
6 | - [x] refactor "Module" object so that delegate stuff is separated from instance identification and Module.define
7 | - [ ] get test262 stuff working and passing again
8 | - [ ] TextEncoder and TextDecoder
9 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [
3 | "./src/quickjs/quickjs.d.ts",
4 | "./src/quickjs-inspect/quickjs-inspect.d.ts",
5 | "./src/builtin-modules/**/*.d.ts"
6 | ],
7 | "include": ["./**/*.js", "./**/*.ts"],
8 | "exclude": [
9 | "tests/oldtests/**/*",
10 | "build/**/*",
11 | "examples/**/*",
12 | "meta/microbench.js",
13 | "src/run-test262/test262",
14 | "src/run-test262/test262o"
15 | ],
16 | "compilerOptions": {
17 | "allowJs": true,
18 | "checkJs": true,
19 | "noEmit": true,
20 | "lib": ["ES2021"],
21 | "module": "ES2020",
22 | "moduleResolution": "node",
23 | "strictNullChecks": true,
24 | "esModuleInterop": true,
25 | "types": ["jest"],
26 | "resolveJsonModule": true
27 | }
28 | }
29 |
--------------------------------------------------------------------------------