├── .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 | --------------------------------------------------------------------------------