diff --git a/benchmark/path/matchesGlob-posix.js b/benchmark/path/matchesGlob-posix.js deleted file mode 100644 index 2cc30ca8ace1fb..00000000000000 --- a/benchmark/path/matchesGlob-posix.js +++ /dev/null @@ -1,28 +0,0 @@ -'use strict'; -const common = require('../common.js'); -const { posix } = require('path'); -const assert = require('assert'); - -const bench = common.createBenchmark(main, { - path: [ - 'src/index.ts', - 'src/index.test.ts', - 'src/foo/bar/biz/baz/index.test.ts', - ], - pattern: [ - 'src/**/baz/*.test.ts', - 'src/**/baz/*.ts', - 'test/**/*.ts', - ], - n: [1e5], -}); - -function main({ path, pattern, n }) { - bench.start(); - let a; - for (let i = 0; i < n; i++) { - a = posix.matchesGlob(path, pattern); - } - bench.end(n); - assert(a + 'a'); -} diff --git a/benchmark/path/matchesGlob-win32.js b/benchmark/path/matchesGlob-win32.js deleted file mode 100644 index 3ef3c6b4434445..00000000000000 --- a/benchmark/path/matchesGlob-win32.js +++ /dev/null @@ -1,29 +0,0 @@ -'use strict'; -const common = require('../common.js'); -const { win32 } = require('path'); -const assert = require('assert'); - -const bench = common.createBenchmark(main, { - path: [ - 'src/index.ts', - 'src\\index.ts', - 'src/foo/bar/biz/baz/index.test.ts', - 'src\\foo\\bar\\biz\\baz\\index.test.ts', - ], - pattern: [ - 'src/**/baz/*.test.ts', - 'src/**/baz/*.ts', - 'test/**/*.ts', - ], - n: [1e5], -}); - -function main({ path, pattern, n }) { - bench.start(); - let a; - for (let i = 0; i < n; i++) { - a = win32.matchesGlob(path, pattern); - } - bench.end(n); - assert(a + 'a'); -} diff --git a/common.gypi b/common.gypi index 8cf118455ec827..214615c3e085d9 100644 --- a/common.gypi +++ b/common.gypi @@ -42,7 +42,7 @@ # Reset this number to 0 on major V8 upgrades. # Increment by one for each non-official patch applied to deps/v8. - 'v8_embedder_string': '-node.26', + 'v8_embedder_string': '-node.25', ##### V8 defaults for Node.js ##### diff --git a/deps/crates/crates.gyp b/deps/crates/crates.gyp index ede5bf788bc760..5c1dc9d782cbfe 100644 --- a/deps/crates/crates.gyp +++ b/deps/crates/crates.gyp @@ -2,7 +2,6 @@ 'variables': { 'cargo%': 'cargo', 'cargo_vendor_dir': './vendor', - 'temporal_capi_dir': 'temporal_capi', 'cargo_rust_target%': '', }, 'conditions': [ @@ -121,7 +120,7 @@ ], 'direct_dependent_settings': { 'include_dirs': [ - '<(cargo_vendor_dir)/<(temporal_capi_dir)/bindings/cpp', + '<(cargo_vendor_dir)/temporal_capi/bindings/cpp', ], }, }, diff --git a/deps/googletest/include/gtest/internal/gtest-port.h b/deps/googletest/include/gtest/internal/gtest-port.h index 650a04558c03ba..b607e0adee74f4 100644 --- a/deps/googletest/include/gtest/internal/gtest-port.h +++ b/deps/googletest/include/gtest/internal/gtest-port.h @@ -2411,7 +2411,9 @@ using StringView = ::std::string_view; #define GTEST_INTERNAL_HAS_STRING_VIEW 0 #endif -#if defined(__cpp_lib_three_way_comparison) +#if (defined(__cpp_lib_three_way_comparison) || \ + (GTEST_INTERNAL_HAS_INCLUDE() && \ + GTEST_INTERNAL_CPLUSPLUS_LANG >= 201907L)) #define GTEST_INTERNAL_HAS_COMPARE_LIB 1 #else #define GTEST_INTERNAL_HAS_COMPARE_LIB 0 diff --git a/deps/googletest/src/gtest.cc b/deps/googletest/src/gtest.cc index b38f551e036f76..ac90786a091b51 100644 --- a/deps/googletest/src/gtest.cc +++ b/deps/googletest/src/gtest.cc @@ -6732,12 +6732,6 @@ static const char kColorEncodedHelpMessage[] = "recreate_environments_when_repeating@D\n" " Sets up and tears down the global test environment on each repeat\n" " of the test.\n" - " @G--" GTEST_FLAG_PREFIX_ - "fail_fast@D\n" - " Stop running tests after the first failure.\n" - " @G--" GTEST_FLAG_PREFIX_ - "fail_if_no_test_linked@D\n" - " Fail if no test is linked into the test program.\n" "\n" "Test Output:\n" " @G--" GTEST_FLAG_PREFIX_ @@ -6750,9 +6744,6 @@ static const char kColorEncodedHelpMessage[] = "print_time=0@D\n" " Don't print the elapsed time of each test.\n" " @G--" GTEST_FLAG_PREFIX_ - "print_utf8=0@D\n" - " Don't print UTF-8 characters as text.\n" - " @G--" GTEST_FLAG_PREFIX_ "output=@Y(@Gjson@Y|@Gxml@Y)[@G:@YDIRECTORY_PATH@G" GTEST_PATH_SEP_ "@Y|@G:@YFILE_PATH]@D\n" " Generate a JSON or XML report in the given directory or with the " @@ -6769,9 +6760,6 @@ static const char kColorEncodedHelpMessage[] = " @G--" GTEST_FLAG_PREFIX_ "death_test_style=@Y(@Gfast@Y|@Gthreadsafe@Y)@D\n" " Set the default death test style.\n" - " @G--" GTEST_FLAG_PREFIX_ - "death_test_use_fork@D\n" - " Use fork() instead of clone() to spawn death test child processes.\n" #endif // GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS " @G--" GTEST_FLAG_PREFIX_ "break_on_failure@D\n" @@ -6784,9 +6772,6 @@ static const char kColorEncodedHelpMessage[] = "catch_exceptions=0@D\n" " Do not report exceptions as test failures. Instead, allow them\n" " to crash the program or throw a pop-up (on Windows).\n" - " @G--" GTEST_FLAG_PREFIX_ - "stack_trace_depth=@Y[NUMBER]@D\n" - " Maximum number of stack frames to print when an assertion fails.\n" "\n" "Except for @G--" GTEST_FLAG_PREFIX_ "list_tests@D, you can alternatively set " diff --git a/deps/libffi/ChangeLog b/deps/libffi/ChangeLog index 0dde93e6adad9e..f2d63988dcf70a 100644 --- a/deps/libffi/ChangeLog +++ b/deps/libffi/ChangeLog @@ -1,953 +1,3 @@ -commit 5c1c43091ed611fdea774374355eb938c73a9157 -Author: Anthony Green -Date: Fri Jul 10 09:50:53 2026 -0400 - - Prepare for 3.7.1 - - Bump version to 3.7.1 and libtool -version-info to 12:1:4 (revision - bump; no ABI change). See the README for the change list. - - Co-Authored-By: Claude Fable 5 - -commit aaeaafb04a8335bea93b2873ca9a2d01107737eb -Author: Anthony Green -Date: Fri Jul 10 09:24:36 2026 -0400 - - testsuite: Make closure_thiscall_fastcall_pop portable across compilers - - The i386 esp-balance shim faulted on Android i686 (clang, -O2): it read - the closure code pointer through an esp-relative memory operand after - manually moving esp, and it did not 16-byte align the stack for the - call, so the -O2-built closure body faulted on aligned SSE. - - Read every operand into a register while esp is still at its incoming - value, 16-byte align the stack at the call per the i386 psABI, avoid ebx - so it works under -fPIC, and return the delta via memory. Verified with - gcc and clang at -O0 and -O2: passes with the fix, still catches the - under-pop without it. - - Co-Authored-By: Claude Fable 5 - -commit ebe306becb167937ead4332290868e041d0c08a7 -Author: Anthony Green -Date: Fri Jul 10 08:54:39 2026 -0400 - - x86: Fix i386 THISCALL/FASTCALL closure stack-pop accounting - - For THISCALL and FASTCALL, ffi_closure_inner force-bumps narg_reg to 2 - when an argument is a 64-bit integer or a struct, so that following - integer arguments are placed on the stack (the Issue #434 rule). The - closure return path then computed the callee stack pop as - cif->bytes - narg_reg * 4, which discounts register slots that were - never used once narg_reg has been forced, under-popping the stack. - - For example FASTCALL void(uint64_t) has cif->bytes == 8 with the uint64 - placed on the stack, but the formula yields a pop of 0 instead of 8; a - callee-clean call site that does not re-sync ESP from a frame pointer is - then left with the argument bytes in place of its return address. - - Encode the pop as the number of bytes actually consumed from the - incoming stack (argp - stack, dir == 1 for these ABIs), which equals the - old formula in the non-forced cases and is correct in the forced ones. - - Adds closure_thiscall_fastcall_pop.c (i386/GNU), which invokes the - generated closure through a minimal callee-clean call site and checks - ESP is balanced; without this fix the imbalance is 8 (FASTCALL uint64) - or 4 (THISCALL this+uint64). - - Co-Authored-By: Claude Fable 5 - -commit a2ef91c79f4f193dd1e828f615c818fb6bcaff0d -Author: Anthony Green -Date: Fri Jul 10 08:20:39 2026 -0400 - - aarch64: Fix argument slab under-budget for large by-value structs - - On AArch64, composites larger than 16 bytes (that are not HFAs) are - passed by invisible reference. ffi_call copies each payload into the - argument slab from the top (next_struct_area, growing down) and, once - X0-X7 are exhausted, spills the by-ref pointer into the same slab from - the bottom (the NSAA, growing up). The generic prep_cif accounting in - cif->bytes only charges the payload copy, not the extra 8-byte pointer - slot, so with enough large structs the two regions collide: a later - payload copy overwrites an already-spilled pointer, and the callee then - receives a corrupt pointer for a by-value argument. - - Reserve an additional 8 bytes in ffi_prep_cif_machdep for each large - by-reference struct argument so the copy and spill regions can never - overlap. - - Adds many_large_structs.c, which passes sixteen 32-byte structs by - value; it crashes without this fix and passes with it. - - Co-Authored-By: Claude Fable 5 - -commit 25283b0d812b6729299a21a1c02433bc291174e9 -Merge: e87b59f5 665c5c00 -Author: Anthony Green -Date: Fri Jul 10 08:22:39 2026 -0400 - - Merge pull request #997 from libffi/windows-arm64-ci - - Add Windows ARM64 (MSVC) CI; fix latent msvcc.sh and testsuite bugs it exposed - -commit 665c5c00f2f492e199a1c9e01bda57a82f4440ef -Author: Anthony Green -Date: Fri Jul 10 07:46:24 2026 -0400 - - testsuite: Pass MSVC warning suppressions in go.exp - - Same latent issue as closure.exp: the Go closure tests ran with no - MSVC warning options, so ffitest.h's benign C4005 PRI-macro - redefinition warnings count as excess errors. Latent until the - msvcc.sh -E fix let the FFI_GO_CLOSURES feature probe succeed under - MSVC; the execution tests themselves pass on both x86 MSVC jobs. - Mirror the call.exp/closure.exp warning-suppression block. - - Co-Authored-By: Claude Fable 5 - -commit 2e897db821a43068e3851a414bd47b2b5592d403 -Author: Anthony Green -Date: Fri Jul 10 07:25:53 2026 -0400 - - testsuite: Skip closure_loc_fn0 memcmp check under trampoline tables - - Now that closure_loc_fn0.c actually executes, it fails on Apple - aarch64: with FFI_EXEC_TRAMPOLINE_TABLE, codeloc points at a - trampoline table entry rather than a copy of the closure, so the - memcmp sanity check is invalid there -- the same reason it is already - skipped for static trampolines. fficonfig.h (included by ffitest.h) - provides the macro. - - Co-Authored-By: Claude Fable 5 - -commit 15b9abc239a1a2f6964a4cfc92f8fb78b3475d37 -Author: Anthony Green -Date: Fri Jul 10 07:19:28 2026 -0400 - - ci: Add Windows ARM64 Visual C++ to the MSVC build matrix - - Build and test on GitHub's windows-11-arm runners. Cygwin and the - amd64_arm64 cross tools (cl, armasm64) are x64-hosted and run under - the runner's x64 emulation; the ARM64 binaries they produce execute - natively, so the full testsuite runs (784 passes, 0 failures). This - is the first CI coverage for the win64_armasm.S closure trampolines. - - The matrix entries grow runner/mflag/pkg/rcedit_arch parameters; the - existing x86 job names and release artifact names are unchanged. - Release tags now also publish ARM64 MSVC binaries - (libffi--arm64-msvc-binaries). - - Replaces the temporary winarm64.yml iteration workflow. - - Co-Authored-By: Claude Fable 5 - -commit e4435739c82106cf13336bf26da99a03281848a0 -Author: Anthony Green -Date: Fri Jul 10 06:58:17 2026 -0400 - - testsuite: Actually run closure_loc_fn0; fix msvcc.sh -S - - closure_loc_fn0.c is the only test in the suite without a dg-do - directive, so DejaGnu has been defaulting to its compile-only action - on every platform -- the ffi_closure_alloc/ffi_prep_closure_loc path - it exists to exercise was never executed. Add the missing - /* { dg-do run } */. - - The compile-only default also exposed msvcc.sh's broken -S handling: - it mapped -S to -FAs alone, which emits a listing but still runs the - link step, failing with unresolved externals since compile-only - invocations pass no libraries. Map -S to a true compile-to-assembly: - -c -FAs with the listing written to the -o target. - - Co-Authored-By: Claude Fable 5 - -commit bf2fa49b6dbe68dac021c02ee3deddb343ec3541 -Author: Anthony Green -Date: Fri Jul 10 06:36:26 2026 -0400 - - testsuite: Pass MSVC warning suppressions to closures C tests - - closure.exp defines additional_options with the same -wd warning - suppressions call.exp uses, but only passed them to the C++ tests; - the C tests ran with "". Under MSVC the benign C4005/C4305/C4477 - warnings then count as excess errors and fail every C test's compile - check. This was latent until the previous commit made the - FFI_CLOSURES feature probe work under msvcc.sh, allowing the closures - suite to run on MSVC at all. - - Co-Authored-By: Claude Fable 5 - -commit c2ace700754b6e63f07efdec39fc8c09ed960d63 -Author: Anthony Green -Date: Fri Jul 10 06:11:00 2026 -0400 - - msvcc.sh: Handle -E (preprocess) so DejaGnu feature tests work - - The testsuite's libffi_feature_test probes compile a snippet with - "-E -o " and treat any compiler output as failure. msvcc.sh - had no -E handling: cl's preprocessed text went to stdout, where the - awk diagnostics filter passed through every line containing the - string "warning" -- including ffi.h's own #pragma warning lines -- - and cl printed the source file name to stderr. The probes therefore - always failed under msvcc.sh, silently marking the entire closures - suite (99 tests) and the bhaible callback tests (80) UNSUPPORTED on - every MSVC CI job. - - Recognize -E, write the preprocessed output to the -o target, and - swallow cl's stderr banner so feature probes see clean output. - - Co-Authored-By: Claude Fable 5 - -commit e994c8a22d6bc480cd13c9daa2abaedf42627f88 -Author: Anthony Green -Date: Fri Jul 10 05:49:48 2026 -0400 - - ci: Add Windows ARM64 Visual C++ job (iteration branch) - - Build and test libffi on GitHub's windows-11-arm runners using - msvcc.sh -marm64 (cl + armasm64 via the amd64_arm64 cross tools, - running under x64 emulation; the resulting ARM64 binaries execute - natively). msvcc.sh already understands -marm64. - - This is a temporary standalone workflow scoped to the - windows-arm64-ci branch so the job can be iterated on without - triggering the full CI matrix; it will be folded into build.yml - once green. - - Co-Authored-By: Claude Fable 5 - -commit e87b59f53f3879cb254a4c331fd0d2c221d6ea73 -Author: Anthony Green -Date: Fri Jul 10 05:40:57 2026 -0400 - - aarch64: Fix clang-cl link failure for HFA helper functions - - clang-cl defines both __clang__ and _MSC_VER. Since the build system - detects clang before MSVC, it sets MSVC=0 and builds sysv.S rather - than win64_armasm.S. But ffi.c gated its C implementations of - extend_hfa_type and compress_hfa_type on !defined(_MSC_VER), so under - clang-cl neither implementation was built and linking failed. - - Gate on defined(_MSC_VER) && !defined(__clang__) instead: clang-cl - supports GCC-style extended inline asm, so the C implementations work - there. Also replace ssize_t (POSIX-only, absent on Windows) with - ptrdiff_t in extend_hfa_type now that it compiles for MSVC targets. - - Fixes #996 - - Co-Authored-By: Claude Fable 5 - -commit cacaf519159668bce9fcbab64082bc85c277e89d -Author: Anthony Green -Date: Thu Jul 9 21:34:47 2026 -0400 - - x86-64: build generic ffi_call_plan fallback on Windows x86-64 - - The ffi_call_plan_* API has two providers: the accelerated x86-64 SysV - implementation in src/x86/ffi64.c, and the portable fallback in - src/prep_cif.c that just wraps ffi_call. ffi64.c is not compiled for - X86_WIN64 (that target builds ffiw64.c/win64.S instead), but clang-cl - and MSYS/mingw both define __x86_64__, so prep_cif.c's guard suppressed - the fallback too -- leaving ffi_call_plan_alloc/invoke/free undefined at - link time on those toolchains. MSVC cl escaped this only because it does - not define __x86_64__. - - Suppress the generic fallback only when ffi64.c actually provides the - functions, i.e. add && !defined(X86_WIN64) to the guard. X86_WIN64 is a - command-line define visible to the generic sources (cf. src/closures.c). - - Fixes #995. - - Co-Authored-By: Claude Opus 4.8 (1M context) - -commit c2c2e538c7c5403bd4edc3e77220bbe25a3ad47f -Merge: 132fb82f 7c3144c8 -Author: Anthony Green -Date: Thu Jul 9 09:10:49 2026 -0400 - - Merge pull request #994 from libffi/fix-993-aarch64-darwin-int128 - - aarch64: Don't split an int128 between x7 and the stack on Darwin - -commit 7c3144c80d8077b745588ee64bffd8504c44f5c9 -Author: Anthony Green -Date: Wed Jul 8 21:23:31 2026 -0400 - - aarch64: Don't split an int128 between x7 and the stack on Darwin - - The Apple arm64 ABI does not round NGRN up to an even number for - 128-bit integer arguments, but like AAPCS64 it still requires the - value to fit entirely in registers: if only x7 remains, the whole - value goes on the stack. allocate_int128_to_reg_or_stack only - checked ngrn < 8, so with ngrn == 7 it wrote the low half to x7 - and the high half past the end of the register array, while the - callee reads the argument from the stack. - - This fixes the libffi.call/i128-1.c failure at iteration 7 (seven - int args followed by an __int128) on aarch64-darwin, for both the - call and closure paths. - - Fixes #993 - - Co-Authored-By: Claude Fable 5 - -commit 132fb82ff4b43c9b8eaeeb533f38c95ae925131c -Author: Anthony Green -Date: Wed Jul 8 07:40:34 2026 -0400 - - Update libtool version to 12:0:4 for 3.7.0 - - 3.7.0 adds three new public interfaces (ffi_call_plan_alloc, - ffi_call_plan_invoke, ffi_call_plan_free) additively, with no - existing interface removed or changed. Per the libtool rules: - increment current, reset revision, increment age (11:1:3 -> 12:0:4). - This keeps the change ABI backward-compatible; the soname stays - libffi.so.8 (current - age = 8). - - Co-Authored-By: Claude Opus 4.8 (1M context) - -commit 0ad8fd46c0a32cbbf18260c72cc60679da66c89f -Author: Anthony Green -Date: Wed Jul 8 06:43:03 2026 -0400 - - Prepare for 3.7.0 - -commit c9275302baf0d109ec474972d4679a6afe8fe64d -Author: Anthony Green -Date: Wed Jul 8 06:29:29 2026 -0400 - - Bump version to 3.7.0 - - Set AC_INIT to 3.7.0 and update the manual's edition/version/date - stamps to match. - - Co-Authored-By: Claude Fable 5 - -commit 4f7ef24565eb60e8a093bfeba0bb77d5c576c311 -Author: Anthony Green -Date: Wed Jul 8 06:28:30 2026 -0400 - - Update copyright year - - Bump the LICENSE year range to 2026. - - Co-Authored-By: Claude Fable 5 - -commit 5ab7b328c3adde14105b1f36ae30adc9cdefae0a -Author: Anthony Green -Date: Wed Jul 8 06:24:26 2026 -0400 - - Add Anthony Green copyright line for 2026 authorship - - Attribute this year's work in the wasm and PA backends and the darwin - PowerPC closure code: - - wasm/ffi.c: widen narrow/unboxed returns to ffi_arg; NULL rvalue. - - pa/ffi.c: bump year (5-8 byte struct slot-sizing fix + avalue copy). - - powerpc/ffi_darwin.c: PPC_LD_* jump-table indexes for the aix/darwin - closure build fix. - - Co-Authored-By: Claude Fable 5 - -commit 5e334c6aa70b937b5c0d30ba10a373e34d7526b6 -Author: Anthony Green -Date: Wed Jul 8 06:17:58 2026 -0400 - - x86-64: add copyright line for the 2026 unix64.S plan trampolines - - The fast-path plan trampoline (ffi_plan_fast_call) and the pure-GP64 - direct thunks (ffi_plan_gp0..6) were added to unix64.S this year, so - add an Anthony Green copyright line alongside the original authors'. - - Co-Authored-By: Claude Fable 5 - -commit 70106b98d0a333a13b509b4bdc554018785be50c -Author: Anthony Green -Date: Wed Jul 8 06:15:53 2026 -0400 - - Update copyright year to 2026 - - Add 2026 to the copyright line on the source files, headers and the - manual touched during this year's development. Following existing - practice, only the maintainer's own copyright line is bumped (the - Anthony Green line, plus the Red Hat line in types.c); the years on - third-party-held backends are left as their original authors set them. - - Co-Authored-By: Claude Fable 5 - -commit 621874d6726007a6470b9c0c90b5a27c3c3ed932 -Author: Anthony Green -Date: Wed Jul 8 05:43:18 2026 -0400 - - configure: derive FFI_VERSION_STRING/NUMBER from AC_INIT - - The 3.6.0 release bumped AC_INIT but left FFI_VERSION_STRING and - FFI_VERSION_NUMBER at 3.5.2/30502, so the released headers report the - wrong version. Compute both from AC_PACKAGE_VERSION instead of - duplicating the version by hand: the string is the package version - verbatim, and the number encodes X.Y.Z as X*10000 + Y*100 + Z with any - non-numeric suffix (e.g. -rc0) ignored. - - Co-Authored-By: Claude Fable 5 - -commit b8c60b06597cc77cbe66bd208083acc20251fae0 -Author: Anthony Green -Date: Tue Jul 7 12:57:06 2026 -0400 - - README: note the ffi_call fixes in the 3.7.0 history - - Co-Authored-By: Claude Fable 5 - -commit ed5da041796cd6e38d2f30b948eadf63cf96139a -Author: Anthony Green -Date: Tue Jul 7 12:30:56 2026 -0400 - - wasm: don't widen unboxed small-struct returns - - The previous commit widened narrow integral returns to ffi_arg, but - unbox_small_structs had already collapsed single-member struct returns - into those same scalar type ids, and a struct return is stored at its - natural size -- the caller's buffer is exactly the struct. This broke - cls_1_1byte and the bhaible small-struct return tests. - - Remember whether the original return type was a struct and widen only - genuine scalar returns. - - Co-Authored-By: Claude Fable 5 - -commit 59a9e68e59205da9c4db854dd1f1bce4e8b58dad -Author: Anthony Green -Date: Tue Jul 7 12:02:20 2026 -0400 - - wasm: widen narrow integral returns to ffi_arg - - The return store wrote only the natural width for integer returns - narrower than a register (one byte for U8/S8, two for U16/S16, four on - wasm64 for 32-bit types), leaving the rest of the caller's ffi_arg - buffer as stack garbage. The documented API widens such returns to a - full ffi_arg, and every native port stores the full register width. - This is what plan_mixed.c caught: two identical calls disagreed in the - garbage bytes of their ffi_arg results. - - Mask or sign-extend the JS result explicitly and store a full ffi_arg - (32 bits on wasm32, 64 on wasm64). - - Co-Authored-By: Claude Fable 5 - -commit 6954bd1715b8a5895adfad7c2196d4eb41094dc7 -Author: Anthony Green -Date: Tue Jul 7 11:30:55 2026 -0400 - - testsuite: use ffi_arg for the integral return in plan.c - - The struct-argument block stored an ffi_type_slong return into a plain - long. Integral returns narrower than a register are widened to - ffi_arg, so on LLP64 targets libffi writes 8 bytes into the 4-byte - variable and clobbers its neighbor; this is what broke plan.c on MSVC - Win64 while mingw survived by stack-layout luck. - - Co-Authored-By: Claude Fable 5 - -commit 822d2e555fd97271c7c0ba8b6325cf4b541e3e9c -Author: Anthony Green -Date: Tue Jul 7 11:30:55 2026 -0400 - - Allow a NULL rvalue in ffi_call; handle it in the wasm port - - Most ports already quietly supported calling with rvalue == NULL by - substituting scratch space for in-memory returns, and the new plan - tests rely on it, but the manual never promised it and the wasm port - passed NULL straight to the callee as the return-by-argument pointer, - writing the result through address zero. - - Document NULL rvalue as supported for any return type, and in the wasm - port allocate stack scratch for return-by-argument callees and skip the - store-back for scalar returns when rvalue is NULL. - - Co-Authored-By: Claude Fable 5 - -commit 110156e1cceab76b4440ced9d95b5abaa84f30f7 -Author: Anthony Green -Date: Tue Jul 7 11:30:55 2026 -0400 - - ffi_call: never modify the caller's avalue array - - Eight ports made large struct arguments pass-by-value by alloca'ing a - copy inside ffi_call and writing the copy's address back into the - caller's avalue[]. Those pointers dangle as soon as ffi_call returns, - so any caller that legally reuses its avalue array for a second call - reads dead stack. The new ffi_call_plan equivalence tests do exactly - that and exposed it as plan_struct.c failures on hppa, MSVC Win64 and - macOS x86_64 under Rosetta; other targets passed only because the dead - copy usually survives between back-to-back calls. - - Clone the pointer array once, on the first argument that needs a copy, - and redirect the clone instead. Document that ffi_call leaves the - caller's vector untouched. - - Co-Authored-By: Claude Fable 5 - -commit e3dae4657710a9ddd4eb4bdbefbcc73722aad9fe -Author: Anthony Green -Date: Tue Jul 7 09:18:31 2026 -0400 - - testsuite: add ffi_call_plan equivalence tests; fix EXTRA_DIST gaps - - Add plan.c, plan_mixed.c, plan_spill.c, plan_struct.c and plan_var.c. - Each builds a reusable call plan (ffi_call_plan_alloc/invoke/free) and - checks it against ffi_call across the x86-64 argument-placement matrix: - the pure-GP64 direct thunks, the mixed GP+SSE fast path, stack-spilled - arguments, struct returns (in-memory including a NULL rvalue, and the - register pair), a struct-by-value argument that falls back to ffi_call, - and variadic calls. On targets without the accelerated backend the - generic plan simply wraps ffi_call, so the tests still pass there. - - While adding these to EXTRA_DIST, register four existing tests that were - never listed (i128-1.c, large_struct_by_value.c, many_small_structs.c - and complex_i128.c). They ran via the .exp globs but were missing from - distribution tarballs. - - Co-Authored-By: Claude Fable 5 - -commit a40ab3a7e85f8c9b193b8a442297bb20de2b4293 -Author: Anthony Green -Date: Tue Jul 7 08:19:33 2026 -0400 - - README: update 3.7.0 history entry - - Co-Authored-By: Claude Fable 5 - -commit 50baa0458db6b2d617e0d661292bbf12354b22ce -Author: Anthony Green -Date: Tue Jul 7 08:10:29 2026 -0400 - - x86_64: size closure argument buffer to match copy loop - - ffi_closure_unix64_inner allocated a fixed 16 bytes when gathering a - register-passed argument's eightbytes, but the copy loop writes n * 8 - bytes and examine_argument can classify up to MAX_CLASSES (4) - eightbytes for vector-classified types. Allocate n * 8 bytes so the - buffer always matches the loop bound. - - Co-Authored-By: Claude Fable 5 - -commit ff30d37615f82ef7bf1130b34528e01e1479b63d -Author: Anthony Green -Date: Tue Jul 7 08:10:29 2026 -0400 - - tramp.c: bound sscanf path field when parsing /proc/self/maps - - The %s conversion into file[PATH_MAX] was unbounded while the input - line buffer is PATH_MAX+100 bytes, so a mapping with a very long - pathname could overflow the destination. Give the conversion an - explicit PATH_MAX field width and size the buffer PATH_MAX+1 to - accommodate the terminating NUL. - - Co-Authored-By: Claude Fable 5 - -commit ed1844e6b50d0498d2b88d1b3ef31cc39b6cf117 -Author: Anthony Green -Date: Tue Jul 7 08:10:29 2026 -0400 - - .gitignore: ignore local build and scan artifact directories - - Co-Authored-By: Claude Fable 5 - -commit 854df63dbbda802985facbfab7a0e7c4169aefd1 -Merge: 2a595a18 731ec08f -Author: Anthony Green -Date: Tue Jul 7 08:06:25 2026 -0400 - - Merge pull request #991 from rorth/freebsd-libffi-note-gnu-stack - - Include .note.GNU-stack on FreeBSD/x86 - -commit 731ec08f3c3bbec883d823a71f1b0f86731cf312 -Author: Rainer Orth -Date: Tue Jul 7 11:14:09 2026 +0200 - - Fix typo. - -commit 19cc1424e924767c7ca43f9e4d4c27fbae27d1d9 -Author: Rainer Orth -Date: Tue Jul 7 10:27:55 2026 +0200 - - Include .note.GNU-stack on FreeBSD/x86 - - When building libffi on FreeBSD/x86 with --disable-shared as is done inside - the GCC tree, all tests FAIL when using GNU ld: - - gld-2.46: warning: win64.o: missing .note.GNU-stack section implies executable stack - gld-2.46: NOTE: This behaviour is deprecated and will be removed in a future version of the linker - - The emission of .note.GNU-stack in src/x86/*.S is currently guarded by - __ELF__ && __linux__, but FreeBSD/x86 uses that NOTE section, too. This - doesn't happen with libffi.so which lacks that NOTE. - - Therefore this patch extends the emission accordingly. - - Tested on amd64-pc-freebsd15.1 with and without --disable-shared. - -commit 2a595a183be146974bdf55716969e7a3e860f112 -Merge: c93f9428 f7b36640 -Author: Anthony Green -Date: Thu Jul 2 20:03:30 2026 -0400 - - Merge pull request #989 from rvandermeulen/dlmalloc-msvc-lock-cast - - dlmalloc: cast Win32 spin-lock word to LONG volatile* in CAS_LOCK/CLEAR_LOCK - -commit f7b36640544a59813ee372ed1a763affe7a67562 -Author: Ryan VanderMeulen -Date: Thu Jul 2 19:32:00 2026 -0400 - - dlmalloc: cast Win32 spin-lock word to LONG volatile* in CAS_LOCK/CLEAR_LOCK - - MLOCK_T is int, but on the Win32 MSC path CAS_LOCK/CLEAR_LOCK map to - _InterlockedExchange(LONG volatile *, LONG), so the int* lock word is - passed to a LONG volatile * parameter. clang 22 promotes - -Wincompatible-pointer-types to an error by default, which breaks the - clang-cl build (32- and 64-bit) with: - - error: incompatible pointer types passing 'int *' to parameter of type - 'volatile long *' [-Wincompatible-pointer-types] - - Cast to (LONG volatile *), matching what the ffi_spin_peek macro just - below already does in its _MSC_VER branch. LONG and int are both 32-bit - on Windows, so this is a type-correctness fix with no behavioral change. - -commit c93f9428d17cde4eb35517b58feeae6fb43aba5b -Author: Anthony Green -Date: Mon Jun 22 17:33:33 2026 -0400 - - testsuite: link threads tests against libpthread on NetBSD - - The libffi.threads/tsan.c test calls pthread_create but the harness - only appended -lpthread for OpenBSD, FreeBSD, and some Linux targets. - On NetBSD this caused an undefined reference to pthread_create. - - Fixes #988 - - Co-Authored-By: Claude Opus 4.8 (1M context) - -commit 9ca53a19833dabaf80b73763638ab988bdbc42cc -Author: Anthony Green -Date: Sun Jun 21 18:42:37 2026 -0400 - - powerpc: fix aix/darwin closure build errors - - unbuildable: the PPC_LD_* jump-table indexes (defined in ffi_powerpc.h, - which this file cannot include) were missing, and the closure helper - functions still declared an ffi_type* return type inconsistent with the - PPC_LD_* constants they now return. - - Define the PPC_LD_* constants locally and change the closure helper - return types to int. - - Fixes #987 - - Co-Authored-By: Claude Opus 4.8 (1M context) - -commit 807a93115f927229f6e7cf5675384e8dc2bf76d5 -Author: Anthony Green -Date: Sun Jun 21 18:31:12 2026 -0400 - - doc: mark the 3.7.0 release date as TBD - - Co-Authored-By: Claude Opus 4.8 (1M context) - -commit c876d4663183c40edbe5218603a26290900e7794 -Author: Anthony Green -Date: Sun Jun 21 18:28:12 2026 -0400 - - doc: add 3.7.0 history entry - - Record the next release (3.7.0, a minor bump for the new reusable call-plan API - on master) and the two PA-RISC fixes merged in #986. - - Co-Authored-By: Claude Opus 4.8 (1M context) - -commit c3e054afebb2d9121ff4d12a56a803eecc31cc41 -Merge: 3cc6beb7 1f04d458 -Author: Anthony Green -Date: Sun Jun 21 18:13:59 2026 -0400 - - Merge pull request #986 from libffi/ci-enable-parisc-regression - - Add PA32/ARCompact struct-by-value regression tests; enable hppa CI - -commit 1f04d458126bd79c58196073477515c88de8163d -Author: Anthony Green -Date: Sun Jun 21 17:37:26 2026 -0400 - - pa: fix stack sizing for 5-8 byte structs passed by value (FFI_PA32) - - ffi_size_stack_pa32 reserved a single stack slot for every struct argument, - but ffi_prep_args_pa32 passes a 5-8 byte struct inline in two even-aligned - slots (like a 64-bit value). With enough such arguments the marshaller's slot - count outgrew cif->bytes and it wrote past the allocated frame -- below sp and - over ffi_call_pa32's saved return pointer -- corrupting the call. - - Make the sizing mirror the marshalling exactly: 1-4 byte structs take one slot, - 5-8 byte structs take "2 + (z & 1)" slots (z stays offset from the marshaller's - slot by the odd FIRST_ARG_SLOT, so the parity matches), and larger structs are - passed by pointer in one slot. Split the PA_HPUX long double case out since it - is passed by pointer. - - Fixes the failure exercised by testsuite/libffi.call/many_small_structs.c. - - Co-Authored-By: Claude Opus 4.8 (1M context) - -commit cf45dfc368ea7373fdd946603845782dbcc1549a -Author: Anthony Green -Date: Sun Jun 21 16:57:56 2026 -0400 - - testsuite: make many_small_structs actually trigger the PA32 overflow - - With only 16 structs the PA-RISC slot under-count overflows ~32 bytes below - sp, landing in the 64-byte register save area where it clobbers nothing live, - so the call still returned correctly and the test passed under qemu. - - Raise the count to 40 so the marshaller's writes sweep past the save area and - over ffi_call_pa32's saved return pointer (sp-20), corrupting the return path. - On a correct backend the arguments still marshal within the allocated frame and - the call returns the expected sums; the test now fails only on an unfixed - FFI_PA32. - - Co-Authored-By: Claude Opus 4.8 (1M context) - -commit d55de2637abd9518d550d18a3e1d89a37bd32f47 -Author: Anthony Green -Date: Sun Jun 21 16:39:30 2026 -0400 - - pa: fix build broken by the int128 generic types - - FFI_TYPE_UINT128/FFI_TYPE_SINT128 were added after FFI_TYPE_COMPLEX, moving - FFI_TYPE_LAST to FFI_TYPE_SINT128. The PA-RISC tripwire still pinned - FFI_PA_TYPE_LAST to FFI_TYPE_COMPLEX, so "#error You likely have broken jump - tables" fired and PA-RISC stopped building once int128 support landed. - - ffi_prep_cif_machdep maps every return type it does not handle explicitly -- - including FFI_TYPE_COMPLEX and the 128-bit integer types -- to FFI_TYPE_INT via - its default case, so cif->flags never exceeds FFI_TYPE_COMPLEX and the linux.S / - hpux32.S return-value jump tables remain sufficient. Bump FFI_PA_TYPE_LAST to - FFI_TYPE_SINT128 to clear the tripwire, with a comment recording why no table - entries were needed. - - Co-Authored-By: Claude Opus 4.8 (1M context) - -commit d7177f2db574c962b4366b1948b7f7e1aaf896c2 -Author: Anthony Green -Date: Sun Jun 21 16:26:53 2026 -0400 - - ci: run the testsuite on PA-RISC 32-bit (hppa) under qemu-user - - Add an hppa-linux-gnu entry to the build-cross-qemu matrix so the testsuite is - cross-compiled with the Debian gcc-hppa-linux-gnu toolchain and run under - qemu-user (binfmt). hppa-linux uses FFI_PA32, so this exercises the - ffi_prep_args_pa32 / ffi_size_stack_pa32 marshalling path and will surface the - many_small_structs.c regression until the backend is fixed. - - Also drop the stale g++-5-hppa-linux-gnu pin from install.sh; the toolchain and - qemu-user-static now come from the CROSS_QEMU block. - - Co-Authored-By: Claude Opus 4.8 (1M context) - -commit a384cba94f3935ac54a38f7c6489b6fbe0488c8c -Author: Anthony Green -Date: Sun Jun 21 16:26:21 2026 -0400 - - testsuite: add struct-by-value regression tests for PA32 and ARCompact - - Two ffi_call tests covering argument-marshalling defects found by a security - review of the backends: - - * many_small_structs.c -- passes sixteen 8-byte structs by value. On - PA-RISC 32-bit (FFI_PA32), ffi_size_stack_pa32 reserves one stack slot per - struct while ffi_prep_args_pa32 consumes two slots for a 5-8 byte struct - passed inline, so enough such args write past the asm-allocated frame. - - * large_struct_by_value.c -- passes one 64-byte struct by value. On - ARCompact (FFI_ARCOMPACT), ffi_call_int sizes the stack arg area at two - words per argument, but a by-value struct is marshalled one word at a time - and words beyond the eight argument registers are written to that - under-sized area without bound. - - Both are written as ordinary correctness tests (valid CIFs, asserts on the - expected sums); they pass on unaffected targets and fail on PA32/ARCompact - until the backends are fixed. - - Co-Authored-By: Claude Opus 4.8 (1M context) - -commit 3cc6beb7d404e12d3458461d76557b79795816ff -Author: Anthony Green -Date: Sun Jun 21 09:47:10 2026 -0400 - - x86-64: replace the transparent plan cache with an explicit ffi_call_plan API - - The transparent plan cache sped up ffi_call and closures by caching a - precomputed argument-placement plan per signature, but it charged every - call a cache lookup and content fingerprint, and it slightly regressed - signatures with no fast path (struct-by-value) that paid the key - computation only to find there was nothing to do. - - Replace it with an opt-in API. ffi_call_plan_alloc builds a plan from a - prepared cif, ffi_call_plan_invoke applies it with no per-call lookup, - and ffi_call_plan_free releases it. The plan is opaque and caller-owned, - immutable once built (so it may be shared across threads), and falls back - to ffi_call for signatures with no fast path. ffi_call and the closure - path return to their original behaviour, so callers that don't adopt the - API pay nothing. - - The x86-64 backend provides an accelerated plan; every other target gets - a portable fallback in prep_cif.c that wraps ffi_call, so the symbols are - defined on all platforms. The per-thread cache, content fingerprint, - thread-exit cleanup and closure demarshal layout are removed. - - Co-Authored-By: Claude Opus 4.8 (1M context) - -commit 0c8f175eb650bb204b679996696b3e7706505d8f -Author: Anthony Green -Date: Sun Jun 21 08:18:14 2026 -0400 - - x86-64: trim the plan-cache fingerprint to the arg_types contents - - abi, nargs and rtype are stored in the slot and compared exactly on every - hit, so folding them into the fingerprint is redundant work on the hot - path. Hashing only the arg_types element pointers keeps the same collision - behaviour and the same protection against a reused arg_types address, while - shortening the per-call hash and speeding up the all-pointer thunk path. - - Co-Authored-By: Claude Opus 4.8 (1M context) - -commit 7b8c3283351298c55d09f580414ab937f358156c -Merge: d4995b5b f1e21329 -Author: Anthony Green -Date: Sat Jun 20 19:48:22 2026 -0400 - - Merge pull request #985 from libffi/plan-cache-fingerprint - - x86-64: content-based plan-cache key and thread-exit cleanup - -commit f1e2132940880128e26b885ee956e76fe3cad2d8 -Author: Anthony Green -Date: Sat Jun 20 19:46:50 2026 -0400 - - x86-64: content-based plan-cache key and thread-exit cleanup - - Follow-up to the plan cache (PR #984), addressing two issues. - - Content key: the cache keyed slots on the abi/nargs/rtype plus the arg_types - *array pointer*. If a cif's arg_types array was freed and the address later - reused for a different signature with the same abi/nargs/rtype, the keys - collided and a stale plan could be returned. Key on a 64-bit fingerprint that - folds in the element ffi_type pointers, so distinct argument types always get - distinct keys. Lookup is now O(nargs) cheap pointer reads -- still far less - than re-classifying the call. - - Thread-exit cleanup: the per-thread cache leaked its malloc'd plans when a - thread exited. Register a pthread key whose destructor frees the thread's - plans. pthread is referenced via weak symbols, so a program that never links - pthread simply skips the registration (the cache is already bounded per thread). - - Co-Authored-By: Claude Opus 4.8 (1M context) - -commit d4995b5bff6e5ecefb46eda218c40717f895baaf -Merge: 918ca8ed de3826a8 -Author: Anthony Green -Date: Sat Jun 20 18:43:14 2026 -0400 - - Merge pull request #984 from libffi/fast-libffi-plan - - x86-64: transparent plan cache for ffi_call and closures - -commit de3826a8063fde113ee8d1e455d459038d5814cc -Author: Anthony Green -Date: Sat Jun 20 18:14:08 2026 -0400 - - x86-64: limit the plan cache to the SysV ABI - - The Win64/EFI64 plan path failed an MSVC execution test (libffi.call/float3) - that can't be reproduced or validated without a Windows toolchain. Revert - ffiw64.c to the proven upstream implementation and build plans only for - FFI_UNIX64, leaving Win64/EFI64/GNUW64 on the existing path. The SysV fast - path -- the bulk of the speedup -- is unaffected. - - Co-Authored-By: Claude Opus 4.8 (1M context) - -commit fdbf8deaf297b308731385f91868ea8b5e7dbd21 -Author: Anthony Green -Date: Sat Jun 20 17:59:54 2026 -0400 - - x86-64: fix Windows build of the plan cache - - Native Windows targets build only ffiw64.c + win64.S for the x86 backend; - ffi64.c (which instantiates the plan-cache storage and ffi_build_plan_arch) - is not compiled, so ffiw64.c's ffi_plan_get left ffi_plan_miss / ffi_plan_cache - unresolved at link time. Instantiate the cache in ffiw64.c when X86_WIN64 is - defined (i.e. when ffi64.c is absent); on Unix x86-64 ffi64.c still does it and - X86_WIN64 is undefined, so there is no duplicate definition. - - Also use a portable thread-local qualifier (FFI_TLS: __declspec(thread) on - MSVC, __thread elsewhere). - - Co-Authored-By: Claude Opus 4.8 (1M context) - -commit 872365e143c521b4a84f712aa60f4e638a8b8053 -Author: Anthony Green -Date: Sat Jun 20 17:25:17 2026 -0400 - - x86-64: reword plan-cache comments to be self-contained - - Drop the in-development "Mechanism A" label and the stale references to the - earlier opt-in arena/slab storage, so the comments describe the cache as it - now stands. No functional change. - - Co-Authored-By: Claude Opus 4.8 (1M context) - -commit c4c9750c37868f26d1ce914592ca826e4ffc0b35 -Author: Anthony Green -Date: Sat Jun 20 13:10:18 2026 -0400 - - x86-64: transparent plan cache for ffi_call and closures - - Precompute each signature's argument placement once and cache it, keyed on - (abi, nargs, rtype, arg_types) in a per-thread direct-mapped cache, so ffi_call - and closures skip the per-call ABI classification. No API or ABI change -- - every caller benefits transparently on a relink. - - - Cache: src/plan-cache.h (inline O(1) probe) + src/plan-cache-impl.h - (per-thread storage, evict-and-free, FFI_PLAN_NONE for non-plan-able sigs). - - SysV (ffi64.c, unix64.S): pure-GP64 direct thunks (avalue -> arg regs), a - lean trampoline for register-only calls, and a plan executor reusing - ffi_call_unix64; closures use a precomputed demarshal layout. CFI on the - thunks/trampoline so exceptions unwind through them. - - Win64/EFI64 (ffiw64.c): plan executor + demarshal. - - W^X preserved: no runtime codegen; thunks shipped in .text. - - Scalar/pointer/int128/float/double args are accelerated; struct/complex/x87 - args fall back to the existing path. Pointer-heavy calls ~5x faster. - - Co-Authored-By: Claude Opus 4.8 (1M context) - -commit 918ca8edddbf542d9034d8e58931edfea036ecca -Author: Anthony Green -Date: Sat Jun 20 12:34:46 2026 -0400 - - ci: actually zip the MSVC binaries before the release upload - - The MSVC 'Create binary distribution' step builds a *directory* - (libffi--x86-bit-msvc-binaries/), but 'Upload to GitHub Release' - pointed softprops/action-gh-release at a .zip file that was never created. With - no matching file the action just warns and succeeds, so on the v3.6.0 tag the - step reported success while attaching nothing. - - Add a pwsh Compress-Archive step to produce the .zip (contents at top level, like - the upload-artifact layout), and set fail_on_unmatched_files: true so a missing - file fails the step loudly instead of silently passing. - - Co-Authored-By: Claude Opus 4.8 (1M context) - commit f6303b8b0dd6353c6a5bb4de2e855a13b86f22cf Author: Anthony Green Date: Sat Jun 20 09:54:12 2026 -0400 diff --git a/deps/libffi/LICENSE b/deps/libffi/LICENSE index 90a8b230284e5c..12b4970e0ddcb4 100644 --- a/deps/libffi/LICENSE +++ b/deps/libffi/LICENSE @@ -1,4 +1,4 @@ -libffi - Copyright (c) 1996-2026 Anthony Green, Red Hat, Inc and others. +libffi - Copyright (c) 1996-2025 Anthony Green, Red Hat, Inc and others. See source files for details. Permission is hereby granted, free of charge, to any person obtaining diff --git a/deps/libffi/README.md b/deps/libffi/README.md index 19f78f632b0cd4..7693f1daf6f850 100644 --- a/deps/libffi/README.md +++ b/deps/libffi/README.md @@ -1,5 +1,5 @@ -libffi-3.7.1 was released on July 10, 2026. +libffi-3.6.0 was released on June 20, 2026. What is libffi? @@ -201,30 +201,6 @@ History See the git log for details at http://github.com/libffi/libffi. - 3.7.1 July-10-2026 - Fix aarch64 ffi_call memory corruption when passing many large - structs by value. - Fix i386 thiscall/fastcall closure stack cleanup for 64-bit - integer and struct arguments. - Fix aarch64 int128 argument split between x7 and the stack on - Darwin (#993). - Fix aarch64 clang-cl link failure for HFA helper functions (#996). - Build a generic ffi_call_plan fallback on Windows x86-64. - Add Windows ARM64 (MSVC) build and continuous integration support. - - 3.7.0 July-7-2026 - Add reusable call plans (ffi_call_plan_alloc/ffi_call_plan_invoke/ffi_call_plan_free). - Fix PA-RISC build broken by the conditional __int128 support added in 3.6.0. - Fix PA-RISC stack overflow passing many small structs by value. - Fix powerpc aix/darwin closure build errors (#987). - Include .note.GNU-stack on FreeBSD/x86 (#991). - Fix MSVC Win32 spin-lock atomics in bundled dlmalloc (#989). - Harden static trampoline and x86_64 closure internals. - Fix ffi_call clobbering the caller's argument pointer array - when passing large structs by value. - Allow a NULL rvalue in ffi_call to discard the return value. - Fix wasm widening of integral returns narrower than ffi_arg. - 3.6.0 Jun-20-2026 Add LoongArch32 support. Add RISC-V static trampoline support. diff --git a/deps/libffi/configure b/deps/libffi/configure index e050ddc8675da1..15b227e6888f82 100755 --- a/deps/libffi/configure +++ b/deps/libffi/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.71 for libffi 3.7.1. +# Generated by GNU Autoconf 2.71 for libffi 3.6.0. # # Report bugs to . # @@ -621,8 +621,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='libffi' PACKAGE_TARNAME='libffi' -PACKAGE_VERSION='3.7.1' -PACKAGE_STRING='libffi 3.7.1' +PACKAGE_VERSION='3.6.0' +PACKAGE_STRING='libffi 3.6.0' PACKAGE_BUGREPORT='http://github.com/libffi/libffi/issues' PACKAGE_URL='' @@ -1417,7 +1417,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures libffi 3.7.1 to adapt to many kinds of systems. +\`configure' configures libffi 3.6.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1489,7 +1489,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of libffi 3.7.1:";; + short | recursive ) echo "Configuration of libffi 3.6.0:";; esac cat <<\_ACEOF @@ -1628,7 +1628,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -libffi configure 3.7.1 +libffi configure 3.6.0 generated by GNU Autoconf 2.71 Copyright (C) 2021 Free Software Foundation, Inc. @@ -2259,7 +2259,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by libffi $as_me 3.7.1, which was +It was created by libffi $as_me 3.6.0, which was generated by GNU Autoconf 2.71. Invocation command line was $ $0$ac_configure_args_raw @@ -3233,11 +3233,8 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_config_headers="$ac_config_headers fficonfig.h" -FFI_VERSION_STRING="3.7.1" -ffi_version_major=`echo "3.7.1" | cut -d. -f1` -ffi_version_minor=`echo "3.7.1" | cut -d. -f2 | sed 's/[^0-9].*//'` -ffi_version_micro=`echo "3.7.1" | cut -d. -f3 | sed 's/[^0-9].*//'` -FFI_VERSION_NUMBER=`expr ${ffi_version_major:-0} \* 10000 + ${ffi_version_minor:-0} \* 100 + ${ffi_version_micro:-0}` +FFI_VERSION_STRING="3.5.2" +FFI_VERSION_NUMBER=30502 @@ -3986,7 +3983,7 @@ fi # Define the identity of the package. PACKAGE='libffi' - VERSION='3.7.1' + VERSION='3.6.0' printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h @@ -20661,7 +20658,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by libffi $as_me 3.7.1, which was +This file was extended by libffi $as_me 3.6.0, which was generated by GNU Autoconf 2.71. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -20729,7 +20726,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ -libffi config.status 3.7.1 +libffi config.status 3.6.0 configured by $0, generated by GNU Autoconf 2.71, with options \\"\$ac_cs_config\\" diff --git a/deps/libffi/configure.ac b/deps/libffi/configure.ac index 3370acc3a39690..2c8ba77970b3b0 100644 --- a/deps/libffi/configure.ac +++ b/deps/libffi/configure.ac @@ -2,17 +2,11 @@ dnl Process this with autoconf to create configure AC_PREREQ([2.68]) -AC_INIT([libffi],[3.7.1],[http://github.com/libffi/libffi/issues]) +AC_INIT([libffi],[3.6.0],[http://github.com/libffi/libffi/issues]) AC_CONFIG_HEADERS([fficonfig.h]) -dnl Derive the version macros from AC_INIT so they cannot drift when the -dnl release version is bumped. FFI_VERSION_NUMBER encodes X.Y.Z as -dnl X*10000 + Y*100 + Z, ignoring any non-numeric suffix (e.g. -rc0). -FFI_VERSION_STRING="AC_PACKAGE_VERSION" -ffi_version_major=`echo "AC_PACKAGE_VERSION" | cut -d. -f1` -ffi_version_minor=`echo "AC_PACKAGE_VERSION" | cut -d. -f2 | sed 's/[[^0-9]].*//'` -ffi_version_micro=`echo "AC_PACKAGE_VERSION" | cut -d. -f3 | sed 's/[[^0-9]].*//'` -FFI_VERSION_NUMBER=`expr ${ffi_version_major:-0} \* 10000 + ${ffi_version_minor:-0} \* 100 + ${ffi_version_micro:-0}` +FFI_VERSION_STRING="3.5.2" +FFI_VERSION_NUMBER=30502 AC_SUBST(FFI_VERSION_STRING) AC_SUBST(FFI_VERSION_NUMBER) diff --git a/deps/libffi/doc/libffi.info b/deps/libffi/doc/libffi.info index b43246f8ed4e3a..b5b81d5bcf815f 100644 --- a/deps/libffi/doc/libffi.info +++ b/deps/libffi/doc/libffi.info @@ -3,7 +3,7 @@ This is libffi.info, produced by makeinfo version 7.1 from libffi.texi. This manual is for libffi, a portable foreign function interface library. - Copyright © 2008-2026 Anthony Green and Red Hat, Inc. + Copyright © 2008-2025 Anthony Green and Red Hat, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -38,7 +38,7 @@ libffi This manual is for libffi, a portable foreign function interface library. - Copyright © 2008-2026 Anthony Green and Red Hat, Inc. + Copyright © 2008-2025 Anthony Green and Red Hat, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -112,7 +112,6 @@ File: libffi.info, Node: Using libffi, Next: Memory Usage, Prev: Introduction * Simple Example:: A simple example. * Types:: libffi type descriptions. * Multiple ABIs:: Different passing styles on one platform. -* Reusable Call Plans:: Building a call plan once and reusing it. * The Closure API:: Writing a generic function. * Closure Example:: A closure example. * Thread Safety:: Thread safety. @@ -201,11 +200,6 @@ function: responsibility to ensure this. If CIF declares that the function returns ‘void’ (using ‘ffi_type_void’), then RVALUE is ignored. - RVALUE may also be ‘NULL’, in which case the call is performed and - the return value is discarded. This works for any return type, - including structures returned in memory; ‘libffi’ supplies internal - scratch space for the callee when needed. - In most situations, ‘libffi’ will handle promotion according to the ABI. However, for historical reasons, there is a special case with return values that must be handled by your code. In particular, @@ -219,9 +213,7 @@ function: AVALUES is a vector of ‘void *’ pointers that point to the memory locations holding the argument values for a call. If CIF declares that the function has no arguments (i.e., NARGS was 0), then - AVALUES is ignored. ‘ffi_call’ does not modify the vector or the - argument values it points to, so both may be reused for subsequent - calls. + AVALUES is ignored. Note that while the return value must be register-sized, arguments should exactly match their declared type. For example, if an @@ -747,7 +739,7 @@ compilers that support them: type descriptors in the previous example.  -File: libffi.info, Node: Multiple ABIs, Next: Reusable Call Plans, Prev: Types, Up: Using libffi +File: libffi.info, Node: Multiple ABIs, Next: The Closure API, Prev: Types, Up: Using libffi 2.4 Multiple ABIs ================= @@ -759,46 +751,9 @@ instance, the x86 platform has both ‘stdcall’ and ‘fastcall’ functions. necessarily platform-specific.  -File: libffi.info, Node: Reusable Call Plans, Next: The Closure API, Prev: Multiple ABIs, Up: Using libffi - -2.5 Reusable Call Plans -======================= - -When the same signature is called many times - as language bindings -typically do - you can build a “call plan” once and reuse it, so that -each call avoids the work ‘ffi_call’ would otherwise repeat on every -invocation. A plan is an opaque, caller-owned object built from a -prepared ‘ffi_cif’. - - -- Function: ffi_call_plan * ffi_call_plan_alloc (ffi_cif *CIF) - Builds and returns a reusable plan for the signature described by - CIF, which must already have been prepared with ‘ffi_prep_cif’. - The plan does not copy CIF; CIF must remain valid for as long as - the plan is used. - - Returns ‘NULL’ only when memory cannot be allocated. A signature - for which no accelerated path exists is still valid: the returned - plan simply falls back to ‘ffi_call’ when it is invoked. - - -- Function: void ffi_call_plan_invoke (ffi_call_plan *PLAN, void *FN, - void *RVALUE, void **AVALUES) - Calls FN using PLAN. The FN, RVALUE, and AVALUES arguments have - exactly the same meaning as for ‘ffi_call’ (*note The Basics::), - including the rule that integral return values narrower than a - register are widened to ‘ffi_arg’. - - A plan is immutable once built, so a single plan may be invoked - concurrently from multiple threads without additional locking. - - -- Function: void ffi_call_plan_free (ffi_call_plan *PLAN) - Releases a plan returned by ‘ffi_call_plan_alloc’. Passing ‘NULL’ - is harmless. The ‘ffi_cif’ the plan was built from is not - affected. - - -File: libffi.info, Node: The Closure API, Next: Closure Example, Prev: Reusable Call Plans, Up: Using libffi +File: libffi.info, Node: The Closure API, Next: Closure Example, Prev: Multiple ABIs, Up: Using libffi -2.6 The Closure API +2.5 The Closure API =================== ‘libffi’ also provides a way to write a generic function - a function @@ -902,7 +857,7 @@ executable addresses.  File: libffi.info, Node: Closure Example, Next: Thread Safety, Prev: The Closure API, Up: Using libffi -2.7 Closure Example +2.6 Closure Example =================== A trivial example that creates a new ‘puts’ by binding ‘fputs’ with @@ -961,7 +916,7 @@ A trivial example that creates a new ‘puts’ by binding ‘fputs’ with  File: libffi.info, Node: Thread Safety, Prev: Closure Example, Up: Using libffi -2.8 Thread Safety +2.7 Thread Safety ================= ‘libffi’ is not completely thread-safe. However, many parts are, and if @@ -1049,21 +1004,17 @@ Index * cif: The Basics. (line 14) * closure API: The Closure API. (line 13) * closures: The Closure API. (line 13) -* const char *: The Basics. (line 113) +* const char *: The Basics. (line 106) * FFI: Introduction. (line 31) * ffi_call: The Basics. (line 72) -* ffi_call_plan *: Reusable Call Plans. (line 12) -* ffi_call_plan_alloc: Reusable Call Plans. (line 12) -* ffi_call_plan_free: Reusable Call Plans. (line 32) -* ffi_call_plan_invoke: Reusable Call Plans. (line 22) * ffi_closure_alloc: The Closure API. (line 19) * ffi_closure_free: The Closure API. (line 26) * FFI_CLOSURES: The Closure API. (line 13) -* ffi_get_closure_size: The Basics. (line 125) -* ffi_get_default_abi: The Basics. (line 122) +* ffi_get_closure_size: The Basics. (line 118) +* ffi_get_default_abi: The Basics. (line 115) * ffi_get_struct_offsets: Size and Alignment. (line 39) -* ffi_get_version: The Basics. (line 113) -* ffi_get_version_number: The Basics. (line 117) +* ffi_get_version: The Basics. (line 106) +* ffi_get_version_number: The Basics. (line 110) * ffi_prep_cif: The Basics. (line 16) * ffi_prep_cif_var: The Basics. (line 39) * ffi_prep_closure_loc: The Closure API. (line 41) @@ -1100,14 +1051,12 @@ Index * ffi_type_ushort: Primitive Types. (line 53) * ffi_type_void: Primitive Types. (line 10) * Foreign Function Interface: Introduction. (line 31) -* size_t: The Basics. (line 125) -* unsigned int: The Basics. (line 122) -* unsigned long: The Basics. (line 117) +* size_t: The Basics. (line 118) +* unsigned int: The Basics. (line 115) +* unsigned long: The Basics. (line 110) * void: The Basics. (line 72) -* void <1>: Reusable Call Plans. (line 22) -* void <2>: Reusable Call Plans. (line 32) -* void <3>: The Closure API. (line 19) -* void <4>: The Closure API. (line 26) +* void <1>: The Closure API. (line 19) +* void <2>: The Closure API. (line 26)  @@ -1115,24 +1064,23 @@ Tag Table: Node: Top1387 Node: Introduction2909 Node: Using libffi4569 -Node: The Basics5172 -Node: Simple Example11346 -Node: Types12403 -Node: Primitive Types12914 -Node: Structures15231 -Node: Size and Alignment16342 -Node: Arrays Unions Enums18613 -Node: Type Example21590 -Node: Complex22896 -Node: Complex Type Example24410 -Node: Multiple ABIs27462 -Node: Reusable Call Plans27849 -Node: The Closure API29566 -Node: Closure Example33908 -Node: Thread Safety35552 -Node: Memory Usage36385 -Node: Missing Features37660 -Node: Index38037 +Node: The Basics5098 +Node: Simple Example10868 +Node: Types11925 +Node: Primitive Types12436 +Node: Structures14753 +Node: Size and Alignment15864 +Node: Arrays Unions Enums18135 +Node: Type Example21112 +Node: Complex22418 +Node: Complex Type Example23932 +Node: Multiple ABIs26984 +Node: The Closure API27367 +Node: Closure Example31703 +Node: Thread Safety33347 +Node: Memory Usage34180 +Node: Missing Features35455 +Node: Index35832  End Tag Table diff --git a/deps/libffi/doc/libffi.pdf b/deps/libffi/doc/libffi.pdf index 250d34faf71159..3aa5406e61e086 100644 Binary files a/deps/libffi/doc/libffi.pdf and b/deps/libffi/doc/libffi.pdf differ diff --git a/deps/libffi/doc/libffi.texi b/deps/libffi/doc/libffi.texi index 251214f890d3f5..73b128f863e7be 100644 --- a/deps/libffi/doc/libffi.texi +++ b/deps/libffi/doc/libffi.texi @@ -18,7 +18,7 @@ This manual is for libffi, a portable foreign function interface library. -Copyright @copyright{} 2008--2026 Anthony Green and Red Hat, Inc. +Copyright @copyright{} 2008--2025 Anthony Green and Red Hat, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -118,7 +118,6 @@ values passed between the two languages. * Simple Example:: A simple example. * Types:: libffi type descriptions. * Multiple ABIs:: Different passing styles on one platform. -* Reusable Call Plans:: Building a call plan once and reusing it. * The Closure API:: Writing a generic function. * Closure Example:: A closure example. * Thread Safety:: Thread safety. @@ -214,11 +213,6 @@ to ensure this. If @var{cif} declares that the function returns @code{void} (using @code{ffi_type_void}), then @var{rvalue} is ignored. -@var{rvalue} may also be @code{NULL}, in which case the call is -performed and the return value is discarded. This works for any -return type, including structures returned in memory; @code{libffi} -supplies internal scratch space for the callee when needed. - In most situations, @code{libffi} will handle promotion according to the ABI. However, for historical reasons, there is a special case with return values that must be handled by your code. In particular, @@ -232,9 +226,7 @@ full @code{ffi_arg} into the return value. @var{avalues} is a vector of @code{void *} pointers that point to the memory locations holding the argument values for a call. If @var{cif} declares that the function has no arguments (i.e., @var{nargs} was 0), -then @var{avalues} is ignored. @code{ffi_call} does not modify the -vector or the argument values it points to, so both may be reused for -subsequent calls. +then @var{avalues} is ignored. Note that while the return value must be register-sized, arguments should exactly match their declared type. For example, if an argument @@ -814,45 +806,6 @@ necessarily platform-specific. @c FIXME: document the platforms -@node Reusable Call Plans -@section Reusable Call Plans - -When the same signature is called many times -- as language bindings -typically do -- you can build a @dfn{call plan} once and reuse it, so -that each call avoids the work @code{ffi_call} would otherwise repeat -on every invocation. A plan is an opaque, caller-owned object built -from a prepared @code{ffi_cif}. - -@findex ffi_call_plan_alloc -@defun {ffi_call_plan *} ffi_call_plan_alloc (ffi_cif *@var{cif}) -Builds and returns a reusable plan for the signature described by -@var{cif}, which must already have been prepared with -@code{ffi_prep_cif}. The plan does not copy @var{cif}; @var{cif} must -remain valid for as long as the plan is used. - -Returns @code{NULL} only when memory cannot be allocated. A signature -for which no accelerated path exists is still valid: the returned plan -simply falls back to @code{ffi_call} when it is invoked. -@end defun - -@findex ffi_call_plan_invoke -@defun void ffi_call_plan_invoke (ffi_call_plan *@var{plan}, void *@var{fn}, void *@var{rvalue}, void **@var{avalues}) -Calls @var{fn} using @var{plan}. The @var{fn}, @var{rvalue}, and -@var{avalues} arguments have exactly the same meaning as for -@code{ffi_call} (@pxref{The Basics}), including the rule that integral -return values narrower than a register are widened to @code{ffi_arg}. - -A plan is immutable once built, so a single plan may be invoked -concurrently from multiple threads without additional locking. -@end defun - -@findex ffi_call_plan_free -@defun void ffi_call_plan_free (ffi_call_plan *@var{plan}) -Releases a plan returned by @code{ffi_call_plan_alloc}. Passing -@code{NULL} is harmless. The @code{ffi_cif} the plan was built from is -not affected. -@end defun - @node The Closure API @section The Closure API diff --git a/deps/libffi/doc/stamp-vti b/deps/libffi/doc/stamp-vti index e755454e9c8215..6658c1da7e7825 100644 --- a/deps/libffi/doc/stamp-vti +++ b/deps/libffi/doc/stamp-vti @@ -1,4 +1,4 @@ -@set UPDATED 10 July 2026 -@set UPDATED-MONTH July 2026 -@set EDITION 3.7.1 -@set VERSION 3.7.1 +@set UPDATED 20 June 2026 +@set UPDATED-MONTH June 2026 +@set EDITION 3.6.0 +@set VERSION 3.6.0 diff --git a/deps/libffi/doc/version.texi b/deps/libffi/doc/version.texi index e755454e9c8215..6658c1da7e7825 100644 --- a/deps/libffi/doc/version.texi +++ b/deps/libffi/doc/version.texi @@ -1,4 +1,4 @@ -@set UPDATED 10 July 2026 -@set UPDATED-MONTH July 2026 -@set EDITION 3.7.1 -@set VERSION 3.7.1 +@set UPDATED 20 June 2026 +@set UPDATED-MONTH June 2026 +@set EDITION 3.6.0 +@set VERSION 3.6.0 diff --git a/deps/libffi/generate-headers.py b/deps/libffi/generate-headers.py index e2d2942deffb84..312b0099ef2f0c 100644 --- a/deps/libffi/generate-headers.py +++ b/deps/libffi/generate-headers.py @@ -7,8 +7,8 @@ from pathlib import Path -LIBFFI_VERSION = '3.7.1' -LIBFFI_VERSION_NUMBER = '30701' +LIBFFI_VERSION = '3.6.0' +LIBFFI_VERSION_NUMBER = '30600' def normalize_arch(target_arch): aliases = { diff --git a/deps/libffi/include/ffi.h.in b/deps/libffi/include/ffi.h.in index 35f09cf43315ca..4ac9c2598c1517 100644 --- a/deps/libffi/include/ffi.h.in +++ b/deps/libffi/include/ffi.h.in @@ -1,6 +1,6 @@ /* -----------------------------------------------------------------*-C-*- libffi @VERSION@ - - Copyright (c) 2011, 2014, 2019, 2021, 2022, 2024, 2025, 2026 Anthony Green + - Copyright (c) 2011, 2014, 2019, 2021, 2022, 2024, 2025 Anthony Green - Copyright (c) 1996-2003, 2007, 2008 Red Hat, Inc. Permission is hereby granted, free of charge, to any person @@ -524,32 +524,6 @@ void ffi_call(ffi_cif *cif, void *rvalue, void **avalue); -/* Reusable call plans. - - A plan captures a signature's argument placement once so that repeated - calls skip the per-call work that ffi_call would otherwise redo. Build one - with ffi_call_plan_alloc, invoke it as many times as you like, and release - it with ffi_call_plan_free. - - The plan is opaque and caller-owned. The cif must outlive the plan. - ffi_call_plan_alloc returns NULL only on allocation failure; a signature - with no fast path is still valid and ffi_call_plan_invoke falls back to - ffi_call for it. A plan is immutable once built, so it may be shared and - invoked concurrently from multiple threads. */ -typedef struct ffi_call_plan ffi_call_plan; - -FFI_API -ffi_call_plan *ffi_call_plan_alloc (ffi_cif *cif); - -FFI_API -void ffi_call_plan_invoke (ffi_call_plan *plan, - void (*fn)(void), - void *rvalue, - void **avalue); - -FFI_API -void ffi_call_plan_free (ffi_call_plan *plan); - FFI_API ffi_status ffi_get_struct_offsets (ffi_abi abi, ffi_type *struct_type, size_t *offsets); diff --git a/deps/libffi/libffi.gyp b/deps/libffi/libffi.gyp index f3122a4e2a0041..2d43dcdb733c97 100644 --- a/deps/libffi/libffi.gyp +++ b/deps/libffi/libffi.gyp @@ -204,6 +204,13 @@ }, ], 'conditions': [ + ['OS == "win" and clang == 1', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['-Wno-incompatible-pointer-types'], + }, + }, + }], ['OS == "win" and (target_arch == "x64" or target_arch == "x86_64")', { 'actions': [ { diff --git a/deps/libffi/libffi.map.in b/deps/libffi/libffi.map.in index f4e366eb60bceb..838acfea503602 100644 --- a/deps/libffi/libffi.map.in +++ b/deps/libffi/libffi.map.in @@ -58,17 +58,6 @@ LIBFFI_BASE_8.1 { ffi_get_closure_size; } LIBFFI_BASE_8.0; -/* ---------------------------------------------------------------------- - Reusable call plans (ffi_call_plan_*). Additive: new symbols only, the - existing struct layout and entry points are untouched. - -------------------------------------------------------------------- */ -LIBFFI_CALL_PLAN_8.4 { - global: - ffi_call_plan_alloc; - ffi_call_plan_invoke; - ffi_call_plan_free; -} LIBFFI_BASE_8.1; - #ifdef FFI_TARGET_HAS_COMPLEX_TYPE LIBFFI_COMPLEX_8.0 { global: diff --git a/deps/libffi/libtool-version b/deps/libffi/libtool-version index c5545eab8bf6c2..656cc6056f7401 100644 --- a/deps/libffi/libtool-version +++ b/deps/libffi/libtool-version @@ -26,4 +26,4 @@ # release, then set age to 0. # # CURRENT:REVISION:AGE -12:1:4 +11:1:3 diff --git a/deps/libffi/msvcc.sh b/deps/libffi/msvcc.sh index a69f57fd98882e..301e2fbd6840fe 100755 --- a/deps/libffi/msvcc.sh +++ b/deps/libffi/msvcc.sh @@ -94,13 +94,6 @@ do cl="clang-cl" shift 1 ;; - -E) - # DejaGnu-style preprocessing (-E -o ): handled specially - # below, since cl writes preprocessed output to stdout and prints - # the source file name to stderr. - preprocess="true" - shift 1 - ;; -O0) args="$args -Od" shift 1 @@ -243,25 +236,19 @@ do shift 1 ;; -S) - # Compile to assembly without linking, like gcc -S: emit the - # listing and stop after the compile step. - args="$args -c -FAs" - compile_to_asm="true" + args="$args -FAs" shift 1 ;; -o) outdir="$(dirname $2)" base="$(basename $2|sed 's/\.[^.]*//g')" - ppout="$2" - if [ -n "$compile_to_asm" ]; then - output="-Fo$outdir/$base.obj -Fa$2" - elif [ -n "$single" ]; then + if [ -n "$single" ]; then output="-Fo$2" else output="-Fe$2" fi armasm_output="-o $2" - if [ -n "$assembly" ] || [ -n "$compile_to_asm" ]; then + if [ -n "$assembly" ]; then args="$args $output" else args="$args $output -Fd$outdir/$base -Fp$outdir/$base -Fa$outdir/$base" @@ -307,19 +294,7 @@ if [ -n "$debug_crt" ]; then md="${md}d" fi -if [ -n "$preprocess" ]; then - # Write the preprocessed output to the -o target (or stdout), and - # swallow stderr: cl prints the source file name there, which the - # testsuite's feature probes would mistake for a diagnostic. - args="$(echo $args | sed 's%[-/]F[edpoa][^ ]*%%g')" - - if test -n "$verbose"; then - echo "$cl -EP $args > ${ppout:-/dev/stdout}" - fi - - eval "\"$cl\" -EP $args" > "${ppout:-/dev/stdout}" 2>/dev/null - result=$? -elif [ -n "$assembly" ]; then +if [ -n "$assembly" ]; then if [ -z "$outdir" ]; then outdir="." fi diff --git a/deps/libffi/src/aarch64/ffi.c b/deps/libffi/src/aarch64/ffi.c index 2e6a2ad2624c07..715e478c0edd14 100644 --- a/deps/libffi/src/aarch64/ffi.c +++ b/deps/libffi/src/aarch64/ffi.c @@ -378,13 +378,13 @@ extend_integer_type (void *source, int type) } } -#if defined(_MSC_VER) && !defined(__clang__) +#if defined(_MSC_VER) void extend_hfa_type (void *dest, void *src, int h); #else static void extend_hfa_type (void *dest, void *src, int h) { - ptrdiff_t f = h - AARCH64_RET_S4; + ssize_t f = h - AARCH64_RET_S4; void *x0; #define BTI_J "hint #36" @@ -449,7 +449,7 @@ extend_hfa_type (void *dest, void *src, int h) } #endif -#if defined(_MSC_VER) && !defined(__clang__) +#if defined(_MSC_VER) void* compress_hfa_type (void *dest, void *src, int h); #else static void * @@ -550,9 +550,7 @@ allocate_int128_to_reg_or_stack (struct call_context *context, ngrn += ngrn & 1; #endif - /* The value must fit entirely in registers, i.e. the low half may - not be allocated to x7 with the high half spilled to the stack. */ - if (ngrn + 2 <= N_X_ARG_REG) + if (ngrn < N_X_ARG_REG) { ret = &context->x[ngrn]; ngrn += 2; @@ -644,23 +642,6 @@ ffi_prep_cif_machdep (ffi_cif *cif) break; } - /* Composites larger than 16 bytes (that are not HFAs) are passed by - invisible reference: ffi_call copies the payload into the argument - slab (next_struct_area, growing down) and, once the X registers are - exhausted, also spills the by-ref pointer into the same slab (the - NSAA, growing up). The generic prep_cif accounting in cif->bytes - only charges the payload copy, not that 8-byte pointer slot, so the - two regions can collide and a later struct copy can overwrite an - already-spilled pointer with the copied payload bytes. Reserve an - extra 8 bytes per such argument so the slab is always large enough - for both. */ - for (i = 0, n = cif->nargs; i < n; i++) - { - ffi_type *ty = cif->arg_types[i]; - if (ty->size > 16 && !is_vfp_type (ty)) - bytes += 8; - } - /* Round the stack up to a multiple of the stack alignment requirement. */ cif->bytes = (unsigned) FFI_ALIGN(bytes, 16); cif->flags = flags; diff --git a/deps/libffi/src/alpha/ffi.c b/deps/libffi/src/alpha/ffi.c index 5830ee3f2109e4..cb683ec14317f8 100644 --- a/deps/libffi/src/alpha/ffi.c +++ b/deps/libffi/src/alpha/ffi.c @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------- - ffi.c - Copyright (c) 2012, 2026 Anthony Green + ffi.c - Copyright (c) 2012 Anthony Green Copyright (c) 1998, 2001, 2007, 2008 Red Hat, Inc. Alpha Foreign Function Interface diff --git a/deps/libffi/src/alpha/ffitarget.h b/deps/libffi/src/alpha/ffitarget.h index da63dc08813a26..f80f13e85fe67e 100644 --- a/deps/libffi/src/alpha/ffitarget.h +++ b/deps/libffi/src/alpha/ffitarget.h @@ -1,5 +1,5 @@ /* -----------------------------------------------------------------*-C-*- - ffitarget.h - Copyright (c) 2012, 2026 Anthony Green + ffitarget.h - Copyright (c) 2012 Anthony Green Copyright (c) 1996-2003 Red Hat, Inc. Target configuration macros for Alpha. diff --git a/deps/libffi/src/closures.c b/deps/libffi/src/closures.c index c1fbcf87ce29b6..124e1f386a7138 100644 --- a/deps/libffi/src/closures.c +++ b/deps/libffi/src/closures.c @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------- - closures.c - Copyright (c) 2019, 2022, 2026 Anthony Green + closures.c - Copyright (c) 2019, 2022 Anthony Green Copyright (c) 2007, 2009, 2010 Red Hat, Inc. Copyright (C) 2007, 2009, 2010 Free Software Foundation, Inc Copyright (c) 2011 Plausible Labs Cooperative, Inc. diff --git a/deps/libffi/src/dlmalloc.c b/deps/libffi/src/dlmalloc.c index 93e81967039359..5b400d65de3db4 100644 --- a/deps/libffi/src/dlmalloc.c +++ b/deps/libffi/src/dlmalloc.c @@ -1916,8 +1916,8 @@ static FORCEINLINE void x86_clear_lock(int* sl) { #define CLEAR_LOCK(sl) x86_clear_lock(sl) #else /* Win32 MSC */ -#define CAS_LOCK(sl) interlockedexchange((LONG volatile *)(sl), (LONG)1) -#define CLEAR_LOCK(sl) interlockedexchange ((LONG volatile *)(sl), (LONG)0) +#define CAS_LOCK(sl) interlockedexchange(sl, (LONG)1) +#define CLEAR_LOCK(sl) interlockedexchange (sl, (LONG)0) #endif /* ... gcc spins locks ... */ diff --git a/deps/libffi/src/frv/ffi.c b/deps/libffi/src/frv/ffi.c index 4eb8e1ddfb87c8..99dd41d32d4c8f 100644 --- a/deps/libffi/src/frv/ffi.c +++ b/deps/libffi/src/frv/ffi.c @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------- - ffi.c - Copyright (C) 2004, 2026 Anthony Green + ffi.c - Copyright (C) 2004 Anthony Green Copyright (C) 2007 Free Software Foundation, Inc. Copyright (C) 2008 Red Hat, Inc. diff --git a/deps/libffi/src/ia64/ffi.c b/deps/libffi/src/ia64/ffi.c index 00e1ae15707c5f..c00ef08543289a 100644 --- a/deps/libffi/src/ia64/ffi.c +++ b/deps/libffi/src/ia64/ffi.c @@ -1,7 +1,7 @@ /* ----------------------------------------------------------------------- ffi.c - Copyright (c) 1998, 2007, 2008, 2012 Red Hat, Inc. Copyright (c) 2000 Hewlett Packard Company - Copyright (c) 2011, 2026 Anthony Green + Copyright (c) 2011 Anthony Green IA64 Foreign Function Interface diff --git a/deps/libffi/src/m32r/ffi.c b/deps/libffi/src/m32r/ffi.c index 21d499abe00302..6fab50b115ed24 100644 --- a/deps/libffi/src/m32r/ffi.c +++ b/deps/libffi/src/m32r/ffi.c @@ -1,7 +1,7 @@ /* ----------------------------------------------------------------------- ffi.c - Copyright (c) 2004 Renesas Technology Copyright (c) 2008 Red Hat, Inc. - Copyright (c) 2022, 2026 Anthony Green + Copyright (c) 2022 Anthony Green M32R Foreign Function Interface @@ -180,10 +180,10 @@ void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue) { extended_cif ecif; ffi_type **arg_types = cif->arg_types; - void **avalue_copy = NULL; int i, nargs = cif->nargs; ecif.cif = cif; + ecif.avalue = avalue; /* If the return value is a struct and we don't have a return value address then we need to make one. */ @@ -196,8 +196,7 @@ void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue) ecif.rvalue = rvalue; /* If we have any large structure arguments, make a copy so we are passing - by value. The pointer array is cloned first: the caller owns avalue[] - and may reuse it for another call, so it must not be modified. */ + by value. */ for (i = 0; i < nargs; i++) { ffi_type *at = arg_types[i]; @@ -205,17 +204,10 @@ void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue) if (at->type == FFI_TYPE_STRUCT && size > 4) { char *argcopy = alloca (size); - if (avalue_copy == NULL) - { - avalue_copy = alloca (nargs * sizeof (void *)); - memcpy (avalue_copy, avalue, nargs * sizeof (void *)); - avalue = avalue_copy; - } memcpy (argcopy, avalue[i], size); avalue[i] = argcopy; } } - ecif.avalue = avalue; switch (cif->abi) { diff --git a/deps/libffi/src/moxie/ffi.c b/deps/libffi/src/moxie/ffi.c index 582c06f5dcebab..ace7b23f855763 100644 --- a/deps/libffi/src/moxie/ffi.c +++ b/deps/libffi/src/moxie/ffi.c @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------- - ffi.c - Copyright (C) 2012, 2013, 2018, 2021, 2022, 2026 Anthony Green + ffi.c - Copyright (C) 2012, 2013, 2018, 2021, 2022 Anthony Green Moxie Foreign Function Interface @@ -129,10 +129,10 @@ void ffi_call(ffi_cif *cif, { extended_cif ecif; ffi_type **arg_types = cif->arg_types; - void **avalue_copy = NULL; int i, nargs = cif->nargs; ecif.cif = cif; + ecif.avalue = avalue; /* If the return value is a struct and we don't have a return */ /* value address then we need to make one */ @@ -146,8 +146,7 @@ void ffi_call(ffi_cif *cif, ecif.rvalue = rvalue; /* If we have any large structure arguments, make a copy so we are passing - by value. The pointer array is cloned first: the caller owns avalue[] - and may reuse it for another call, so it must not be modified. */ + by value. */ for (i = 0; i < nargs; i++) { ffi_type *at = arg_types[i]; @@ -155,17 +154,10 @@ void ffi_call(ffi_cif *cif, if (at->type == FFI_TYPE_STRUCT) /* && size > 4) All struct args?? */ { char *argcopy = alloca (size); - if (avalue_copy == NULL) - { - avalue_copy = alloca (nargs * sizeof (void *)); - memcpy (avalue_copy, avalue, nargs * sizeof (void *)); - avalue = avalue_copy; - } memcpy (argcopy, avalue[i], size); avalue[i] = argcopy; } } - ecif.avalue = avalue; switch (cif->abi) { diff --git a/deps/libffi/src/or1k/ffi.c b/deps/libffi/src/or1k/ffi.c index 9303880a5e82b8..0393ea4af42af6 100644 --- a/deps/libffi/src/or1k/ffi.c +++ b/deps/libffi/src/or1k/ffi.c @@ -121,7 +121,6 @@ void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue) int i; int size; ffi_type **arg; - void **avalue_copy = NULL; /* Calculate size to allocate on stack */ @@ -136,21 +135,13 @@ void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue) size += 8; /* If we have any large structure arguments, make a copy so we are passing - by value. The pointer array is cloned first: the caller owns - avalue[] and may reuse it for another call, so it must not be - modified. */ + by value. */ { ffi_type *at = cif->arg_types[i]; int size = at->size; if (at->type == FFI_TYPE_STRUCT) /* && size > 4) All struct args? */ { char *argcopy = alloca (size); - if (avalue_copy == NULL) - { - avalue_copy = alloca (cif->nargs * sizeof (void *)); - memcpy (avalue_copy, avalue, cif->nargs * sizeof (void *)); - avalue = avalue_copy; - } memcpy (argcopy, avalue[i], size); avalue[i] = argcopy; } diff --git a/deps/libffi/src/pa/ffi.c b/deps/libffi/src/pa/ffi.c index e5de1dc72eb38e..5460ca2fdbe147 100644 --- a/deps/libffi/src/pa/ffi.c +++ b/deps/libffi/src/pa/ffi.c @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------- - ffi.c - (c) 2011, 2026 Anthony Green + ffi.c - (c) 2011 Anthony Green (c) 2008 Red Hat, Inc. (c) 2006 Free Software Foundation, Inc. (c) 2003-2004 Randolph Chung @@ -280,24 +280,9 @@ static void ffi_size_stack_pa32(ffi_cif *cif) #ifdef PA_HPUX case FFI_TYPE_LONGDOUBLE: - z += 1; /* passed by pointer, like a large struct */ - break; #endif - case FFI_TYPE_STRUCT: - /* This must mirror the slot accounting in ffi_prep_args_pa32: - structs of 1-4 bytes occupy one slot, structs of 5-8 bytes are - passed inline in two even-aligned slots (exactly like a 64-bit - value), and larger structs are passed by pointer in one slot. - z stays offset from the marshaller's slot by FIRST_ARG_SLOT (odd), - so (z & 1) tracks the same alignment the marshaller applies. */ - { - size_t len = (*ptr)->size; - if (len <= 4 || len > 8) - z += 1; - else - z += 2 + (z & 1); - } + z += 1; /* pass by ptr, callee will copy */ break; default: /* <= 32-bit values */ @@ -378,13 +363,12 @@ void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue) extended_cif ecif; size_t i, nargs = cif->nargs; ffi_type **arg_types = cif->arg_types; - void **avalue_copy = NULL; ecif.cif = cif; + ecif.avalue = avalue; /* If we have any large structure arguments, make a copy so we are passing - by value. The pointer array is cloned first: the caller owns avalue[] - and may reuse it for another call, so it must not be modified. */ + by value. */ for (i = 0; i < nargs; i++) { ffi_type *at = arg_types[i]; @@ -392,17 +376,10 @@ void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue) if (at->type == FFI_TYPE_STRUCT && size > 8) { char *argcopy = alloca (size); - if (avalue_copy == NULL) - { - avalue_copy = alloca (nargs * sizeof (void *)); - memcpy (avalue_copy, avalue, nargs * sizeof (void *)); - avalue = avalue_copy; - } memcpy (argcopy, avalue[i], size); avalue[i] = argcopy; } } - ecif.avalue = avalue; /* If the return value is a struct and we don't have a return value address then we need to make one. */ diff --git a/deps/libffi/src/pa/ffitarget.h b/deps/libffi/src/pa/ffitarget.h index f6f09975cfac36..dae854a695bb30 100644 --- a/deps/libffi/src/pa/ffitarget.h +++ b/deps/libffi/src/pa/ffitarget.h @@ -1,5 +1,5 @@ /* -----------------------------------------------------------------*-C-*- - ffitarget.h - Copyright (c) 2012, 2026 Anthony Green + ffitarget.h - Copyright (c) 2012 Anthony Green Copyright (c) 1996-2003 Red Hat, Inc. Target configuration macros for hppa. @@ -82,19 +82,11 @@ typedef enum ffi_abi { #define FFI_TYPE_SMALL_STRUCT7 -7 #define FFI_TYPE_SMALL_STRUCT8 -8 -/* The return-value jump tables in linux.S and hpux32.S are indexed by - cif->flags, which ffi_prep_cif_machdep derives from the return type. Any - return type it does not handle explicitly -- including FFI_TYPE_COMPLEX and - the 128-bit integer types FFI_TYPE_UINT128/FFI_TYPE_SINT128 -- falls through - to the default case and is mapped to FFI_TYPE_INT, so cif->flags never - exceeds FFI_TYPE_COMPLEX and the existing tables remain sufficient. Bump - FFI_PA_TYPE_LAST to the current FFI_TYPE_LAST once you have confirmed any - newly added generic type is likewise handled (or the tables extended). */ -#define FFI_PA_TYPE_LAST FFI_TYPE_SINT128 - -/* Tripwire: when a new generic type is added FFI_TYPE_LAST changes and this - fires, forcing a review of ffi_prep_cif_machdep and the linux.S / hpux32.S - jump tables before FFI_PA_TYPE_LAST above is bumped. */ +/* linux.S and hpux32.S expect FFI_TYPE_COMPLEX is the last generic type. */ +#define FFI_PA_TYPE_LAST FFI_TYPE_COMPLEX + +/* If new generic types are added, the jump tables in linux.S and hpux32.S + likely need updating. */ #if FFI_TYPE_LAST != FFI_PA_TYPE_LAST # error "You likely have broken jump tables" #endif diff --git a/deps/libffi/src/powerpc/ffi.c b/deps/libffi/src/powerpc/ffi.c index 69725242344c32..1afa582ebead82 100644 --- a/deps/libffi/src/powerpc/ffi.c +++ b/deps/libffi/src/powerpc/ffi.c @@ -1,6 +1,6 @@ /* ----------------------------------------------------------------------- ffi.c - Copyright (C) 2013 IBM - Copyright (C) 2011, 2026 Anthony Green + Copyright (C) 2011 Anthony Green Copyright (C) 2011 Kyle Moffett Copyright (C) 2008 Red Hat, Inc Copyright (C) 2007, 2008 Free Software Foundation, Inc diff --git a/deps/libffi/src/powerpc/ffi_darwin.c b/deps/libffi/src/powerpc/ffi_darwin.c index 01e2a43701d7a1..d62737f47150c2 100644 --- a/deps/libffi/src/powerpc/ffi_darwin.c +++ b/deps/libffi/src/powerpc/ffi_darwin.c @@ -4,7 +4,6 @@ Copyright (C) 1998 Geoffrey Keating Copyright (C) 2001 John Hornkvist Copyright (C) 2002, 2006, 2007, 2009, 2010 Free Software Foundation, Inc. - Copyright (C) 2026 Anthony Green FFI support for Darwin and AIX. diff --git a/deps/libffi/src/powerpc/ffi_linux64.c b/deps/libffi/src/powerpc/ffi_linux64.c index b1f1468ed5f856..4a4fe86beff1f2 100644 --- a/deps/libffi/src/powerpc/ffi_linux64.c +++ b/deps/libffi/src/powerpc/ffi_linux64.c @@ -1,6 +1,6 @@ /* ----------------------------------------------------------------------- ffi_linux64.c - Copyright (C) 2013 IBM - Copyright (C) 2011, 2026 Anthony Green + Copyright (C) 2011 Anthony Green Copyright (C) 2011 Kyle Moffett Copyright (C) 2008 Red Hat, Inc Copyright (C) 2007, 2008 Free Software Foundation, Inc diff --git a/deps/libffi/src/powerpc/ffi_powerpc.h b/deps/libffi/src/powerpc/ffi_powerpc.h index bad33efe3b8cc1..c366315baefefe 100644 --- a/deps/libffi/src/powerpc/ffi_powerpc.h +++ b/deps/libffi/src/powerpc/ffi_powerpc.h @@ -1,6 +1,6 @@ /* ----------------------------------------------------------------------- ffi_powerpc.h - Copyright (C) 2013 IBM - Copyright (C) 2011, 2026 Anthony Green + Copyright (C) 2011 Anthony Green Copyright (C) 2011 Kyle Moffett Copyright (C) 2008 Red Hat, Inc Copyright (C) 2007, 2008 Free Software Foundation, Inc diff --git a/deps/libffi/src/powerpc/ffi_sysv.c b/deps/libffi/src/powerpc/ffi_sysv.c index a33039e3dbdb1a..975c58c2c0e049 100644 --- a/deps/libffi/src/powerpc/ffi_sysv.c +++ b/deps/libffi/src/powerpc/ffi_sysv.c @@ -1,6 +1,6 @@ /* ----------------------------------------------------------------------- ffi_sysv.c - Copyright (C) 2013 IBM - Copyright (C) 2011, 2026 Anthony Green + Copyright (C) 2011 Anthony Green Copyright (C) 2011 Kyle Moffett Copyright (C) 2008 Red Hat, Inc Copyright (C) 2007, 2008 Free Software Foundation, Inc diff --git a/deps/libffi/src/powerpc/ffitarget.h b/deps/libffi/src/powerpc/ffitarget.h index 7ffe36b682e4a7..cf67a9bba6ee33 100644 --- a/deps/libffi/src/powerpc/ffitarget.h +++ b/deps/libffi/src/powerpc/ffitarget.h @@ -1,5 +1,5 @@ /* -----------------------------------------------------------------*-C-*- - ffitarget.h - Copyright (c) 2012, 2026 Anthony Green + ffitarget.h - Copyright (c) 2012 Anthony Green Copyright (C) 2007, 2008, 2010 Free Software Foundation, Inc Copyright (c) 1996-2003 Red Hat, Inc. diff --git a/deps/libffi/src/prep_cif.c b/deps/libffi/src/prep_cif.c index 1836270d1a9cb7..47c9259470c5ee 100644 --- a/deps/libffi/src/prep_cif.c +++ b/deps/libffi/src/prep_cif.c @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------- - prep_cif.c - Copyright (c) 2011, 2012, 2021, 2025, 2026 Anthony Green + prep_cif.c - Copyright (c) 2011, 2012, 2021, 2025 Anthony Green Copyright (c) 1996, 1998, 2007 Red Hat, Inc. Copyright (c) 2022 Oracle and/or its affiliates. @@ -278,42 +278,3 @@ ffi_get_struct_offsets (ffi_abi abi, ffi_type *struct_type, size_t *offsets) return initialize_aggregate(struct_type, offsets); } - -/* Generic ffi_call_plan: a portable fallback compiled on every target that does - not provide its own accelerated implementation. The x86-64 SysV backend - (ffi64.c) defines these with a fast path under __x86_64__ && !__ILP32__, but - that file is not built for Windows x86-64 (X86_WIN64), which uses ffiw64.c - instead -- and clang-cl and MSYS/mingw both define __x86_64__ there. So - exclude the fallback only when ffi64.c actually provides it; everywhere else - this plan just records the cif and invoke calls ffi_call, so the API is - always present and links on all targets. The cif must outlive the plan. */ -#if !(defined(__x86_64__) && !defined(__ILP32__) && !defined(X86_WIN64)) - -struct ffi_call_plan -{ - ffi_cif *cif; -}; - -ffi_call_plan * -ffi_call_plan_alloc (ffi_cif *cif) -{ - ffi_call_plan *plan = malloc (sizeof (struct ffi_call_plan)); - if (plan != NULL) - plan->cif = cif; - return plan; -} - -void -ffi_call_plan_invoke (ffi_call_plan *plan, void (*fn) (void), - void *rvalue, void **avalue) -{ - ffi_call (plan->cif, fn, rvalue, avalue); -} - -void -ffi_call_plan_free (ffi_call_plan *plan) -{ - free (plan); -} - -#endif /* generic ffi_call_plan fallback */ diff --git a/deps/libffi/src/s390/ffitarget.h b/deps/libffi/src/s390/ffitarget.h index 4fc8ef02cfe83b..6a9c3234289cb5 100644 --- a/deps/libffi/src/s390/ffitarget.h +++ b/deps/libffi/src/s390/ffitarget.h @@ -1,5 +1,5 @@ /* -----------------------------------------------------------------*-C-*- - ffitarget.h - Copyright (c) 2012, 2026 Anthony Green + ffitarget.h - Copyright (c) 2012 Anthony Green Copyright (c) 1996-2003 Red Hat, Inc. Target configuration macros for S390. diff --git a/deps/libffi/src/sh64/ffi.c b/deps/libffi/src/sh64/ffi.c index 2df6e23a3fda29..4545b0fa72141b 100644 --- a/deps/libffi/src/sh64/ffi.c +++ b/deps/libffi/src/sh64/ffi.c @@ -1,6 +1,6 @@ /* ----------------------------------------------------------------------- ffi.c - Copyright (c) 2003, 2004, 2006, 2007, 2012 Kaz Kojima - Copyright (c) 2008, 2026 Anthony Green + Copyright (c) 2008 Anthony Green SuperH SHmedia Foreign Function Interface diff --git a/deps/libffi/src/sparc/ffi.c b/deps/libffi/src/sparc/ffi.c index bcda1ff2219703..98142b206ecd44 100644 --- a/deps/libffi/src/sparc/ffi.c +++ b/deps/libffi/src/sparc/ffi.c @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------- - ffi.c - Copyright (c) 2011, 2013, 2026 Anthony Green + ffi.c - Copyright (c) 2011, 2013 Anthony Green Copyright (c) 1996, 2003-2004, 2007-2008 Red Hat, Inc. SPARC Foreign Function Interface @@ -288,7 +288,6 @@ ffi_call_int (ffi_cif *cif, void (*fn)(void), void *rvalue, size_t bytes = cif->bytes; size_t i, nargs = cif->nargs; ffi_type **arg_types = cif->arg_types; - void **avalue_copy = NULL; FFI_ASSERT (cif->abi == FFI_V8); @@ -299,8 +298,7 @@ ffi_call_int (ffi_cif *cif, void (*fn)(void), void *rvalue, bytes += FFI_ALIGN (cif->rtype->size, 8); /* If we have any structure arguments, make a copy so we are passing - by value. The pointer array is cloned first: the caller owns avalue[] - and may reuse it for another call, so it must not be modified. */ + by value. */ for (i = 0; i < nargs; i++) { ffi_type *at = arg_types[i]; @@ -308,12 +306,6 @@ ffi_call_int (ffi_cif *cif, void (*fn)(void), void *rvalue, if (at->type == FFI_TYPE_STRUCT) { char *argcopy = alloca (size); - if (avalue_copy == NULL) - { - avalue_copy = alloca (nargs * sizeof (void *)); - memcpy (avalue_copy, avalue, nargs * sizeof (void *)); - avalue = avalue_copy; - } memcpy (argcopy, avalue[i], size); avalue[i] = argcopy; } diff --git a/deps/libffi/src/sparc/ffi64.c b/deps/libffi/src/sparc/ffi64.c index 5eec1499e1708d..09a5e59dfdd0c7 100644 --- a/deps/libffi/src/sparc/ffi64.c +++ b/deps/libffi/src/sparc/ffi64.c @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------- - ffi.c - Copyright (c) 2011, 2013, 2026 Anthony Green + ffi.c - Copyright (c) 2011, 2013 Anthony Green Copyright (c) 1996, 2003-2004, 2007-2008 Red Hat, Inc. SPARC Foreign Function Interface @@ -429,7 +429,6 @@ ffi_call_int(ffi_cif *cif, void (*fn)(void), void *rvalue, size_t bytes = cif->bytes; size_t i, nargs = cif->nargs; ffi_type **arg_types = cif->arg_types; - void **avalue_copy = NULL; FFI_ASSERT (cif->abi == FFI_V9); @@ -437,8 +436,7 @@ ffi_call_int(ffi_cif *cif, void (*fn)(void), void *rvalue, bytes += FFI_ALIGN (cif->rtype->size, 16); /* If we have any large structure arguments, make a copy so we are passing - by value. The pointer array is cloned first: the caller owns avalue[] - and may reuse it for another call, so it must not be modified. */ + by value. */ for (i = 0; i < nargs; i++) { ffi_type *at = arg_types[i]; @@ -446,16 +444,10 @@ ffi_call_int(ffi_cif *cif, void (*fn)(void), void *rvalue, if (at->type == FFI_TYPE_STRUCT && size > 4) { char *argcopy = alloca (size); - if (avalue_copy == NULL) - { - avalue_copy = alloca (nargs * sizeof (void *)); - memcpy (avalue_copy, avalue, nargs * sizeof (void *)); - avalue = avalue_copy; - } memcpy (argcopy, avalue[i], size); avalue[i] = argcopy; } - } + } ffi_call_v9(cif, fn, rvalue, avalue, -bytes, closure); } diff --git a/deps/libffi/src/tramp.c b/deps/libffi/src/tramp.c index 525f8154715671..76a9dfe75c2c3b 100644 --- a/deps/libffi/src/tramp.c +++ b/deps/libffi/src/tramp.c @@ -1,6 +1,6 @@ /* ----------------------------------------------------------------------- tramp.c - Copyright (c) 2020 Madhavan T. Venkataraman - Copyright (c) 2022, 2026 Anthony Green + Copyright (c) 2022 Anthony Green API and support functions for managing statically defined closure trampolines. @@ -201,18 +201,11 @@ static int tramp_table_alloc (void); #if defined (__linux__) || defined (__CYGWIN__) -/* Stringify a macro value for use in a scanf field-width specifier. */ -#define FFI_TRAMP_STR_(x) #x -#define FFI_TRAMP_STR(x) FFI_TRAMP_STR_(x) - static int ffi_tramp_get_libffi (void) { FILE *fp; - /* `file' is sized PATH_MAX+1 so a scanf field width of PATH_MAX - (which permits PATH_MAX characters plus the terminating NUL) cannot - overflow. */ - char file[PATH_MAX+1], line[PATH_MAX+100], perm[10], dev[10]; + char file[PATH_MAX], line[PATH_MAX+100], perm[10], dev[10]; unsigned long start, end, offset, inode; uintptr_t addr = (uintptr_t) tramp_globals.text; int nfields, found; @@ -222,7 +215,7 @@ ffi_tramp_get_libffi (void) open_flags |= O_CLOEXEC; #endif - snprintf (file, sizeof (file), "/proc/%d/maps", getpid()); + snprintf (file, PATH_MAX, "/proc/%d/maps", getpid()); fp = fopen (file, "r"); if (fp == NULL) return 0; @@ -232,10 +225,7 @@ ffi_tramp_get_libffi (void) if (fgets (line, sizeof (line), fp) == 0) break; - /* Bound the path field width so a long mapping name cannot overflow - the fixed-size `file' buffer. */ - nfields = sscanf (line, - "%lx-%lx %9s %lx %9s %ld %" FFI_TRAMP_STR(PATH_MAX) "s", + nfields = sscanf (line, "%lx-%lx %9s %lx %9s %ld %s", &start, &end, perm, &offset, dev, &inode, file); if (nfields != 7) continue; diff --git a/deps/libffi/src/types.c b/deps/libffi/src/types.c index 4ac45a4ac0d790..c6e8ebe2a881be 100644 --- a/deps/libffi/src/types.c +++ b/deps/libffi/src/types.c @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------- - types.c - Copyright (c) 1996, 1998, 2024, 2025, 2026 Red Hat, Inc. + types.c - Copyright (c) 1996, 1998, 2024, 2025 Red Hat, Inc. Predefined ffi_types needed by libffi. diff --git a/deps/libffi/src/wasm/ffi.c b/deps/libffi/src/wasm/ffi.c index b631b1b2bef9cf..486ffa7856aa82 100644 --- a/deps/libffi/src/wasm/ffi.c +++ b/deps/libffi/src/wasm/ffi.c @@ -1,6 +1,5 @@ /* ----------------------------------------------------------------------- ffi.c - Copyright (c) 2018-2023 Hood Chatham, Brion Vibber, Kleis Auke Wolthuizen, and others. - Copyright (c) 2026 Anthony Green wasm32/emscripten Foreign Function Interface @@ -71,13 +70,6 @@ EM_JS_DEPS(libffi, "$getWasmTableEntry,$setWasmTableEntry,$getEmptyTableSlot,$co #define DEREF_PTR(addr, offset) DEREF_U32(addr, offset) #define DEREF_PTR_NUMBER(addr, offset) DEREF_PTR(addr, offset) -// Store an integral return value widened to ffi_arg (32 bits on wasm32), as -// the libffi API promises for integral returns narrower than a register. -// The typed-array store wraps modulo 2^32, so a sign-extended negative -// Number is stored correctly by both variants. -#define STORE_ARG_WIDENED_UNSIGNED(rvalue, x) (DEREF_U32(rvalue, 0) = (x)) -#define STORE_ARG_WIDENED_SIGNED(rvalue, x) (DEREF_U32(rvalue, 0) = (x)) - CHECK_FIELD_OFFSET(ffi_cif, abi, 4*0); CHECK_FIELD_OFFSET(ffi_cif, nargs, 4*1); CHECK_FIELD_OFFSET(ffi_cif, arg_types, 4*2); @@ -117,16 +109,6 @@ CHECK_FIELD_OFFSET(ffi_type, elements, 8); #define DEREF_PTR(addr, offset) DEREF_U64(addr, offset) #define DEREF_PTR_NUMBER(addr, offset) DEC_PTR(DEREF_PTR(addr, offset)) -// Store an integral return value widened to ffi_arg (64 bits on wasm64), as -// the libffi API promises for integral returns narrower than a register. -// BigInt masking treats the value as infinite-precision two's complement, so -// the unsigned variant zero-extends a possibly-negative i32 Number, while -// the BigUint64Array store wraps the signed variant modulo 2^64, which is -// sign extension. -#define STORE_ARG_WIDENED_UNSIGNED(rvalue, x) \ - (DEREF_U64(rvalue, 0) = BigInt(x) & BigInt(4294967295)) -#define STORE_ARG_WIDENED_SIGNED(rvalue, x) (DEREF_U64(rvalue, 0) = BigInt(x)) - CHECK_FIELD_OFFSET(ffi_cif, abi, 0); CHECK_FIELD_OFFSET(ffi_cif, nargs, 4); CHECK_FIELD_OFFSET(ffi_cif, arg_types, 8); @@ -265,10 +247,6 @@ ffi_call_js, (ffi_cif *cif, ffi_fp fn, void *rvalue, void **avalue), var rtype_unboxed = unbox_small_structs(CIF__RTYPE(cif)); var rtype_ptr = rtype_unboxed[0]; var rtype_id = rtype_unboxed[1]; - // A small struct return is unboxed to a scalar type id above, but it is - // still a struct to the caller: its buffer is exactly the struct's size, - // so it must not get the integral widening to ffi_arg. - var rtype_widen = FFI_TYPE__TYPEID(DEC_PTR(CIF__RTYPE(cif))) !== FFI_TYPE_STRUCT; var orig_stack_ptr = stackSave(); var cur_stack_ptr = orig_stack_ptr; @@ -289,15 +267,6 @@ ffi_call_js, (ffi_cif *cif, ffi_fp fn, void *rvalue, void **avalue), // just use this. We also mark a flag that we don't need to convert the return // value of the dynamic call back to C. if (rtype_id === FFI_TYPE_LONGDOUBLE || rtype_id === FFI_TYPE_STRUCT) { - if (rvalue === 0) { - // NULL rvalue: the caller discards the result, but the callee still - // needs somewhere to write it. Allocate scratch space on the stack; - // it is released by the stackRestore after the call. - var rsize = DEC_PTR(FFI_TYPE__SIZE(rtype_ptr)); - var ralign = FFI_TYPE__ALIGN(rtype_ptr); - STACK_ALLOC(cur_stack_ptr, rsize, ralign); - rvalue = cur_stack_ptr; - } args.push(ENC_PTR(rvalue)); ret_by_arg = true; } @@ -461,31 +430,15 @@ ffi_call_js, (ffi_cif *cif, ffi_fp fn, void *rvalue, void **avalue), return; } - // A NULL rvalue means the caller discards the return value. - if (rvalue === 0) { - return; - } - // Otherwise the result was automatically converted from C into Javascript and // we need to manually convert it back to C. - // Integral returns narrower than a register are widened to ffi_arg, as - // documented and as every native port does -- but only genuine scalar - // returns: an unboxed small-struct return keeps its natural width. switch (rtype_id) { case FFI_TYPE_VOID: break; case FFI_TYPE_INT: - case FFI_TYPE_SINT32: - if (rtype_widen) - STORE_ARG_WIDENED_SIGNED(rvalue, result | 0); - else - DEREF_U32(rvalue, 0) = result; - break; case FFI_TYPE_UINT32: - if (rtype_widen) - STORE_ARG_WIDENED_UNSIGNED(rvalue, result | 0); - else - DEREF_U32(rvalue, 0) = result; + case FFI_TYPE_SINT32: + DEREF_U32(rvalue, 0) = result; break; case FFI_TYPE_FLOAT: DEREF_F32(rvalue, 0) = result; @@ -494,28 +447,12 @@ ffi_call_js, (ffi_cif *cif, ffi_fp fn, void *rvalue, void **avalue), DEREF_F64(rvalue, 0) = result; break; case FFI_TYPE_UINT8: - if (rtype_widen) - STORE_ARG_WIDENED_UNSIGNED(rvalue, result & 0xff); - else - DEREF_U8(rvalue, 0) = result; - break; case FFI_TYPE_SINT8: - if (rtype_widen) - STORE_ARG_WIDENED_SIGNED(rvalue, (result << 24) >> 24); - else - DEREF_U8(rvalue, 0) = result; + DEREF_U8(rvalue, 0) = result; break; case FFI_TYPE_UINT16: - if (rtype_widen) - STORE_ARG_WIDENED_UNSIGNED(rvalue, result & 0xffff); - else - DEREF_U16(rvalue, 0) = result; - break; case FFI_TYPE_SINT16: - if (rtype_widen) - STORE_ARG_WIDENED_SIGNED(rvalue, (result << 16) >> 16); - else - DEREF_U16(rvalue, 0) = result; + DEREF_U16(rvalue, 0) = result; break; case FFI_TYPE_UINT64: case FFI_TYPE_SINT64: diff --git a/deps/libffi/src/x86/ffi.c b/deps/libffi/src/x86/ffi.c index 27f17b0c884957..40d153fb399e21 100644 --- a/deps/libffi/src/x86/ffi.c +++ b/deps/libffi/src/x86/ffi.c @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------- - ffi.c - Copyright (c) 2017, 2022, 2026 Anthony Green + ffi.c - Copyright (c) 2017, 2022 Anthony Green Copyright (c) 1996, 1998, 1999, 2001, 2007, 2008 Red Hat, Inc. Copyright (c) 2002 Ranjit Mathew Copyright (c) 2002 Bo Thorsen @@ -554,14 +554,8 @@ ffi_closure_inner (struct closure_frame *frame, char *stack) return flags | (cif->bytes << X86_RET_POP_SHIFT); case FFI_THISCALL: case FFI_FASTCALL: - /* The callee must pop exactly the bytes that were passed on the - stack. Deriving that from cif->bytes minus narg_reg * 4 is wrong - once narg_reg has been force-bumped to 2 (above) for a 64-bit or - struct argument that is itself placed on the stack: the subtraction - then discounts register slots that were never used, under-popping - the stack. argp has advanced past exactly the stack-resident - arguments (dir == 1 for these ABIs), so use that directly. */ - return flags | (((unsigned) (argp - stack)) << X86_RET_POP_SHIFT); + return flags | ((cif->bytes - (narg_reg * FFI_SIZEOF_ARG)) + << X86_RET_POP_SHIFT); default: return flags; } diff --git a/deps/libffi/src/x86/ffi64.c b/deps/libffi/src/x86/ffi64.c index c24db38c4364b7..db0aa563a87947 100644 --- a/deps/libffi/src/x86/ffi64.c +++ b/deps/libffi/src/x86/ffi64.c @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------- - ffi64.c - Copyright (c) 2011, 2018, 2022, 2026 Anthony Green + ffi64.c - Copyright (c) 2011, 2018, 2022 Anthony Green Copyright (c) 2013 The Written Word, Inc. Copyright (c) 2008, 2010 Red Hat, Inc. Copyright (c) 2002, 2007 Bo Thorsen @@ -33,8 +33,6 @@ #include #include #include -#include -#include #include #include "internal64.h" @@ -736,340 +734,6 @@ ffi_call_int (ffi_cif *cif, void (*fn)(void), void *rvalue, } #ifndef __ILP32__ -/* ===================================================================== - Precompiled argument-placement plan, used by the ffi_call_plan API. - - ffi_prep_cif_machdep classifies the signature once, but ffi_call_int then - re-derives the same per-argument placement on every call (~650 instructions - for a 3-argument call). A "plan" captures that placement as a flat move - list, built once, so the register_args + stack buffer can be filled with no - re-classification before handing off to the unchanged ffi_call_unix64. For - the common case (only 64-bit GP arguments) a direct thunk loads the values - straight into the argument registers, skipping the buffer entirely. - - A plan is built by ffi_call_plan_alloc and applied by ffi_call_plan_invoke; - the caller owns it and reuses it across calls. - - Scalar, pointer, int128, float and double arguments are handled; any struct, - complex, or x87 long double argument has no plan, so the caller's invoke - falls back to ffi_call. */ - -enum ffi_move_op -{ - FFI_MOVE_SE8, FFI_MOVE_SE16, FFI_MOVE_SE32, /* sign-extend N bytes -> gpr */ - FFI_MOVE_GP64, /* copy a full 8-byte word -> gpr */ - FFI_MOVE_GP, /* zero gpr, copy len(<8) bytes */ - FFI_MOVE_SSE64, FFI_MOVE_SSE32, /* copy 8/4 bytes -> sse slot */ - FFI_MOVE_STACK /* copy len bytes -> stack */ -}; - -typedef struct -{ - unsigned src_idx; /* avalue[] index */ - unsigned src_off; /* byte offset within avalue[src_idx] (chunk * 8) */ - unsigned dst_off; /* byte offset within the register_args+stack buf */ - unsigned len; /* bytes for FFI_MOVE_GP / FFI_MOVE_STACK */ - unsigned char op; -} ffi_move; - -typedef struct -{ - unsigned nmoves; - unsigned ssecount; /* -> reg_args->rax */ - unsigned bytes; /* stack-arg area size (== cif->bytes) */ - unsigned flags; /* == cif->flags */ - unsigned ret_in_mem; /* nonzero -> reg_args->gpr[0] = rvalue */ - unsigned fast; /* nonzero -> lean trampoline eligible */ - unsigned retcode; /* UNIX64_RET_* (low byte of flags) for the store */ - int thunk_n; /* >=0 -> ffi_gp_thunks[thunk_n], else -1 */ - ffi_move moves[]; -} ffi_plan; - -/* Return of the lean trampoline / direct thunks: callee's rax in .i, xmm0 in .d. */ -struct ffi_ret2 { UINT64 i; double d; }; -extern struct ffi_ret2 ffi_plan_fast_call (struct register_args *img, - void (*fn) (void)) FFI_HIDDEN; - -/* Count-based direct thunks: load avalue[0..N-1] into arg registers, call. */ -extern struct ffi_ret2 ffi_plan_gp0 (void **, void (*)(void)) FFI_HIDDEN; -extern struct ffi_ret2 ffi_plan_gp1 (void **, void (*)(void)) FFI_HIDDEN; -extern struct ffi_ret2 ffi_plan_gp2 (void **, void (*)(void)) FFI_HIDDEN; -extern struct ffi_ret2 ffi_plan_gp3 (void **, void (*)(void)) FFI_HIDDEN; -extern struct ffi_ret2 ffi_plan_gp4 (void **, void (*)(void)) FFI_HIDDEN; -extern struct ffi_ret2 ffi_plan_gp5 (void **, void (*)(void)) FFI_HIDDEN; -extern struct ffi_ret2 ffi_plan_gp6 (void **, void (*)(void)) FFI_HIDDEN; -static struct ffi_ret2 (*const ffi_gp_thunks[7]) (void **, void (*)(void)) = - { ffi_plan_gp0, ffi_plan_gp1, ffi_plan_gp2, ffi_plan_gp3, - ffi_plan_gp4, ffi_plan_gp5, ffi_plan_gp6 }; - -/* Store the callee return value, replicating the unix64.S store_table widths. */ -static inline void -store_ret (void *rvalue, unsigned retcode, struct ffi_ret2 r) -{ - switch (retcode) - { - case UNIX64_RET_VOID: break; - case UNIX64_RET_UINT8: *(UINT64 *) rvalue = (UINT8) r.i; break; - case UNIX64_RET_UINT16: *(UINT64 *) rvalue = (UINT16) r.i; break; - case UNIX64_RET_UINT32: *(UINT64 *) rvalue = (UINT32) r.i; break; - case UNIX64_RET_SINT8: *(UINT64 *) rvalue = (UINT64)(SINT64)(SINT8) r.i; break; - case UNIX64_RET_SINT16: *(UINT64 *) rvalue = (UINT64)(SINT64)(SINT16) r.i; break; - case UNIX64_RET_SINT32: *(UINT64 *) rvalue = (UINT64)(SINT64)(SINT32) r.i; break; - case UNIX64_RET_INT64: *(UINT64 *) rvalue = r.i; break; - case UNIX64_RET_XMM32: memcpy (rvalue, &r.d, 4); break; - case UNIX64_RET_XMM64: memcpy (rvalue, &r.d, 8); break; - } -} - -/* Build the move-list for CIF, or NULL if not plan-able (caller falls back). */ -static ffi_plan * -build_plan (ffi_cif *cif) -{ - unsigned i, avn = cif->nargs; - enum x86_64_reg_class classes[MAX_CLASSES]; - unsigned nm, gprcount, ssecount; - size_t argp_off; - ffi_plan *plan; - int all_gp64 = 1; /* every arg is exactly one 64-bit GP move? */ - - if (cif->abi != FFI_UNIX64) - return NULL; - - /* Reject arg types this cut doesn't encode; returns are handled by flags. */ - for (i = 0; i < avn; i++) - { - int t = cif->arg_types[i]->type; - if (t == FFI_TYPE_STRUCT || t == FFI_TYPE_COMPLEX) - return NULL; -#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE - if (t == FFI_TYPE_LONGDOUBLE) - return NULL; -#endif - } - - /* One self-contained allocation: header + moves, released with plain free(). */ - plan = malloc (sizeof (ffi_plan) + sizeof (ffi_move) * (2 * avn + 1)); - if (plan == NULL) - return NULL; - - nm = gprcount = ssecount = 0; - argp_off = 0; - plan->ret_in_mem = (cif->flags & UNIX64_FLAG_RET_IN_MEM) ? 1 : 0; - if (plan->ret_in_mem) - gprcount++; /* sret pointer occupies gpr[0] */ - - for (i = 0; i < avn; i++) - { - ffi_type *at = cif->arg_types[i]; - size_t size = at->size, n, rem; - int ngpr, nsse; - unsigned j; - - n = examine_argument (at, classes, 0, &ngpr, &nsse); - if (n == 0 - || gprcount + ngpr > MAX_GPR_REGS - || ssecount + nsse > MAX_SSE_REGS) - { - long align = at->alignment; - ffi_move *m = &plan->moves[nm++]; - all_gp64 = 0; - if (align < 8) - align = 8; - argp_off = FFI_ALIGN (argp_off, align); - m->op = FFI_MOVE_STACK; - m->src_idx = i; - m->src_off = 0; - m->dst_off = (unsigned) (sizeof (struct register_args) + argp_off); - m->len = (unsigned) size; - argp_off += size; - continue; - } - - for (j = 0, rem = size; j < n; j++, rem -= 8) - { - ffi_move m; - m.src_idx = i; - m.src_off = j * 8; - switch (classes[j]) - { - case X86_64_NO_CLASS: - case X86_64_SSEUP_CLASS: - continue; /* nothing placed for this 8-byte */ - case X86_64_INTEGER_CLASS: - case X86_64_INTEGERSI_CLASS: - m.dst_off = gprcount * 8; /* offsetof(register_args,gpr) == 0 */ - switch (at->type) - { - case FFI_TYPE_SINT8: m.op = FFI_MOVE_SE8; all_gp64 = 0; break; - case FFI_TYPE_SINT16: m.op = FFI_MOVE_SE16; all_gp64 = 0; break; - case FFI_TYPE_SINT32: m.op = FFI_MOVE_SE32; all_gp64 = 0; break; - default: - if (rem >= 8) - m.op = FFI_MOVE_GP64; - else - { m.op = FFI_MOVE_GP; m.len = (unsigned) rem; all_gp64 = 0; } - break; - } - gprcount++; - break; - case X86_64_SSE_CLASS: - case X86_64_SSEDF_CLASS: - m.dst_off = (unsigned) (offsetof (struct register_args, sse) - + ssecount * sizeof (union big_int_union)); - m.op = FFI_MOVE_SSE64; - ssecount++; - all_gp64 = 0; - break; - case X86_64_SSESF_CLASS: - m.dst_off = (unsigned) (offsetof (struct register_args, sse) - + ssecount * sizeof (union big_int_union)); - m.op = FFI_MOVE_SSE32; - ssecount++; - all_gp64 = 0; - break; - default: - free (plan); /* X87 etc. in registers: bail */ - return NULL; - } - plan->moves[nm++] = m; - } - } - - plan->nmoves = nm; - plan->ssecount = ssecount; - plan->bytes = cif->bytes; - plan->flags = cif->flags; - plan->retcode = cif->flags & 0xff; /* UNIX64_RET_* */ - /* Lean-trampoline eligible: no spilled stack args and a simple return - (VOID..XMM64, codes 0..9; RET_IN_MEM has low byte VOID). Struct-in-regs - (>=12) and x87 (10,11) returns stay on ffi_call_unix64. */ - plan->fast = (cif->bytes == 0 && plan->retcode <= UNIX64_RET_XMM64) ? 1 : 0; - /* Pure-GP64 direct thunk: every arg is one 64-bit GP value (so a plain load - per arg is exact), <=6 of them, no sret, simple return -> load avalue - straight into the arg registers, no register image. */ - plan->thunk_n = - (all_gp64 && !plan->ret_in_mem && nm == avn && avn <= MAX_GPR_REGS - && plan->fast) - ? (int) avn : -1; - return plan; -} - -/* Execute PLAN: rebuild register_args + stack buffer, then ffi_call_unix64. */ -FFI_ASAN_NO_SANITIZE -static inline __attribute__ ((always_inline)) void -plan_exec (ffi_cif *cif, ffi_plan *plan, void (*fn) (void), - void *rvalue, void **avalue) -{ - unsigned flags = plan->flags; - struct register_args local __attribute__ ((aligned (16))); - char *stack = NULL; - struct register_args *reg_args; - unsigned k; - - if (rvalue == NULL) - { - if (flags & UNIX64_FLAG_RET_IN_MEM) - rvalue = alloca (cif->rtype->size); - else - flags = UNIX64_RET_VOID; - } - - if (plan->thunk_n >= 0) - { - /* Pure-GP64: load avalue straight into arg regs, no image at all. */ - struct ffi_ret2 r = ffi_gp_thunks[plan->thunk_n] (avalue, fn); - if (rvalue != NULL) - store_ret (rvalue, plan->retcode, r); - return; - } - - if (plan->fast) - reg_args = &local; /* no stack args: fixed local image */ - else - { - stack = alloca (sizeof (struct register_args) + plan->bytes + 4 * 8); - reg_args = (struct register_args *) stack; - } - reg_args->r10 = 0; /* closure (none for ffi_call) */ - if (plan->ret_in_mem) - reg_args->gpr[0] = (UINT64) (uintptr_t) rvalue; - - for (k = 0; k < plan->nmoves; k++) - { - ffi_move *m = &plan->moves[k]; - char *src = (char *) avalue[m->src_idx] + m->src_off; - char *dst = (char *) reg_args + m->dst_off; - switch (m->op) - { - /* x86-64: unaligned scalar loads from avalue[] are fine. */ - case FFI_MOVE_SE8: *(UINT64 *) dst = (UINT64) (SINT64) *(SINT8 *) src; break; - case FFI_MOVE_SE16: *(UINT64 *) dst = (UINT64) (SINT64) *(SINT16 *) src; break; - case FFI_MOVE_SE32: *(UINT64 *) dst = (UINT64) (SINT64) *(SINT32 *) src; break; - case FFI_MOVE_GP64: *(UINT64 *) dst = *(UINT64 *) src; break; - case FFI_MOVE_GP: *(UINT64 *) dst = 0; memcpy (dst, src, m->len); break; - case FFI_MOVE_SSE64: *(UINT64 *) dst = *(UINT64 *) src; break; - case FFI_MOVE_SSE32: *(UINT32 *) dst = *(UINT32 *) src; break; - case FFI_MOVE_STACK: memcpy (dst, src, m->len); break; - } - } - reg_args->rax = plan->ssecount; - - if (plan->fast) - { - /* No stack args; lean trampoline + return store replicating the - unix64.S store_table widths. ret_in_mem already wrote gpr[0]. */ - struct ffi_ret2 r = ffi_plan_fast_call (reg_args, fn); - if (rvalue != NULL) - store_ret (rvalue, plan->retcode, r); - return; - } - - ffi_call_unix64 (stack, plan->bytes + sizeof (struct register_args), - flags, rvalue, fn); -} - -/* Reusable call plan: an opaque, caller-owned handle wrapping a prebuilt plan. - ffi_call_plan_invoke applies it directly, skipping the per-call argument - classification ffi_call does every time. Signatures with no fast path - (FAST is NULL) fall back to ffi_call. The plan is immutable after alloc, so - it carries no per-thread state and can be invoked from any thread. */ -struct ffi_call_plan -{ - ffi_cif *cif; - ffi_plan *fast; /* prebuilt plan, or NULL -> fall back to ffi_call */ -}; - -ffi_call_plan * -ffi_call_plan_alloc (ffi_cif *cif) -{ - ffi_call_plan *plan = malloc (sizeof (struct ffi_call_plan)); - if (plan == NULL) - return NULL; - plan->cif = cif; - plan->fast = build_plan (cif); /* NULL if this signature has no fast path */ - return plan; -} - -void -ffi_call_plan_invoke (ffi_call_plan *plan, void (*fn) (void), - void *rvalue, void **avalue) -{ - if (plan->fast != NULL) - plan_exec (plan->cif, plan->fast, fn, rvalue, avalue); - else - ffi_call (plan->cif, fn, rvalue, avalue); -} - -void -ffi_call_plan_free (ffi_call_plan *plan) -{ - if (plan != NULL) - { - free (plan->fast); - free (plan); - } -} - extern void ffi_call_efi64(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue); #endif @@ -1078,13 +742,11 @@ void ffi_call (ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue) { ffi_type **arg_types = cif->arg_types; - void **avalue_copy = NULL; int i, nargs = cif->nargs; const int max_reg_struct_size = cif->abi == FFI_GNUW64 ? 8 : 16; /* If we have any large structure arguments, make a copy so we are passing - by value. The pointer array is cloned first: the caller owns avalue[] - and may reuse it for another call, so it must not be modified. */ + by value. */ for (i = 0; i < nargs; i++) { ffi_type *at = arg_types[i]; @@ -1092,12 +754,6 @@ ffi_call (ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue) if (at->type == FFI_TYPE_STRUCT && size > max_reg_struct_size) { char *argcopy = alloca (size); - if (avalue_copy == NULL) - { - avalue_copy = alloca (nargs * sizeof (void *)); - memcpy (avalue_copy, avalue, nargs * sizeof (void *)); - avalue = avalue_copy; - } memcpy (argcopy, avalue[i], size); avalue[i] = argcopy; } @@ -1228,7 +884,6 @@ ffi_closure_unix64_inner(ffi_cif *cif, avn = cif->nargs; flags = cif->flags; - avalue = alloca(avn * sizeof(void *)); gprcount = ssecount = 0; @@ -1285,7 +940,7 @@ ffi_closure_unix64_inner(ffi_cif *cif, /* Otherwise, allocate space to make them consecutive. */ else { - char *a = alloca (n * 8); + char *a = alloca (16); unsigned int j; avalue[i] = a; diff --git a/deps/libffi/src/x86/ffitarget.h b/deps/libffi/src/x86/ffitarget.h index d702235f90fed5..995469b0f59b2c 100644 --- a/deps/libffi/src/x86/ffitarget.h +++ b/deps/libffi/src/x86/ffitarget.h @@ -1,5 +1,5 @@ /* -----------------------------------------------------------------*-C-*- - ffitarget.h - Copyright (c) 2012, 2014, 2018, 2026 Anthony Green + ffitarget.h - Copyright (c) 2012, 2014, 2018 Anthony Green Copyright (c) 1996-2003, 2010 Red Hat, Inc. Copyright (C) 2008 Free Software Foundation, Inc. diff --git a/deps/libffi/src/x86/ffiw64.c b/deps/libffi/src/x86/ffiw64.c index f8db4e0d96236b..37d9a7e9c036d9 100644 --- a/deps/libffi/src/x86/ffiw64.c +++ b/deps/libffi/src/x86/ffiw64.c @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------- - ffiw64.c - Copyright (c) 2018, 2026 Anthony Green + ffiw64.c - Copyright (c) 2018 Anthony Green Copyright (c) 2014 Red Hat, Inc. x86 win64 Foreign Function Interface @@ -126,15 +126,12 @@ ffi_call_int (ffi_cif *cif, void (*fn)(void), void *rvalue, size_t rsize; struct win64_call_frame *frame; ffi_type **arg_types = cif->arg_types; - void **avalue_copy = NULL; int nargs = cif->nargs; FFI_ASSERT(cif->abi == FFI_GNUW64 || cif->abi == FFI_WIN64); /* If we have any int128 or irregularly sized structure arguments, - make a copy so we are passing by value. The pointer array is cloned - first: the caller owns avalue[] and may reuse it for another call, - so it must not be modified. */ + make a copy so we are passing by value. */ for (i = 0; i < nargs; i++) { ffi_type *at = arg_types[i]; @@ -162,12 +159,6 @@ ffi_call_int (ffi_cif *cif, void (*fn)(void), void *rvalue, if (needcopy) { char *argcopy = alloca (size); - if (avalue_copy == NULL) - { - avalue_copy = alloca (nargs * sizeof (void *)); - memcpy (avalue_copy, avalue, nargs * sizeof (void *)); - avalue = avalue_copy; - } memcpy (argcopy, avalue[i], size); avalue[i] = argcopy; } diff --git a/deps/libffi/src/x86/sysv.S b/deps/libffi/src/x86/sysv.S index dac841421d9b2b..b5969df1560143 100644 --- a/deps/libffi/src/x86/sysv.S +++ b/deps/libffi/src/x86/sysv.S @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------- - sysv.S - Copyright (c) 2017, 2026 Anthony Green + sysv.S - Copyright (c) 2017 Anthony Green - Copyright (c) 2013 The Written Word, Inc. - Copyright (c) 1996,1998,2001-2003,2005,2008,2010 Red Hat, Inc. @@ -1269,6 +1269,6 @@ L(EFDE9): #endif /* ifdef __i386__ */ -#if defined __ELF__ && (defined __linux__ || defined __FreeBSD__) +#if defined __ELF__ && defined __linux__ .section .note.GNU-stack,"",@progbits #endif diff --git a/deps/libffi/src/x86/sysv_intel.S b/deps/libffi/src/x86/sysv_intel.S index c7de159916ac9c..efdde14ac20290 100644 --- a/deps/libffi/src/x86/sysv_intel.S +++ b/deps/libffi/src/x86/sysv_intel.S @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------- - sysv.S - Copyright (c) 2017, 2022, 2026 Anthony Green + sysv.S - Copyright (c) 2017, 2022 Anthony Green - Copyright (c) 2013 The Written Word, Inc. - Copyright (c) 1996,1998,2001-2003,2005,2008,2010 Red Hat, Inc. @@ -992,7 +992,7 @@ L(EFDE9): #endif /* ifndef _MSC_VER */ #endif /* ifndef __x86_64__ */ -#if defined __ELF__ && (defined __linux__ || defined __FreeBSD__) +#if defined __ELF__ && defined __linux__ .section .note.GNU-stack,"",@progbits #endif #endif diff --git a/deps/libffi/src/x86/unix64.S b/deps/libffi/src/x86/unix64.S index 61983ef3bcf5ec..da30ab7577a7c3 100644 --- a/deps/libffi/src/x86/unix64.S +++ b/deps/libffi/src/x86/unix64.S @@ -2,7 +2,6 @@ unix64.S - Copyright (c) 2013 The Written Word, Inc. - Copyright (c) 2008 Red Hat, Inc - Copyright (c) 2002 Bo Thorsen - - Copyright (c) 2026 Anthony Green x86-64 Foreign Function Interface @@ -243,154 +242,6 @@ L(load_sse): L(UW4): ENDF(C(ffi_call_unix64)) -/* Lean trampoline for the plan fast path: no stack args, simple return. - struct { UINT64 rax; double xmm0; } - ffi_plan_fast_call (struct register_args *img /rdi/, void (*fn)(void) /rsi/); - - Loads the argument registers from IMG, calls FN, and lets the callee's - rax/xmm0 flow straight out as this function's {UINT64,double} return -- so - the C caller recovers both with no return-dispatch table. Skips the frame - relocation that ffi_call_unix64 needs only for stack args and struct/x87 - returns. Caller guarantees img has no spilled stack arguments. */ - .balign 8 - .globl C(ffi_plan_fast_call) - FFI_HIDDEN(C(ffi_plan_fast_call)) -C(ffi_plan_fast_call): - .cfi_startproc - _CET_ENDBR - movq %rsi, %r11 /* fn */ - movq %rdi, %rax /* img */ - movl 0xb0(%rax), %r10d /* ssecount */ - testl %r10d, %r10d - jz 1f - movdqa 0x30(%rax), %xmm0 - movdqa 0x40(%rax), %xmm1 - movdqa 0x50(%rax), %xmm2 - movdqa 0x60(%rax), %xmm3 - movdqa 0x70(%rax), %xmm4 - movdqa 0x80(%rax), %xmm5 - movdqa 0x90(%rax), %xmm6 - movdqa 0xa0(%rax), %xmm7 -1: - movq 0x00(%rax), %rdi - movq 0x08(%rax), %rsi - movq 0x10(%rax), %rdx - movq 0x18(%rax), %rcx - movq 0x20(%rax), %r8 - movq 0x28(%rax), %r9 - movl %r10d, %eax /* %al = ssecount */ - subq $8, %rsp /* realign to 16 across the call */ - .cfi_adjust_cfa_offset 8 - call *%r11 - addq $8, %rsp - .cfi_adjust_cfa_offset -8 - ret /* rax + xmm0 carry the callee's return */ - .cfi_endproc - ENDF(C(ffi_plan_fast_call)) - -/* Count-based direct thunks for the pure-GP64 fast path: load avalue[0..N-1] - straight into the argument registers (no register_args image) and call. - struct { UINT64 rax; double xmm0; } - ffi_plan_gpN (void **avalue /rdi/, void (*fn)(void) /rsi/); - Eligible only when every arg is a single 64-bit GP value (no sign-extension, - no SSE, no stack spill, no sret) -- so a plain 8-byte load per arg is exact. - rax/xmm0 flow out as the {UINT64,double} return; C stores per return code. */ - -#define FFI_GP_HEAD \ - .cfi_startproc; \ - _CET_ENDBR; \ - movq %rsi, %r11; \ - movq %rdi, %rax -#define FFI_GP_LD(OFS, REG) \ - movq OFS(%rax), %r10; \ - movq (%r10), REG -#define FFI_GP_TAIL \ - xorl %eax, %eax; \ - subq $8, %rsp; \ - .cfi_adjust_cfa_offset 8; \ - call *%r11; \ - addq $8, %rsp; \ - .cfi_adjust_cfa_offset -8; \ - ret; \ - .cfi_endproc - - .balign 8 - .globl C(ffi_plan_gp0) - FFI_HIDDEN(C(ffi_plan_gp0)) -C(ffi_plan_gp0): - FFI_GP_HEAD - FFI_GP_TAIL - ENDF(C(ffi_plan_gp0)) - - .balign 8 - .globl C(ffi_plan_gp1) - FFI_HIDDEN(C(ffi_plan_gp1)) -C(ffi_plan_gp1): - FFI_GP_HEAD - FFI_GP_LD(0x00, %rdi) - FFI_GP_TAIL - ENDF(C(ffi_plan_gp1)) - - .balign 8 - .globl C(ffi_plan_gp2) - FFI_HIDDEN(C(ffi_plan_gp2)) -C(ffi_plan_gp2): - FFI_GP_HEAD - FFI_GP_LD(0x08, %rsi) - FFI_GP_LD(0x00, %rdi) - FFI_GP_TAIL - ENDF(C(ffi_plan_gp2)) - - .balign 8 - .globl C(ffi_plan_gp3) - FFI_HIDDEN(C(ffi_plan_gp3)) -C(ffi_plan_gp3): - FFI_GP_HEAD - FFI_GP_LD(0x08, %rsi) - FFI_GP_LD(0x10, %rdx) - FFI_GP_LD(0x00, %rdi) - FFI_GP_TAIL - ENDF(C(ffi_plan_gp3)) - - .balign 8 - .globl C(ffi_plan_gp4) - FFI_HIDDEN(C(ffi_plan_gp4)) -C(ffi_plan_gp4): - FFI_GP_HEAD - FFI_GP_LD(0x08, %rsi) - FFI_GP_LD(0x10, %rdx) - FFI_GP_LD(0x18, %rcx) - FFI_GP_LD(0x00, %rdi) - FFI_GP_TAIL - ENDF(C(ffi_plan_gp4)) - - .balign 8 - .globl C(ffi_plan_gp5) - FFI_HIDDEN(C(ffi_plan_gp5)) -C(ffi_plan_gp5): - FFI_GP_HEAD - FFI_GP_LD(0x08, %rsi) - FFI_GP_LD(0x10, %rdx) - FFI_GP_LD(0x18, %rcx) - FFI_GP_LD(0x20, %r8) - FFI_GP_LD(0x00, %rdi) - FFI_GP_TAIL - ENDF(C(ffi_plan_gp5)) - - .balign 8 - .globl C(ffi_plan_gp6) - FFI_HIDDEN(C(ffi_plan_gp6)) -C(ffi_plan_gp6): - FFI_GP_HEAD - FFI_GP_LD(0x08, %rsi) - FFI_GP_LD(0x10, %rdx) - FFI_GP_LD(0x18, %rcx) - FFI_GP_LD(0x20, %r8) - FFI_GP_LD(0x28, %r9) - FFI_GP_LD(0x00, %rdi) - FFI_GP_TAIL - ENDF(C(ffi_plan_gp6)) - /* 6 general registers, 8 vector registers, 32 bytes of rvalue, 8 bytes of alignment. */ #define ffi_closure_OFS_G 0 @@ -873,6 +724,6 @@ L(EFDE5): #endif #endif /* __x86_64__ */ -#if defined __ELF__ && (defined __linux__ || defined __FreeBSD__) +#if defined __ELF__ && defined __linux__ .section .note.GNU-stack,"",@progbits #endif diff --git a/deps/libffi/src/x86/win64.S b/deps/libffi/src/x86/win64.S index 185f0a3048fba7..e994fa9e1eea62 100644 --- a/deps/libffi/src/x86/win64.S +++ b/deps/libffi/src/x86/win64.S @@ -255,6 +255,6 @@ C(ffi_closure_win64_alt): #endif #endif /* __x86_64__ */ -#if defined __ELF__ && (defined __linux__ || defined __FreeBSD__) +#if defined __ELF__ && defined __linux__ .section .note.GNU-stack,"",@progbits #endif diff --git a/deps/libffi/src/x86/win64_intel.S b/deps/libffi/src/x86/win64_intel.S index e9eff00da3ce6d..b278a7440040d4 100644 --- a/deps/libffi/src/x86/win64_intel.S +++ b/deps/libffi/src/x86/win64_intel.S @@ -237,7 +237,7 @@ ffi_closure_win64_2 LABEL near cfi_endproc C(ffi_closure_win64) endp -#if defined __ELF__ && (defined __linux__ || defined __FreeBSD__) +#if defined __ELF__ && defined __linux__ .section .note.GNU-stack,"",@progbits #endif _text ends diff --git a/deps/libffi/testsuite/Makefile.am b/deps/libffi/testsuite/Makefile.am index c14a880959d835..2e422b9a44e8e0 100644 --- a/deps/libffi/testsuite/Makefile.am +++ b/deps/libffi/testsuite/Makefile.am @@ -15,13 +15,9 @@ EXTRA_DIST = config/default.exp emscripten/build.sh emscripten/conftest.py \ libffi.call/align_stdcall.c libffi.call/bpo_38748.c libffi.call/call.exp \ libffi.call/err_bad_typedef.c libffi.call/ffitest.h libffi.call/float.c \ libffi.call/float1.c libffi.call/float2.c libffi.call/float3.c \ - libffi.call/float4.c libffi.call/float_va.c libffi.call/i128-1.c \ - libffi.call/large_struct_by_value.c libffi.call/many.c \ + libffi.call/float4.c libffi.call/float_va.c libffi.call/many.c \ libffi.call/many2.c libffi.call/many_double.c libffi.call/many_mixed.c \ - libffi.call/many_small_structs.c \ libffi.call/negint.c libffi.call/offsets.c libffi.call/overread.c \ - libffi.call/plan.c libffi.call/plan_mixed.c libffi.call/plan_spill.c \ - libffi.call/plan_struct.c libffi.call/plan_var.c \ libffi.call/pr1172638.c libffi.call/promotion.c libffi.call/pyobjc_tc.c libffi.call/return_dbl.c \ libffi.call/return_dbl1.c libffi.call/return_dbl2.c libffi.call/return_fl.c \ libffi.call/return_fl1.c libffi.call/return_fl2.c libffi.call/return_fl3.c \ @@ -81,7 +77,7 @@ EXTRA_DIST = config/default.exp emscripten/build.sh emscripten/conftest.py \ libffi.complex/cls_complex_va_double.c libffi.complex/cls_complex_va_float.c libffi.complex/cls_complex_va_longdouble.c \ libffi.complex/complex.exp libffi.complex/complex.inc libffi.complex/complex_defs_double.inc \ libffi.complex/complex_defs_float.inc libffi.complex/complex_defs_longdouble.inc libffi.complex/complex_double.c \ - libffi.complex/complex_float.c libffi.complex/complex_i128.c libffi.complex/complex_int.c libffi.complex/complex_longdouble.c \ + libffi.complex/complex_float.c libffi.complex/complex_int.c libffi.complex/complex_longdouble.c \ libffi.complex/ffitest.h libffi.complex/many_complex.inc libffi.complex/many_complex_double.c \ libffi.complex/many_complex_float.c libffi.complex/many_complex_longdouble.c libffi.complex/return_complex.inc \ libffi.complex/return_complex1.inc libffi.complex/return_complex1_double.c libffi.complex/return_complex1_float.c \ diff --git a/deps/libffi/testsuite/Makefile.in b/deps/libffi/testsuite/Makefile.in index 1b29b90d363346..04f864ca251555 100644 --- a/deps/libffi/testsuite/Makefile.in +++ b/deps/libffi/testsuite/Makefile.in @@ -303,13 +303,9 @@ EXTRA_DIST = config/default.exp emscripten/build.sh emscripten/conftest.py \ libffi.call/align_stdcall.c libffi.call/bpo_38748.c libffi.call/call.exp \ libffi.call/err_bad_typedef.c libffi.call/ffitest.h libffi.call/float.c \ libffi.call/float1.c libffi.call/float2.c libffi.call/float3.c \ - libffi.call/float4.c libffi.call/float_va.c libffi.call/i128-1.c \ - libffi.call/large_struct_by_value.c libffi.call/many.c \ + libffi.call/float4.c libffi.call/float_va.c libffi.call/many.c \ libffi.call/many2.c libffi.call/many_double.c libffi.call/many_mixed.c \ - libffi.call/many_small_structs.c \ libffi.call/negint.c libffi.call/offsets.c libffi.call/overread.c \ - libffi.call/plan.c libffi.call/plan_mixed.c libffi.call/plan_spill.c \ - libffi.call/plan_struct.c libffi.call/plan_var.c \ libffi.call/pr1172638.c libffi.call/promotion.c libffi.call/pyobjc_tc.c libffi.call/return_dbl.c \ libffi.call/return_dbl1.c libffi.call/return_dbl2.c libffi.call/return_fl.c \ libffi.call/return_fl1.c libffi.call/return_fl2.c libffi.call/return_fl3.c \ @@ -369,7 +365,7 @@ EXTRA_DIST = config/default.exp emscripten/build.sh emscripten/conftest.py \ libffi.complex/cls_complex_va_double.c libffi.complex/cls_complex_va_float.c libffi.complex/cls_complex_va_longdouble.c \ libffi.complex/complex.exp libffi.complex/complex.inc libffi.complex/complex_defs_double.inc \ libffi.complex/complex_defs_float.inc libffi.complex/complex_defs_longdouble.inc libffi.complex/complex_double.c \ - libffi.complex/complex_float.c libffi.complex/complex_i128.c libffi.complex/complex_int.c libffi.complex/complex_longdouble.c \ + libffi.complex/complex_float.c libffi.complex/complex_int.c libffi.complex/complex_longdouble.c \ libffi.complex/ffitest.h libffi.complex/many_complex.inc libffi.complex/many_complex_double.c \ libffi.complex/many_complex_float.c libffi.complex/many_complex_longdouble.c libffi.complex/return_complex.inc \ libffi.complex/return_complex1.inc libffi.complex/return_complex1_double.c libffi.complex/return_complex1_float.c \ diff --git a/deps/libffi/testsuite/lib/libffi.exp b/deps/libffi/testsuite/lib/libffi.exp index 55afb8f094a026..0a98a07daf6702 100644 --- a/deps/libffi/testsuite/lib/libffi.exp +++ b/deps/libffi/testsuite/lib/libffi.exp @@ -411,10 +411,6 @@ proc libffi_target_compile { source dest type options } { lappend options "libs= -lpthread" } - if { [string match "*-*-netbsd*" $target_triplet] } { - lappend options "libs= -lpthread" - } - lappend options "libs= -lffi" if { [string match "aarch64*-*-linux*" $target_triplet] } { diff --git a/deps/libffi/testsuite/libffi.call/i128-1.c b/deps/libffi/testsuite/libffi.call/i128-1.c deleted file mode 100644 index 1395f5e9426026..00000000000000 --- a/deps/libffi/testsuite/libffi.call/i128-1.c +++ /dev/null @@ -1,103 +0,0 @@ -/* Area: ffi_call - Purpose: Check int128 call and return. - Limitations: none. - PR: none. */ - -/* { dg-do run } */ -#include "ffitest.h" - -#if defined(FFI_TARGET_HAS_INT128) && defined(__SIZEOF_INT128__) - -typedef __int128_t i128; - -static const i128 val = ((i128)0x01020304050607ull << 64) | 0x08090a0b0c0d0e0full; -static const int dummy = 0xdeadbeef; - -#define D(X) int X __attribute__((unused)) - -static i128 f0(i128 x) -{ - return x; -} - -static i128 f1(D(a), i128 x) -{ - return x; -} - -static i128 f2(D(a), D(b), i128 x) -{ - return x; -} - -static i128 f3(D(a), D(b), D(c), i128 x) -{ - return x; -} - -static i128 f4(D(a), D(b), D(c), D(d), i128 x) -{ - return x; -} - -static i128 f5(D(a), D(b), D(c), D(d), D(e), i128 x) -{ - return x; -} - -static i128 f6(D(a), D(b), D(c), D(d), D(e), D(f), i128 x) -{ - return x; -} - -static i128 f7(D(a), D(b), D(c), D(d), D(e), D(f), D(g), i128 x) -{ - return x; -} - -static i128 f8(D(a), D(b), D(c), D(d), D(e), D(f), D(g), D(h), i128 x) -{ - return x; -} - -#define N 9 - -static void * const funcs[N] = { - f0, f1, f2, f3, f4, f5, f6, f7, f8 -}; - -int main (void) -{ - int i; - - for (i = 0; i < N; i++) - { - ffi_cif cif; - ffi_status s; - ffi_type *args[N]; - void *values[N]; - i128 ret; - int j; - - for (j = 0; j < i; j++) - { - args[j] = &ffi_type_sint; - values[j] = (void *)&dummy; - } - args[i] = &ffi_type_sint128; - values[i] = (void *)&val; - - s = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, i + 1, - &ffi_type_sint128, args); - CHECK(s == FFI_OK); - - ffi_call(&cif, FFI_FN(funcs[i]), &ret, values); - CHECK(ret == val); - } - - return 0; -} - -#else -int main (void) { return 0; } -#endif diff --git a/deps/libffi/testsuite/libffi.call/large_struct_by_value.c b/deps/libffi/testsuite/libffi.call/large_struct_by_value.c deleted file mode 100644 index 016870dfe1096c..00000000000000 --- a/deps/libffi/testsuite/libffi.call/large_struct_by_value.c +++ /dev/null @@ -1,63 +0,0 @@ -/* Area: ffi_call - Purpose: Pass a large struct by value (more words than arg registers). - Limitations: none. - PR: none. - Originator: secscan regression (ARCompact used_stack overflow). - - Regression test: on ARCompact (ARC 32-bit), ffi_call_int sized the stack - argument area at two words per argument, but a by-value struct is marshalled - one word ("atom") at a time, and words beyond the argument registers were - written to that under-sized area without bound. Passing a 64-byte struct by - value (sixteen 32-bit words, eight more than the eight argument registers) - must marshal correctly and return the expected sum. */ - -/* { dg-do run } */ -#include "ffitest.h" - -typedef struct { int v[8]; } big_struct; - -static int ABI_ATTR -sum_big (big_struct s) -{ - int i, sum = 0; - for (i = 0; i < 8; i++) - sum += s.v[i]; - return sum; -} - -int main (void) -{ - ffi_cif cif; - ffi_type *args[1]; - void *values[1]; - ffi_type bs_type; - ffi_type *bs_elements[9]; - big_struct in; - ffi_arg result = 0; - int i, expected = 0; - - bs_type.size = 0; - bs_type.alignment = 0; - bs_type.type = FFI_TYPE_STRUCT; - for (i = 0; i < 8; i++) - bs_elements[i] = &ffi_type_sint; - bs_elements[8] = NULL; - bs_type.elements = bs_elements; - - for (i = 0; i < 8; i++) - { - in.v[i] = 0x1111 * (i + 1); - expected += in.v[i]; - } - - args[0] = &bs_type; - values[0] = ∈ - - CHECK(ffi_prep_cif(&cif, ABI_NUM, 1, &ffi_type_sint, args) == FFI_OK); - - ffi_call(&cif, FFI_FN(sum_big), &result, values); - - CHECK((int) result == expected); - - exit(0); -} diff --git a/deps/libffi/testsuite/libffi.call/many_small_structs.c b/deps/libffi/testsuite/libffi.call/many_small_structs.c deleted file mode 100644 index 242c38cfc135bf..00000000000000 --- a/deps/libffi/testsuite/libffi.call/many_small_structs.c +++ /dev/null @@ -1,92 +0,0 @@ -/* Area: ffi_call - Purpose: Pass many small (8-byte) structs by value. - Limitations: none. - PR: none. - Originator: secscan regression (PA-RISC 32-bit slot under-count). - - Regression test: on PA-RISC 32-bit (FFI_PA32), ffi_size_stack_pa32 reserves - one stack slot per struct (pa/ffi.c: "z += 1"), but ffi_prep_args_pa32 - consumes two slots for a 5-8 byte struct passed inline. ffi_call_pa32 sets - the argument base to sp + cif->bytes and ffi_prep_args_pa32 writes each - argument at (base - slot*4), so once the marshaller's slot count exceeds - cif->bytes/4 the writes spill below sp -- first into the 64-byte register - save area, then over ffi_call_pa32's own saved return pointer. - - A few small structs only overflow into harmless scratch (the call still - returns correctly), so this uses enough 8-byte structs that the overflow - reaches the saved return pointer and corrupts the return path. On a correct - backend all arguments marshal within the allocated frame and the call simply - returns the expected sums; the test passes everywhere except an unfixed - FFI_PA32. */ - -/* { dg-do run } */ -#include "ffitest.h" - -typedef struct { int a; int b; } small_struct; - -#define NARGS 40 - -static small_struct ABI_ATTR -many_small (small_struct s0, small_struct s1, small_struct s2, small_struct s3, - small_struct s4, small_struct s5, small_struct s6, small_struct s7, - small_struct s8, small_struct s9, small_struct s10, small_struct s11, - small_struct s12, small_struct s13, small_struct s14, small_struct s15, - small_struct s16, small_struct s17, small_struct s18, small_struct s19, - small_struct s20, small_struct s21, small_struct s22, small_struct s23, - small_struct s24, small_struct s25, small_struct s26, small_struct s27, - small_struct s28, small_struct s29, small_struct s30, small_struct s31, - small_struct s32, small_struct s33, small_struct s34, small_struct s35, - small_struct s36, small_struct s37, small_struct s38, small_struct s39) -{ - small_struct r; - r.a = s0.a + s1.a + s2.a + s3.a + s4.a + s5.a + s6.a + s7.a - + s8.a + s9.a + s10.a + s11.a + s12.a + s13.a + s14.a + s15.a - + s16.a + s17.a + s18.a + s19.a + s20.a + s21.a + s22.a + s23.a - + s24.a + s25.a + s26.a + s27.a + s28.a + s29.a + s30.a + s31.a - + s32.a + s33.a + s34.a + s35.a + s36.a + s37.a + s38.a + s39.a; - r.b = s0.b + s1.b + s2.b + s3.b + s4.b + s5.b + s6.b + s7.b - + s8.b + s9.b + s10.b + s11.b + s12.b + s13.b + s14.b + s15.b - + s16.b + s17.b + s18.b + s19.b + s20.b + s21.b + s22.b + s23.b - + s24.b + s25.b + s26.b + s27.b + s28.b + s29.b + s30.b + s31.b - + s32.b + s33.b + s34.b + s35.b + s36.b + s37.b + s38.b + s39.b; - return r; -} - -int main (void) -{ - ffi_cif cif; - ffi_type *args[NARGS]; - void *values[NARGS]; - ffi_type ss_type; - ffi_type *ss_elements[3]; - small_struct in[NARGS]; - small_struct result = { 0, 0 }; - int i, expected_a = 0, expected_b = 0; - - ss_type.size = 0; - ss_type.alignment = 0; - ss_type.type = FFI_TYPE_STRUCT; - ss_type.elements = ss_elements; - ss_elements[0] = &ffi_type_sint; - ss_elements[1] = &ffi_type_sint; - ss_elements[2] = NULL; - - for (i = 0; i < NARGS; i++) - { - in[i].a = i + 1; - in[i].b = -(i + 1); - expected_a += in[i].a; - expected_b += in[i].b; - args[i] = &ss_type; - values[i] = &in[i]; - } - - CHECK(ffi_prep_cif(&cif, ABI_NUM, NARGS, &ss_type, args) == FFI_OK); - - ffi_call(&cif, FFI_FN(many_small), &result, values); - - CHECK(result.a == expected_a); - CHECK(result.b == expected_b); - - exit(0); -} diff --git a/deps/libffi/testsuite/libffi.call/plan.c b/deps/libffi/testsuite/libffi.call/plan.c deleted file mode 100644 index afed0977e6b1cb..00000000000000 --- a/deps/libffi/testsuite/libffi.call/plan.c +++ /dev/null @@ -1,127 +0,0 @@ -/* Area: ffi_call_plan - Purpose: Check that a reusable call plan reproduces ffi_call for the - pure-GP64 fast path, pointer arguments and repeated reuse, - and that a signature with no fast path still yields a usable - plan that falls back to ffi_call. - Limitations: none. - PR: none. - Originator: ffi_call_plan tests */ - -/* { dg-do run } */ -#include "ffitest.h" - -static uint64_t gp6(uint64_t a, uint64_t b, uint64_t c, - uint64_t d, uint64_t e, uint64_t f) -{ - return a + b * 2 + c * 3 + d * 4 + e * 5 + f * 6; -} - -static void *ptr_ident(void *p) -{ - return p; -} - -struct small_pair { long x; long y; }; - -static long ssum(struct small_pair s) -{ - return s.x - s.y; -} - -int main (void) -{ - ffi_cif cif; - ffi_type *args[6]; - void *values[6]; - ffi_call_plan *plan; - uint64_t a[6], r_call, r_plan; - int i, k; - - /* Pure GP64: every argument is one 64-bit integer, so build_plan - selects the ffi_plan_gpN direct thunk. Reuse the plan across many - invocations with changing values. */ - for (i = 0; i < 6; i++) - args[i] = &ffi_type_uint64; - CHECK(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 6, &ffi_type_uint64, args) - == FFI_OK); - plan = ffi_call_plan_alloc(&cif); - CHECK(plan != NULL); - - for (k = 0; k < 100; k++) - { - for (i = 0; i < 6; i++) - { - a[i] = (uint64_t) (k * 7 + i + 1); - values[i] = &a[i]; - } - ffi_call(&cif, FFI_FN(gp6), &r_call, values); - ffi_call_plan_invoke(plan, FFI_FN(gp6), &r_plan, values); - CHECK(r_call == r_plan); - CHECK(r_plan == gp6(a[0], a[1], a[2], a[3], a[4], a[5])); - } - ffi_call_plan_free(plan); - - /* Pointer argument and pointer return. */ - { - ffi_cif cifp; - ffi_type *pargs[1]; - void *pvalues[1]; - void *in, *rc, *rp; - ffi_call_plan *planp; - - pargs[0] = &ffi_type_pointer; - CHECK(ffi_prep_cif(&cifp, FFI_DEFAULT_ABI, 1, &ffi_type_pointer, pargs) - == FFI_OK); - planp = ffi_call_plan_alloc(&cifp); - CHECK(planp != NULL); - - in = &cifp; - pvalues[0] = ∈ - ffi_call(&cifp, FFI_FN(ptr_ident), &rc, pvalues); - ffi_call_plan_invoke(planp, FFI_FN(ptr_ident), &rp, pvalues); - CHECK(rc == in); - CHECK(rp == in); - ffi_call_plan_free(planp); - } - - /* No fast path: a struct-by-value argument. build_plan returns NULL for - the fast plan, but ffi_call_plan_alloc must still hand back a valid plan - whose invoke falls back to ffi_call and produces the same result. */ - { - ffi_cif cifs; - ffi_type *sargs[1]; - ffi_type stype; - ffi_type *selements[3]; - void *svalues[1]; - struct small_pair s; - ffi_arg rc, rp; - ffi_call_plan *plans; - - selements[0] = &ffi_type_slong; - selements[1] = &ffi_type_slong; - selements[2] = NULL; - stype.size = stype.alignment = 0; - stype.type = FFI_TYPE_STRUCT; - stype.elements = selements; - - sargs[0] = &stype; - CHECK(ffi_prep_cif(&cifs, FFI_DEFAULT_ABI, 1, &ffi_type_slong, sargs) - == FFI_OK); - plans = ffi_call_plan_alloc(&cifs); - CHECK(plans != NULL); - - s.x = 123; - s.y = 45; - svalues[0] = &s; - ffi_call(&cifs, FFI_FN(ssum), &rc, svalues); - ffi_call_plan_invoke(plans, FFI_FN(ssum), &rp, svalues); - CHECK(rc == rp); - CHECK((long) rp == ssum(s)); - ffi_call_plan_free(plans); - } - - /* Freeing NULL is documented to be harmless. */ - ffi_call_plan_free(NULL); - - exit(0); -} diff --git a/deps/libffi/testsuite/libffi.call/plan_mixed.c b/deps/libffi/testsuite/libffi.call/plan_mixed.c deleted file mode 100644 index 54b1f6290c4422..00000000000000 --- a/deps/libffi/testsuite/libffi.call/plan_mixed.c +++ /dev/null @@ -1,153 +0,0 @@ -/* Area: ffi_call_plan - Purpose: Check that a reusable call plan reproduces ffi_call for - signatures that mix general-purpose and SSE registers, for - float and double returns, for signed narrow arguments, and - for integer returns narrower than a register (which the plan - must widen to ffi_arg exactly as ffi_call does). - Limitations: none. - PR: none. - Originator: ffi_call_plan tests */ - -/* { dg-do run } */ -#include "ffitest.h" - -static double mixed(int a, double b, long c, float d, - long long e, double f) -{ - return (double) a + b + (double) c + (double) d + (double) e + f; -} - -static float faddf(float a, float b) -{ - return a + b; -} - -static signed char ret_sc(signed char x) -{ - return (signed char) (x + 1); -} - -static unsigned char ret_uc(unsigned char x) -{ - return (unsigned char) (x + 1); -} - -int main (void) -{ - /* Mixed GP + SSE arguments, double return: exercises the fast local-image - path with both integer and SSE moves and the ssecount (al) setup. */ - { - ffi_cif cif; - ffi_type *args[6]; - void *values[6]; - ffi_call_plan *plan; - int a = 3; - double b = 1.5, f = -2.25, rc, rp; - long c = 7; - float d = 0.5f; - long long e = -11; - - args[0] = &ffi_type_sint; - args[1] = &ffi_type_double; - args[2] = &ffi_type_slong; - args[3] = &ffi_type_float; - args[4] = &ffi_type_sint64; - args[5] = &ffi_type_double; - values[0] = &a; values[1] = &b; values[2] = &c; - values[3] = &d; values[4] = &e; values[5] = &f; - - CHECK(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 6, &ffi_type_double, args) - == FFI_OK); - plan = ffi_call_plan_alloc(&cif); - CHECK(plan != NULL); - - ffi_call(&cif, FFI_FN(mixed), &rc, values); - ffi_call_plan_invoke(plan, FFI_FN(mixed), &rp, values); - CHECK_DOUBLE_EQ(rc, rp); - CHECK_DOUBLE_EQ(rp, mixed(a, b, c, d, e, f)); - ffi_call_plan_free(plan); - } - - /* Float arguments and float return. */ - { - ffi_cif cif; - ffi_type *args[2]; - void *values[2]; - ffi_call_plan *plan; - float a = 1.25f, b = 2.5f, rc, rp; - - args[0] = &ffi_type_float; - args[1] = &ffi_type_float; - values[0] = &a; - values[1] = &b; - CHECK(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 2, &ffi_type_float, args) - == FFI_OK); - plan = ffi_call_plan_alloc(&cif); - CHECK(plan != NULL); - - ffi_call(&cif, FFI_FN(faddf), &rc, values); - ffi_call_plan_invoke(plan, FFI_FN(faddf), &rp, values); - CHECK_FLOAT_EQ(rc, rp); - CHECK_FLOAT_EQ(rp, faddf(a, b)); - ffi_call_plan_free(plan); - } - - /* Signed narrow argument and signed narrow return: the plan sign-extends - the argument and widens the return to ffi_arg just as ffi_call does. */ - { - ffi_cif cif; - ffi_type *args[1]; - void *values[1]; - ffi_call_plan *plan; - signed char in; - ffi_arg rc, rp; - int v; - - args[0] = &ffi_type_schar; - CHECK(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 1, &ffi_type_schar, args) - == FFI_OK); - plan = ffi_call_plan_alloc(&cif); - CHECK(plan != NULL); - - for (v = -128; v < 128; v++) - { - in = (signed char) v; - values[0] = ∈ - ffi_call(&cif, FFI_FN(ret_sc), &rc, values); - ffi_call_plan_invoke(plan, FFI_FN(ret_sc), &rp, values); - CHECK(rc == rp); - CHECK((signed char) rp == ret_sc(in)); - } - ffi_call_plan_free(plan); - } - - /* Unsigned narrow argument and unsigned narrow return. */ - { - ffi_cif cif; - ffi_type *args[1]; - void *values[1]; - ffi_call_plan *plan; - unsigned char in; - ffi_arg rc, rp; - int v; - - args[0] = &ffi_type_uchar; - CHECK(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 1, &ffi_type_uchar, args) - == FFI_OK); - plan = ffi_call_plan_alloc(&cif); - CHECK(plan != NULL); - - for (v = 0; v < 256; v++) - { - in = (unsigned char) v; - values[0] = ∈ - ffi_call(&cif, FFI_FN(ret_uc), &rc, values); - ffi_call_plan_invoke(plan, FFI_FN(ret_uc), &rp, values); - CHECK(rc == rp); - CHECK((unsigned char) rp == ret_uc(in)); - } - ffi_call_plan_free(plan); - } - - exit(0); -} diff --git a/deps/libffi/testsuite/libffi.call/plan_spill.c b/deps/libffi/testsuite/libffi.call/plan_spill.c deleted file mode 100644 index c073c99142e423..00000000000000 --- a/deps/libffi/testsuite/libffi.call/plan_spill.c +++ /dev/null @@ -1,142 +0,0 @@ -/* Area: ffi_call_plan - Purpose: Check that a reusable call plan reproduces ffi_call when - arguments spill past the argument registers onto the stack. - This drives the non-fast plan path (a move list handed to - ffi_call_unix64) for GP spill, SSE spill, and a mix of both. - Limitations: none. - PR: none. - Originator: ffi_call_plan tests */ - -/* { dg-do run } */ -#include "ffitest.h" - -/* 10 integers: 6 in registers, 4 spilled to the stack. */ -static long long gp10(long long a1, long long a2, long long a3, long long a4, - long long a5, long long a6, long long a7, long long a8, - long long a9, long long a10) -{ - return a1 + a2 * 2 + a3 * 3 + a4 * 4 + a5 * 5 - + a6 * 6 + a7 * 7 + a8 * 8 + a9 * 9 + a10 * 10; -} - -/* 12 doubles: 8 in SSE registers, 4 spilled to the stack. */ -static double sse12(double a1, double a2, double a3, double a4, - double a5, double a6, double a7, double a8, - double a9, double a10, double a11, double a12) -{ - return a1 + a2 * 2 + a3 * 3 + a4 * 4 + a5 * 5 + a6 * 6 - + a7 * 7 + a8 * 8 + a9 * 9 + a10 * 10 + a11 * 11 + a12 * 12; -} - -/* 8 ints + 8 doubles: both classes spill. */ -static double mix16(long i1, long i2, long i3, long i4, - long i5, long i6, long i7, long i8, - double d1, double d2, double d3, double d4, - double d5, double d6, double d7, double d8) -{ - double s = 0.0; - s += (double) (i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8); - s += d1 + d2 + d3 + d4 + d5 + d6 + d7 + d8; - return s; -} - -int main (void) -{ - /* GP spill. */ - { - ffi_cif cif; - ffi_type *args[10]; - void *values[10]; - long long a[10]; - long long rc, rp; - ffi_call_plan *plan; - int i; - - for (i = 0; i < 10; i++) - { - args[i] = &ffi_type_sint64; - a[i] = (long long) (i + 1) * 100 - 3; - values[i] = &a[i]; - } - CHECK(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 10, &ffi_type_sint64, args) - == FFI_OK); - plan = ffi_call_plan_alloc(&cif); - CHECK(plan != NULL); - - ffi_call(&cif, FFI_FN(gp10), &rc, values); - ffi_call_plan_invoke(plan, FFI_FN(gp10), &rp, values); - CHECK(rc == rp); - CHECK(rp == gp10(a[0], a[1], a[2], a[3], a[4], - a[5], a[6], a[7], a[8], a[9])); - ffi_call_plan_free(plan); - } - - /* SSE spill. */ - { - ffi_cif cif; - ffi_type *args[12]; - void *values[12]; - double a[12]; - double rc, rp; - ffi_call_plan *plan; - int i; - - for (i = 0; i < 12; i++) - { - args[i] = &ffi_type_double; - a[i] = (double) i + 0.25; - values[i] = &a[i]; - } - CHECK(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 12, &ffi_type_double, args) - == FFI_OK); - plan = ffi_call_plan_alloc(&cif); - CHECK(plan != NULL); - - ffi_call(&cif, FFI_FN(sse12), &rc, values); - ffi_call_plan_invoke(plan, FFI_FN(sse12), &rp, values); - CHECK_DOUBLE_EQ(rc, rp); - CHECK_DOUBLE_EQ(rp, sse12(a[0], a[1], a[2], a[3], a[4], a[5], - a[6], a[7], a[8], a[9], a[10], a[11])); - ffi_call_plan_free(plan); - } - - /* Both classes spill. */ - { - ffi_cif cif; - ffi_type *args[16]; - void *values[16]; - long iv[8]; - double dv[8]; - double rc, rp; - ffi_call_plan *plan; - int i; - - for (i = 0; i < 8; i++) - { - args[i] = &ffi_type_slong; - iv[i] = (long) (i + 1); - values[i] = &iv[i]; - } - for (i = 0; i < 8; i++) - { - args[8 + i] = &ffi_type_double; - dv[i] = (double) (i + 1) * 0.5; - values[8 + i] = &dv[i]; - } - CHECK(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 16, &ffi_type_double, args) - == FFI_OK); - plan = ffi_call_plan_alloc(&cif); - CHECK(plan != NULL); - - ffi_call(&cif, FFI_FN(mix16), &rc, values); - ffi_call_plan_invoke(plan, FFI_FN(mix16), &rp, values); - CHECK_DOUBLE_EQ(rc, rp); - CHECK_DOUBLE_EQ(rp, mix16(iv[0], iv[1], iv[2], iv[3], - iv[4], iv[5], iv[6], iv[7], - dv[0], dv[1], dv[2], dv[3], - dv[4], dv[5], dv[6], dv[7])); - ffi_call_plan_free(plan); - } - - exit(0); -} diff --git a/deps/libffi/testsuite/libffi.call/plan_struct.c b/deps/libffi/testsuite/libffi.call/plan_struct.c deleted file mode 100644 index 816296d77a8afa..00000000000000 --- a/deps/libffi/testsuite/libffi.call/plan_struct.c +++ /dev/null @@ -1,163 +0,0 @@ -/* Area: ffi_call_plan - Purpose: Check that a reusable call plan reproduces ffi_call for struct - returns. A struct return does not disable planning (only a - struct *argument* does), so this drives both the in-memory - return path (RET_IN_MEM, including a NULL rvalue) and the - register-pair struct return path, plus a large struct argument - that forces the ffi_call by-value copy fallback. - Limitations: none. - PR: none. - Originator: ffi_call_plan tests */ - -/* { dg-do run } */ -#include "ffitest.h" - -static int call_count = 0; - -/* 24 bytes: returned in memory (a hidden pointer in the first argument). */ -struct big3 { double a, b, c; }; - -static struct big3 make_big3(double a, double b, double c) -{ - struct big3 r; - call_count++; - r.a = a + 1.0; - r.b = b + 2.0; - r.c = c + 3.0; - return r; -} - -/* A struct larger than 16 bytes passed by value forces ffi_call to make a - copy; build_plan has no fast path for a struct argument, so this exercises - the plan's fallback to ffi_call. */ -static double sum_big3(struct big3 s) -{ - return s.a + s.b + s.c; -} - -/* 16 bytes: returned in a register pair (RAX:RDX on x86-64). */ -struct pair2 { long x, y; }; - -static struct pair2 make_pair2(long x, long y) -{ - struct pair2 r; - r.x = x * 2; - r.y = y * 3; - return r; -} - -int main (void) -{ - ffi_type *big3_elements[4]; - ffi_type big3_t; - ffi_type *pair2_elements[3]; - ffi_type pair2_t; - - big3_elements[0] = &ffi_type_double; - big3_elements[1] = &ffi_type_double; - big3_elements[2] = &ffi_type_double; - big3_elements[3] = NULL; - big3_t.size = big3_t.alignment = 0; - big3_t.type = FFI_TYPE_STRUCT; - big3_t.elements = big3_elements; - - pair2_elements[0] = &ffi_type_slong; - pair2_elements[1] = &ffi_type_slong; - pair2_elements[2] = NULL; - pair2_t.size = pair2_t.alignment = 0; - pair2_t.type = FFI_TYPE_STRUCT; - pair2_t.elements = pair2_elements; - - /* In-memory struct return with scalar arguments. */ - { - ffi_cif cif; - ffi_type *args[3]; - void *values[3]; - ffi_call_plan *plan; - double a = 10.0, b = 20.0, c = 30.0; - struct big3 rc, rp; - int before; - - args[0] = &ffi_type_double; - args[1] = &ffi_type_double; - args[2] = &ffi_type_double; - values[0] = &a; - values[1] = &b; - values[2] = &c; - CHECK(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 3, &big3_t, args) == FFI_OK); - plan = ffi_call_plan_alloc(&cif); - CHECK(plan != NULL); - - ffi_call(&cif, FFI_FN(make_big3), &rc, values); - ffi_call_plan_invoke(plan, FFI_FN(make_big3), &rp, values); - CHECK_DOUBLE_EQ(rc.a, rp.a); - CHECK_DOUBLE_EQ(rc.b, rp.b); - CHECK_DOUBLE_EQ(rc.c, rp.c); - CHECK_DOUBLE_EQ(rp.a, a + 1.0); - CHECK_DOUBLE_EQ(rp.b, b + 2.0); - CHECK_DOUBLE_EQ(rp.c, c + 3.0); - - /* A NULL rvalue for an in-memory struct return must not crash: libffi - supplies scratch space and discards the result, but the callee still - runs. Confirm the call actually happened. */ - before = call_count; - ffi_call_plan_invoke(plan, FFI_FN(make_big3), NULL, values); - CHECK(call_count == before + 1); - - ffi_call_plan_free(plan); - } - - /* Large struct argument: no fast path, falls back to ffi_call. */ - { - ffi_cif cif; - ffi_type *args[1]; - void *values[1]; - ffi_call_plan *plan; - struct big3 s; - double rc, rp; - - s.a = 1.5; - s.b = 2.5; - s.c = 3.5; - args[0] = &big3_t; - values[0] = &s; - CHECK(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 1, &ffi_type_double, args) - == FFI_OK); - plan = ffi_call_plan_alloc(&cif); - CHECK(plan != NULL); - - ffi_call(&cif, FFI_FN(sum_big3), &rc, values); - ffi_call_plan_invoke(plan, FFI_FN(sum_big3), &rp, values); - CHECK_DOUBLE_EQ(rc, rp); - CHECK_DOUBLE_EQ(rp, sum_big3(s)); - ffi_call_plan_free(plan); - } - - /* Register-pair struct return. */ - { - ffi_cif cif; - ffi_type *args[2]; - void *values[2]; - ffi_call_plan *plan; - long x = 7, y = 11; - struct pair2 rc, rp; - - args[0] = &ffi_type_slong; - args[1] = &ffi_type_slong; - values[0] = &x; - values[1] = &y; - CHECK(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 2, &pair2_t, args) == FFI_OK); - plan = ffi_call_plan_alloc(&cif); - CHECK(plan != NULL); - - ffi_call(&cif, FFI_FN(make_pair2), &rc, values); - ffi_call_plan_invoke(plan, FFI_FN(make_pair2), &rp, values); - CHECK(rc.x == rp.x); - CHECK(rc.y == rp.y); - CHECK(rp.x == x * 2); - CHECK(rp.y == y * 3); - ffi_call_plan_free(plan); - } - - exit(0); -} diff --git a/deps/libffi/testsuite/libffi.call/plan_var.c b/deps/libffi/testsuite/libffi.call/plan_var.c deleted file mode 100644 index e630156b23f077..00000000000000 --- a/deps/libffi/testsuite/libffi.call/plan_var.c +++ /dev/null @@ -1,73 +0,0 @@ -/* Area: ffi_call_plan - Purpose: Check that a reusable call plan reproduces ffi_call for a - variadic signature prepared with ffi_prep_cif_var. The - variadic double arguments travel in SSE registers, so the - fast path must set the vector-register count (al) correctly. - Limitations: none. - PR: none. - Originator: ffi_call_plan tests */ - -/* { dg-do run } */ -#include -#include "ffitest.h" - -static double vsum(int n, ...) -{ - va_list ap; - double s = 0.0; - int i; - - va_start(ap, n); - for (i = 0; i < n; i++) - s += va_arg(ap, double); - va_end(ap); - return s; -} - -static void -run (int nvar) -{ - ffi_cif cif; - ffi_type *args[1 + 8]; - void *values[1 + 8]; - double d[8]; - int n = nvar; - double rc, rp, expect; - ffi_call_plan *plan; - int i; - - CHECK(nvar <= 8); - - args[0] = &ffi_type_sint; - values[0] = &n; - expect = 0.0; - for (i = 0; i < nvar; i++) - { - d[i] = (double) (i + 1) * 1.5; - args[1 + i] = &ffi_type_double; - values[1 + i] = &d[i]; - expect += d[i]; - } - - CHECK(ffi_prep_cif_var(&cif, FFI_DEFAULT_ABI, 1, 1 + nvar, - &ffi_type_double, args) == FFI_OK); - plan = ffi_call_plan_alloc(&cif); - CHECK(plan != NULL); - - ffi_call(&cif, FFI_FN(vsum), &rc, values); - ffi_call_plan_invoke(plan, FFI_FN(vsum), &rp, values); - CHECK_DOUBLE_EQ(rc, rp); - CHECK_DOUBLE_EQ(rp, expect); - - ffi_call_plan_free(plan); -} - -int main (void) -{ - run (0); - run (1); - run (3); - run (5); - run (8); - exit(0); -} diff --git a/deps/libffi/testsuite/libffi.closures/closure.exp b/deps/libffi/testsuite/libffi.closures/closure.exp index 9d53294909ee73..ed4145ca843b4a 100644 --- a/deps/libffi/testsuite/libffi.closures/closure.exp +++ b/deps/libffi/testsuite/libffi.closures/closure.exp @@ -36,7 +36,7 @@ if { [string match $compiler_vendor "microsoft"] } { set tlist [lsort [glob -nocomplain -- $srcdir/$subdir/*.c]] if { [libffi_feature_test "#if FFI_CLOSURES"] } { - run-many-tests $tlist $additional_options + run-many-tests $tlist "" } else { foreach test $tlist { unsupported "$test" diff --git a/deps/libffi/testsuite/libffi.closures/closure_loc_fn0.c b/deps/libffi/testsuite/libffi.closures/closure_loc_fn0.c index 179485d9b57b87..f344a6074e1429 100644 --- a/deps/libffi/testsuite/libffi.closures/closure_loc_fn0.c +++ b/deps/libffi/testsuite/libffi.closures/closure_loc_fn0.c @@ -6,7 +6,6 @@ PR: none. Originator: 20030828 */ -/* { dg-do run } */ #include "ffitest.h" @@ -81,10 +80,8 @@ int main (void) CHECK(ffi_prep_closure_loc(pcl, &cif, closure_loc_test_fn0, (void *) 3 /* userdata */, codeloc) == FFI_OK); -#if !defined(FFI_EXEC_STATIC_TRAMP) && !defined(__EMSCRIPTEN__) \ - && !(defined(FFI_EXEC_TRAMPOLINE_TABLE) && FFI_EXEC_TRAMPOLINE_TABLE) - /* With static trampolines or a trampoline table (Apple aarch64), the - codeloc does not point to the closure */ +#if !defined(FFI_EXEC_STATIC_TRAMP) && !defined(__EMSCRIPTEN__) + /* With static trampolines, the codeloc does not point to closure */ CHECK(memcmp(pcl, FFI_CL(codeloc), sizeof(*pcl)) == 0); #endif diff --git a/deps/libffi/testsuite/libffi.complex/complex_i128.c b/deps/libffi/testsuite/libffi.complex/complex_i128.c deleted file mode 100644 index a599d556e12083..00000000000000 --- a/deps/libffi/testsuite/libffi.complex/complex_i128.c +++ /dev/null @@ -1,118 +0,0 @@ -/* Area: ffi_call - Purpose: Check complex int128 call and return. - Limitations: none. - PR: none. */ - -/* { dg-do run } */ -#include "ffitest.h" - -/* clang defines __SIZEOF_INT128__ but does not support _Complex __int128, - so exclude it here and fall through to the trivial main() below. */ -#if defined(FFI_TARGET_HAS_INT128) && \ - defined(FFI_TARGET_HAS_COMPLEX_TYPE) && \ - defined(__SIZEOF_INT128__) && \ - !defined(__clang__) - -typedef __int128_t i128; -typedef _Complex __int128 ci128; - -static const ci128 val = - (((i128)0x01020304050607ull << 64) | 0x08090a0b0c0d0e0full) + - (((i128)0x10203040506070ull << 64) | 0x8090a0b0c0d0e0f0ull) * 1i; -static const int dummy = 0xdeadbeef; - -#define D(X) int X __attribute__((unused)) - -static ci128 f0(ci128 x) -{ - return x; -} - -static ci128 f1(D(a), ci128 x) -{ - return x; -} - -static ci128 f2(D(a), D(b), ci128 x) -{ - return x; -} - -static ci128 f3(D(a), D(b), D(c), ci128 x) -{ - return x; -} - -static ci128 f4(D(a), D(b), D(c), D(d), ci128 x) -{ - return x; -} - -static ci128 f5(D(a), D(b), D(c), D(d), D(e), ci128 x) -{ - return x; -} - -static ci128 f6(D(a), D(b), D(c), D(d), D(e), D(f), ci128 x) -{ - return x; -} - -static ci128 f7(D(a), D(b), D(c), D(d), D(e), D(f), D(g), ci128 x) -{ - return x; -} - -static ci128 f8(D(a), D(b), D(c), D(d), D(e), D(f), D(g), D(h), ci128 x) -{ - return x; -} - -#define N 9 - -static void * const funcs[N] = { - f0, f1, f2, f3, f4, f5, f6, f7, f8 -}; - -static ffi_type ffi_type_ci128 = { - sizeof(ci128), - _Alignof(ci128), - FFI_TYPE_COMPLEX, - (ffi_type *[2]){ &ffi_type_sint128, NULL }, -}; - -int main (void) -{ - int i; - - for (i = 0; i < N; i++) - { - ffi_cif cif; - ffi_status s; - ffi_type *args[N]; - void *values[N]; - ci128 ret; - int j; - - for (j = 0; j < i; j++) - { - args[j] = &ffi_type_sint; - values[j] = (void *)&dummy; - } - args[i] = &ffi_type_ci128; - values[i] = (void *)&val; - - s = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, i + 1, - &ffi_type_ci128, args); - CHECK(s == FFI_OK); - - ffi_call(&cif, FFI_FN(funcs[i]), &ret, values); - CHECK(ret == val); - } - - return 0; -} - -#else -int main (void) { return 0; } -#endif diff --git a/deps/libffi/testsuite/libffi.go/go.exp b/deps/libffi/testsuite/libffi.go/go.exp index f477e99ade6911..100c5e75b40915 100644 --- a/deps/libffi/testsuite/libffi.go/go.exp +++ b/deps/libffi/testsuite/libffi.go/go.exp @@ -19,23 +19,10 @@ libffi-init global srcdir subdir -if { [string match $compiler_vendor "microsoft"] } { - # -wd4005 macro redefinition - # -wd4244 implicit conversion to type of smaller size - # -wd4305 truncation to smaller type - # -wd4477 printf %lu of uintptr_t - # -wd4312 implicit conversion to type of greater size - # -wd4311 pointer truncation to unsigned long - # -EHsc C++ Exception Handling (no SEH exceptions) - set additional_options "-wd4005 -wd4244 -wd4305 -wd4477 -wd4312 -wd4311 -EHsc"; -} else { - set additional_options ""; -} - set tlist [lsort [glob -nocomplain -- $srcdir/$subdir/*.{c,cc}]] if { [libffi_feature_test "#ifdef FFI_GO_CLOSURES"] } { - run-many-tests $tlist $additional_options + run-many-tests $tlist "" } else { foreach test $tlist { unsupported "$test" diff --git a/deps/v8/src/wasm/jump-table-assembler.cc b/deps/v8/src/wasm/jump-table-assembler.cc index dfebd423c7c068..47d21e29785be8 100644 --- a/deps/v8/src/wasm/jump-table-assembler.cc +++ b/deps/v8/src/wasm/jump-table-assembler.cc @@ -114,10 +114,6 @@ void JumpTableAssembler::EmitLazyCompileJumpSlot(uint32_t func_index, } bool JumpTableAssembler::EmitJumpSlot(Address target) { - intptr_t displacement = target - (pc_ + kJumpTableSlotEntryMarkerSize + - MacroAssembler::kIntraSegmentJmpInstrSize); - if (!is_int32(displacement)) return false; - #ifdef V8_ENABLE_CET_IBT uint32_t endbr_insn = 0xfa1e0ff3; uint32_t nop = 0x00401f0f; @@ -126,7 +122,11 @@ bool JumpTableAssembler::EmitJumpSlot(Address target) { emit(nop, kRelaxedStore); #endif - uint8_t inst[8] = { + intptr_t displacement = + target - (pc_ + MacroAssembler::kIntraSegmentJmpInstrSize); + if (!is_int32(displacement)) return false; + + uint8_t inst[kJumpTableSlotSize] = { 0xe9, 0, 0, 0, 0, // near_jmp displacement 0xcc, 0xcc, 0xcc, // int3 * 3 }; diff --git a/deps/v8/src/wasm/jump-table-assembler.h b/deps/v8/src/wasm/jump-table-assembler.h index 91252dc6e67c03..2024d74928136c 100644 --- a/deps/v8/src/wasm/jump-table-assembler.h +++ b/deps/v8/src/wasm/jump-table-assembler.h @@ -184,10 +184,8 @@ class V8_EXPORT_PRIVATE JumpTableAssembler { #if V8_TARGET_ARCH_X64 #ifdef V8_ENABLE_CET_IBT static constexpr int kJumpTableSlotSize = 16; - static constexpr int kJumpTableSlotEntryMarkerSize = 8; #else // V8_ENABLE_CET_IBT static constexpr int kJumpTableSlotSize = 8; - static constexpr int kJumpTableSlotEntryMarkerSize = 0; #endif static constexpr int kJumpTableLineSize = kJumpTableSlotSize; static constexpr int kFarJumpTableSlotSize = 16; diff --git a/doc/api/cli.md b/doc/api/cli.md index b3a42183558dc4..8c241e7202bdb8 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -2079,6 +2079,14 @@ added: v21.2.0 Disable exposition of [Navigator API][] on the global scope. +### `--no-experimental-repl-await` + + + +Use this flag to disable top-level await in REPL. + ### `--no-experimental-require-module` -Type: End-of-Life +Type: Runtime The `node:_tls_common` and `node:_tls_wrap` modules are deprecated as they should be considered an internal nodejs implementation rather than a public facing API, use `node:tls` instead. diff --git a/doc/api/embedding.md b/doc/api/embedding.md index 0309e112096928..114f1128af0a42 100644 --- a/doc/api/embedding.md +++ b/doc/api/embedding.md @@ -115,7 +115,7 @@ int RunNodeInstance(MultiIsolatePlatform* platform, const std::vector& exec_args) { int exit_code = 0; - // Set up a libuv event loop, v8::Isolate, and Node.js Environment. + // Setup up a libuv event loop, v8::Isolate, and Node.js Environment. std::vector errors; std::unique_ptr setup = CommonEnvironmentSetup::Create(platform, &errors, args, exec_args); diff --git a/doc/api/errors.md b/doc/api/errors.md index eee48b5ea7cf9b..d321f666bb48d8 100644 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -768,6 +768,12 @@ An operation outside the bounds of a `Buffer` was attempted. An attempt has been made to create a `Buffer` larger than the maximum allowed size. + + +### `ERR_CANNOT_WATCH_SIGINT` + +Node.js was unable to watch for the `SIGINT` signal. + ### `ERR_CHILD_CLOSED_BEFORE_REPLY` @@ -2138,6 +2144,13 @@ An invalid `options.protocol` was passed to `http.request()`. Both `breakEvalOnSigint` and `eval` options were set in the [`REPL`][] config, which is not supported. + + +### `ERR_INVALID_REPL_INPUT` + +The input may not be used in the [`REPL`][]. The conditions under which this +error is used are described in the [`REPL`][] documentation. + ### `ERR_INVALID_RETURN_PROPERTY` diff --git a/doc/api/fs.md b/doc/api/fs.md index 9cb84007b3b815..51e1a6f40b985a 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -196,7 +196,7 @@ changes: strings anymore. --> -* `data` {string|Buffer|TypedArray|DataView|AsyncIterable|Iterable} +* `data` {string|Buffer|TypedArray|DataView|AsyncIterable|Iterable|Stream} * `options` {Object|string} * `encoding` {string|null} **Default:** `'utf8'` * `signal` {AbortSignal|undefined} allows aborting an in-progress writeFile. **Default:** `undefined` @@ -1007,7 +1007,7 @@ changes: strings anymore. --> -* `data` {string|Buffer|TypedArray|DataView|AsyncIterable|Iterable} +* `data` {string|Buffer|TypedArray|DataView|AsyncIterable|Iterable|Stream} * `options` {Object|string} * `encoding` {string|null} The expected character encoding when `data` is a string. **Default:** `'utf8'` @@ -1238,15 +1238,10 @@ changes: - v20.10.0 pr-url: https://github.com/nodejs/node/pull/50095 description: The `flush` option is now supported. - - version: - - v15.14.0 - - v14.18.0 - pr-url: https://github.com/nodejs/node/pull/37490 - description: The `data` argument supports `AsyncIterable`, `Iterable`, and `Stream`. --> * `path` {string|Buffer|URL|FileHandle} filename or {FileHandle} -* `data` {string|Buffer|TypedArray|DataView|AsyncIterable|Iterable} +* `data` {string|Buffer} * `options` {Object|string} * `encoding` {string|null} **Default:** `'utf8'` * `mode` {integer} **Default:** `0o666` @@ -1256,7 +1251,7 @@ changes: * Returns: {Promise} Fulfills with `undefined` upon success. Asynchronously append data to a file, creating the file if it does not yet -`data` can be a string, a buffer, an {AsyncIterable}, or an {Iterable} object. +exist. `data` can be a string or a {Buffer}. If `options` is a string, then it specifies the `encoding`. @@ -2270,7 +2265,7 @@ changes: --> * `file` {string|Buffer|URL|FileHandle} filename or `FileHandle` -* `data` {string|Buffer|TypedArray|DataView|AsyncIterable|Iterable} +* `data` {string|Buffer|TypedArray|DataView|AsyncIterable|Iterable|Stream} * `options` {Object|string} * `encoding` {string|null} **Default:** `'utf8'` * `mode` {integer} **Default:** `0o666` diff --git a/doc/api/http2.md b/doc/api/http2.md index 0b5d665dbc2b71..1003fb3fb0e90e 100644 --- a/doc/api/http2.md +++ b/doc/api/http2.md @@ -722,11 +722,6 @@ registered as a listener on the `'timeout'` event. * Type: {net.Socket|tls.TLSSocket} @@ -734,12 +729,11 @@ changes: Returns a `Proxy` object that acts as a `net.Socket` (or `tls.TLSSocket`) but limits available methods to ones safe to use with HTTP/2. -`emit`, `end`, `pause`, `read`, `resume`, and `write` will throw +`destroy`, `emit`, `end`, `pause`, `read`, `resume`, and `write` will throw an error with code `ERR_HTTP2_NO_SOCKET_MANIPULATION`. See [`Http2Session` and Sockets][] for more information. -`destroy`, `setTimeout`, `ref`, and `unref` methods will be called on this -`Http2Session`. +`setTimeout` method will be called on this `Http2Session`. All other interactions will be routed directly to the socket. diff --git a/doc/api/modules.md b/doc/api/modules.md index 01a5343e92dd72..87650979f66508 100644 --- a/doc/api/modules.md +++ b/doc/api/modules.md @@ -1133,7 +1133,7 @@ exports = { hello: false }; // Not exported, only available in the module When the `module.exports` property is being completely replaced by a new object, it is common to also reassign `exports`: - + ```js module.exports = exports = function Constructor() { diff --git a/doc/api/net.md b/doc/api/net.md index 410a3742462c47..81863d9b0a13c5 100644 --- a/doc/api/net.md +++ b/doc/api/net.md @@ -1402,17 +1402,6 @@ added: v0.5.10 The numeric representation of the remote port. For example, `80` or `21`. Value may be `undefined` if the socket is destroyed (for example, if the client disconnected). -### `socket.server` - - - -* Type: {net.Server|null} - -Reference to the server that accepted the socket. This is `null` for sockets -that were not accepted by a server. - ### `socket.resetAndDestroy()` - -* Type: {string\[]} One or more of `'zlib'`, `'brotli'`, or `'zstd'`, in - preference order. - -Enables TLS certificate compression ([RFC 8879][]) for this session. When -omitted, certificate compression is disabled. - -On the server side, the certificate chain is compressed using the first -listed algorithm that the client advertises support for. On the client side, -the listed algorithms are advertised to the server so that the server may -compress its certificate. When client authentication is in use, the option -also controls compression of the client's certificate. - -Compressing the certificate chain is especially useful for QUIC because it -reduces the size of the server's first flight, which is bounded by the -anti-amplification limit (see [Certificate size and handshake -performance][]). Certificate compression requires TLS 1.3, which QUIC always -uses. - -At most three algorithms may be specified. The option is silently ignored if -Node.js was built against a shared OpenSSL that lacks certificate compression -support. - #### `sessionOptions.ciphers` + +The REPL uses the [`domain`][] module to catch all uncaught exceptions for that +REPL session. + +This use of the [`domain`][] module in the REPL has these side effects: + +* Uncaught exceptions only emit the [`'uncaughtException'`][] event in the + standalone REPL. Adding a listener for this event in a REPL within + another Node.js program results in [`ERR_INVALID_REPL_INPUT`][]. + + ```js + const r = repl.start(); + + r.write('process.on("uncaughtException", () => console.log("Foobar"));\n'); + // Output stream includes: + // TypeError [ERR_INVALID_REPL_INPUT]: Listeners for `uncaughtException` + // cannot be used in the REPL + + r.close(); + ``` + +* Trying to use [`process.setUncaughtExceptionCaptureCallback()`][] throws + an [`ERR_DOMAIN_CANNOT_SET_UNCAUGHT_EXCEPTION_CAPTURE`][] error. + #### Assignment of the `_` (underscore) variable - Support for the `await` keyword is enabled at the top level. ```console @@ -232,6 +261,25 @@ undefined undefined ``` +One known limitation of using the `await` keyword in the REPL is that +it will invalidate the lexical scoping of the `const` keywords. + +For example: + +```console +> const m = await Promise.resolve(123) +undefined +> m +123 +> m = await Promise.resolve(234) +234 +// redeclaring the constant does error +> const m = await Promise.resolve(345) +Uncaught SyntaxError: Identifier 'm' has already been declared +``` + +[`--no-experimental-repl-await`][] shall disable top-level await in REPL. + ### Reverse-i-search - -* Type: {string|boolean|null} - -The negotiated ALPN protocol. This is `null` before the handshake completes. -Once the handshake completes, it settles as either the negotiated protocol -name, or `false` if the peers did not negotiate an ALPN protocol. - ### `tlsSocket.authorizationError` - -* Type: {string|boolean|null} - -The SNI (Server Name Indication) host name associated with the socket. This is -`null` before the handshake completes. Once the handshake completes it settles -as either the host name string, or `false` if SNI was not used. - ### `tlsSocket.setKeyCert(context)` \n'); +}; + +process.on('uncaughtException', (e) => { + Error.prepareStackTrace = origPrepareStackTrace; + throw e; +}); + +const tests = [ + { + // test .load for a file that throws + command: `.load ${fixtures.path('repl-pretty-stack.js')}`, + expected: 'Uncaught Error: Whoops!--->\nREPL1:*:*--->\nd (REPL1:*:*)' + + '--->\nc (REPL1:*:*)--->\nb (REPL1:*:*)--->\na (REPL1:*:*)\n' + }, + { + command: 'let x y;', + expected: /let x y;\n {6}\^\n\nUncaught SyntaxError: Unexpected identifier.*\n/ + }, + { + command: 'throw new Error(\'Whoops!\')', + expected: 'Uncaught Error: Whoops!\n' + }, + { + command: 'foo = bar;', + expected: 'Uncaught ReferenceError: bar is not defined\n' + }, + // test anonymous IIFE + { + command: '(function() { throw new Error(\'Whoops!\'); })()', + expected: 'Uncaught Error: Whoops!--->\nREPL5:*:*\n' + }, +]; + +tests.forEach(run); + +// Verify that the stack can be generated when Error.prepareStackTrace is deleted. +delete Error.prepareStackTrace; +run({ + command: 'throw new TypeError(\'Whoops!\')', + expected: 'Uncaught TypeError: Whoops!\n' +}); diff --git a/test/parallel/test-repl-pretty-custom-stack.mjs b/test/parallel/test-repl-pretty-custom-stack.mjs deleted file mode 100644 index 9c159007c2f246..00000000000000 --- a/test/parallel/test-repl-pretty-custom-stack.mjs +++ /dev/null @@ -1,88 +0,0 @@ -import '../common/index.mjs'; -import assert from 'node:assert'; -import fixtures from '../common/fixtures.js'; -import { startNewREPLServer } from '../common/repl.js'; - -// Normalize line/column numbers (and the inspector-assigned REPL source -// number, which depends on how many evaluations have run in the process). -const stackRegExp = /(REPL)\d+:[0-9]+:[0-9]+/g; - -async function runTest({ command, expected }) { - const { replServer, output, run } = startNewREPLServer({ - terminal: false, - useColors: false - }); - - await run(`${command}\n`); - if (typeof expected === 'string') { - assert.strictEqual( - output.accumulator.replace(stackRegExp, '$1:*:*'), - expected.replace(stackRegExp, '$1:*:*') - ); - } else { - assert.match( - output.accumulator.replace(stackRegExp, '$1:*:*'), - expected - ); - } - replServer.close(); -} - -const origPrepareStackTrace = Error.prepareStackTrace; -Error.prepareStackTrace = (err, stack) => { - if (err instanceof SyntaxError) - return err.toString(); - // Insert the error at the beginning of the stack - stack.unshift(err); - return stack.join('--->\n'); -}; - -process.on('uncaughtException', (e) => { - Error.prepareStackTrace = origPrepareStackTrace; - throw e; -}); - -// The REPL now evaluates asynchronously through the V8 inspector and surfaces -// the live Error object. Reading its `.stack` runs the custom -// `Error.prepareStackTrace` over the full V8 stack, so after the user's REPL -// frames the inspector/eval internals also appear. The custom formatter is -// still honored (frames joined with `--->`, error pushed to the front), which -// is what these tests verify; we match the user-visible prefix and tolerate -// the trailing internal frames. -const tests = [ - { - // test .load for a file that throws - command: `.load ${fixtures.path('repl-pretty-stack.js')}`, - expected: /^\| Uncaught Error: Whoops!--->\nREPL:\*:\*--->\nd \(REPL:\*:\*\)/m - }, - { - command: 'let x y;', - expected: /^Uncaught SyntaxError: Unexpected identifier.*\n/ - }, - { - command: 'throw new Error(\'Whoops!\')', - expected: /^Uncaught Error: Whoops!--->\nREPL:\*:\*/ - }, - { - command: 'foo = bar;', - expected: /^Uncaught ReferenceError: bar is not defined--->\nREPL:\*:\*/ - }, - // test anonymous IIFE - { - command: '(function() { throw new Error(\'Whoops!\'); })()', - expected: /^Uncaught Error: Whoops!--->\nREPL:\*:\*--->\nREPL:\*:\*/ - }, -]; - -for (const test of tests) { - await runTest(test); -} - -// Verify that the stack can be generated when Error.prepareStackTrace is -// deleted. The default formatter is restored, so the live error prints its -// normal stack. -delete Error.prepareStackTrace; -await runTest({ - command: 'throw new TypeError(\'Whoops!\')', - expected: /^Uncaught TypeError: Whoops!\n {4}at REPL:\*:\*/ -}); diff --git a/test/parallel/test-repl-pretty-stack-custom-writer.js b/test/parallel/test-repl-pretty-stack-custom-writer.js new file mode 100644 index 00000000000000..e31460dbc93efb --- /dev/null +++ b/test/parallel/test-repl-pretty-stack-custom-writer.js @@ -0,0 +1,15 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const { startNewREPLServer } = require('../common/repl'); + +const testingReplPrompt = '_REPL_TESTING_PROMPT_>'; + +const { replServer, output } = startNewREPLServer({ prompt: testingReplPrompt }); + +replServer.write('throw new Error("foo[a]")\n'); + +assert.strictEqual( + output.accumulator.split('\n').filter((line) => !line.includes(testingReplPrompt)).join(''), + 'Uncaught Error: foo[a]' +); diff --git a/test/parallel/test-repl-pretty-stack-custom-writer.mjs b/test/parallel/test-repl-pretty-stack-custom-writer.mjs deleted file mode 100644 index 13acd0b0b3fb3c..00000000000000 --- a/test/parallel/test-repl-pretty-stack-custom-writer.mjs +++ /dev/null @@ -1,22 +0,0 @@ -import '../common/index.mjs'; -import assert from 'node:assert'; -import { startNewREPLServer } from '../common/repl.js'; - -const testingReplPrompt = '_REPL_TESTING_PROMPT_>'; - -const { replServer, output, run } = startNewREPLServer({ prompt: testingReplPrompt }); - -await run('throw new Error("foo[a]")\n'); - -// The REPL now evaluates via the inspector and surfaces the live Error object, -// so the printed stack includes a `at REPL:line:col` frame after the -// message. Strip those frames before comparing the error header. -assert.strictEqual( - output.accumulator - .split('\n') - .filter((line) => !line.includes(testingReplPrompt) && !/^\s+at /.test(line)) - .join(''), - 'Uncaught Error: foo[a]' -); - -replServer.close(); diff --git a/test/parallel/test-repl-pretty-stack.js b/test/parallel/test-repl-pretty-stack.js new file mode 100644 index 00000000000000..b2f9cc82c08df0 --- /dev/null +++ b/test/parallel/test-repl-pretty-stack.js @@ -0,0 +1,70 @@ +'use strict'; +require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const { startNewREPLServer } = require('../common/repl'); + +const stackRegExp = /(at .*REPL\d+:)[0-9]+:[0-9]+/g; + +function run({ command, expected, ...extraREPLOptions }, i) { + const { replServer, output } = startNewREPLServer({ + terminal: false, + useColors: false, + ...extraREPLOptions + }); + + replServer.write(`${command}\n`); + if (typeof expected === 'string') { + assert.strictEqual( + output.accumulator.replace(stackRegExp, '$1*:*'), + expected.replace(stackRegExp, '$1*:*') + ); + } else { + assert.match( + output.accumulator.replace(stackRegExp, '$1*:*'), + expected + ); + } + replServer.close(); +} + +const tests = [ + { + // Test .load for a file that throws. + command: `.load ${fixtures.path('repl-pretty-stack.js')}`, + expected: 'Uncaught Error: Whoops!\n at REPL1:*:*\n' + + ' at d (REPL1:*:*)\n at c (REPL1:*:*)\n' + + ' at b (REPL1:*:*)\n at a (REPL1:*:*)\n' + }, + { + command: 'let x y;', + expected: /^let x y;\n {6}\^\n\nUncaught SyntaxError: Unexpected identifier.*\n/ + }, + { + command: 'throw new Error(\'Whoops!\')', + expected: 'Uncaught Error: Whoops!\n' + }, + { + command: '(() => { const err = Error(\'Whoops!\'); ' + + 'err.foo = \'bar\'; throw err; })()', + expected: "Uncaught Error: Whoops!\n at REPL4:*:* {\n foo: 'bar'\n}\n", + }, + { + command: '(() => { const err = Error(\'Whoops!\'); ' + + 'err.foo = \'bar\'; throw err; })()', + expected: 'Uncaught Error: Whoops!\n at REPL5:*:* {\n foo: ' + + "\u001b[32m'bar'\u001b[39m\n}\n", + useColors: true + }, + { + command: 'foo = bar;', + expected: 'Uncaught ReferenceError: bar is not defined\n' + }, + // Test anonymous IIFE. + { + command: '(function() { throw new Error(\'Whoops!\'); })()', + expected: 'Uncaught Error: Whoops!\n at REPL7:*:*\n' + }, +]; + +tests.forEach(run); diff --git a/test/parallel/test-repl-pretty-stack.mjs b/test/parallel/test-repl-pretty-stack.mjs deleted file mode 100644 index 82f418e5be4963..00000000000000 --- a/test/parallel/test-repl-pretty-stack.mjs +++ /dev/null @@ -1,85 +0,0 @@ -import '../common/index.mjs'; -import assert from 'node:assert'; -import fixtures from '../common/fixtures.js'; -import { startNewREPLServer } from '../common/repl.js'; - -// The REPL now evaluates asynchronously through the V8 inspector. Errors are -// surfaced as the live Error object, so stack frames are printed with a -// `REPL` source id whose number depends on how many evaluations have run in -// the process. Normalize both the line/column and the REPL id so the assertion -// only checks the shape of the stack, not the global evaluation counter. -const stackRegExp = /(at .*REPL)\d+:[0-9]+:[0-9]+/g; - -async function runTest({ command, expected, ...extraREPLOptions }) { - const { replServer, output, run } = startNewREPLServer({ - terminal: false, - useColors: false, - ...extraREPLOptions - }); - - await run(`${command}\n`); - if (typeof expected === 'string') { - assert.strictEqual( - output.accumulator.replace(stackRegExp, '$1*:*:*'), - expected.replace(stackRegExp, '$1*:*:*') - ); - } else { - assert.match( - output.accumulator.replace(stackRegExp, '$1*:*:*'), - expected - ); - } - replServer.close(); -} - -const tests = [ - { - // Test .load for a file that throws. The whole file is evaluated as a - // single inspector eval, so every frame shares the same REPL source id. - // The loaded source is echoed with a `| ` prefix and the inspector surfaces - // the innermost (anonymous callback) frame and the top-level call frame in - // addition to the a/b/c/d frames. - command: `.load ${fixtures.path('repl-pretty-stack.js')}`, - expected: '| Uncaught Error: Whoops!\n at REPL*:*:*\n' + - ' at d (REPL*:*:*)\n at c (REPL*:*:*)\n' + - ' at b (REPL*:*:*)\n at a (REPL*:*:*)\n' + - ' at REPL*:*:*\n' - }, - { - // The async evaluator reports syntax errors without echoing the source and - // caret that the synchronous vm path used to print. - command: 'let x y;', - expected: /^Uncaught SyntaxError: Unexpected identifier.*\n/ - }, - { - command: 'throw new Error(\'Whoops!\')', - expected: 'Uncaught Error: Whoops!\n at REPL*:*:*\n' - }, - { - command: '(() => { const err = Error(\'Whoops!\'); ' + - 'err.foo = \'bar\'; throw err; })()', - expected: 'Uncaught Error: Whoops!\n at REPL*:*:*\n' + - " at REPL*:*:* {\n foo: 'bar'\n}\n", - }, - { - command: '(() => { const err = Error(\'Whoops!\'); ' + - 'err.foo = \'bar\'; throw err; })()', - expected: 'Uncaught Error: Whoops!\n at REPL*:*:*\n' + - ' at REPL*:*:* {\n foo: ' + - "'bar'\n}\n", - useColors: true - }, - { - command: 'foo = bar;', - expected: 'Uncaught ReferenceError: bar is not defined\n at REPL*:*:*\n' - }, - // Test anonymous IIFE. - { - command: '(function() { throw new Error(\'Whoops!\'); })()', - expected: 'Uncaught Error: Whoops!\n at REPL*:*:*\n at REPL*:*:*\n' - }, -]; - -for (const test of tests) { - await runTest(test); -} diff --git a/test/parallel/test-repl-preview-newlines.js b/test/parallel/test-repl-preview-newlines.js new file mode 100644 index 00000000000000..34a944beb538d7 --- /dev/null +++ b/test/parallel/test-repl-preview-newlines.js @@ -0,0 +1,19 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { startNewREPLServer } = require('../common/repl'); + +common.skipIfInspectorDisabled(); + +const { input, output } = startNewREPLServer({ useColors: true }); + +output.accumulator = ''; + +for (const char of ['\\n', '\\v', '\\r']) { + input.emit('data', `"${char}"()`); + // Make sure the output is on a single line + assert.strictEqual(output.accumulator, `"${char}"()\n\x1B[90mTypeError: "\x1B[39m\x1B[7G\x1B[1A`); + input.run(['']); + output.accumulator = ''; +} diff --git a/test/parallel/test-repl-preview-newlines.mjs b/test/parallel/test-repl-preview-newlines.mjs deleted file mode 100644 index 6620a6216ef227..00000000000000 --- a/test/parallel/test-repl-preview-newlines.mjs +++ /dev/null @@ -1,18 +0,0 @@ -import * as common from '../common/index.mjs'; -import assert from 'node:assert'; -import { startNewREPLServer } from '../common/repl.js'; - -common.skipIfInspectorDisabled(); - -const { output, run, replServer } = startNewREPLServer({ useColors: true }); - -for (const char of ['\\n', '\\v', '\\r']) { - output.accumulator = ''; - // Evaluation (and therefore the preview) is produced asynchronously. - await run(`"${char}"()`); - // Make sure the output is on a single line - assert.strictEqual(output.accumulator, `"${char}"()\n\x1B[90mTypeError: "\x1B[39m\x1B[7G\x1B[1A`); - await run(['']); -} - -replServer.close(); diff --git a/test/parallel/test-repl-preview-without-inspector.js b/test/parallel/test-repl-preview-without-inspector.js new file mode 100644 index 00000000000000..67090a928feba4 --- /dev/null +++ b/test/parallel/test-repl-preview-without-inspector.js @@ -0,0 +1,161 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { REPLServer } = require('repl'); +const { Stream } = require('stream'); + +if (process.features.inspector) + common.skip('test is for node compiled with --without-inspector only'); + +// Ignore terminal settings. This is so the test can be run intact if TERM=dumb. +process.env.TERM = ''; +const PROMPT = 'repl > '; + +class REPLStream extends Stream { + readable = true; + writable = true; + + constructor() { + super(); + this.lines = ['']; + } + run(data) { + for (const entry of data) { + this.emit('data', entry); + } + this.emit('data', '\n'); + } + write(chunk) { + const chunkLines = chunk.toString('utf8').split('\n'); + this.lines[this.lines.length - 1] += chunkLines[0]; + if (chunkLines.length > 1) { + this.lines.push(...chunkLines.slice(1)); + } + this.emit('line'); + return true; + } + wait() { + this.lines = ['']; + return new Promise((resolve, reject) => { + const onError = (err) => { + this.removeListener('line', onLine); + reject(err); + }; + const onLine = () => { + if (this.lines[this.lines.length - 1].includes(PROMPT)) { + this.removeListener('error', onError); + this.removeListener('line', onLine); + resolve(this.lines); + } + }; + this.once('error', onError); + this.on('line', onLine); + }); + } + pause() { } + resume() { } +} + +function runAndWait(cmds, repl) { + const promise = repl.inputStream.wait(); + for (const cmd of cmds) { + repl.inputStream.run(cmd); + } + return promise; +} + +const repl = new REPLServer({ + prompt: PROMPT, + stream: new REPLStream(), + ignoreUndefined: true, + useColors: true, + terminal: true, +}); + +repl.inputStream.run([ + 'function foo(x) { return x; }', + 'function koo() { console.log("abc"); }', + 'a = undefined;', + 'const r = 5;', +]); + +const testCases = [{ + input: 'foo', + preview: [ + 'foo\r', + '\x1B[36m[Function: foo]\x1B[39m', + ] +}, { + input: 'r', + preview: [ + 'r\r', + '\x1B[33m5\x1B[39m', + ] +}, { + input: 'koo', + preview: [ + 'koo\r', + '\x1B[36m[Function: koo]\x1B[39m', + ] +}, { + input: 'a', + preview: ['a\r'] // No "undefined" preview. +}, { + input: " { b: 1 }['b'] === 1", + preview: [ + " { b: 1 }['b'] === 1\r", + '\x1B[33mtrue\x1B[39m', + ] +}, { + input: "{ b: 1 }['b'] === 1;", + preview: [ + "{ b: 1 }['b'] === 1;\r", + '\x1B[33mfalse\x1B[39m', + ] +}, { + input: '{ a: true }', + preview: [ + '{ a: true }\r', + '{ a: \x1B[33mtrue\x1B[39m }', + ] +}, { + input: '{ a: true };', + preview: [ + '{ a: true };\r', + '\x1B[33mtrue\x1B[39m', + ] +}, { + input: ' \t { a: true};', + preview: [ + ' { a: true};\r', + '\x1B[33mtrue\x1B[39m', + ] +}, { + input: '1n + 2n', + preview: [ + '1n + 2n\r', + '\x1B[33m3n\x1B[39m', + ] +}, { + input: '{};1', + preview: [ + '{};1\r', + '\x1B[33m1\x1B[39m', + ], +}]; + +async function runTest() { + for (const { input, preview } of testCases) { + const toBeRun = input.split('\n'); + let lines = await runAndWait(toBeRun, repl); + // Remove error messages. That allows the code to run in different + // engines. + // eslint-disable-next-line no-control-regex + lines = lines.map((line) => line.replace(/Error: .+?\x1B/, '')); + assert.strictEqual(lines.pop(), '\x1B[1G\x1B[0Jrepl > \x1B[8G'); + assert.deepStrictEqual(lines, preview); + } +} + +runTest().then(common.mustCall()); diff --git a/test/parallel/test-repl-preview.mjs b/test/parallel/test-repl-preview.js similarity index 65% rename from test/parallel/test-repl-preview.mjs rename to test/parallel/test-repl-preview.js index 4ca989ec54eb71..9ab84b5c9f3ae4 100644 --- a/test/parallel/test-repl-preview.mjs +++ b/test/parallel/test-repl-preview.js @@ -1,9 +1,11 @@ -import * as common from '../common/index.mjs'; -import assert from 'node:assert'; -import events from 'node:events'; -import { REPLServer } from 'node:repl'; -import { Stream } from 'node:stream'; -import { inspect } from 'node:util'; +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const events = require('events'); +const { REPLServer } = require('repl'); +const { Stream } = require('stream'); +const { inspect } = require('util'); common.skipIfInspectorDisabled(); @@ -18,34 +20,14 @@ class REPLStream extends Stream { constructor() { super(); this.lines = ['']; - this.writeCount = 0; } - async run(data) { + run(data) { for (const entry of data) { this.emit('data', entry); - await this.settle(); } this.emit('data', '\n'); } - settle() { - return new Promise((resolve) => { - let last = this.writeCount; - let quiet = 0; - const check = () => { - if (this.writeCount !== last) { - last = this.writeCount; - quiet = 0; - } else if (++quiet >= 10) { - resolve(); - return; - } - setImmediate(check); - }; - setImmediate(check); - }); - } write(chunk) { - this.writeCount++; const chunkLines = chunk.toString('utf8').split('\n'); this.lines[this.lines.length - 1] += chunkLines[0]; if (chunkLines.length > 1) { @@ -66,18 +48,14 @@ class REPLStream extends Stream { resume() {} } -async function runAndWait(cmds, repl) { +function runAndWait(cmds, repl) { const promise = repl.inputStream.wait(); for (const cmd of cmds) { - await repl.inputStream.run(cmd); + repl.inputStream.run(cmd); } return promise; } -function stripStackFrames(lines) { - return lines.filter((line) => !/^\s*at /.test(line)); -} - async function tests(options) { const repl = new REPLServer({ prompt: PROMPT, @@ -87,11 +65,11 @@ async function tests(options) { ...options }); - await runAndWait([ - 'function foo(x) { return x; } ' + - 'function koo() { console.log("abc"); } ' + + repl.inputStream.run([ + 'function foo(x) { return x; }', + 'function koo() { console.log("abc"); }', 'a = undefined;', - ], repl); + ]); const testCases = [{ input: 'foo', @@ -172,16 +150,12 @@ async function tests(options) { '\x1B[33m3n\x1B[39m', ] }, { - // The inspector evaluator treats `{};1` as a block statement followed by a - // labelled/expression statement and rejects the trailing expression. The - // eager-eval preview still shows the result of the wrapped expression (`1`), - // but the committed evaluation reports a SyntaxError. input: '{};1', - noPreview: 'Uncaught SyntaxError: Unexpected token \';\'', + noPreview: '\x1B[33m1\x1B[39m', preview: [ '{};1', '\x1B[90m1\x1B[39m\x1B[12G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r', - 'Uncaught SyntaxError: Unexpected token \';\'', + '\x1B[33m1\x1B[39m', ] }, { input: 'aaaa', @@ -192,16 +166,22 @@ async function tests(options) { ] }, { input: '/0', - noPreview: 'Uncaught SyntaxError: Invalid regular expression: missing /', + noPreview: '/0', preview: [ '/0\r', + '/0', + '^', + '', 'Uncaught SyntaxError: Invalid regular expression: missing /', ] }, { input: '{})', - noPreview: "Uncaught SyntaxError: Unexpected token ')'", + noPreview: '{})', preview: [ '{})\r', + '{})', + ' ^', + '', "Uncaught SyntaxError: Unexpected token ')'", ], }, { @@ -223,9 +203,7 @@ async function tests(options) { input: '{[Symbol.for("{")]: 0 }', noPreview: '{ \x1B[32mSymbol({)\x1B[39m: \x1B[33m0\x1B[39m }', preview: [ - '{[Sym\x1B[90mbol\x1B[39m\x1B[13G\x1B[0Kb\x1B[90mol\x1B[39m\x1B[14G\x1B[0Ko' + - '\x1B[90ml\x1B[39m\x1B[15G\x1B[0Kl.f\x1B[90mor\x1B[39m\x1B[18G\x1B[0Ko' + - '\x1B[90mr\x1B[39m\x1B[19G\x1B[0Kr("{")]: 0 }\r', + '{[Symbol.for("{")]: 0 }\r', '{ \x1B[32mSymbol({)\x1B[39m: \x1B[33m0\x1B[39m }', ], }, { @@ -236,6 +214,12 @@ async function tests(options) { '\x1B[90m{}\x1B[39m\x1B[13G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r', '{}', ], + }, { + input: '{} //', + noPreview: 'repl > ', + preview: [ + '{} //\r', + ], }, { input: '{} //;', noPreview: 'repl > ', @@ -243,17 +227,12 @@ async function tests(options) { '{} //;\r', ], }, { - // The inspector evaluator wraps the input in parentheses to detect an - // expression, which turns `{throw 0}` into invalid syntax. The eager-eval - // preview still shows the thrown value, but the committed evaluation reports - // a SyntaxError. input: '{throw 0}', - noPreview: 'Uncaught SyntaxError: Unexpected number', + noPreview: 'Uncaught \x1B[33m0\x1B[39m', preview: [ - // Typing `{thr` offers the `throw` keyword as an inline completion preview. - '{thr\x1B[90mow\x1B[39m\x1B[12G\x1B[0Ko\x1B[90mw\x1B[39m\x1B[13G\x1B[0Kw 0}', + '{throw 0}', '\x1B[90m0\x1B[39m\x1B[17G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r', - 'Uncaught SyntaxError: Unexpected number', + 'Uncaught \x1B[33m0\x1B[39m', ], }]; @@ -266,9 +245,6 @@ async function tests(options) { const toBeRun = input.split('\n'); let lines = await runAndWait(toBeRun, repl); - // Drop unstable inspector stack frames (see `stripStackFrames`). - lines = stripStackFrames(lines); - if (hasPreview) { // Remove error messages. That allows the code to run in different // engines. @@ -277,25 +253,20 @@ async function tests(options) { assert.strictEqual(lines.pop(), '\x1B[1G\x1B[0Jrepl > \x1B[8G'); assert.deepStrictEqual(lines, preview); } else { - assert.ok(lines[0].includes(noPreview), lines.map(inspect).join('\n')); + assert.ok(lines[0].includes(noPreview), lines.map(inspect)); if (preview.length !== 1 || preview[0] !== `${input}\r`) { - assert.strictEqual(lines.length, 2); + if (preview[preview.length - 1].includes('Uncaught SyntaxError')) { + assert.strictEqual(lines.length, 5); + } else { + assert.strictEqual(lines.length, 2); + } } } } - - repl.close(); } -// NOTE: The input `{} //` (a block followed by a line comment, with no trailing -// semicolon) is intentionally NOT exercised here. Under the inspector-based -// evaluator it is treated as an incomplete statement: the REPL emits the -// continuation prompt (`| `) and waits for more input forever instead of -// completing. This is a regression from the previous vm-based REPL, where the -// same input evaluated to `undefined` and completed. See the test report. - -await tests({ terminal: false }); // No preview -await tests({ terminal: true }); // Preview -await tests({ terminal: false, preview: false }); // No preview -await tests({ terminal: false, preview: true }); // No preview -await tests({ terminal: true, preview: true }); // Preview +tests({ terminal: false }); // No preview +tests({ terminal: true }); // Preview +tests({ terminal: false, preview: false }); // No preview +tests({ terminal: false, preview: true }); // No preview +tests({ terminal: true, preview: true }); // Preview diff --git a/test/parallel/test-repl-programmatic-history-setup-history.js b/test/parallel/test-repl-programmatic-history-setup-history.js index a0c848f72eef86..544f3994ef331b 100644 --- a/test/parallel/test-repl-programmatic-history-setup-history.js +++ b/test/parallel/test-repl-programmatic-history-setup-history.js @@ -8,8 +8,6 @@ const assert = require('assert'); const fs = require('fs'); const os = require('os'); -common.skipIfInspectorDisabled(); - if (process.env.TERM === 'dumb') { common.skip('skipping - dumb terminal'); } diff --git a/test/parallel/test-repl-programmatic-history.js b/test/parallel/test-repl-programmatic-history.js index 6c4977726c2a94..c2bb6c88e52ed9 100644 --- a/test/parallel/test-repl-programmatic-history.js +++ b/test/parallel/test-repl-programmatic-history.js @@ -8,8 +8,6 @@ const assert = require('assert'); const fs = require('fs'); const os = require('os'); -common.skipIfInspectorDisabled(); - if (process.env.TERM === 'dumb') { common.skip('skipping - dumb terminal'); } diff --git a/test/parallel/test-repl-promise-gc.mjs b/test/parallel/test-repl-promise-gc.mjs deleted file mode 100644 index 94fa28bbddb4f6..00000000000000 --- a/test/parallel/test-repl-promise-gc.mjs +++ /dev/null @@ -1,21 +0,0 @@ -// Flags: --expose-gc - -import '../common/index.mjs'; -import assert from 'node:assert'; -import { startNewREPLServer } from '../common/repl.js'; - -const { output, replServer, run } = startNewREPLServer({ - terminal: false, - useColors: false, -}); - -// The promise is not retained by user code. Force a collection while the REPL -// is awaiting it to verify that the inspector keeps it alive until settlement. -await run(`new Promise((resolve) => setImmediate(() => { - globalThis.gc(); - resolve(42); -}))\n`); - -assert.strictEqual(output.accumulator, '| | | 42\n'); - -replServer.close(); diff --git a/test/parallel/test-repl-recoverable.js b/test/parallel/test-repl-recoverable.js new file mode 100644 index 00000000000000..74dcd5dfbf585d --- /dev/null +++ b/test/parallel/test-repl-recoverable.js @@ -0,0 +1,41 @@ +'use strict'; + +require('../common'); +const ArrayStream = require('../common/arraystream'); +const assert = require('assert'); +const repl = require('repl'); + +let evalCount = 0; +let recovered = false; +let rendered = false; + +function customEval(code, context, file, cb) { + evalCount++; + + return cb(evalCount === 1 ? new repl.Recoverable() : null, true); +} + +const putIn = new ArrayStream(); + +putIn.write = function(msg) { + if (msg === '| ') { + recovered = true; + } + + if (msg === 'true\n') { + rendered = true; + } +}; + +repl.start('', putIn, customEval); + +// https://github.com/nodejs/node/issues/2939 +// Expose recoverable errors to the consumer. +putIn.emit('data', '1\n'); +putIn.emit('data', '2\n'); + +process.on('exit', function() { + assert(recovered, 'REPL never recovered'); + assert(rendered, 'REPL never rendered the result'); + assert.strictEqual(evalCount, 2); +}); diff --git a/test/parallel/test-repl-recoverable.mjs b/test/parallel/test-repl-recoverable.mjs deleted file mode 100644 index 9441935353ef4a..00000000000000 --- a/test/parallel/test-repl-recoverable.mjs +++ /dev/null @@ -1,23 +0,0 @@ -import '../common/index.mjs'; -import assert from 'node:assert'; -import repl from 'node:repl'; -import { startNewREPLServer } from '../common/repl.js'; - -let evalCount = 0; - -function customEval(code, context, file, cb) { - evalCount++; - - return cb(evalCount === 1 ? new repl.Recoverable() : null, true); -} - -const { output, run } = startNewREPLServer({ eval: customEval }); - -// https://github.com/nodejs/node/issues/2939 -// Expose recoverable errors to the consumer. -await run('1\n'); -await run('2\n'); - -assert(output.accumulator.includes('| '), 'REPL never recovered'); -assert(output.accumulator.includes('true\n'), 'REPL never rendered the result'); -assert.strictEqual(evalCount, 2); diff --git a/test/parallel/test-repl-require-after-write.js b/test/parallel/test-repl-require-after-write.js index d25b7cf07bbb23..a374c57628f12a 100644 --- a/test/parallel/test-repl-require-after-write.js +++ b/test/parallel/test-repl-require-after-write.js @@ -5,8 +5,6 @@ const tmpdir = require('../common/tmpdir'); const assert = require('assert'); const spawn = require('child_process').spawn; -common.skipIfInspectorDisabled(); - tmpdir.refresh(); const requirePath = JSON.stringify(tmpdir.resolve('non-existent.json')); diff --git a/test/parallel/test-repl-require-cache.js b/test/parallel/test-repl-require-cache.js index 79c9cca414c2c8..b8fe3a75375976 100644 --- a/test/parallel/test-repl-require-cache.js +++ b/test/parallel/test-repl-require-cache.js @@ -21,14 +21,14 @@ 'use strict'; require('../common'); -const { startNewREPLServer } = require('../common/repl'); const assert = require('assert'); +const repl = require('repl'); // https://github.com/joyent/node/issues/3226 require.cache.something = 1; assert.strictEqual(require.cache.something, 1); -startNewREPLServer({ useGlobal: false }).replServer.close(); +repl.start({ useGlobal: false }).close(); assert.strictEqual(require.cache.something, 1); diff --git a/test/parallel/test-repl-require-context.js b/test/parallel/test-repl-require-context.js index ad637a00d4921e..070ec727537f59 100644 --- a/test/parallel/test-repl-require-context.js +++ b/test/parallel/test-repl-require-context.js @@ -1,6 +1,5 @@ 'use strict'; const common = require('../common'); -common.skipIfInspectorDisabled(); const assert = require('assert'); const cp = require('child_process'); const child = cp.spawn(process.execPath, ['--interactive']); diff --git a/test/parallel/test-repl-require-self-referential.js b/test/parallel/test-repl-require-self-referential.js index fd9f511c4e722c..e22e2cfe883d13 100644 --- a/test/parallel/test-repl-require-self-referential.js +++ b/test/parallel/test-repl-require-self-referential.js @@ -6,8 +6,6 @@ const assert = require('assert'); const { spawn } = require('child_process'); const { isMainThread } = require('worker_threads'); -common.skipIfInspectorDisabled(); - if (!isMainThread) { common.skip('process.chdir is not available in Workers'); } diff --git a/test/parallel/test-repl-require.js b/test/parallel/test-repl-require.js index fe5c1056d7156a..e740acef08b068 100644 --- a/test/parallel/test-repl-require.js +++ b/test/parallel/test-repl-require.js @@ -6,8 +6,6 @@ const assert = require('assert'); const net = require('net'); const { isMainThread } = require('worker_threads'); -common.skipIfInspectorDisabled(); - if (!isMainThread) { common.skip('process.chdir is not available in Workers'); } diff --git a/test/parallel/test-repl-reverse-search.js b/test/parallel/test-repl-reverse-search.js index 5a6412bf6534e6..cbe848afee082a 100644 --- a/test/parallel/test-repl-reverse-search.js +++ b/test/parallel/test-repl-reverse-search.js @@ -13,8 +13,6 @@ if (process.env.TERM === 'dumb') { common.skip('skipping - dumb terminal'); } -common.skipIfInspectorDisabled(); - common.allowGlobals('aaaa'); const tmpdir = require('../common/tmpdir'); @@ -192,6 +190,7 @@ const tests = [ { env: { NODE_REPL_HISTORY: defaultHistoryPath }, showEscapeCodes: true, + skip: !process.features.inspector, checkTotal: true, useColors: false, test: [ @@ -288,7 +287,13 @@ function runTest() { const opts = tests.shift(); if (!opts) return; // All done - const { expected } = opts; + const { expected, skip } = opts; + + // Test unsupported on platform. + if (skip) { + setImmediate(runTestWrap, true); + return; + } const lastChunks = []; let i = 0; diff --git a/test/parallel/test-repl-save-load-editor-mode.js b/test/parallel/test-repl-save-load-editor-mode.js index 9217486372d3e4..83a57cdaa55a77 100644 --- a/test/parallel/test-repl-save-load-editor-mode.js +++ b/test/parallel/test-repl-save-load-editor-mode.js @@ -1,6 +1,6 @@ 'use strict'; -const common = require('../common'); +require('../common'); const assert = require('node:assert'); const fs = require('node:fs'); const path = require('node:path'); @@ -11,7 +11,9 @@ tmpdir.refresh(); // Test for saving a REPL session in editor mode -const { replServer, input, run, waitForIdle } = startNewREPLServer(); +const { replServer, input } = startNewREPLServer(); + +input.run(['.editor']); const commands = [ 'function testSave() {', @@ -19,23 +21,15 @@ const commands = [ '}', ]; -const filePath = path.resolve(tmpdir.path, 'test.save.js'); - -async function main() { - await run(['.editor']); +input.run(commands); - // Editor-mode lines are buffered synchronously; the evaluation is triggered - // only when editor mode ends (Ctrl+D), and that evaluation is asynchronous. - input.run(commands); - replServer.write('', { ctrl: true, name: 'd' }); - await waitForIdle(); +replServer.write('', { ctrl: true, name: 'd' }); - await run([`.save ${filePath}`]); +const filePath = path.resolve(tmpdir.path, 'test.save.js'); - assert.strictEqual(fs.readFileSync(filePath, 'utf8'), - `${commands.join('\n')}\n`); +input.run([`.save ${filePath}`]); - replServer.close(); -} +assert.strictEqual(fs.readFileSync(filePath, 'utf8'), + `${commands.join('\n')}\n`); -main().then(common.mustCall()); +replServer.close(); diff --git a/test/parallel/test-repl-save-load.js b/test/parallel/test-repl-save-load.js index 8f78e1167c1f6e..d8401c2d427893 100644 --- a/test/parallel/test-repl-save-load.js +++ b/test/parallel/test-repl-save-load.js @@ -32,7 +32,7 @@ tmpdir.refresh(); // Tests that a REPL session data can be saved to and loaded from a file -const { replServer, run } = startNewREPLServer({ terminal: false }); +const { replServer, input } = startNewREPLServer({ terminal: false }); const filePath = path.resolve(tmpdir.path, 'test.save.js'); @@ -42,43 +42,37 @@ const testFileContents = [ '})()', ]; -const innerOCompletions = [['inner.one'], 'inner.o']; - -// Evaluation is asynchronous (the REPL now drives the inspector), so completion -// must be awaited as well. -function complete(text) { - return new Promise((resolve) => { - replServer.completer(text, common.mustSucceed(resolve)); - }); -} +input.run(testFileContents); +input.run([`.save ${filePath}`]); -async function main() { - await run(testFileContents); - await run([`.save ${filePath}`]); +assert.strictEqual(fs.readFileSync(filePath, 'utf8'), + testFileContents.join('\n')); - assert.strictEqual(fs.readFileSync(filePath, 'utf8'), - testFileContents.join('\n')); - - // Double check that the data is still present in the repl after the save - assert.deepStrictEqual(await complete('inner.o'), innerOCompletions); +const innerOCompletions = [['inner.one'], 'inner.o']; - // Clear the repl context - await run(['.clear']); +// Double check that the data is still present in the repl after the save +replServer.completer('inner.o', common.mustSucceed((data) => { + assert.deepStrictEqual(data, innerOCompletions); +})); - // Double check that the data is no longer present in the repl - assert.deepStrictEqual(await complete('inner.o'), [[], 'inner.o']); +// Clear the repl context +input.run(['.clear']); - // Load the file back in. - await run([`.load ${filePath}`]); +// Double check that the data is no longer present in the repl +replServer.completer('inner.o', common.mustSucceed((data) => { + assert.deepStrictEqual(data, [[], 'inner.o']); +})); - // Make sure loading doesn't insert extra indentation - // https://github.com/nodejs/node/issues/47673 - assert.strictEqual(replServer.line, ''); +// Load the file back in. +input.run([`.load ${filePath}`]); - // Make sure that the loaded data is present - assert.deepStrictEqual(await complete('inner.o'), innerOCompletions); +// Make sure loading doesn't insert extra indentation +// https://github.com/nodejs/node/issues/47673 +assert.strictEqual(replServer.line, ''); - replServer.close(); -} +// Make sure that the loaded data is present +replServer.complete('inner.o', common.mustSucceed((data) => { + assert.deepStrictEqual(data, innerOCompletions); +})); -main().then(common.mustCall()); +replServer.close(); diff --git a/test/parallel/test-repl-setprompt.js b/test/parallel/test-repl-setprompt.js index 1c83edd201d3c5..9901f8f974f646 100644 --- a/test/parallel/test-repl-setprompt.js +++ b/test/parallel/test-repl-setprompt.js @@ -25,8 +25,6 @@ const assert = require('assert'); const spawn = require('child_process').spawn; const os = require('os'); -common.skipIfInspectorDisabled(); - const args = [ '-e', 'var e = new (require("repl")).REPLServer("foo.. "); e.context.e = e;', diff --git a/test/parallel/test-repl-sigint-nested-eval.js b/test/parallel/test-repl-sigint-nested-eval.js new file mode 100644 index 00000000000000..555802b725d145 --- /dev/null +++ b/test/parallel/test-repl-sigint-nested-eval.js @@ -0,0 +1,53 @@ +'use strict'; +const common = require('../common'); +if (common.isWindows) { + // No way to send CTRL_C_EVENT to processes from JS right now. + common.skip('platform not supported'); +} + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('No signal handling available in Workers'); +} + +const assert = require('assert'); +const spawn = require('child_process').spawn; + +const child = spawn(process.execPath, [ '-i' ], { + stdio: [null, null, 2, 'ipc'] +}); + +let stdout = ''; +child.stdout.setEncoding('utf8'); +child.stdout.on('data', function(c) { + stdout += c; +}); + +child.stdout.once('data', common.mustCall(() => { + child.on('message', common.mustCall((msg) => { + assert.strictEqual(msg, 'repl is busy'); + process.kill(child.pid, 'SIGINT'); + child.stdout.once('data', common.mustCall(() => { + // Make sure REPL still works. + child.stdin.end('"foobar"\n'); + })); + })); + + child.stdin.write( + 'vm.runInThisContext("process.send(\'repl is busy\'); while(true){}", ' + + '{ breakOnSigint: true });\n' + ); +})); + +child.on('close', common.mustCall((code) => { + const expected = 'Script execution was interrupted by `SIGINT`'; + assert.ok( + stdout.includes(expected), + `Expected stdout to contain "${expected}", got ${stdout}` + ); + assert.ok( + stdout.includes('foobar'), + `Expected stdout to contain "foobar", got ${stdout}` + ); +})); diff --git a/test/parallel/test-repl-sigint.js b/test/parallel/test-repl-sigint.js index 848bca552170cf..33495f80a77a2a 100644 --- a/test/parallel/test-repl-sigint.js +++ b/test/parallel/test-repl-sigint.js @@ -5,8 +5,6 @@ if (common.isWindows) { common.skip('platform not supported'); } -common.skipIfInspectorDisabled(); - const { isMainThread } = require('worker_threads'); if (!isMainThread) { diff --git a/test/parallel/test-repl-stdin-push-null.js b/test/parallel/test-repl-stdin-push-null.js index c626f4ac25e580..53ba9ff7c33166 100644 --- a/test/parallel/test-repl-stdin-push-null.js +++ b/test/parallel/test-repl-stdin-push-null.js @@ -5,7 +5,5 @@ if (!process.stdin.isTTY) { common.skip('does not apply on non-TTY stdin'); } -common.skipIfInspectorDisabled(); - process.stdin.destroy(); process.stdin.setRawMode(true); diff --git a/test/parallel/test-repl-syntax-error-handling.js b/test/parallel/test-repl-syntax-error-handling.js index bca548b774af60..2b57af8c80117f 100644 --- a/test/parallel/test-repl-syntax-error-handling.js +++ b/test/parallel/test-repl-syntax-error-handling.js @@ -23,8 +23,6 @@ const common = require('../common'); const assert = require('assert'); -common.skipIfInspectorDisabled(); - switch (process.argv[2]) { case 'child': return child(); diff --git a/test/parallel/test-repl-tab-complete-buffer.js b/test/parallel/test-repl-tab-complete-buffer.js new file mode 100644 index 00000000000000..25a5dc6fe6c8af --- /dev/null +++ b/test/parallel/test-repl-tab-complete-buffer.js @@ -0,0 +1,62 @@ +'use strict'; + +const common = require('../common'); +const { hijackStderr, restoreStderr } = require('../common/hijackstdio'); +const assert = require('assert'); +const { startNewREPLServer } = require('../common/repl'); + +const { replServer, input } = startNewREPLServer(); + +for (const type of [ + Array, + Buffer, + + Uint8Array, + Uint16Array, + Uint32Array, + + Uint8ClampedArray, + Int8Array, + Int16Array, + Int32Array, + Float32Array, + Float64Array, +]) { + input.run(['.clear']); + + if (type === Array) { + input.run([ + 'var ele = [];', + 'for (let i = 0; i < 1e6 + 1; i++) ele[i] = 0;', + 'ele.biu = 1;', + ]); + } else if (type === Buffer) { + input.run(['var ele = Buffer.alloc(1e6 + 1); ele.biu = 1;']); + } else { + input.run([`var ele = new ${type.name}(1e6 + 1); ele.biu = 1;`]); + } + + hijackStderr(common.mustNotCall()); + replServer.complete( + 'ele.', + common.mustCall((err, data) => { + restoreStderr(); + assert.ifError(err); + + const ele = + type === Array ? [] : type === Buffer ? Buffer.alloc(0) : new type(0); + + assert.strictEqual(data[0].includes('ele.biu'), true); + + for (const key of data[0]) { + if (!key || key === 'ele.biu') return; + assert.notStrictEqual(ele[key.slice(4)], undefined); + } + }) + ); +} + +// check Buffer.prototype.length not crashing. +// Refs: https://github.com/nodejs/node/pull/11961 +input.run(['.clear']); +replServer.complete('Buffer.prototype.', common.mustCall()); diff --git a/test/parallel/test-repl-tab-complete-buffer.mjs b/test/parallel/test-repl-tab-complete-buffer.mjs deleted file mode 100644 index d043cb0fa3c3e9..00000000000000 --- a/test/parallel/test-repl-tab-complete-buffer.mjs +++ /dev/null @@ -1,57 +0,0 @@ -import * as common from '../common/index.mjs'; -import hijackstdio from '../common/hijackstdio.js'; -import assert from 'node:assert'; -import { startNewREPLServer, complete } from '../common/repl.js'; - -const { replServer, run } = startNewREPLServer(); - -for (const type of [ - Array, - Buffer, - - Uint8Array, - Uint16Array, - Uint32Array, - - Uint8ClampedArray, - Int8Array, - Int16Array, - Int32Array, - Float32Array, - Float64Array, -]) { - await run(['.clear']); - - if (type === Array) { - await run([ - 'var ele = [];', - 'for (let i = 0; i < 1e6 + 1; i++) ele[i] = 0;', - 'ele.biu = 1;', - ]); - } else if (type === Buffer) { - await run(['var ele = Buffer.alloc(1e6 + 1); ele.biu = 1;']); - } else { - await run([`var ele = new ${type.name}(1e6 + 1); ele.biu = 1;`]); - } - - hijackstdio.hijackStderr(common.mustNotCall()); - const data = await complete(replServer, 'ele.'); - hijackstdio.restoreStderr(); - - const ele = - type === Array ? [] : type === Buffer ? Buffer.alloc(0) : new type(0); - - assert.strictEqual(data[0].includes('ele.biu'), true); - - for (const key of data[0]) { - if (!key || key === 'ele.biu') break; - assert.notStrictEqual(ele[key.slice(4)], undefined); - } -} - -// check Buffer.prototype.length not crashing. -// Refs: https://github.com/nodejs/node/pull/11961 -await run(['.clear']); -await complete(replServer, 'Buffer.prototype.'); - -replServer.close(); diff --git a/test/parallel/test-repl-tab-complete-computed-props.js b/test/parallel/test-repl-tab-complete-computed-props.js index 3466e194ad7504..418dc5059e9132 100644 --- a/test/parallel/test-repl-tab-complete-computed-props.js +++ b/test/parallel/test-repl-tab-complete-computed-props.js @@ -1,36 +1,28 @@ 'use strict'; -require('../common'); +const common = require('../common'); const { startNewREPLServer } = require('../common/repl'); const { describe, it, before, after } = require('node:test'); const assert = require('assert'); -const testCompletion = (replServer, { input, expectedCompletions }) => - new Promise((resolve, reject) => { - // No need to wrap in `mustCall`, this test won't - // exit until this function is falled - replServer.complete(input, (error, data) => { - try { - // eslint-disable-next-line node-core/must-call-assert - assert.ifError(error); - // eslint-disable-next-line node-core/must-call-assert - assert.deepStrictEqual(data, [expectedCompletions, input]); - } catch (err) { - reject(err); - } - resolve(); - }); - }); +function testCompletion(replServer, { input, expectedCompletions }) { + replServer.complete( + input, + common.mustCall((_error, data) => { + assert.deepStrictEqual(data, [expectedCompletions, input]); + }), + ); +}; describe('REPL tab object completion on computed properties', () => { describe('simple string cases', () => { let replServer; - before(async () => { - const { replServer: server, run } = startNewREPLServer(); + before(() => { + const { replServer: server, input } = startNewREPLServer(); replServer = server; - await run([ + input.run([ ` const obj = { one: 1, @@ -47,56 +39,51 @@ describe('REPL tab object completion on computed properties', () => { replServer.close(); }); - it('works with double quoted strings', () => - testCompletion(replServer, { - input: 'obj["one"].toFi', - expectedCompletions: ['obj["one"].toFixed'], - })); + it('works with double quoted strings', () => testCompletion(replServer, { + input: 'obj["one"].toFi', + expectedCompletions: ['obj["one"].toFixed'], + })); - it('works with single quoted strings', () => - testCompletion(replServer, { - input: "obj['one'].toFi", - expectedCompletions: ["obj['one'].toFixed"], - })); + it('works with single quoted strings', () => testCompletion(replServer, { + input: "obj['one'].toFi", + expectedCompletions: ["obj['one'].toFixed"], + })); - it('works with template strings', () => - testCompletion(replServer, { - input: 'obj[`one`].toFi', - expectedCompletions: ['obj[`one`].toFixed'], - })); + it('works with template strings', () => testCompletion(replServer, { + input: 'obj[`one`].toFi', + expectedCompletions: ['obj[`one`].toFixed'], + })); - it('works with nested objects', async () => { - await testCompletion(replServer, { + it('works with nested objects', () => { + testCompletion(replServer, { input: 'obj["innerObj"].tw', expectedCompletions: ['obj["innerObj"].two'], }); - await testCompletion(replServer, { + testCompletion(replServer, { input: 'obj["innerObj"].two.tofi', expectedCompletions: ['obj["innerObj"].two.toFixed'], }); }); - it('works with nested objects combining different type of strings', () => - testCompletion(replServer, { - input: 'obj["innerObj"][`two`].tofi', - expectedCompletions: ['obj["innerObj"][`two`].toFixed'], - })); + it('works with nested objects combining different type of strings', () => testCompletion(replServer, { + input: 'obj["innerObj"][`two`].tofi', + expectedCompletions: ['obj["innerObj"][`two`].toFixed'], + })); - it('works with strings with spaces', () => - testCompletion(replServer, { - input: 'obj["inner object"].th', - expectedCompletions: ['obj["inner object"].three'], - })); + it('works with strings with spaces', () => testCompletion(replServer, { + input: 'obj["inner object"].th', + expectedCompletions: ['obj["inner object"].three'], + })); }); describe('variables as indexes', () => { let replServer; - before(async () => { - const { replServer: server, run } = startNewREPLServer(); + before(() => { + const { replServer: server, input } = startNewREPLServer(); replServer = server; - await run([ + input.run([ ` const oneStr = 'One'; const helloWorldStr = 'Hello' + ' ' + 'World'; @@ -118,43 +105,38 @@ describe('REPL tab object completion on computed properties', () => { replServer.close(); }); - it('works with a simple variable', () => - testCompletion(replServer, { - input: 'obj[oneStr].toFi', - expectedCompletions: ['obj[oneStr].toFixed'], - })); + it('works with a simple variable', () => testCompletion(replServer, { + input: 'obj[oneStr].toFi', + expectedCompletions: ['obj[oneStr].toFixed'], + })); - it('works with a computed variable', () => - testCompletion(replServer, { - input: 'obj[helloWorldStr].tolocaleup', - expectedCompletions: ['obj[helloWorldStr].toLocaleUpperCase'], - })); + it('works with a computed variable', () => testCompletion(replServer, { + input: 'obj[helloWorldStr].tolocaleup', + expectedCompletions: ['obj[helloWorldStr].toLocaleUpperCase'], + })); - it('works with a simple inlined computed property', () => - testCompletion(replServer, { - input: 'obj["Hello " + "World"].tolocaleup', - expectedCompletions: ['obj["Hello " + "World"].toLocaleUpperCase'], - })); + it('works with a simple inlined computed property', () => testCompletion(replServer, { + input: 'obj["Hello " + "World"].tolocaleup', + expectedCompletions: ['obj["Hello " + "World"].toLocaleUpperCase'], + })); - it('works with a ternary inlined computed property', () => - testCompletion(replServer, { - input: - 'obj[(1 + 2 > 5) ? oneStr : "Hello " + "World"].toLocaleUpperCase', - expectedCompletions: [ - 'obj[(1 + 2 > 5) ? oneStr : "Hello " + "World"].toLocaleUpperCase', - ], - })); + it('works with a ternary inlined computed property', () => testCompletion(replServer, { + input: 'obj[(1 + 2 > 5) ? oneStr : "Hello " + "World"].toLocaleUpperCase', + expectedCompletions: ['obj[(1 + 2 > 5) ? oneStr : "Hello " + "World"].toLocaleUpperCase'], + })); it('works with an inlined computed property with a nested property lookup', () => testCompletion(replServer, { input: 'obj[lookupObj.stringLookup].tolocaleupp', expectedCompletions: ['obj[lookupObj.stringLookup].toLocaleUpperCase'], - })); + }) + ); it('works with an inlined computed property with a nested inlined computer property lookup', () => testCompletion(replServer, { input: 'obj[lookupObj["number" + " lookup"]].toFi', expectedCompletions: ['obj[lookupObj["number" + " lookup"]].toFixed'], - })); + }) + ); }); }); diff --git a/test/parallel/test-repl-tab-complete-nested-repls.js b/test/parallel/test-repl-tab-complete-nested-repls.js new file mode 100644 index 00000000000000..3cac02f20562bc --- /dev/null +++ b/test/parallel/test-repl-tab-complete-nested-repls.js @@ -0,0 +1,23 @@ +// Tab completion sometimes uses a separate REPL instance under the hood. +// That REPL instance has its own domain. Make sure domain errors trickle back +// up to the main REPL. +// +// Ref: https://github.com/nodejs/node/issues/21586 + +'use strict'; + +require('../common'); +const fixtures = require('../common/fixtures'); + +const assert = require('assert'); +const { spawnSync } = require('child_process'); + +const testFile = fixtures.path('repl-tab-completion-nested-repls.js'); +const result = spawnSync(process.execPath, [testFile]); + +// The spawned process will fail. In Node.js 10.11.0, it will fail silently. The +// test here is to make sure that the error information bubbles up to the +// calling process. +assert.ok(result.status, 'testFile swallowed its error'); +const err = result.stderr.toString(); +assert.ok(err.includes('fhqwhgads'), err); diff --git a/test/parallel/test-repl-tab-complete-nosideeffects.js b/test/parallel/test-repl-tab-complete-nosideeffects.js index 34ca6d8cdf9799..54562e2f1d2954 100644 --- a/test/parallel/test-repl-tab-complete-nosideeffects.js +++ b/test/parallel/test-repl-tab-complete-nosideeffects.js @@ -1,9 +1,15 @@ 'use strict'; -require('../common'); +const common = require('../common'); const { describe, it } = require('node:test'); const assert = require('assert'); -const { startNewREPLServer, complete } = require('../common/repl'); +const { startNewREPLServer } = require('../common/repl'); + +function getNoResultsFunction() { + return common.mustSucceed((data) => { + assert.deepStrictEqual(data[0], []); + }); +} describe('REPL tab completion without side effects', () => { const setup = [ @@ -21,11 +27,10 @@ describe('REPL tab completion without side effects', () => { 'arr[incCounter()].b', ]) { it(`does not evaluate with side effects (${code})`, async () => { - const { replServer, run } = startNewREPLServer(); - await run(setup); + const { replServer, input } = startNewREPLServer(); + input.run(setup); - const data = await complete(replServer, code); - assert.deepStrictEqual(data[0], []); + replServer.complete(code, getNoResultsFunction()); assert.strictEqual(replServer.context.counter, 0); replServer.close(); diff --git a/test/parallel/test-repl-tab-complete-on-editor-mode.js b/test/parallel/test-repl-tab-complete-on-editor-mode.js index e6d12f6a1da900..6e2ef8b5670db4 100644 --- a/test/parallel/test-repl-tab-complete-on-editor-mode.js +++ b/test/parallel/test-repl-tab-complete-on-editor-mode.js @@ -29,19 +29,7 @@ const { startNewREPLServer } = require('../common/repl'); replServer.write('.editor\n'); replServer.write('a'); - - // Tab completion is asynchronous (it is driven by the inspector), so the - // REPL must only be closed once the completion has settled. Closing it while - // a completion is still in flight would make the completion callback resume a - // closed readline interface and throw `ERR_USE_AFTER_CLOSE`. - const originalCompleter = replServer.completer; - replServer.completer = common.mustCall((text, cb) => { - originalCompleter.call(replServer, text, common.mustCall((...args) => { - const result = cb(...args); - replServer.close(); - return result; - })); - }); - replServer.write(null, { name: 'tab' }); // Should not throw + + replServer.close(); } diff --git a/test/parallel/test-repl-tab-complete.js b/test/parallel/test-repl-tab-complete.js index a21085c6c0d8a7..d4df6c31787968 100644 --- a/test/parallel/test-repl-tab-complete.js +++ b/test/parallel/test-repl-tab-complete.js @@ -22,121 +22,168 @@ 'use strict'; const common = require('../common'); -const { startNewREPLServer, complete } = require('../common/repl'); +const { startNewREPLServer } = require('../common/repl'); const { describe, it } = require('node:test'); const assert = require('assert'); -async function expectNoResults(replServer, query) { - const data = await complete(replServer, query); - assert.deepStrictEqual(data[0], []); +function getNoResultsFunction() { + return common.mustSucceed((data) => { + assert.deepStrictEqual(data[0], []); + }); } describe('REPL tab completion (core functionality)', () => { - it('does not break with variable declarations without an initialization', async () => { + it('does not break with variable declarations without an initialization', () => { const { replServer } = startNewREPLServer(); - await expectNoResults(replServer, 'let a'); + replServer.complete('let a', getNoResultsFunction()); replServer.close(); }); - it('does not break in an object literal', async () => { - const { replServer, run } = startNewREPLServer(); + it('does not break in an object literal', () => { + const { replServer, input } = startNewREPLServer(); - await run(['var inner = {', 'one:1']); + input.run(['var inner = {', 'one:1']); - await expectNoResults(replServer, 'inner.o'); + replServer.complete('inner.o', getNoResultsFunction()); - assert.deepStrictEqual( - await complete(replServer, 'console.lo'), - [['console.log'], 'console.lo']); + replServer.complete( + 'console.lo', + common.mustCall(function(_error, data) { + assert.deepStrictEqual(data, [['console.log'], 'console.lo']); + }) + ); replServer.close(); }); - it('works with optional chaining', async () => { + it('works with optional chaining', () => { const { replServer } = startNewREPLServer(); - assert.deepStrictEqual( - await complete(replServer, 'console?.lo'), - [['console?.log'], 'console?.lo']); - - assert.deepStrictEqual( - await complete(replServer, 'console?.zzz'), - [[], 'console?.zzz']); - - const data = await complete(replServer, 'console?.'); - assert(data[0].includes('console?.log')); - assert.strictEqual(data[1], 'console?.'); + replServer.complete( + 'console?.lo', + common.mustCall((_error, data) => { + assert.deepStrictEqual(data, [['console?.log'], 'console?.lo']); + }) + ); + + replServer.complete( + 'console?.zzz', + common.mustCall((_error, data) => { + assert.deepStrictEqual(data, [[], 'console?.zzz']); + }) + ); + + replServer.complete( + 'console?.', + common.mustCall((_error, data) => { + assert(data[0].includes('console?.log')); + assert.strictEqual(data[1], 'console?.'); + }) + ); replServer.close(); }); - it('returns object completions', async () => { - const { replServer, run } = startNewREPLServer(); + it('returns object completions', () => { + const { replServer, input } = startNewREPLServer(); - await run(['var inner = {', 'one:1']); + input.run(['var inner = {', 'one:1']); - await run(['};']); + input.run(['};']); - assert.deepStrictEqual( - await complete(replServer, 'inner.o'), - [['inner.one'], 'inner.o']); + replServer.complete( + 'inner.o', + common.mustCall(function(_error, data) { + assert.deepStrictEqual(data, [['inner.one'], 'inner.o']); + }) + ); replServer.close(); }); - it('does not break in a ternary operator with ()', async () => { - const { replServer, run } = startNewREPLServer(); + it('does not break in a ternary operator with ()', () => { + const { replServer, input } = startNewREPLServer(); - await run(['var inner = ( true ', '?', '{one: 1} : ']); + input.run(['var inner = ( true ', '?', '{one: 1} : ']); - await expectNoResults(replServer, 'inner.o'); + replServer.complete('inner.o', getNoResultsFunction()); replServer.close(); }); - it('works on literals', async () => { + it('works on literals', () => { const { replServer } = startNewREPLServer(); - assert.strictEqual((await complete(replServer, '``.a'))[0].includes('``.at'), true); - assert.strictEqual((await complete(replServer, "''.a"))[0].includes("''.at"), true); - assert.strictEqual((await complete(replServer, '"".a'))[0].includes('"".at'), true); - assert.strictEqual((await complete(replServer, '("").a'))[0].includes('("").at'), true); - assert.strictEqual((await complete(replServer, '[].a'))[0].includes('[].at'), true); - assert.deepStrictEqual((await complete(replServer, '{}.a'))[0], []); + replServer.complete( + '``.a', + common.mustCall((err, data) => { + assert.strictEqual(data[0].includes('``.at'), true); + }) + ); + replServer.complete( + "''.a", + common.mustCall((err, data) => { + assert.strictEqual(data[0].includes("''.at"), true); + }) + ); + replServer.complete( + '"".a', + common.mustCall((err, data) => { + assert.strictEqual(data[0].includes('"".at'), true); + }) + ); + replServer.complete( + '("").a', + common.mustCall((err, data) => { + assert.strictEqual(data[0].includes('("").at'), true); + }) + ); + replServer.complete( + '[].a', + common.mustCall((err, data) => { + assert.strictEqual(data[0].includes('[].at'), true); + }) + ); + replServer.complete( + '{}.a', + common.mustCall((err, data) => { + assert.deepStrictEqual(data[0], []); + }) + ); replServer.close(); }); - it("does not return a function's local variable", async () => { - const { replServer, run } = startNewREPLServer(); + it("does not return a function's local variable", () => { + const { replServer, input } = startNewREPLServer(); - await run(['var top = function() {', 'var inner = {one:1};', '}']); + input.run(['var top = function() {', 'var inner = {one:1};', '}']); - await expectNoResults(replServer, 'inner.o'); + replServer.complete('inner.o', getNoResultsFunction()); replServer.close(); }); - it("does not return a function's local variable even when the function has parameters", async () => { - const { replServer, run } = startNewREPLServer(); + it("does not return a function's local variable even when the function has parameters", () => { + const { replServer, input } = startNewREPLServer(); - await run([ + input.run([ 'var top = function(one, two) {', 'var inner = {', ' one:1', '};', ]); - await expectNoResults(replServer, 'inner.o'); + replServer.complete('inner.o', getNoResultsFunction()); replServer.close(); }); it("does not return a function's local variable" + - 'even if the scope is nested inside an immediately executed function', async () => { - const { replServer, run } = startNewREPLServer(); + 'even if the scope is nested inside an immediately executed function', () => { + const { replServer, input } = startNewREPLServer(); - await run([ + input.run([ 'var top = function() {', '(function test () {', 'var inner = {', @@ -144,17 +191,17 @@ describe('REPL tab completion (core functionality)', () => { '};', ]); - await expectNoResults(replServer, 'inner.o'); + replServer.complete('inner.o', getNoResultsFunction()); replServer.close(); }); it("does not return a function's local variable" + 'even if the scope is nested inside an immediately executed function' + - '(the definition has the params and { on a separate line)', async () => { - const { replServer, run } = startNewREPLServer(); + '(the definition has the params and { on a separate line)', () => { + const { replServer, input } = startNewREPLServer(); - await run([ + input.run([ 'var top = function() {', 'r = function test (', ' one, two) {', @@ -163,15 +210,15 @@ describe('REPL tab completion (core functionality)', () => { '};', ]); - await expectNoResults(replServer, 'inner.o'); + replServer.complete('inner.o', getNoResultsFunction()); replServer.close(); }); - it('currently does not work, but should not break (local inner)', async () => { - const { replServer, run } = startNewREPLServer(); + it('currently does not work, but should not break (local inner)', () => { + const { replServer, input } = startNewREPLServer(); - await run([ + input.run([ 'var top = function() {', 'r = function test ()', '{', @@ -180,15 +227,15 @@ describe('REPL tab completion (core functionality)', () => { '};', ]); - await expectNoResults(replServer, 'inner.o'); + replServer.complete('inner.o', getNoResultsFunction()); replServer.close(); }); - it('currently does not work, but should not break (local inner parens next line)', async () => { - const { replServer, run } = startNewREPLServer(); + it('currently does not work, but should not break (local inner parens next line)', () => { + const { replServer, input } = startNewREPLServer(); - await run([ + input.run([ 'var top = function() {', 'r = function test (', ')', @@ -198,71 +245,98 @@ describe('REPL tab completion (core functionality)', () => { '};', ]); - await expectNoResults(replServer, 'inner.o'); + replServer.complete('inner.o', getNoResultsFunction()); replServer.close(); }); - it('works on non-Objects', async () => { - const { replServer, run } = startNewREPLServer(); + it('works on non-Objects', () => { + const { replServer, input } = startNewREPLServer(); - await run(['var str = "test";']); + input.run(['var str = "test";']); - assert.deepStrictEqual( - await complete(replServer, 'str.len'), - [['str.length'], 'str.len']); + replServer.complete( + 'str.len', + common.mustCall(function(_error, data) { + assert.deepStrictEqual(data, [['str.length'], 'str.len']); + }) + ); replServer.close(); }); - it('should be case-insensitive if member part is lower-case', async () => { - const { replServer, run } = startNewREPLServer(); + it('should be case-insensitive if member part is lower-case', () => { + const { replServer, input } = startNewREPLServer(); - await run(['var foo = { barBar: 1, BARbuz: 2, barBLA: 3 };']); + input.run(['var foo = { barBar: 1, BARbuz: 2, barBLA: 3 };']); - assert.deepStrictEqual( - await complete(replServer, 'foo.b'), - [['foo.BARbuz', 'foo.barBLA', 'foo.barBar'], 'foo.b']); + replServer.complete( + 'foo.b', + common.mustCall(function(_error, data) { + assert.deepStrictEqual(data, [ + ['foo.BARbuz', 'foo.barBLA', 'foo.barBar'], + 'foo.b', + ]); + }) + ); replServer.close(); }); - it('should be case-insensitive if member part is upper-case', async () => { - const { replServer, run } = startNewREPLServer(); + it('should be case-insensitive if member part is upper-case', () => { + const { replServer, input } = startNewREPLServer(); - await run(['var foo = { barBar: 1, BARbuz: 2, barBLA: 3 };']); + input.run(['var foo = { barBar: 1, BARbuz: 2, barBLA: 3 };']); - assert.deepStrictEqual( - await complete(replServer, 'foo.B'), - [['foo.BARbuz', 'foo.barBLA', 'foo.barBar'], 'foo.B']); + replServer.complete( + 'foo.B', + common.mustCall(function(_error, data) { + assert.deepStrictEqual(data, [ + ['foo.BARbuz', 'foo.barBLA', 'foo.barBar'], + 'foo.B', + ]); + }) + ); replServer.close(); }); - it('should not break on spaces', async () => { + it('should not break on spaces', () => { const { replServer } = startNewREPLServer(); - const data = await complete(replServer, ' '); - assert.strictEqual(data[1], ''); - assert.ok(data[0].includes('globalThis')); + const spaceTimeout = setTimeout(function() { + throw new Error('timeout'); + }, 1000); + + replServer.complete( + ' ', + common.mustSucceed((data) => { + assert.strictEqual(data[1], ''); + assert.ok(data[0].includes('globalThis')); + clearTimeout(spaceTimeout); + }) + ); replServer.close(); }); - it(`should pick up the global "toString" object, and any other properties up the "global" object's prototype chain`, async () => { + it(`should pick up the global "toString" object, and any other properties up the "global" object's prototype chain`, () => { const { replServer } = startNewREPLServer(); - assert.deepStrictEqual( - await complete(replServer, 'toSt'), - [['toString'], 'toSt']); + replServer.complete( + 'toSt', + common.mustCall(function(_error, data) { + assert.deepStrictEqual(data, [['toString'], 'toSt']); + }) + ); replServer.close(); }); - it('should make own properties shadow properties on the prototype', async () => { - const { replServer, run } = startNewREPLServer(); + it('should make own properties shadow properties on the prototype', () => { + const { replServer, input } = startNewREPLServer(); - await run([ + input.run([ 'var x = Object.create(null);', 'x.a = 1;', 'x.b = 2;', @@ -271,197 +345,220 @@ describe('REPL tab completion (core functionality)', () => { 'y.c = 4;', ]); - assert.deepStrictEqual( - await complete(replServer, 'y.'), - [['y.b', '', 'y.a', 'y.c'], 'y.']); + replServer.complete( + 'y.', + common.mustCall(function(_error, data) { + assert.deepStrictEqual(data, [['y.b', '', 'y.a', 'y.c'], 'y.']); + }) + ); replServer.close(); }); - it('works on context properties', async () => { - const { replServer, run } = startNewREPLServer(); + it('works on context properties', () => { + const { replServer, input } = startNewREPLServer(); - await run(['var custom = "test";']); + input.run(['var custom = "test";']); - assert.deepStrictEqual( - await complete(replServer, 'cus'), - [['CustomEvent', 'custom'], 'cus']); + replServer.complete( + 'cus', + common.mustCall(function(_error, data) { + assert.deepStrictEqual(data, [['CustomEvent', 'custom'], 'cus']); + }) + ); replServer.close(); }); - it("doesn't crash REPL with half-baked proxy objects", async () => { - const { replServer, run } = startNewREPLServer(); + it("doesn't crash REPL with half-baked proxy objects", () => { + const { replServer, input } = startNewREPLServer(); - await run([ + input.run([ 'var proxy = new Proxy({}, {ownKeys: () => { throw new Error(); }});', ]); - const data = await complete(replServer, 'proxy.'); - assert(Array.isArray(data)); + replServer.complete( + 'proxy.', + common.mustCall(function(error, data) { + assert.strictEqual(error, null); + assert(Array.isArray(data)); + }) + ); replServer.close(); }); - it('does not include integer members of an Array', async () => { - const { replServer, run } = startNewREPLServer(); + it('does not include integer members of an Array', () => { + const { replServer, input } = startNewREPLServer(); - await run(['var ary = [1,2,3];']); + input.run(['var ary = [1,2,3];']); - const data = await complete(replServer, 'ary.'); - assert.strictEqual(data[0].includes('ary.0'), false); - assert.strictEqual(data[0].includes('ary.1'), false); - assert.strictEqual(data[0].includes('ary.2'), false); + replServer.complete( + 'ary.', + common.mustCall(function(_error, data) { + assert.strictEqual(data[0].includes('ary.0'), false); + assert.strictEqual(data[0].includes('ary.1'), false); + assert.strictEqual(data[0].includes('ary.2'), false); + }) + ); replServer.close(); }); - it('does not include integer keys in an object', async () => { - const { replServer, run } = startNewREPLServer(); + it('does not include integer keys in an object', () => { + const { replServer, input } = startNewREPLServer(); - await run(['var obj = {1:"a","1a":"b",a:"b"};']); + input.run(['var obj = {1:"a","1a":"b",a:"b"};']); - const data = await complete(replServer, 'obj.'); - assert.strictEqual(data[0].includes('obj.1'), false); - assert.strictEqual(data[0].includes('obj.1a'), false); - assert(data[0].includes('obj.a')); + replServer.complete( + 'obj.', + common.mustCall(function(_error, data) { + assert.strictEqual(data[0].includes('obj.1'), false); + assert.strictEqual(data[0].includes('obj.1a'), false); + assert(data[0].includes('obj.a')); + }) + ); replServer.close(); }); - it('does not try to complete results of non-simple expressions', async () => { - const { replServer, run } = startNewREPLServer(); + it('does not try to complete results of non-simple expressions', () => { + const { replServer, input } = startNewREPLServer(); - await run(['function a() {}']); + input.run(['function a() {}']); - await expectNoResults(replServer, 'a().b.'); + replServer.complete('a().b.', getNoResultsFunction()); replServer.close(); }); - it('works when prefixed with spaces', async () => { - const { replServer, run } = startNewREPLServer(); + it('works when prefixed with spaces', () => { + const { replServer, input } = startNewREPLServer(); - await run(['var obj = {1:"a","1a":"b",a:"b"};']); + input.run(['var obj = {1:"a","1a":"b",a:"b"};']); - const data = await complete(replServer, ' obj.'); - assert.strictEqual(data[0].includes('obj.1'), false); - assert.strictEqual(data[0].includes('obj.1a'), false); - assert(data[0].includes('obj.a')); + replServer.complete( + ' obj.', + common.mustCall((_error, data) => { + assert.strictEqual(data[0].includes('obj.1'), false); + assert.strictEqual(data[0].includes('obj.1a'), false); + assert(data[0].includes('obj.a')); + }) + ); replServer.close(); }); - it('works inside assignments', async () => { + it('works inside assignments', () => { const { replServer } = startNewREPLServer(); - assert.deepStrictEqual( - await complete(replServer, 'var log = console.lo'), - [['console.log'], 'console.lo']); - - replServer.close(); - }); - - it('does not complete identifiers inside an unterminated string literal', async () => { - // The completion point sits inside the text of an open string, where there - // is nothing to complete. Regression test: previously the partial path was - // mistaken for a bare identifier and completed against the global scope. - const { replServer } = startNewREPLServer(); - - await expectNoResults(replServer, 'const x = "./n'); - await expectNoResults(replServer, "const x = './n"); - await expectNoResults(replServer, 'const x = `./n'); - - replServer.close(); - }); - - it('does not complete inside fs path strings when blocking is disabled', async () => { - // With `allowBlockingCompletions` off, `fs.("…` is just an open string; - // it must not fall back to completing the partial path as a global. - const { replServer } = startNewREPLServer({ allowBlockingCompletions: false }); - - await expectNoResults(replServer, 'fs.readFileSync("./n'); - await expectNoResults(replServer, 'fs.promises.readFile("./n'); - await expectNoResults(replServer, 'fs.readFileSync(`./n'); + replServer.complete( + 'var log = console.lo', + common.mustCall((_error, data) => { + assert.deepStrictEqual(data, [['console.log'], 'console.lo']); + }) + ); replServer.close(); }); - it('works for defined commands', async () => { - const { replServer, run } = startNewREPLServer(); + it('works for defined commands', () => { + const { replServer, input } = startNewREPLServer(); - assert.deepStrictEqual( - await complete(replServer, '.b'), - [['break'], 'b']); + replServer.complete( + '.b', + common.mustCall((error, data) => { + assert.deepStrictEqual(data, [['break'], 'b']); + }) + ); - await run(['var obj = {"hello, world!": "some string", "key": 123}']); + input.run(['var obj = {"hello, world!": "some string", "key": 123}']); - const data = await complete(replServer, 'obj.'); - assert.strictEqual(data[0].includes('obj.hello, world!'), false); - assert(data[0].includes('obj.key')); + replServer.complete( + 'obj.', + common.mustCall((error, data) => { + assert.strictEqual(data[0].includes('obj.hello, world!'), false); + assert(data[0].includes('obj.key')); + }) + ); replServer.close(); }); - it('does not include __defineSetter__ and friends', async () => { - const { replServer, run } = startNewREPLServer(); + it('does not include __defineSetter__ and friends', () => { + const { replServer, input } = startNewREPLServer(); - await run(['var obj = {};']); + input.run(['var obj = {};']); - const data = await complete(replServer, 'obj.'); - assert.strictEqual(data[0].includes('obj.__defineGetter__'), false); - assert.strictEqual(data[0].includes('obj.__defineSetter__'), false); - assert.strictEqual(data[0].includes('obj.__lookupGetter__'), false); - assert.strictEqual(data[0].includes('obj.__lookupSetter__'), false); - assert.strictEqual(data[0].includes('obj.__proto__'), true); + replServer.complete( + 'obj.', + common.mustCall(function(error, data) { + assert.strictEqual(data[0].includes('obj.__defineGetter__'), false); + assert.strictEqual(data[0].includes('obj.__defineSetter__'), false); + assert.strictEqual(data[0].includes('obj.__lookupGetter__'), false); + assert.strictEqual(data[0].includes('obj.__lookupSetter__'), false); + assert.strictEqual(data[0].includes('obj.__proto__'), true); + }) + ); replServer.close(); }); - it('works with builtin values', async () => { + it('works with builtin values', () => { const { replServer } = startNewREPLServer(); - assert.deepStrictEqual( - await complete(replServer, 'I'), - [ - [ - 'if', - 'import', - 'in', - 'instanceof', - '', - 'Infinity', - 'Int16Array', - 'Int32Array', - 'Int8Array', - ...(common.hasIntl ? ['Intl'] : []), - 'Iterator', - 'inspector', - 'isFinite', - 'isNaN', - '', - 'isPrototypeOf', - ], - 'I', - ]); + replServer.complete( + 'I', + common.mustCall((error, data) => { + assert.deepStrictEqual(data, [ + [ + 'if', + 'import', + 'in', + 'instanceof', + '', + 'Infinity', + 'Int16Array', + 'Int32Array', + 'Int8Array', + ...(common.hasIntl ? ['Intl'] : []), + 'Iterator', + 'inspector', + 'isFinite', + 'isNaN', + '', + 'isPrototypeOf', + ], + 'I', + ]); + }) + ); replServer.close(); }); - it('works with lexically scoped variables', async () => { - const { replServer, run } = startNewREPLServer(); + it('works with lexically scoped variables', () => { + const { replServer, input } = startNewREPLServer(); - await run([ + input.run([ 'let lexicalLet = true;', 'const lexicalConst = true;', 'class lexicalKlass {}', ]); - for (const type of ['Let', 'Const', 'Klass']) { + ['Let', 'Const', 'Klass'].forEach((type) => { const query = `lexical${type[0]}`; - const expected = [[`lexical${type}`], query]; - assert.deepStrictEqual(await complete(replServer, query), expected); - } + const hasInspector = process.features.inspector; + const expected = hasInspector ? + [[`lexical${type}`], query] : + [[], `lexical${type[0]}`]; + replServer.complete( + query, + common.mustCall((error, data) => { + assert.deepStrictEqual(data, expected); + }) + ); + }); replServer.close(); }); diff --git a/test/parallel/test-repl-tab.js b/test/parallel/test-repl-tab.js index 86b5f0103bd726..710fca9fae2d1e 100644 --- a/test/parallel/test-repl-tab.js +++ b/test/parallel/test-repl-tab.js @@ -1,11 +1,13 @@ 'use strict'; const common = require('../common'); -const { startNewREPLServer } = require('../common/repl'); +const repl = require('repl'); +const zlib = require('zlib'); -const { replServer } = startNewREPLServer({ - eval: function(cmd, context, filename, callback) { - callback(null, cmd); - }, +// Just use builtin stream inherited from Duplex +const putIn = zlib.createGzip(); +const testMe = repl.start('', putIn, function(cmd, context, filename, + callback) { + callback(null, cmd); }); -replServer.complete('', common.mustSucceed()); +testMe.complete('', common.mustSucceed()); diff --git a/test/parallel/test-repl-throw-null-or-undefined.js b/test/parallel/test-repl-throw-null-or-undefined.js index 5eec175777de79..3b4657ce98c0f3 100644 --- a/test/parallel/test-repl-throw-null-or-undefined.js +++ b/test/parallel/test-repl-throw-null-or-undefined.js @@ -1,14 +1,13 @@ 'use strict'; require('../common'); -const { startNewREPLServer } = require('../common/repl'); // This test ensures that the repl does not // crash or emit error when throwing `null|undefined` // ie `throw null` or `throw undefined`. -const { replServer } = startNewREPLServer(); +const r = require('repl').start(); // Should not throw. -replServer.write('throw null\n'); -replServer.write('throw undefined\n'); -replServer.write('.exit\n'); +r.write('throw null\n'); +r.write('throw undefined\n'); +r.write('.exit\n'); diff --git a/test/parallel/test-repl-top-level-await.js b/test/parallel/test-repl-top-level-await.js new file mode 100644 index 00000000000000..4917dffc0e7674 --- /dev/null +++ b/test/parallel/test-repl-top-level-await.js @@ -0,0 +1,230 @@ +'use strict'; + +const common = require('../common'); +const ArrayStream = require('../common/arraystream'); +const assert = require('assert'); +const events = require('events'); +const { stripVTControlCharacters } = require('internal/util/inspect'); +const repl = require('repl'); + +common.skipIfInspectorDisabled(); + +// Flags: --expose-internals + +const PROMPT = 'await repl > '; + +class REPLStream extends ArrayStream { + constructor() { + super(); + this.waitingForResponse = false; + this.lines = ['']; + } + write(chunk, encoding, callback) { + if (Buffer.isBuffer(chunk)) { + chunk = chunk.toString(encoding); + } + const chunkLines = stripVTControlCharacters(chunk).split('\n'); + this.lines[this.lines.length - 1] += chunkLines[0]; + if (chunkLines.length > 1) { + this.lines.push(...chunkLines.slice(1)); + } + this.emit('line', this.lines[this.lines.length - 1]); + if (callback) callback(); + return true; + } + + async wait() { + if (this.waitingForResponse) { + throw new Error('Currently waiting for response to another command'); + } + this.lines = ['']; + for await (const [line] of events.on(this, 'line')) { + if (line.includes(PROMPT)) { + return this.lines; + } + } + } +} + +const putIn = new REPLStream(); +const testMe = repl.start({ + prompt: PROMPT, + stream: putIn, + terminal: true, + useColors: true, + breakEvalOnSigint: true +}); + +function runAndWait(cmds) { + const promise = putIn.wait(); + for (const cmd of cmds) { + if (typeof cmd === 'string') { + putIn.run([cmd]); + } else { + testMe.write('', cmd); + } + } + return promise; +} + +async function ordinaryTests() { + // These tests were created based on + // https://cs.chromium.org/chromium/src/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-top-level-await.js?rcl=5d0ea979f0ba87655b7ef0e03b58fa3c04986ba6 + putIn.run([ + 'function foo(x) { return x; }', + 'function koo() { return Promise.resolve(4); }', + ]); + const testCases = [ + ['await Promise.resolve(0)', '0'], + ['{ a: await Promise.resolve(1) }', '{ a: 1 }'], + ['_', '{ a: 1 }'], + ['let { aa, bb } = await Promise.resolve({ aa: 1, bb: 2 }), f = 5;'], + ['aa', '1'], + ['bb', '2'], + ['f', '5'], + ['let cc = await Promise.resolve(2)'], + ['cc', '2'], + ['let dd;'], + ['dd'], + ['let [ii, { abc: { kk } }] = [0, { abc: { kk: 1 } }];'], + ['ii', '0'], + ['kk', '1'], + ['var ll = await Promise.resolve(2);'], + ['ll', '2'], + ['foo(await koo())', '4'], + ['_', '4'], + ['const m = foo(await koo());'], + ['m', '4'], + ['const n = foo(await\nkoo());', + ['const n = foo(await\r', '| koo());\r', 'undefined']], + ['n', '4'], + // eslint-disable-next-line no-template-curly-in-string + ['`status: ${(await Promise.resolve({ status: 200 })).status}`', + "'status: 200'"], + ['for (let i = 0; i < 2; ++i) await i'], + ['for (let i = 0; i < 2; ++i) { await i }'], + ['await 0', '0'], + ['await 0; function foo() {}'], + ['foo', '[Function: foo]'], + ['class Foo {}; await 1;', '1'], + ['Foo', '[class Foo]'], + ['if (await true) { function bar() {}; }'], + ['bar', '[Function: bar]'], + ['if (await true) { class Bar {}; }'], + ['Bar', 'Uncaught ReferenceError: Bar is not defined'], + ['await 0; function* gen(){}'], + ['for (var i = 0; i < 10; ++i) { await i; }'], + ['i', '10'], + ['for (let j = 0; j < 5; ++j) { await j; }'], + ['j', 'Uncaught ReferenceError: j is not defined', { line: 0 }], + ['gen', '[GeneratorFunction: gen]'], + ['return 42; await 5;', 'Uncaught SyntaxError: Illegal return statement', + { line: 3 }], + ['let o = await 1, p'], + ['p'], + ['let q = 1, s = await 2'], + ['s', '2'], + ['for await (let i of [1,2,3]) console.log(i)', + [ + 'for await (let i of [1,2,3]) console.log(i)\r', + '1', + '2', + '3', + 'undefined', + ], + ], + ['await Promise..resolve()', + [ + 'await Promise..resolve()\r', + 'Uncaught SyntaxError: ', + 'await Promise..resolve()', + ' ^', + '', + 'Unexpected token \'.\'', + ], + ], + ['for (const x of [1,2,3]) {\nawait x\n}', [ + 'for (const x of [1,2,3]) {\r', + '| await x\r', + '| }\r', + 'undefined', + ]], + ['for (const x of [1,2,3]) {\nawait x;\n}', [ + 'for (const x of [1,2,3]) {\r', + '| await x;\r', + '| }\r', + 'undefined', + ]], + ['for await (const x of [1,2,3]) {\nconsole.log(x)\n}', [ + 'for await (const x of [1,2,3]) {\r', + '| console.log(x)\r', + '| }\r', + '1', + '2', + '3', + 'undefined', + ]], + ['for await (const x of [1,2,3]) {\nconsole.log(x);\n}', [ + 'for await (const x of [1,2,3]) {\r', + '| console.log(x);\r', + '| }\r', + '1', + '2', + '3', + 'undefined', + ]], + // Testing documented behavior of `const`s (see: https://github.com/nodejs/node/issues/45918) + ['const k = await Promise.resolve(123)'], + ['k', '123'], + ['k = await Promise.resolve(234)', '234'], + ['k', '234'], + ['const k = await Promise.resolve(345)', "Uncaught SyntaxError: Identifier 'k' has already been declared"], + // Regression test for https://github.com/nodejs/node/issues/43777. + ['await Promise.resolve(123), Promise.resolve(456)', 'Promise { 456 }', { line: 0 }], + ['await Promise.resolve(123), await Promise.resolve(456)', '456'], + ['await (Promise.resolve(123), Promise.resolve(456))', '456'], + ]; + + for (const [input, expected = [`${input}\r`], options = {}] of testCases) { + console.log(`Testing ${input}`); + const toBeRun = input.split('\n'); + const lines = await runAndWait(toBeRun); + if (Array.isArray(expected)) { + if (expected.length === 1) + expected.push('undefined'); + if (lines[0] === input) + lines.shift(); + assert.deepStrictEqual(lines, [...expected, PROMPT]); + } else if ('line' in options) { + assert.strictEqual(lines[toBeRun.length + options.line], expected); + } else { + const echoed = toBeRun.map((a, i) => `${i > 0 ? '| ' : ''}${a}\r`); + assert.deepStrictEqual(lines, [...echoed, expected, PROMPT]); + } + } +} + +async function ctrlCTest() { + console.log('Testing Ctrl+C'); + const output = await runAndWait([ + 'await new Promise(() => {})', + { ctrl: true, name: 'c' }, + ]); + assert.deepStrictEqual(output.slice(0, 3), [ + 'await new Promise(() => {})\r', + 'Uncaught:', + '[Error [ERR_SCRIPT_EXECUTION_INTERRUPTED]: ' + + 'Script execution was interrupted by `SIGINT`] {', + ]); + assert.deepStrictEqual(output.slice(-2), [ + '}', + PROMPT, + ]); +} + +async function main() { + await ordinaryTests(); + await ctrlCTest(); +} + +main().then(common.mustCall()); diff --git a/test/parallel/test-repl-uncaught-exception-after-input-ended.js b/test/parallel/test-repl-uncaught-exception-after-input-ended.js index e27f2230305926..1e2ca86a9f079c 100644 --- a/test/parallel/test-repl-uncaught-exception-after-input-ended.js +++ b/test/parallel/test-repl-uncaught-exception-after-input-ended.js @@ -4,8 +4,6 @@ const { start } = require('node:repl'); const { PassThrough } = require('node:stream'); const assert = require('node:assert'); -common.skipIfInspectorDisabled(); - // This test verifies that uncaught exceptions in the REPL // do not bring down the process, even if stdin may already // have been ended at that point (and the REPL closed as diff --git a/test/parallel/test-repl-uncaught-exception-async.js b/test/parallel/test-repl-uncaught-exception-async.js new file mode 100644 index 00000000000000..e5373cdaca4d8d --- /dev/null +++ b/test/parallel/test-repl-uncaught-exception-async.js @@ -0,0 +1,36 @@ +'use strict'; + +// This verifies that adding an `uncaughtException` listener in an REPL instance +// does not suppress errors in the whole application. Adding such listener +// should throw. + +const common = require('../common'); +const assert = require('assert'); +const { startNewREPLServer } = require('../common/repl'); + +const { replServer, output } = startNewREPLServer({ + prompt: '', + terminal: false, + useColors: false, + global: false, +}); + +replServer.write( + 'process.nextTick(() => {\n' + + ' process.on("uncaughtException", () => console.log("Foo"));\n' + + ' throw new TypeError("foobar");\n' + + '});\n' +); +replServer.write( + 'setTimeout(() => {\n' + + ' throw new RangeError("abc");\n' + + '}, 1);console.log()\n' +); + +setTimeout(common.mustCall(() => { + replServer.close(); + const len = process.listenerCount('uncaughtException'); + process.removeAllListeners('uncaughtException'); + assert.strictEqual(len, 0); + assert.match(output.accumulator, /ERR_INVALID_REPL_INPUT.*(?!Type)RangeError: abc/s); +}), 2); diff --git a/test/parallel/test-repl-uncaught-exception-standalone.js b/test/parallel/test-repl-uncaught-exception-standalone.js new file mode 100644 index 00000000000000..8edf47b2436895 --- /dev/null +++ b/test/parallel/test-repl-uncaught-exception-standalone.js @@ -0,0 +1,37 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cp = require('child_process'); +const child = cp.spawn(process.execPath, ['-i']); +let output = ''; + +child.stdout.setEncoding('utf8'); +child.stdout.on('data', (data) => { + output += data; +}); + +child.on('exit', common.mustCall(() => { + const results = output.split('\n'); + results.shift(); + assert.deepStrictEqual( + results, + [ + 'Type ".help" for more information.', + // x\n + '> Uncaught ReferenceError: x is not defined', + // Added `uncaughtException` listener. + '> short', + 'undefined', + // x\n + '> Foobar', + '> ', + ] + ); +})); + +child.stdin.write('x\n'); +child.stdin.write( + 'process.on("uncaughtException", () => console.log("Foobar"));' + + 'console.log("short")\n'); +child.stdin.write('x\n'); +child.stdin.end(); diff --git a/test/parallel/test-repl-uncaught-exception-standalone.mjs b/test/parallel/test-repl-uncaught-exception-standalone.mjs deleted file mode 100644 index 933f60f3a95deb..00000000000000 --- a/test/parallel/test-repl-uncaught-exception-standalone.mjs +++ /dev/null @@ -1,17 +0,0 @@ -import '../common/index.mjs'; -import assert from 'node:assert'; -import { startNewREPLServer } from '../common/repl.js'; - -const { replServer, output, run } = startNewREPLServer(); - -await run('x\n'); -await run( - 'process.on("uncaughtException", () => console.log("Foobar"));' + - 'console.log("short")\n'); -await run('x\n'); - -assert.match(output.accumulator, /ReferenceError: x is not defined/); -assert.match(output.accumulator, /short/); -assert.match(output.accumulator, /Foobar/); - -replServer.close(); diff --git a/test/parallel/test-repl-uncaught-exception.js b/test/parallel/test-repl-uncaught-exception.js new file mode 100644 index 00000000000000..012c7f59ebc8a8 --- /dev/null +++ b/test/parallel/test-repl-uncaught-exception.js @@ -0,0 +1,70 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const { startNewREPLServer } = require('../common/repl'); + +let count = 0; + +function run({ command, expected, useColors = false }) { + const { replServer, output } = startNewREPLServer({ + prompt: '', + terminal: false, + useColors, + }); + + replServer.write(`${command}\n`); + + if (typeof expected === 'string') { + assert.strictEqual(output.accumulator, expected); + } else { + assert.match(output.accumulator, expected); + } + + // Verify that the repl is still working as expected. + output.accumulator = ''; + replServer.write('1 + 1\n'); + // eslint-disable-next-line no-control-regex + assert.strictEqual(output.accumulator.replace(/\u001b\[[0-9]+m/g, ''), '2\n'); + replServer.close(); + count++; +} + +const tests = [ + { + useColors: true, + command: 'x', + expected: 'Uncaught ReferenceError: x is not defined\n' + }, + { + useColors: true, + command: 'throw { foo: "test" }', + expected: "Uncaught { foo: \x1B[32m'test'\x1B[39m }\n" + }, + { + command: 'process.on("uncaughtException", () => console.log("Foobar"));\n', + expected: /^Uncaught:\nTypeError \[ERR_INVALID_REPL_INPUT]: Listeners for `/ + }, + { + command: 'x;\n', + expected: 'Uncaught ReferenceError: x is not defined\n' + }, + { + command: 'process.on("uncaughtException", () => console.log("Foobar"));' + + 'console.log("Baz");\n', + expected: /^Uncaught:\nTypeError \[ERR_INVALID_REPL_INPUT]: Listeners for `/ + }, + { + command: 'console.log("Baz");' + + 'process.on("uncaughtException", () => console.log("Foobar"));\n', + expected: /^Baz\nUncaught:\nTypeError \[ERR_INVALID_REPL_INPUT]:.*uncaughtException/ + }, +]; + +process.on('exit', () => { + // To actually verify that the test passed we have to make sure no + // `uncaughtException` listeners exist anymore. + process.removeAllListeners('uncaughtException'); + assert.strictEqual(count, tests.length); +}); + +tests.forEach(run); diff --git a/test/parallel/test-repl-underscore.js b/test/parallel/test-repl-underscore.js new file mode 100644 index 00000000000000..c9ae7ca0e7ca0c --- /dev/null +++ b/test/parallel/test-repl-underscore.js @@ -0,0 +1,212 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const repl = require('repl'); +const { startNewREPLServer } = require('../common/repl'); + +const testingReplPrompt = '_REPL_TESTING_PROMPT_>'; + +testSloppyMode(); +testStrictMode(); +testResetContext(); +testResetContextGlobal(); +testError(); + +function testSloppyMode() { + const { replServer, output } = startNewREPLServer({ + prompt: testingReplPrompt, + mode: repl.REPL_MODE_SLOPPY, + }); + + // Cannot use `let` in sloppy mode + replServer.write(`_; // initial value undefined + var x = 10; // evaluates to undefined + _; // still undefined + y = 10; // evaluates to 10 + _; // 10 from last eval + _ = 20; // explicitly set to 20 + _; // 20 from user input + _ = 30; // make sure we can set it twice and no prompt + _; // 30 from user input + y = 40; // make sure eval doesn't change _ + _; // remains 30 from user input + `); + + assertOutput(output, [ + 'undefined', + 'undefined', + 'undefined', + '10', + '10', + 'Expression assignment to _ now disabled.', + '20', + '20', + '30', + '30', + '40', + '30', + ]); +} + +function testStrictMode() { + const { replServer, output } = startNewREPLServer({ + prompt: testingReplPrompt, + mode: repl.REPL_MODE_STRICT, + }); + + replServer.write(`_; // initial value undefined + var x = 10; // evaluates to undefined + _; // still undefined + let _ = 20; // use 'let' only in strict mode - evals to undefined + _; // 20 from user input + _ = 30; // make sure we can set it twice and no prompt + _; // 30 from user input + var y = 40; // make sure eval doesn't change _ + _; // remains 30 from user input + function f() { let _ = 50; } // undefined + f(); // undefined + _; // remains 30 from user input + `); + + assertOutput(output, [ + 'undefined', + 'undefined', + 'undefined', + 'undefined', + '20', + '30', + '30', + 'undefined', + '30', + 'undefined', + 'undefined', + '30', + ]); +} + +function testResetContext() { + const { replServer, output } = startNewREPLServer({ + prompt: testingReplPrompt, + }); + + replServer.write(`_ = 10; // explicitly set to 10 + _; // 10 from user input + .clear // Clearing context... + _; // remains 10 + x = 20; // but behavior reverts to last eval + _; // expect 20 + `); + + assertOutput(output, [ + 'Expression assignment to _ now disabled.', + '10', + '10', + 'Clearing context...', + '10', + '20', + '20', + ]); +} + +function testResetContextGlobal() { + const { replServer, output } = startNewREPLServer({ + prompt: testingReplPrompt, + useGlobal: true, + }); + + replServer.write(`_ = 10; // explicitly set to 10 + _; // 10 from user input + .clear // No output because useGlobal is true + _; // remains 10 + `); + + assertOutput(output, [ + 'Expression assignment to _ now disabled.', + '10', + '10', + '10', + ]); + + // Delete globals leaked by REPL when `useGlobal` is `true` + delete globalThis.module; + delete globalThis.require; +} + +function testError() { + const { replServer, output } = startNewREPLServer({ + prompt: testingReplPrompt, + replMode: repl.REPL_MODE_STRICT, + preview: false, + }); + + replServer.write(`_error; // initial value undefined + throw new Error('foo'); // throws error + _error; // shows error + fs.readdirSync('/nonexistent?'); // throws error, sync + _error.code; // shows error code + _error.syscall; // shows error syscall + setImmediate(() => { throw new Error('baz'); }); undefined; + // throws error, async + `); + + setImmediate(common.mustCall(() => { + const lines = output.accumulator.trim().split('\n').filter( + (line) => !line.includes(testingReplPrompt) || line.includes('Uncaught Error') + ); + const expectedLines = [ + 'undefined', + + // The error, both from the original throw and the `_error` echo. + 'Uncaught Error: foo', + '[Error: foo]', + + // The sync error, with individual property echoes + /^Uncaught Error: ENOENT: no such file or directory, scandir '.*nonexistent\?'/, + /Object\.readdirSync/, + /^ {2}errno: -(2|4058),$/, + " code: 'ENOENT',", + " syscall: 'scandir',", + /^ {2}path: '*'/, + '}', + "'ENOENT'", + "'scandir'", + + // Dummy 'undefined' from the explicit silencer + one from the comment + 'undefined', + 'undefined', + + // The message from the original throw + /Uncaught Error: baz/, + ]; + for (const line of lines) { + const expected = expectedLines.shift(); + if (typeof expected === 'string') + assert.strictEqual(line, expected); + else + assert.match(line, expected); + } + assert.strictEqual(expectedLines.length, 0); + + // Reset output, check that '_error' is the asynchronously caught error. + output.accumulator = ''; + replServer.write(`_error.message // show the message + _error = 0; // disable auto-assignment + throw new Error('quux'); // new error + _error; // should not see the new error + `); + + assertOutput(output, [ + "'baz'", + 'Expression assignment to _error now disabled.', + '0', + 'Uncaught Error: quux', + '0', + ]); + })); +} + +function assertOutput(output, expected) { + const lines = output.accumulator.trim().split('\n').filter((line) => !line.includes(testingReplPrompt)); + assert.deepStrictEqual(lines, expected); +} diff --git a/test/parallel/test-repl-underscore.mjs b/test/parallel/test-repl-underscore.mjs deleted file mode 100644 index 0126858f6f4e5a..00000000000000 --- a/test/parallel/test-repl-underscore.mjs +++ /dev/null @@ -1,246 +0,0 @@ -import '../common/index.mjs'; -import assert from 'node:assert'; -import repl from 'node:repl'; -import { startNewREPLServer } from '../common/repl.js'; - -const testingReplPrompt = '_REPL_TESTING_PROMPT_>'; - -// Feed a block of source into the REPL one line at a time, awaiting each line -// so the asynchronous evaluator settles before the next line is written. -// Blank/whitespace-only and comment-only lines produce no evaluation output -// and are skipped (the previous synchronous REPL coalesced the whole block in -// a single write, so a trailing comment line evaluated to `undefined`; feeding -// line by line we simply omit it to keep output deterministic). -async function feed(run, block) { - for (const line of block.split('\n')) { - const trimmed = line.trim(); - if (trimmed === '' || trimmed.startsWith('//')) continue; - await run(`${line}\n`); - } -} - -await testSloppyMode(); -await testStrictMode(); -await testResetContext(); -await testResetContextGlobal(); -await testError(); - -async function testSloppyMode() { - const { replServer, output, run } = startNewREPLServer({ - prompt: testingReplPrompt, - mode: repl.REPL_MODE_SLOPPY, - }); - - // Cannot use `let` in sloppy mode - await feed(run, `_; // initial value undefined - var x = 10; // evaluates to undefined - _; // still undefined - y = 10; // evaluates to 10 - _; // 10 from last eval - _ = 20; // explicitly set to 20 - _; // 20 from user input - _ = 30; // make sure we can set it twice and no prompt - _; // 30 from user input - y = 40; // make sure eval doesn't change _ - _; // remains 30 from user input - `); - - assertOutput(output, [ - 'undefined', - 'undefined', - 'undefined', - '10', - '10', - 'Expression assignment to _ now disabled.', - '20', - '20', - '30', - '30', - '40', - '30', - ]); - - replServer.close(); -} - -async function testStrictMode() { - const { replServer, output, run } = startNewREPLServer({ - prompt: testingReplPrompt, - mode: repl.REPL_MODE_STRICT, - }); - - await feed(run, `_; // initial value undefined - var x = 10; // evaluates to undefined - _; // still undefined - let _ = 20; // use 'let' only in strict mode - evals to undefined - _; // 20 from user input - _ = 30; // make sure we can set it twice and no prompt - _; // 30 from user input - var y = 40; // make sure eval doesn't change _ - _; // remains 30 from user input - function f() { let _ = 50; } // undefined - f(); // undefined - _; // remains 30 from user input - `); - - assertOutput(output, [ - 'undefined', - 'undefined', - 'undefined', - 'undefined', - '20', - '30', - '30', - 'undefined', - '30', - 'undefined', - 'undefined', - '30', - ]); - - replServer.close(); -} - -async function testResetContext() { - const { replServer, output, run } = startNewREPLServer({ - prompt: testingReplPrompt, - }); - - await feed(run, `_ = 10; // explicitly set to 10 - _; // 10 from user input - .clear // Clearing context... - _; // remains 10 - x = 20; // but behavior reverts to last eval - _; // expect 20 - `); - - assertOutput(output, [ - 'Expression assignment to _ now disabled.', - '10', - '10', - 'Clearing context...', - '10', - '20', - '20', - ]); - - replServer.close(); -} - -async function testResetContextGlobal() { - const { replServer, output, run } = startNewREPLServer({ - prompt: testingReplPrompt, - useGlobal: true, - }); - - await feed(run, `_ = 10; // explicitly set to 10 - _; // 10 from user input - .clear // No output because useGlobal is true - _; // remains 10 - `); - - assertOutput(output, [ - 'Expression assignment to _ now disabled.', - '10', - '10', - '10', - ]); - - replServer.close(); - - // Delete globals leaked by REPL when `useGlobal` is `true` - delete globalThis.module; - delete globalThis.require; -} - -async function testError() { - const { replServer, output, run } = startNewREPLServer({ - prompt: testingReplPrompt, - replMode: repl.REPL_MODE_STRICT, - preview: false, - }); - - await feed(run, `_error; // initial value undefined - throw new Error('foo'); // throws error - _error; // shows error - fs.readdirSync('/nonexistent?'); // throws error, sync - _error.code; // shows error code - _error.syscall; // shows error syscall - setImmediate(() => { throw new Error('baz'); }); undefined; - // throws error, async - `); - - const lines = output.accumulator.trim().split('\n').filter( - (line) => !line.includes(testingReplPrompt) || line.includes('Uncaught Error') - ); - const expectedLines = [ - 'undefined', - - // The error, both from the original throw and the `_error` echo. The - // inspector-based evaluator returns the real Error object, so both the - // thrown report and the echo now include a stack trace rather than the - // compact `[Error: foo]` form produced by the previous vm-based REPL. - 'Uncaught Error: foo', - /^ +at REPL\d+:/, - 'Error: foo', - /^ +at REPL\d+:/, - - // The sync error, with individual property echoes. The thrown report now - // includes stack frames before the property listing. - /^Uncaught Error: ENOENT: no such file or directory, scandir '.*nonexistent\?'/, - /Object\.readdirSync/, - /^ +at REPL\d+:.*\{$/, - /^ {2}errno: -(2|4058),$/, - " code: 'ENOENT',", - " syscall: 'scandir',", - /^ {2}path: '*'/, - '}', - "'ENOENT'", - "'scandir'", - - // 'undefined' from the explicit silencer on the setImmediate line. - 'undefined', - - // The message from the asynchronously thrown error. - /Uncaught Error: baz/, - ]; - for (const line of lines) { - const expected = expectedLines.shift(); - if (typeof expected === 'string') - assert.strictEqual(line, expected); - else - assert.match(line, expected); - } - assert.strictEqual(expectedLines.length, 0); - - // Reset output, check that '_error' is the asynchronously caught error. - // Because input is now fed one line at a time and the accumulator is reset - // mid-stream, the first line's echo is no longer preceded by a prompt and so - // survives the prompt filter; ignore that leading echo. The thrown error also - // now reports a stack frame. - output.accumulator = ''; - await feed(run, `_error.message // show the message - _error = 0; // disable auto-assignment - throw new Error('quux'); // new error - _error; // should not see the new error - `); - - const errorLines = output.accumulator.trim().split('\n') - .filter((line) => !line.includes(testingReplPrompt) && - !line.includes('_error.message') && - !/^ +at REPL\d+:/.test(line)); - assert.deepStrictEqual(errorLines, [ - "'baz'", - 'Expression assignment to _error now disabled.', - '0', - 'Uncaught Error: quux', - '0', - ]); - - replServer.close(); -} - -function assertOutput(output, expected) { - const lines = output.accumulator.trim().split('\n').filter((line) => !line.includes(testingReplPrompt)); - assert.deepStrictEqual(lines, expected); -} diff --git a/test/parallel/test-repl-unexpected-token-recoverable.js b/test/parallel/test-repl-unexpected-token-recoverable.js index 2b0725fc9032b1..f81855c879b979 100644 --- a/test/parallel/test-repl-unexpected-token-recoverable.js +++ b/test/parallel/test-repl-unexpected-token-recoverable.js @@ -5,8 +5,6 @@ const common = require('../common'); const assert = require('assert'); -common.skipIfInspectorDisabled(); - const spawn = require('child_process').spawn; // Use -i to force node into interactive mode, despite stdout not being a TTY const args = [ '-i' ]; diff --git a/test/parallel/test-repl-unsafe-array-iteration.js b/test/parallel/test-repl-unsafe-array-iteration.js index 4e1b21430dcbd7..3fc65f54cf1f37 100644 --- a/test/parallel/test-repl-unsafe-array-iteration.js +++ b/test/parallel/test-repl-unsafe-array-iteration.js @@ -3,8 +3,6 @@ const common = require('../common'); const assert = require('assert'); const { spawn } = require('child_process'); -common.skipIfInspectorDisabled(); - const replProcess = spawn(process.argv0, ['--interactive'], { stdio: ['pipe', 'pipe', 'inherit'], windowsHide: true, diff --git a/test/parallel/test-repl-unsupported-option.js b/test/parallel/test-repl-unsupported-option.js index e228e7ce9b575d..210e056b3ab0ae 100644 --- a/test/parallel/test-repl-unsupported-option.js +++ b/test/parallel/test-repl-unsupported-option.js @@ -1,12 +1,10 @@ 'use strict'; -const common = require('../common'); +require('../common'); const assert = require('assert'); const { spawnSync } = require('child_process'); -common.skipIfInspectorDisabled(); - const result = spawnSync(process.execPath, ['-i', '--input-type=module']); assert.strictEqual(result.stderr.toString(), 'Cannot specify --input-type for REPL\n'); diff --git a/test/parallel/test-repl-use-global.js b/test/parallel/test-repl-use-global.js new file mode 100644 index 00000000000000..06cda54f4d6fa2 --- /dev/null +++ b/test/parallel/test-repl-use-global.js @@ -0,0 +1,81 @@ +'use strict'; + +// Flags: --expose-internals + +const common = require('../common'); +const stream = require('stream'); +const repl = require('internal/repl'); +const assert = require('assert'); + +// Array of [useGlobal, expectedResult] pairs +const globalTestCases = [ + [false, 'undefined'], + [true, '\'tacos\''], + [undefined, 'undefined'], +]; + +const globalTest = (useGlobal, cb, output) => (err, repl) => { + if (err) + return cb(err); + + let str = ''; + output.on('data', (data) => (str += data)); + globalThis.lunch = 'tacos'; + repl.write('globalThis.lunch;\n'); + repl.close(); + delete globalThis.lunch; + cb(null, str.trim()); +}; + +// Test how the global object behaves in each state for useGlobal +for (const [option, expected] of globalTestCases) { + runRepl(option, globalTest, common.mustSucceed((output) => { + assert.strictEqual(output, expected); + })); +} + +// Test how shadowing the process object via `let` +// behaves in each useGlobal state. Note: we can't +// actually test the state when useGlobal is true, +// because the exception that's generated is caught +// (see below), but errors are printed, and the test +// suite is aware of it, causing a failure to be flagged. +// +const processTestCases = [false, undefined]; +const processTest = (useGlobal, cb, output) => (err, repl) => { + if (err) + return cb(err); + + let str = ''; + output.on('data', (data) => (str += data)); + + // If useGlobal is false, then `let process` should work + repl.write('let process;\n'); + repl.write('21 * 2;\n'); + repl.close(); + cb(null, str.trim()); +}; + +for (const option of processTestCases) { + runRepl(option, processTest, common.mustSucceed((output) => { + assert.strictEqual(output, 'undefined\n42'); + })); +} + +function runRepl(useGlobal, testFunc, cb) { + const inputStream = new stream.PassThrough(); + const outputStream = new stream.PassThrough(); + const opts = { + input: inputStream, + output: outputStream, + useGlobal: useGlobal, + useColors: false, + terminal: false, + prompt: '' + }; + + repl.createInternalRepl( + process.env, + opts, + testFunc(useGlobal, cb, opts.output)); +} diff --git a/test/parallel/test-repl-use-global.mjs b/test/parallel/test-repl-use-global.mjs deleted file mode 100644 index bb80dacb59af44..00000000000000 --- a/test/parallel/test-repl-use-global.mjs +++ /dev/null @@ -1,48 +0,0 @@ -import '../common/index.mjs'; -import assert from 'node:assert'; -import { startNewREPLServer } from '../common/repl.js'; - -// Array of [useGlobal, expectedResult] pairs -const globalTestCases = [ - [false, 'undefined'], - [true, '\'tacos\''], - [undefined, 'undefined'], -]; - -// Test how the global object behaves in each state for useGlobal. -for (const [useGlobal, expected] of globalTestCases) { - const { replServer, output, run } = startNewREPLServer({ - terminal: false, - useColors: false, - useGlobal, - }); - - globalThis.lunch = 'tacos'; - await run('globalThis.lunch;\n'); - replServer.close(); - delete globalThis.lunch; - - assert.strictEqual(output.accumulator.trim(), expected); -} - -// Test how shadowing the process object via `let` behaves in each useGlobal -// state. Note: we can't actually test the state when useGlobal is true, -// because the exception that's generated is caught (see below), but errors -// are printed, and the test suite is aware of it, causing a failure to be -// flagged. -const processTestCases = [false, undefined]; - -for (const useGlobal of processTestCases) { - const { replServer, output, run } = startNewREPLServer({ - terminal: false, - useColors: false, - useGlobal, - }); - - // If useGlobal is false, then `let process` should work. - await run('let process;\n'); - await run('21 * 2;\n'); - replServer.close(); - - assert.strictEqual(output.accumulator.trim(), 'undefined\n42'); -} diff --git a/test/parallel/test-repl-user-error-handler.js b/test/parallel/test-repl-user-error-handler.js index 1b9e591e629a6a..31bd46b13d36d1 100644 --- a/test/parallel/test-repl-user-error-handler.js +++ b/test/parallel/test-repl-user-error-handler.js @@ -7,12 +7,10 @@ const { once } = require('node:events'); const test = require('node:test'); const { spawn } = require('node:child_process'); -common.skipIfInspectorDisabled(); - function* generateCases() { for (const async of [false, true]) { for (const handleErrorReturn of ['ignore', 'print', 'unhandled', 'badvalue']) { - if (handleErrorReturn === 'badvalue') { + if (handleErrorReturn === 'badvalue' && async) { // Handled through a separate test using a child process continue; } @@ -41,20 +39,19 @@ for (const { async, handleErrorReturn } of generateCases()) { } const repl = start(options); - - let outputString = ''; - options.output.on('data', (chunk) => { outputString += chunk; }); - const inputString = async ? 'setImmediate(() => { throw new Error("testerror") })\n42\n' : 'throw new Error("testerror")\n42\n'; + if (handleErrorReturn === 'badvalue') { + assert.throws(() => options.input.end(inputString), /ERR_INVALID_STATE/); + return; + } options.input.end(inputString); await once(repl, 'handled-error'); assert.strictEqual(err.message, 'testerror'); - while (!/42/.test(outputString)) { - await once(options.output, 'data'); - } + const outputString = options.output.read(); + assert.match(outputString, /42/); if (handleErrorReturn === 'print') { assert.match(outputString, /testerror/); diff --git a/test/parallel/test-repl.js b/test/parallel/test-repl.js index 95591ca1bfcb80..c325abb6b4ec75 100644 --- a/test/parallel/test-repl.js +++ b/test/parallel/test-repl.js @@ -21,9 +21,6 @@ 'use strict'; const common = require('../common'); - -common.skipIfInspectorDisabled(); - const fixtures = require('../common/fixtures'); const tmpdir = require('../common/tmpdir'); const assert = require('assert'); @@ -43,6 +40,10 @@ globalThis.invoke_me = function(arg) { return `invoked ${arg}`; }; +// Helpers for describing the expected output: +const kArrow = /^ *\^+ *$/; // Arrow of ^ pointing to syntax error location +const kSource = Symbol('kSource'); // Placeholder standing for input readback + async function runReplTests(socket, prompt, tests) { let lineBuffer = ''; @@ -54,7 +55,11 @@ async function runReplTests(socket, prompt, tests) { console.error('out:', JSON.stringify(send)); socket.write(`${send}\n`); - for (const expectedLine of expectedLines) { + for (let expectedLine of expectedLines) { + // Special value: kSource refers to last sent source text + if (expectedLine === kSource) + expectedLine = send; + while (!lineBuffer.includes('\n')) { lineBuffer += await event(socket, expect); @@ -121,7 +126,7 @@ const unixTests = [ const strictModeTests = [ { send: 'ref = 1', - expect: [/^Uncaught ReferenceError:\s/, /^ {4}at /] + expect: [/^Uncaught ReferenceError:\s/] }, ]; @@ -140,7 +145,7 @@ const errorTests = [ // Uncaught error throws and prints out { send: 'throw new Error(\'test error\');', - expect: ['Uncaught Error: test error', /^ {4}at /] + expect: ['Uncaught Error: test error'] }, { send: "throw { foo: 'bar' };", @@ -239,7 +244,12 @@ const errorTests = [ // should throw { send: '/(/;', - expect: [/^Uncaught SyntaxError: /] + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] }, // invalid RegExp modifiers are a special case of syntax error, // should throw (GH-4012) @@ -250,27 +260,58 @@ const errorTests = [ // Strict mode syntax errors should be caught (GH-5178) { send: '(function() { "use strict"; return 0755; })()', - expect: [/^Uncaught SyntaxError: /] + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] }, { send: '(function(a, a, b) { "use strict"; return a + b + c; })()', - expect: [/^Uncaught SyntaxError: /] + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] }, { send: '(function() { "use strict"; with (this) {} })()', - expect: [/^Uncaught SyntaxError: /] + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] }, { send: '(function() { "use strict"; var x; delete x; })()', - expect: [/^Uncaught SyntaxError: /] + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] }, { send: '(function() { "use strict"; eval = 17; })()', - expect: [/^Uncaught SyntaxError: /] + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] }, { send: '(function() { "use strict"; if (true) function f() { } })()', - expect: ['Uncaught:', /^SyntaxError: /] + expect: [ + kSource, + kArrow, + '', + 'Uncaught:', + /^SyntaxError: /, + ] }, // Named functions can be used: { @@ -293,7 +334,12 @@ const errorTests = [ }, { send: '}', - expect: [/^Uncaught SyntaxError: /] + expect: [ + '{}),({}', + kArrow, + '', + /^Uncaught SyntaxError: /, + ] }, { send: '{ a: ', @@ -406,7 +452,12 @@ const errorTests = [ // Fail when we are not inside a String and a line continuation is used { send: '[] \\', - expect: [/^Uncaught SyntaxError: /] + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] }, // Do not fail when a String is created with line continuation { @@ -458,7 +509,12 @@ const errorTests = [ }, { send: '{ a: 1 }.a;', // { a: 1 }.a; - expect: [/^Uncaught SyntaxError: /] + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] }, { send: '{ a: 1 }["a"] === 1', // ({ a: 1 }['a'] === 1); @@ -478,6 +534,18 @@ const errorTests = [ send: '\'the\\\n\\\nfourtheye\'\n', expect: '| | \'thefourtheye\'' }, + // Regression test for https://github.com/nodejs/node/issues/597 + { + send: '/(.)(.)(.)(.)(.)(.)(.)(.)(.)/.test(\'123456789\')\n', + expect: 'true' + }, + // The following test's result depends on the RegExp's match from the above + { + send: 'RegExp.$1\nRegExp.$2\nRegExp.$3\nRegExp.$4\nRegExp.$5\n' + + 'RegExp.$6\nRegExp.$7\nRegExp.$8\nRegExp.$9\n', + expect: ['\'1\'', '\'2\'', '\'3\'', '\'4\'', '\'5\'', '\'6\'', + '\'7\'', '\'8\'', '\'9\''] + }, // Regression tests for https://github.com/nodejs/node/issues/2749 { send: 'function x() {\nreturn \'\\n\';\n }', @@ -552,7 +620,6 @@ const errorTests = [ /^ {4}at .*/, // Some stack frame that we have to capture otherwise error message is buggy. /^ {4}at .*/, // Some stack frame that we have to capture otherwise error message is buggy. /^ {4}at .*/, // Some stack frame that we have to capture otherwise error message is buggy. - /^ {4}at .*/, // Some stack frame that we have to capture otherwise error message is buggy. " code: 'MODULE_NOT_FOUND',", " requireStack: [ '' ]", '}', @@ -575,11 +642,20 @@ const errorTests = [ send: 'function x(s) {\nreturn s.replace(/.*/,"");\n}', expect: '| | undefined' }, + { + send: '{ var x = 4; }', + expect: 'undefined' + }, // Illegal token is not recoverable outside string literal, RegExp literal, // or block comment. https://github.com/nodejs/node/issues/3611 { send: 'a = 3.5e', - expect: [/^Uncaught SyntaxError: /] + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] }, // Mitigate https://github.com/nodejs/node/issues/548 { @@ -593,12 +669,22 @@ const errorTests = [ // Avoid emitting repl:line-number for SyntaxError { send: 'a = 3.5e', - expect: [/^Uncaught SyntaxError: /] + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] }, // Avoid emitting stack trace { send: 'a = 3.5e', - expect: [/^Uncaught SyntaxError: /] + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] }, // https://github.com/nodejs/node/issues/9850 @@ -661,7 +747,12 @@ const errorTests = [ // Do not parse `...[]` as a REPL keyword { send: '...[]', - expect: [/^Uncaught SyntaxError: /] + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] }, // Bring back the repl to prompt { @@ -670,15 +761,30 @@ const errorTests = [ }, { send: 'console.log("Missing comma in arg list" process.version)', - expect: [/^Uncaught SyntaxError: /] + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] }, { send: 'x = {\nfield\n{', - expect: [/^\| \| Uncaught SyntaxError: /] + expect: [ + '| | {', + kArrow, + '', + /^Uncaught SyntaxError: /, + ] }, { send: '(2 + 3))', - expect: [/^Uncaught SyntaxError: /] + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] }, { send: 'if (typeof process === "object"); {', @@ -690,7 +796,12 @@ const errorTests = [ }, { send: '} else {', - expect: [/^Uncaught SyntaxError: /] + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] }, { send: 'console', @@ -716,11 +827,13 @@ const errorTests = [ / {2}dirxml: \[Function: (dirxml|log)],/, / {2}groupCollapsed: \[Function: (groupCollapsed|group)],/, / {2}Console: \[Function: Console],?/, - ' profile: [Function: profile],', - ' profileEnd: [Function: profileEnd],', - ' timeStamp: [Function: timeStamp],', - ' context: [Function: context],', - ' createTask: [Function: createTask]', + ...process.features.inspector ? [ + ' profile: [Function: profile],', + ' profileEnd: [Function: profileEnd],', + ' timeStamp: [Function: timeStamp],', + ' context: [Function: context],', + ' createTask: [Function: createTask]', + ] : [], '}', ] }, @@ -743,6 +856,83 @@ const tcpTests = [ send: `require(${JSON.stringify(moduleFilename)}).number`, expect: '42' }, + { + send: 'import comeOn from \'fhqwhgads\'', + expect: [ + kSource, + kArrow, + '', + 'Uncaught:', + 'SyntaxError: Cannot use import statement inside the Node.js REPL, \ +alternatively use dynamic import: const { default: comeOn } = await import("fhqwhgads");', + ] + }, + { + send: 'import { export1, export2 } from "module-name"', + expect: [ + kSource, + kArrow, + '', + 'Uncaught:', + 'SyntaxError: Cannot use import statement inside the Node.js REPL, \ +alternatively use dynamic import: const { export1, export2 } = await import("module-name");', + ] + }, + { + send: 'import * as name from "module-name";', + expect: [ + kSource, + kArrow, + '', + 'Uncaught:', + 'SyntaxError: Cannot use import statement inside the Node.js REPL, \ +alternatively use dynamic import: const name = await import("module-name");', + ] + }, + { + send: 'import "module-name";', + expect: [ + kSource, + kArrow, + '', + 'Uncaught:', + 'SyntaxError: Cannot use import statement inside the Node.js REPL, \ +alternatively use dynamic import: await import("module-name");', + ] + }, + { + send: 'import { export1 as localName1, export2 } from "bar";', + expect: [ + kSource, + kArrow, + '', + 'Uncaught:', + 'SyntaxError: Cannot use import statement inside the Node.js REPL, \ +alternatively use dynamic import: const { export1: localName1, export2 } = await import("bar");', + ] + }, + { + send: 'import alias from "bar";', + expect: [ + kSource, + kArrow, + '', + 'Uncaught:', + 'SyntaxError: Cannot use import statement inside the Node.js REPL, \ +alternatively use dynamic import: const { default: alias } = await import("bar");', + ] + }, + { + send: 'import alias, {namedExport} from "bar";', + expect: [ + kSource, + kArrow, + '', + 'Uncaught:', + 'SyntaxError: Cannot use import statement inside the Node.js REPL, \ +alternatively use dynamic import: const { default: alias, namedExport } = await import("bar");', + ] + }, ]; (async function() { diff --git a/test/parallel/test-stream-iter-push-writer.js b/test/parallel/test-stream-iter-push-writer.js index 32961612804211..f085f465951449 100644 --- a/test/parallel/test-stream-iter-push-writer.js +++ b/test/parallel/test-stream-iter-push-writer.js @@ -147,16 +147,10 @@ async function testOndrainRejectsOnConsumerThrow() { // Consumer throws via iterator.throw() before draining enough // to clear backpressure. The drain should reject. const iter = readable[Symbol.asyncIterator](); - const err = new Error('consumer error'); - const drainRejects = assert.rejects(drainPromise, (e) => e === err); - const pendingWriteRejects = pendingWrite.catch(() => {}); - await assert.rejects( - () => iter.throw(err), - (e) => e === err, - ); + await iter.throw(new Error('consumer error')); - await drainRejects; - await pendingWriteRejects; // Ignore write rejection + await assert.rejects(drainPromise, /consumer error/); + await pendingWrite.catch(() => {}); // Ignore write rejection } async function testWritev() { @@ -309,11 +303,7 @@ async function testConsumerThrowRejectsWrites() { writer.writeSync('a'); const iter = readable[Symbol.asyncIterator](); - const err = new Error('consumer boom'); - await assert.rejects( - () => iter.throw(err), - (e) => e === err, - ); + await iter.throw(new Error('consumer boom')); // Subsequent async writes should reject with the consumer's error await assert.rejects( @@ -322,18 +312,6 @@ async function testConsumerThrowRejectsWrites() { ); } -async function testConsumerThrowRejectsWithThrownError() { - const { readable } = push(); - - const iter = readable[Symbol.asyncIterator](); - const err = new Error('boom'); - - await assert.rejects( - () => iter.throw(err), - (e) => e === err, - ); -} - // end() resolves a pending read as done:true async function testEndResolvesPendingRead() { const { writer, readable } = push(); @@ -395,16 +373,14 @@ async function testConsumerThrowRejectsPendingRead() { await new Promise(setImmediate); const err = new Error('consumer read boom'); - const readRejects = assert.rejects( - () => readPromise, - (e) => e === err, - ); + const throwResult = await iter.throw(err); + assert.strictEqual(throwResult.value, undefined); + assert.strictEqual(throwResult.done, true); + await assert.rejects( - () => iter.throw(err), + () => readPromise, (e) => e === err, ); - - await readRejects; } // end() while writes are pending rejects those writes @@ -551,7 +527,6 @@ Promise.all([ testWriteUint8Array(), testOndrainWaitsForDrain(), testConsumerThrowRejectsWrites(), - testConsumerThrowRejectsWithThrownError(), testEndResolvesPendingRead(), testFailRejectsPendingRead(), testFailRejectsFutureReadWithFalsyReason(), diff --git a/test/parallel/test-stream-iter-validation.js b/test/parallel/test-stream-iter-validation.js index 08eeaeee685656..ecefaeb171ec41 100644 --- a/test/parallel/test-stream-iter-validation.js +++ b/test/parallel/test-stream-iter-validation.js @@ -3,9 +3,8 @@ const common = require('../common'); const assert = require('assert'); -const { Writable } = require('stream'); const { - from, fromSync, pull, pullSync, pipeTo, fromWritable, + from, fromSync, pull, pullSync, pipeTo, push, duplex, broadcast, Broadcast, share, shareSync, Share, SyncShare, bytes, bytesSync, text, textSync, @@ -43,19 +42,6 @@ assert.throws(() => push({ signal: {} }), { code: 'ERR_INVALID_ARG_TYPE' }); assert.throws(() => push(42, {}), { code: 'ERR_INVALID_ARG_TYPE' }); assert.throws(() => push('bad', {}), { code: 'ERR_INVALID_ARG_TYPE' }); -// Writer options.signal must be AbortSignal -{ - const { writer } = push(); - const badOptions = { signal: 'bad' }; - assert.throws(() => writer.write('a', badOptions), - { code: 'ERR_INVALID_ARG_TYPE' }); - assert.throws(() => writer.writev(['b'], badOptions), - { code: 'ERR_INVALID_ARG_TYPE' }); - assert.throws(() => writer.end(badOptions), - { code: 'ERR_INVALID_ARG_TYPE' }); - writer.endSync(); -} - // Writer.writev requires array { const { writer } = push(); @@ -161,19 +147,6 @@ assert.throws(() => broadcast({ highWaterMark: Number.MAX_SAFE_INTEGER + 1 }), assert.throws(() => broadcast({ signal: {} }), { code: 'ERR_INVALID_ARG_TYPE' }); assert.throws(() => broadcast({ backpressure: 'bad' }), { code: 'ERR_INVALID_ARG_VALUE' }); -// BroadcastWriter options.signal must be AbortSignal -{ - const { writer } = broadcast(); - const badOptions = { signal: 'bad' }; - assert.throws(() => writer.write('a', badOptions), - { code: 'ERR_INVALID_ARG_TYPE' }); - assert.throws(() => writer.writev(['b'], badOptions), - { code: 'ERR_INVALID_ARG_TYPE' }); - assert.throws(() => writer.end(badOptions), - { code: 'ERR_INVALID_ARG_TYPE' }); - writer.endSync(); -} - // BroadcastWriter.writev requires array { const { writer } = broadcast(); @@ -187,24 +160,6 @@ assert.throws(() => broadcast({ backpressure: 'bad' }), { code: 'ERR_INVALID_ARG // Broadcast.from rejects non-streamable input assert.throws(() => Broadcast.from(42), { code: 'ERR_INVALID_ARG_TYPE' }); -// fromWritable Writer options.signal must be AbortSignal -{ - const writable = new Writable({ - write(chunk, encoding, callback) { - callback(); - }, - }); - const writer = fromWritable(writable); - const badOptions = { signal: 'bad' }; - assert.throws(() => writer.write('a', badOptions), - { code: 'ERR_INVALID_ARG_TYPE' }); - assert.throws(() => writer.writev(['b'], badOptions), - { code: 'ERR_INVALID_ARG_TYPE' }); - assert.throws(() => writer.end(badOptions), - { code: 'ERR_INVALID_ARG_TYPE' }); - writable.destroy(); -} - // ============================================================================= // share() / shareSync() validation // ============================================================================= diff --git a/test/parallel/test-stream-pipe-multiple-destinations-error.js b/test/parallel/test-stream-pipe-multiple-destinations-error.js deleted file mode 100644 index eec7da6e971768..00000000000000 --- a/test/parallel/test-stream-pipe-multiple-destinations-error.js +++ /dev/null @@ -1,48 +0,0 @@ -'use strict'; -const common = require('../common'); - -// Regression test for https://github.com/nodejs/node/issues/53185 -// When a source is piped to multiple destinations and one destination errors -// synchronously in `_write`, the source must keep flowing to the remaining -// healthy destination(s) instead of stalling forever. - -const assert = require('assert'); -const { PassThrough, Writable, finished } = require('stream'); - -const source = new PassThrough({ highWaterMark: 16 * 1024 }); - -// A destination that errors synchronously on its first write. -const failing = new Writable({ - highWaterMark: 16 * 1024, - write(chunk, encoding, callback) { - callback(new Error('boom')); - }, -}); -failing.on('error', common.mustCall()); - -// A healthy destination that counts everything it receives. -let received = 0; -const healthy = new Writable({ - highWaterMark: 16 * 1024, - write(chunk, encoding, callback) { - received += chunk.length; - callback(); - }, -}); - -source.pipe(failing); -source.pipe(healthy); - -// Two chunks each larger than the highWaterMark so that `write()` returns -// false and the source is paused awaiting a drain. -const chunk = Buffer.alloc(20000, 'a'); -source.write(chunk); -source.write(chunk); -source.end(); - -finished(healthy, common.mustSucceed(() => { - // Without the fix, the errored destination stays in the source's - // awaitDrainWriters set, the source never resumes, and `healthy` only - // receives the first chunk. - assert.strictEqual(received, chunk.length * 2); -})); diff --git a/test/parallel/test-tls-psk-alpn-callback-exception-handling.js b/test/parallel/test-tls-psk-alpn-callback-exception-handling.js index 0ba55f9153b947..31d7802931fb36 100644 --- a/test/parallel/test-tls-psk-alpn-callback-exception-handling.js +++ b/test/parallel/test-tls-psk-alpn-callback-exception-handling.js @@ -95,7 +95,10 @@ describe('TLS callback exception handling', () => { reject(e); } })); - server.on('secureConnection', common.mustNotCall('secureConnection listener')); + + server.on('secureConnection', () => { + reject(new Error('secureConnection should not fire')); + }); await new Promise((res) => server.listen(0, res)); @@ -110,7 +113,7 @@ describe('TLS callback exception handling', () => { }), }); - client.on('error', common.mustCall()); + client.on('error', () => {}); await promise; }); @@ -304,6 +307,9 @@ describe('TLS callback exception handling', () => { } })); + server.on('secureConnection', () => { + reject(new Error('secureConnection should not fire')); + }); await new Promise((res) => server.listen(0, res)); @@ -318,7 +324,7 @@ describe('TLS callback exception handling', () => { }), }); - client.on('error', common.mustCall()); + client.on('error', () => {}); await promise; }); @@ -348,7 +354,10 @@ describe('TLS callback exception handling', () => { reject(e); } })); - server.on('secureConnection', common.mustNotCall('secureConnection listener')); + + server.on('secureConnection', () => { + reject(new Error('secureConnection should not fire')); + }); await new Promise((res) => server.listen(0, res)); @@ -359,7 +368,7 @@ describe('TLS callback exception handling', () => { ALPNProtocols: ['http/1.1', 'h2'], }); - client.on('error', common.mustCall()); + client.on('error', () => {}); await promise; }); @@ -388,7 +397,10 @@ describe('TLS callback exception handling', () => { reject(e); } })); - server.on('secureConnection', common.mustNotCall('secureConnection listener')); + + server.on('secureConnection', () => { + reject(new Error('secureConnection should not fire')); + }); await new Promise((res) => server.listen(0, res)); @@ -399,7 +411,7 @@ describe('TLS callback exception handling', () => { ALPNProtocols: ['http/1.1'], }); - client.on('error', common.mustCall()); + client.on('error', () => {}); await promise; }); @@ -418,7 +430,9 @@ describe('TLS callback exception handling', () => { const { promise, resolve, reject } = createTestPromise(); - server.on('secureConnection', common.mustNotCall('secureConnection listener')); + server.on('secureConnection', () => { + reject(new Error('secureConnection should not fire')); + }); await new Promise((res) => server.listen(0, res)); @@ -460,7 +474,9 @@ describe('TLS callback exception handling', () => { const { promise, resolve, reject } = createTestPromise(); - server.on('secureConnection', common.mustNotCall('secureConnection listener')); + server.on('secureConnection', () => { + reject(new Error('secureConnection should not fire')); + }); await new Promise((res) => server.listen(0, res)); @@ -511,7 +527,10 @@ describe('TLS callback exception handling', () => { reject(e); } })); - server.on('secureConnection', common.mustNotCall('secureConnection listener')); + + server.on('secureConnection', () => { + reject(new Error('secureConnection should not fire')); + }); await new Promise((res) => server.listen(0, res)); @@ -522,7 +541,7 @@ describe('TLS callback exception handling', () => { rejectUnauthorized: false, }); - client.on('error', common.mustCall()); + client.on('error', () => {}); await promise; }); @@ -555,7 +574,10 @@ describe('TLS callback exception handling', () => { reject(e); } })); - server.on('secureConnection', common.mustNotCall('secureConnection listener')); + + server.on('secureConnection', () => { + reject(new Error('secureConnection should not fire')); + }); await new Promise((res) => server.listen(0, res)); @@ -566,7 +588,7 @@ describe('TLS callback exception handling', () => { rejectUnauthorized: false, }); - client.on('error', common.mustCall()); + client.on('error', () => {}); await promise; }); diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index 1e305f0a45fb7c..6a1da6d4129fbd 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -107,7 +107,7 @@ assert.strictEqual(util.inspect({}), '{}'); assert.strictEqual(util.inspect({ a: 1 }), '{ a: 1 }'); assert.strictEqual(util.inspect({ a: function() {} }), '{ a: [Function: a] }'); assert.strictEqual(util.inspect({ a: () => {} }), '{ a: [Function: a] }'); -// eslint-disable-next-line node-core/func-name-matching +// eslint-disable-next-line func-name-matching assert.strictEqual(util.inspect({ a: async function abc() {} }), '{ a: [AsyncFunction: abc] }'); assert.strictEqual(util.inspect({ a: async () => {} }), diff --git a/test/parallel/test-util-sigint-watchdog.js b/test/parallel/test-util-sigint-watchdog.js new file mode 100644 index 00000000000000..88d5b9aa71629c --- /dev/null +++ b/test/parallel/test-util-sigint-watchdog.js @@ -0,0 +1,61 @@ +// Flags: --expose-internals +'use strict'; +const common = require('../common'); +if (common.isWindows) { + // No way to send CTRL_C_EVENT to processes from JS right now. + common.skip('platform not supported'); +} + +const { describe, test } = require('node:test'); +const assert = require('assert'); +const { internalBinding } = require('internal/test/binding'); +const binding = internalBinding('contextify'); + +describe({ concurrency: false }, () => { + test('with no signal observed', (_, next) => { + binding.startSigintWatchdog(); + const hadPendingSignals = binding.stopSigintWatchdog(); + assert.strictEqual(hadPendingSignals, false); + next(); + }); + test('with one call to the watchdog, one signal', (_, next) => { + binding.startSigintWatchdog(); + process.kill(process.pid, 'SIGINT'); + waitForPendingSignal(common.mustCall(() => { + const hadPendingSignals = binding.stopSigintWatchdog(); + assert.strictEqual(hadPendingSignals, true); + next(); + })); + }); + test('Nested calls are okay', (_, next) => { + binding.startSigintWatchdog(); + binding.startSigintWatchdog(); + process.kill(process.pid, 'SIGINT'); + waitForPendingSignal(common.mustCall(() => { + const hadPendingSignals1 = binding.stopSigintWatchdog(); + const hadPendingSignals2 = binding.stopSigintWatchdog(); + assert.strictEqual(hadPendingSignals1, true); + assert.strictEqual(hadPendingSignals2, false); + next(); + })); + }); + test('Signal comes in after first call to stop', (_, done) => { + binding.startSigintWatchdog(); + binding.startSigintWatchdog(); + const hadPendingSignals1 = binding.stopSigintWatchdog(); + process.kill(process.pid, 'SIGINT'); + waitForPendingSignal(common.mustCall(() => { + const hadPendingSignals2 = binding.stopSigintWatchdog(); + assert.strictEqual(hadPendingSignals1, false); + assert.strictEqual(hadPendingSignals2, true); + done(); + })); + }); +}); + +function waitForPendingSignal(cb) { + if (binding.watchdogHasPendingSigint()) + cb(); + else + setTimeout(waitForPendingSignal, 10, cb); +} diff --git a/test/parallel/test-vfs-fs-promises.js b/test/parallel/test-vfs-fs-promises.js index 0f09c96f83aec5..d4b151162f2802 100644 --- a/test/parallel/test-vfs-fs-promises.js +++ b/test/parallel/test-vfs-fs-promises.js @@ -75,16 +75,6 @@ const vfs = require('node:vfs'); await fsp.utimes(p('src/hello.txt'), now, now); await fsp.lutimes(p('src/hello.txt'), now, now); - await fsp.lchmod(p('src/plnk.txt'), 0o700); - assert.strictEqual( - (await fsp.lstat(p('src/hello.txt'))).mode & 0o777, - 0o644, - ); - assert.strictEqual( - (await fsp.lstat(p('src/plnk.txt'))).mode & 0o777, - 0o700, - ); - // FileHandle via fsp.open const handle = await fsp.open(p('src/hello.txt'), 'r'); assert.strictEqual(await handle.readFile('utf8'), 'hello'); diff --git a/test/parallel/test-vfs-stream-explicit-fd.js b/test/parallel/test-vfs-stream-explicit-fd.js index bac86b561493cc..d439e441a8699d 100644 --- a/test/parallel/test-vfs-stream-explicit-fd.js +++ b/test/parallel/test-vfs-stream-explicit-fd.js @@ -32,18 +32,6 @@ function readStream(stream) { })); } -// ReadStream with explicit fd via for-await; autoClose:false leaves fd open -(async () => { - const fd = myVfs.openSync('/file.txt', 'r'); - const stream = myVfs.createReadStream('/file.txt', { fd, autoClose: false }); - const chunks = []; - for await (const chunk of stream) { - chunks.push(chunk); - } - assert.strictEqual(Buffer.concat(chunks).toString(), 'hello world'); - myVfs.closeSync(fd); -})().then(common.mustCall()); - // WriteStream with explicit fd; autoClose:false leaves the fd open (async () => { const fd = myVfs.openSync('/fd-write.txt', 'w'); diff --git a/test/parallel/test-vfs-streams.js b/test/parallel/test-vfs-streams.js index 977834d719bdeb..83625d0072daaa 100644 --- a/test/parallel/test-vfs-streams.js +++ b/test/parallel/test-vfs-streams.js @@ -229,18 +229,6 @@ const { pipeline } = require('stream/promises'); })); } -// createReadStream via for-await iteration -(async () => { - const myVfs = vfs.create(); - myVfs.writeFileSync('/iter.txt', 'abc'); - const rs = myVfs.createReadStream('/iter.txt', { encoding: 'utf8' }); - const chunks = []; - for await (const chunk of rs) { - chunks.push(chunk); - } - assert.strictEqual(chunks.join(''), 'abc'); -})().then(common.mustCall()); - // start: beyond file size → empty stream { const myVfs = vfs.create(); @@ -269,17 +257,6 @@ const { pipeline } = require('stream/promises'); assert.strictEqual(myVfs.readFileSync('/out.txt', 'utf8'), 'hello world'); })().then(common.mustCall()); -// Pipeline read -(async () => { - const myVfs = vfs.create(); - myVfs.writeFileSync('/in.txt', 'hello world'); - await pipeline( - myVfs.createReadStream('/in.txt'), - myVfs.createWriteStream('/copied.txt'), - ); - assert.strictEqual(myVfs.readFileSync('/copied.txt', 'utf8'), 'hello world'); -})().then(common.mustCall()); - // Pipeline write with start position (async () => { const myVfs = vfs.create(); diff --git a/test/parallel/test-warn-tls-common-deprecation.js b/test/parallel/test-warn-tls-common-deprecation.js new file mode 100644 index 00000000000000..00ce2690a95676 --- /dev/null +++ b/test/parallel/test-warn-tls-common-deprecation.js @@ -0,0 +1,11 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) common.skip('missing crypto'); + +// _tls_common is deprecated. + +common.expectWarning('DeprecationWarning', + 'The _tls_common module is deprecated. Use `node:tls` instead.', 'DEP0192'); + +require('_tls_common'); diff --git a/test/parallel/test-warn-tls-wrap-deprecation.js b/test/parallel/test-warn-tls-wrap-deprecation.js new file mode 100644 index 00000000000000..53fc1e69830f99 --- /dev/null +++ b/test/parallel/test-warn-tls-wrap-deprecation.js @@ -0,0 +1,11 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) common.skip('missing crypto'); + +// _tls_wrap is deprecated. + +common.expectWarning('DeprecationWarning', + 'The _tls_wrap module is deprecated. Use `node:tls` instead.', 'DEP0192'); + +require('_tls_wrap'); diff --git a/test/pseudo-tty/repl-dumb-tty.js b/test/pseudo-tty/repl-dumb-tty.js new file mode 100644 index 00000000000000..7563ea43402de2 --- /dev/null +++ b/test/pseudo-tty/repl-dumb-tty.js @@ -0,0 +1,38 @@ +'use strict'; +const common = require('../common'); +const process = require('process'); + +process.env.TERM = 'dumb'; + +const repl = require('repl'); +const ArrayStream = require('../common/arraystream'); + +repl.start('> '); + +// Verify + D support. +{ + const stream = new ArrayStream(); + const replServer = new repl.REPLServer({ + prompt: '> ', + terminal: true, + input: stream, + output: process.stdout, + useColors: false, + }); + + replServer.on('close', common.mustCall()); + // Verify that + R or + C does not trigger the reverse search. + replServer.write(null, { ctrl: true, name: 'r' }); + replServer.write(null, { ctrl: true, name: 's' }); + replServer.write(null, { ctrl: true, name: 'd' }); +} + +process.stdin.push('conso'); // No completion preview. +process.stdin.push('le.log("foo")\n'); +process.stdin.push('1 + 2'); // No input preview. +process.stdin.push('\n'); +process.stdin.push('"str"\n'); +process.stdin.push('console.dir({ a: 1 })\n'); +process.stdin.push('{ a: 1 }\n'); +process.stdin.push('\n'); +process.stdin.push('.exit\n'); diff --git a/test/pseudo-tty/repl-dumb-tty.out b/test/pseudo-tty/repl-dumb-tty.out new file mode 100644 index 00000000000000..590267e9264b3f --- /dev/null +++ b/test/pseudo-tty/repl-dumb-tty.out @@ -0,0 +1,15 @@ +> > +console.log("foo") +foo +undefined +> 1 + 2 +3 +> "str" +'str' +> console.dir({ a: 1 }) +{ a: 1 } +undefined +> { a: 1 } +{ a: 1 } +> +> .exit diff --git a/test/sequential/test-repl-timeout-throw.js b/test/sequential/test-repl-timeout-throw.js index 20c60ca73fd5b7..fcebf762647fbf 100644 --- a/test/sequential/test-repl-timeout-throw.js +++ b/test/sequential/test-repl-timeout-throw.js @@ -1,8 +1,5 @@ 'use strict'; const common = require('../common'); - -common.skipIfInspectorDisabled(); - const assert = require('assert'); const spawn = require('child_process').spawn; diff --git a/test/wpt/status/WebCryptoAPI.cjs b/test/wpt/status/WebCryptoAPI.cjs index 4572f7e1dbcb94..60111e61edf95d 100644 --- a/test/wpt/status/WebCryptoAPI.cjs +++ b/test/wpt/status/WebCryptoAPI.cjs @@ -85,7 +85,7 @@ if (process.features.openssl_is_boringssl) { ['encap_decap/encap_decap_keys.tentative.https.any.js', /ml-kem-512/i], ['generateKey/failures_ML-KEM.tentative.https.any.js', /ml-kem-512/i], ['generateKey/successes_ML-KEM.tentative.https.any.js', /ml-kem-512/i], - ['getPublicKey.tentative.https.any.js', /(?:ed448|x448|ml-kem-512)/i], + ['getPublicKey.tentative.https.any.js', /ml-kem-512/i], ['import_export/ML-KEM_importKey.tentative.https.any.js', /ml-kem-512/i], ['serialization/mlkem.tentative.https.any.js', /ml-kem-512/i], ['supports-modern.tentative.https.any.js', /ml-kem-512/i]); diff --git a/tools/actions/merge.sh b/tools/actions/merge.sh index b811c18eb2c5e6..45947f64c2a4db 100755 --- a/tools/actions/merge.sh +++ b/tools/actions/merge.sh @@ -48,7 +48,7 @@ fi git log -1 HEAD --pretty='format:%B' | git interpret-trailers --parse --no-divider | \ grep -q -x "^PR-URL: https://github.com/$OWNER/$REPOSITORY/pull/$pr$" || { - echo "Invalid PR-URL trailer - have you run 'git node land' to add it?" + echo "Invalid PR-URL trailer" exit 1 } git log -1 HEAD^ --pretty='format:%B' | git interpret-trailers --parse --no-divider | \ diff --git a/tools/eslint-rules/func-name-matching.js b/tools/eslint-rules/func-name-matching.js deleted file mode 100644 index a2ff661f2cc7d7..00000000000000 --- a/tools/eslint-rules/func-name-matching.js +++ /dev/null @@ -1,329 +0,0 @@ -// @fileoverview Rule to require function names to match the name of the variable or property to which they -// are assigned. -// @author Annie Zhang -// @author Pavel Strashkin - -'use strict'; - -//-------------------------------------------------------------------------- -// Requirements -//-------------------------------------------------------------------------- - -const astUtils = require('../eslint/node_modules/eslint/lib/rules/utils/ast-utils'); -const esutils = require('../eslint/node_modules/esutils'); - -//-------------------------------------------------------------------------- -// Helpers -//-------------------------------------------------------------------------- - -/** - * Determines if a pattern is `module.exports` or `module['exports']` - * @param {ASTNode} pattern The left side of the AssignmentExpression - * @returns {boolean} True if the pattern is `module.exports` or `module['exports']` - */ -function isModuleExports(pattern) { - if ( - pattern.type === 'MemberExpression' && - pattern.object.type === 'Identifier' && - pattern.object.name === 'module' - ) { - // module.exports - if ( - pattern.property.type === 'Identifier' && - pattern.property.name === 'exports' - ) { - return true; - } - - // module['exports'] - if ( - pattern.property.type === 'Literal' && - pattern.property.value === 'exports' - ) { - return true; - } - } - return false; -} - -/** - * Determines if a string name is a valid identifier - * @param {string} name The string to be checked - * @param {number} ecmaVersion The ECMAScript version if specified in the parserOptions config - * @returns {boolean} True if the string is a valid identifier - */ -function isIdentifier(name, ecmaVersion) { - if (ecmaVersion >= 2015) { - return esutils.keyword.isIdentifierES6(name); - } - return esutils.keyword.isIdentifierES5(name); -} - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -const alwaysOrNever = { enum: ['always', 'never'] }; -const optionsObject = { - type: 'object', - properties: { - considerPropertyDescriptor: { - type: 'boolean', - }, - includeCommonJSModuleExports: { - type: 'boolean', - }, - }, - additionalProperties: false, -}; - -/** @type {import('../types').Rule.RuleModule} */ -module.exports = { - meta: { - type: 'suggestion', - - docs: { - description: - 'Require function names to match the name of the variable or property to which they are assigned', - recommended: false, - frozen: true, - url: 'https://eslint.org/docs/latest/rules/func-name-matching', - }, - - schema: { - anyOf: [ - { - type: 'array', - additionalItems: false, - items: [alwaysOrNever, optionsObject], - }, - { - type: 'array', - additionalItems: false, - items: [optionsObject], - }, - ], - }, - - messages: { - matchProperty: - 'Function name `{{funcName}}` should match property name `{{name}}`.', - matchVariable: - 'Function name `{{funcName}}` should match variable name `{{name}}`.', - notMatchProperty: - 'Function name `{{funcName}}` should not match property name `{{name}}`.', - notMatchVariable: - 'Function name `{{funcName}}` should not match variable name `{{name}}`.', - }, - }, - - create(context) { - const options = - (typeof context.options[0] === 'object' ? - context.options[0] : context.options[1]) || {}; - const nameMatches = - typeof context.options[0] === 'string' ? - context.options[0] : 'always'; - const considerPropertyDescriptor = options.considerPropertyDescriptor; - const includeModuleExports = options.includeCommonJSModuleExports; - const ecmaVersion = context.languageOptions.ecmaVersion; - - /** - * Check whether node is a certain CallExpression. - * @param {string} objName object name - * @param {string} funcName function name - * @param {ASTNode} node The node to check - * @returns {boolean} `true` if node matches CallExpression - */ - function isPropertyCall(objName, funcName, node) { - if (!node) { - return false; - } - return ( - node.type === 'CallExpression' && - astUtils.isSpecificMemberAccess(node.callee, objName, funcName) - ); - } - - function isIdentifierCall(expectedIdentifierName, node) { - if (!node) { - return false; - } - return node.type === 'CallExpression' && - astUtils.isSpecificId(node.callee, expectedIdentifierName); - } - - /** - * Compares identifiers based on the nameMatches option - * @param {string} x the first identifier - * @param {string} y the second identifier - * @returns {boolean} whether the two identifiers should warn. - */ - function shouldWarn(x, y) { - return ( - (nameMatches === 'always' && x !== y) || - (nameMatches === 'never' && x === y) - ); - } - - /** - * Reports - * @param {ASTNode} node The node to report - * @param {string} name The variable or property name - * @param {string} funcName The function name - * @param {boolean} isProp True if the reported node is a property assignment - * @returns {void} - */ - function report(node, name, funcName, isProp) { - let messageId; - - if (nameMatches === 'always' && isProp) { - messageId = 'matchProperty'; - } else if (nameMatches === 'always') { - messageId = 'matchVariable'; - } else if (isProp) { - messageId = 'notMatchProperty'; - } else { - messageId = 'notMatchVariable'; - } - context.report({ - node, - messageId, - data: { - name, - funcName, - }, - }); - } - - /** - * Determines whether a given node is a string literal - * @param {ASTNode} node The node to check - * @returns {boolean} `true` if the node is a string literal - */ - function isStringLiteral(node) { - return node.type === 'Literal' && typeof node.value === 'string'; - } - - //-------------------------------------------------------------------------- - // Public - //-------------------------------------------------------------------------- - - return { - 'VariableDeclarator[init.type="FunctionExpression"][id.type="Identifier"]'(node) { - if ( - node.init.id && - shouldWarn(node.id.name, node.init.id.name) - ) { - report(node, node.id.name, node.init.id.name, false); - } - }, - - 'AssignmentExpression[right.type="FunctionExpression"][left.type=/^(Identifier|MemberExpression)$/]'(node) { - if ( - (node.left.computed && node.left.property.type !== 'Literal') || - (!includeModuleExports && isModuleExports(node.left)) - ) { - return; - } - - const isProp = node.left.type === 'MemberExpression'; - const name = isProp ? astUtils.getStaticPropertyName(node.left) : node.left.name; - - if ( - node.right.id && - name && - isIdentifier(name) && - shouldWarn(name, node.right.id.name) - ) { - report(node, name, node.right.id.name, isProp); - } - }, - - ':matches(Property,PropertyDefinition)[value.type="FunctionExpression"][value.id]'(node) { - - if (node.key.type === 'Identifier' && !node.computed) { - const functionName = node.value.id.name; - let propertyName = node.key.name; - - if ( - considerPropertyDescriptor && - propertyName === 'value' && - node.parent.type === 'ObjectExpression' - ) { - if ( - isPropertyCall( - 'Object', - 'defineProperty', - node.parent.parent, - ) || - isPropertyCall( - 'Reflect', - 'defineProperty', - node.parent.parent, - ) || - isIdentifierCall('ObjectDefineProperty', node.parent.parent) || - isIdentifierCall('ReflectDefineProperty', node.parent.parent) - ) { - const property = node.parent.parent.arguments[1]; - - if ( - isStringLiteral(property) && - shouldWarn(property.value, functionName) - ) { - report( - node, - property.value, - functionName, - true, - ); - } - } else if ( - isPropertyCall( - 'Object', - 'defineProperties', - node.parent.parent.parent.parent, - ) || - isIdentifierCall('ObjectDefineProperties', node.parent.parent.parent.parent) - ) { - propertyName = node.parent.parent.key.name; - if ( - !node.parent.parent.computed && - shouldWarn(propertyName, functionName) - ) { - report(node, propertyName, functionName, true); - } - } else if ( - isPropertyCall( - 'Object', - 'create', - node.parent.parent.parent.parent, - ) - ) { - propertyName = node.parent.parent.key.name; - if ( - !node.parent.parent.computed && - shouldWarn(propertyName, functionName) - ) { - report(node, propertyName, functionName, true); - } - } else if (shouldWarn(propertyName, functionName)) { - report(node, propertyName, functionName, true); - } - } else if (shouldWarn(propertyName, functionName)) { - report(node, propertyName, functionName, true); - } - return; - } - - if ( - isStringLiteral(node.key) && - isIdentifier(node.key.value, ecmaVersion) && - shouldWarn(node.key.value, node.value.id.name) - ) { - report(node, node.key.value, node.value.id.name, true); - } - }, - }; - }, -}; diff --git a/tools/nix/pkgs-26.05.nix b/tools/nix/pkgs-26.05.nix index c87a0cad2262f4..cbee28de68e2f4 100644 --- a/tools/nix/pkgs-26.05.nix +++ b/tools/nix/pkgs-26.05.nix @@ -1,10 +1,10 @@ arg: let repo = "https://github.com/NixOS/nixpkgs"; - rev = "572a2c2b6faebd71246e3162e4217d7ca63a9300"; + rev = "fad15a0c9ebf6432dcba932743decbf8905cb024"; nixpkgs = import (builtins.fetchTarball { url = "${repo}/archive/${rev}.tar.gz"; - sha256 = "0c5xyqgip1kf7hinqbmfvsf8c7jwipbyj7dlb337gd058kz7zmwm"; + sha256 = "0q845gkz18nz463cwmkhvficl3jpncdvvnx8xlrj5gdp4c285rcn"; }) arg; in # Unstable channel no longer supports Intel architecture for macOS. We can use the 26.05 channel diff --git a/tools/nix/pkgs.nix b/tools/nix/pkgs.nix index 80954b54ad422a..62d48ecc98c776 100644 --- a/tools/nix/pkgs.nix +++ b/tools/nix/pkgs.nix @@ -1,10 +1,10 @@ arg: let repo = "https://github.com/NixOS/nixpkgs"; - rev = "2065d53daf2c81ed7b57947e2e56682ded62723a"; + rev = "d33369954a67ae3322177dc9a3d564092912120c"; nixpkgs = import (builtins.fetchTarball { url = "${repo}/archive/${rev}.tar.gz"; - sha256 = "0ib5k850qp6zgzzvkbkpv0iwy649hwd8ab6id5w2jq4jplnf6zr3"; + sha256 = "0ffi2k8hllbgqw9vvdfaxw88qz53gl7myll4lwri6ynq5l0lnvbc"; }) arg; in # Unstable channel no longer supports Intel architecture for macOS. We can use the 26.05 channel diff --git a/tsconfig.json b/tsconfig.json index d08908af141a62..c584b09c5456b6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -20,6 +20,8 @@ "_http_incoming": ["./lib/_http_incoming.js"], "_http_outgoing": ["./lib/_http_outgoing.js"], "_http_server": ["./lib/_http_server.js"], + "_tls_common": ["./lib/_tls_common.js"], + "_tls_wrap": ["./lib/_tls_wrap.js"], "assert": ["./lib/assert.js"], "assert/strict": ["./lib/assert/strict.js"], "async_hooks": ["./lib/async_hooks.js"],