diff --git a/.editorconfig b/.editorconfig index eebdfb046db52d..a42e2e3ff51519 100644 --- a/.editorconfig +++ b/.editorconfig @@ -16,12 +16,13 @@ end_of_line = crlf indent_size = 8 indent_style = tab -[{deps}/**] +[deps/**] charset = unset end_of_line = unset indent_size = unset indent_style = unset +insert_final_newline = unset trim_trailing_whitespace = unset -[{test/fixtures,deps,tools/eslint/node_modules,tools/gyp,tools/icu,tools/msvs}/**] +[{test/fixtures,tools/eslint/node_modules,tools/gyp,tools/icu,tools/msvs}/**] insert_final_newline = false diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 34b54a52ab4b5c..22fef9e8c2201e 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -243,3 +243,11 @@ # dev container /.devcontainer/* @nodejs/devcontainer /doc/contributing/using-devcontainer.md @nodejs/devcontainer + +# FFI +/deps/libffi/ @nodejs/ffi +/doc/api/ffi.md @nodejs/ffi +/lib/ffi.js @nodejs/ffi +/src/ffi/ @nodejs/ffi +/src/node_ffi.* @nodejs/ffi +/test/ffi/ @nodejs/ffi diff --git a/.github/actions/build-shared/action.yml b/.github/actions/build-shared/action.yml new file mode 100644 index 00000000000000..757440f1f36812 --- /dev/null +++ b/.github/actions/build-shared/action.yml @@ -0,0 +1,72 @@ +name: Build Node.js (shared libraries) +description: > + Downloads the slim tarball built by the `build-tarball` job, extracts it, + installs Nix (+ cachix + sccache), then builds Node.js and runs the CI + test suite inside the pinned nix-shell. + +inputs: + system: + description: System label (e.g. x86_64-linux, aarch64-darwin). + required: true + extra-nix-args: + description: Additional arguments appended to the nix-shell invocation. + required: false + default: '' + cachix-auth-token: + description: Cachix auth token for nodejs.cachix.org. + required: false + default: '' + +runs: + using: composite + steps: + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + if: ${{ github.event_name != 'workflow_dispatch' }} + with: + name: tarballs + path: tarballs + + - name: Extract tarball + if: ${{ github.event_name != 'workflow_dispatch' }} + shell: bash + run: | + tar xzf tarballs/*.tar.gz -C "$RUNNER_TEMP" + echo "TAR_DIR=$RUNNER_TEMP/$(basename tarballs/*.tar.gz .tar.gz)" >> "$GITHUB_ENV" + + - uses: cachix/install-nix-action@96951a368ba55167b55f1c916f7d416bac6505fe # v31.10.3 + with: + extra_nix_config: sandbox = true + + - uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17 + with: + name: nodejs + authToken: ${{ inputs.cachix-auth-token }} + + - name: Configure sccache + if: github.base_ref == 'main' || github.ref_name == 'main' + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + core.exportVariable('SCCACHE_GHA_ENABLED', 'on'); + core.exportVariable('ACTIONS_CACHE_SERVICE_V2', 'on'); + core.exportVariable('ACTIONS_RESULTS_URL', process.env.ACTIONS_RESULTS_URL || ''); + core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); + core.exportVariable('NIX_SCCACHE', '(import {}).sccache'); + + - name: Build Node.js and run tests + shell: bash + run: | + nix-shell \ + -I "nixpkgs=$TAR_DIR/tools/nix/pkgs.nix" \ + --pure --keep TAR_DIR --keep FLAKY_TESTS \ + --keep SCCACHE_GHA_ENABLED --keep ACTIONS_CACHE_SERVICE_V2 --keep ACTIONS_RESULTS_URL --keep ACTIONS_RUNTIME_TOKEN \ + --arg loadJSBuiltinsDynamically false \ + --arg useSeparateDerivationForV8 true \ + --arg ccache "${NIX_SCCACHE:-null}" \ + --arg devTools '[]' \ + --arg benchmarkTools '[]' \ + ${{ endsWith(inputs.system, '-darwin') && '--arg withAmaro false --arg withLief false --arg withSQLite false --arg withFFI false --arg extraConfigFlags ''["--without-inspector" "--without-node-options"]'' \' || '\' }} + ${{ inputs.extra-nix-args }} \ + --run ' + make -C "$TAR_DIR" run-ci -j4 V=1 TEST_CI_ARGS="-p actions --measure-flakiness 9 --skip-tests=$CI_SKIP_TESTS" + ' "$TAR_DIR/shell.nix" diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 37b865597e5d25..c63475dc60f5d2 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -52,6 +52,8 @@ updates: semver-major-days: 5 semver-minor-days: 5 semver-patch-days: 5 + exclude: + - '@node-core/doc-kit' commit-message: prefix: tools open-pull-requests-limit: 10 diff --git a/.github/workflows/auto-start-ci.yml b/.github/workflows/auto-start-ci.yml index 71df132f7208e3..34488eeed6d72d 100644 --- a/.github/workflows/auto-start-ci.yml +++ b/.github/workflows/auto-start-ci.yml @@ -46,7 +46,7 @@ jobs: runs-on: ubuntu-slim steps: - name: Install Node.js - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: ${{ env.NODE_VERSION }} diff --git a/.github/workflows/commit-lint.yml b/.github/workflows/commit-lint.yml index 5770bcf5513871..8f652a91782aea 100644 --- a/.github/workflows/commit-lint.yml +++ b/.github/workflows/commit-lint.yml @@ -27,7 +27,7 @@ jobs: persist-credentials: false - run: git reset HEAD^2 - name: Install Node.js - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: ${{ env.NODE_VERSION }} - name: Validate commit message diff --git a/.github/workflows/commit-queue.yml b/.github/workflows/commit-queue.yml index 43e5ef30fe8b4b..dda452bed46f92 100644 --- a/.github/workflows/commit-queue.yml +++ b/.github/workflows/commit-queue.yml @@ -69,7 +69,7 @@ jobs: # Install dependencies - name: Install Node.js - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: ${{ env.NODE_VERSION }} - name: Install @node-core/utils diff --git a/.github/workflows/create-release-proposal.yml b/.github/workflows/create-release-proposal.yml index 7e340bf51b8833..3c1437e0ac76fd 100644 --- a/.github/workflows/create-release-proposal.yml +++ b/.github/workflows/create-release-proposal.yml @@ -40,7 +40,7 @@ jobs: # Install dependencies - name: Install Node.js - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: ${{ env.NODE_VERSION }} diff --git a/.github/workflows/daily-wpt-fyi.yml b/.github/workflows/daily-wpt-fyi.yml index 3899b6bd28e1a5..3aa8e5139f686e 100644 --- a/.github/workflows/daily-wpt-fyi.yml +++ b/.github/workflows/daily-wpt-fyi.yml @@ -52,7 +52,7 @@ jobs: run: echo "NIGHTLY=$(curl -s https://nodejs.org/download/nightly/index.json | jq -r '[.[] | select(.files[] | contains("linux-arm64"))][0].version')" >> $GITHUB_ENV - name: Install Node.js id: setup-node - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: ${{ env.NIGHTLY || matrix.node-version }} check-latest: true diff --git a/.github/workflows/daily.yml b/.github/workflows/daily.yml index 545685c2581f17..80e7f8294d693f 100644 --- a/.github/workflows/daily.yml +++ b/.github/workflows/daily.yml @@ -19,7 +19,7 @@ jobs: with: persist-credentials: false - name: Use Node.js ${{ env.NODE_VERSION }} - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: ${{ env.NODE_VERSION }} - name: Environment Information diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index 0f1ab6cff6d185..f2281dc7e71ea5 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -28,7 +28,7 @@ jobs: with: persist-credentials: false - name: Use Node.js ${{ env.NODE_VERSION }} - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: ${{ env.NODE_VERSION }} - name: Environment Information diff --git a/.github/workflows/find-inactive-collaborators.yml b/.github/workflows/find-inactive-collaborators.yml index ad6c4ff9d0e502..1453cb24520da1 100644 --- a/.github/workflows/find-inactive-collaborators.yml +++ b/.github/workflows/find-inactive-collaborators.yml @@ -25,7 +25,7 @@ jobs: persist-credentials: false - name: Use Node.js ${{ env.NODE_VERSION }} - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: ${{ env.NODE_VERSION }} diff --git a/.github/workflows/find-inactive-tsc.yml b/.github/workflows/find-inactive-tsc.yml index ca0f1fdcb4acec..b3e6d7e2c733ca 100644 --- a/.github/workflows/find-inactive-tsc.yml +++ b/.github/workflows/find-inactive-tsc.yml @@ -34,7 +34,7 @@ jobs: repository: nodejs/TSC - name: Use Node.js ${{ env.NODE_VERSION }} - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: ${{ env.NODE_VERSION }} diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index eb86e8e8c5d40f..1ff1eb21002771 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -29,7 +29,7 @@ jobs: with: persist-credentials: false - name: Use Node.js ${{ env.NODE_VERSION }} - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: ${{ env.NODE_VERSION }} - name: Environment Information @@ -61,7 +61,7 @@ jobs: fetch-depth: 0 persist-credentials: false - name: Use Node.js ${{ env.NODE_VERSION }} - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: ${{ env.NODE_VERSION }} - name: Set up Python ${{ env.PYTHON_VERSION }} @@ -99,7 +99,7 @@ jobs: with: persist-credentials: false - name: Use Node.js ${{ env.NODE_VERSION }} - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: ${{ env.NODE_VERSION }} - name: Environment Information diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index b05d325860edc8..ea106a95accadf 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -35,7 +35,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1 + uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40 # v2.19.0 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs diff --git a/.github/workflows/test-shared.yml b/.github/workflows/test-shared.yml index c746b2d75e9fde..2564250e98c2b1 100644 --- a/.github/workflows/test-shared.yml +++ b/.github/workflows/test-shared.yml @@ -47,6 +47,7 @@ on: - vcbuild.bat - .** - '!.github/workflows/test-shared.yml' + - '!.github/actions/build-shared/**' types: [opened, synchronize, reopened, ready_for_review] push: branches: @@ -97,6 +98,7 @@ on: - vcbuild.bat - .** - '!.github/workflows/test-shared.yml' + - '!.github/actions/build-shared/**' concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} @@ -144,8 +146,9 @@ jobs: include: - runner: ubuntu-24.04 system: x86_64-linux - - runner: ubuntu-24.04-arm - system: aarch64-linux + # built separately in build-aarch64-linux-v8 + # - runner: ubuntu-24.04-arm + # system: aarch64-linux - runner: macos-15-intel system: x86_64-darwin - runner: macos-latest @@ -153,50 +156,111 @@ jobs: name: '${{ matrix.system }}: with shared libraries' runs-on: ${{ matrix.runner }} steps: - - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 if: ${{ github.event_name != 'workflow_dispatch' }} + with: + persist-credentials: false + sparse-checkout: .github/actions + - uses: ./.github/actions/build-shared + if: ${{ github.event_name != 'workflow_dispatch' }} + with: + system: ${{ matrix.system }} + cachix-auth-token: ${{ secrets.CACHIX_AUTH_TOKEN }} + + build-aarch64-linux-v8: + needs: build-tarball + runs-on: ubuntu-24.04-arm + name: 'aarch64-linux: Cache V8 build' + steps: + - name: Check if Cachix is available + id: cachix-check + run: echo 'IS_AVAILABLE=${{ secrets.CACHIX_AUTH_TOKEN && 'true' }}' >> "$GITHUB_OUTPUT" + + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + if: ${{ steps.cachix-check.outputs.IS_AVAILABLE == 'true' }} with: name: tarballs path: tarballs - name: Extract tarball - if: ${{ github.event_name != 'workflow_dispatch' }} + if: ${{ steps.cachix-check.outputs.IS_AVAILABLE == 'true' }} + shell: bash run: | tar xzf tarballs/*.tar.gz -C "$RUNNER_TEMP" echo "TAR_DIR=$RUNNER_TEMP/$(basename tarballs/*.tar.gz .tar.gz)" >> "$GITHUB_ENV" - uses: cachix/install-nix-action@96951a368ba55167b55f1c916f7d416bac6505fe # v31.10.3 + if: ${{ steps.cachix-check.outputs.IS_AVAILABLE == 'true' }} with: extra_nix_config: sandbox = true - uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17 + if: ${{ steps.cachix-check.outputs.IS_AVAILABLE == 'true' }} with: name: nodejs authToken: ${{ secrets.CACHIX_AUTH_TOKEN }} - - name: Configure sccache - if: github.base_ref == 'main' || github.ref_name == 'main' - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + - name: Build V8 derivation + if: ${{ steps.cachix-check.outputs.IS_AVAILABLE == 'true' }} + run: | + nix-build "$( + nix-instantiate -E "builtins.filter (p: p.pname == ''v8'') (import $TAR_DIR/shell.nix { useSeparateDerivationForV8=true; }).buildInputs" + )" + + # Builds the matrix for `build-openssl` from tools/nix/openssl-matrix.json. + # Output shape: + # [{ "version": "3.6.1", "attr": "openssl_3_6", "continue-on-error": false }, ...] + collect-openssl-versions: + if: github.event.pull_request.draft == false + runs-on: ubuntu-slim + outputs: + matrix: ${{ steps.query.outputs.matrix }} + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - script: | - core.exportVariable('SCCACHE_GHA_ENABLED', 'on'); - core.exportVariable('ACTIONS_CACHE_SERVICE_V2', 'on'); - core.exportVariable('ACTIONS_RESULTS_URL', process.env.ACTIONS_RESULTS_URL || ''); - core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); - core.exportVariable('NIX_SCCACHE', '(import {}).sccache'); - - - name: Build Node.js and run tests + persist-credentials: false + sparse-checkout: tools/nix/openssl-matrix.json + sparse-checkout-cone-mode: false + - id: query run: | - nix-shell \ - -I "nixpkgs=$TAR_DIR/tools/nix/pkgs.nix" \ - --pure --keep TAR_DIR --keep FLAKY_TESTS \ - --keep SCCACHE_GHA_ENABLED --keep ACTIONS_CACHE_SERVICE_V2 --keep ACTIONS_RESULTS_URL --keep ACTIONS_RUNTIME_TOKEN \ - --arg loadJSBuiltinsDynamically false \ - --arg useSeparateDerivationForV8 true \ - --arg ccache "${NIX_SCCACHE:-null}" \ - --arg devTools '[]' \ - --arg benchmarkTools '[]' \ - ${{ endsWith(matrix.system, '-darwin') && '--arg withAmaro false --arg withLief false --arg withSQLite false --arg withFFI false --arg extraConfigFlags ''["--without-inspector" "--without-node-options"]'' \' || '\' }} - --run ' - make -C "$TAR_DIR" run-ci -j4 V=1 TEST_CI_ARGS="-p actions --measure-flakiness 9 --skip-tests=$CI_SKIP_TESTS" - ' "$TAR_DIR/shell.nix" + { + echo 'matrix<> "$GITHUB_OUTPUT" + + # Builds and tests Node.js with shared libraries against every supported + # OpenSSL release version available in the repo-pinned nixpkgs. The default + # shared `openssl` from tools/nix/sharedLibDeps.nix is overridden per matrix + # entry, while all other shared libs remain at their defaults. Only runs on + # a single runner/system (aarch64-linux) to keep the matrix to a minimum. + build-openssl: + needs: + - build-aarch64-linux-v8 + - collect-openssl-versions + strategy: + fail-fast: false + matrix: + openssl: ${{ fromJSON(needs.collect-openssl-versions.outputs.matrix) }} + name: 'aarch64-linux: with shared ${{ matrix.openssl.attr }} (${{ matrix.openssl.version }})' + runs-on: ubuntu-24.04-arm + continue-on-error: ${{ matrix.openssl['continue-on-error'] }} + env: + OPENSSL_ATTR: ${{ matrix.openssl.attr }} + OPENSSL_VERSION: ${{ matrix.openssl.version }} + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + sparse-checkout: .github/actions + - uses: ./.github/actions/build-shared + with: + system: aarch64-linux + cachix-auth-token: ${{ secrets.CACHIX_AUTH_TOKEN }} + # Override just the `openssl` attr of the default shared-lib set with + # the matrix-selected nixpkgs attribute (e.g. `openssl_3_6`). All + # other shared libs (brotli, cares, libuv, …) keep their defaults. + # `permittedInsecurePackages` whitelists just the matrix-selected + # release (e.g. `openssl-1.1.1w`) so EOL-with-extended-support + # cycles evaluate without relaxing nixpkgs' meta check globally. + extra-nix-args: --arg sharedLibDeps "(import $TAR_DIR/tools/nix/sharedLibDeps.nix {}) // { openssl = (import $TAR_DIR/tools/nix/pkgs.nix { config.permittedInsecurePackages = [ \"openssl-$OPENSSL_VERSION\" ]; }).$OPENSSL_ATTR; }" diff --git a/.github/workflows/tools.yml b/.github/workflows/tools.yml index f45ffd68ca6b76..ad244c41eddb93 100644 --- a/.github/workflows/tools.yml +++ b/.github/workflows/tools.yml @@ -307,10 +307,6 @@ jobs: tail -n1 temp-output | grep "NEW_VERSION=" >> "$GITHUB_ENV" || true rm temp-output steps: - - name: Setup Git config - run: | - git config --global user.name "Node.js GitHub Bot" - git config --global user.email "github-bot@iojs.org" - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 if: github.event_name == 'schedule' || inputs.id == 'all' || inputs.id == matrix.id with: @@ -334,7 +330,7 @@ jobs: if: env.COMMIT_MSG == '' && (github.event_name == 'schedule' || inputs.id == 'all' || inputs.id == matrix.id) run: | echo "COMMIT_MSG=${{ matrix.subsystem }}: update ${{ matrix.id }} to ${{ env.NEW_VERSION }}" >> "$GITHUB_ENV" - - uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0 + - uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 if: github.event_name == 'schedule' || inputs.id == 'all' || inputs.id == matrix.id # Creates a PR or update the Action's existing PR, or # no-op if the base branch is already up-to-date. @@ -346,3 +342,5 @@ jobs: labels: ${{ matrix.label }} title: '${{ matrix.subsystem }}: update ${{ matrix.id }} to ${{ env.NEW_VERSION }}' body: This is an automated update of ${{ matrix.id }} to ${{ env.NEW_VERSION }}. + committer: ${{ github.actor }} <${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com> + author: Node.js GitHub Bot diff --git a/.github/workflows/update-v8.yml b/.github/workflows/update-v8.yml index 532fa2731f4a94..8d2db8261cf455 100644 --- a/.github/workflows/update-v8.yml +++ b/.github/workflows/update-v8.yml @@ -30,7 +30,7 @@ jobs: ~/.npm key: ${{ runner.os }}-build-${{ env.cache-name }} - name: Install Node.js - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: ${{ env.NODE_VERSION }} - name: Install @node-core/utils @@ -45,7 +45,7 @@ jobs: cat temp-output tail -n1 temp-output | grep "NEW_VERSION=" >> "$GITHUB_ENV" || true rm temp-output - - uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0 + - uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 # Creates a PR or update the Action's existing PR, or # no-op if the base branch is already up-to-date. with: diff --git a/.github/workflows/update-wpt.yml b/.github/workflows/update-wpt.yml index 71e5669d10bfdc..85a3115848135f 100644 --- a/.github/workflows/update-wpt.yml +++ b/.github/workflows/update-wpt.yml @@ -32,7 +32,7 @@ jobs: persist-credentials: false - name: Install Node.js - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: ${{ env.NODE_VERSION }} diff --git a/Makefile b/Makefile index 12851f2bc2911a..fb4a43bfed7864 100644 --- a/Makefile +++ b/Makefile @@ -391,17 +391,15 @@ DOC_KIT ?= tools/doc/node_modules/@node-core/doc-kit/bin/cli.mjs node_use_openssl_and_icu = $(call available-node,"-p" \ "process.versions.openssl != undefined && process.versions.icu != undefined") -test/addons/.docbuildstamp: $(DOCBUILDSTAMP_PREREQS) tools/doc/node_modules +test/addons/.docbuildstamp: $(DOCBUILDSTAMP_PREREQS) tools/doc/addon-verify.mjs @if [ "$(shell $(node_use_openssl_and_icu))" != "true" ]; then \ echo "Skipping .docbuildstamp (no crypto and/or no ICU)"; \ else \ $(RM) -r test/addons/??_*/; \ $(call available-node, \ - $(DOC_KIT) generate \ - -t addon-verify \ - -i doc/api/addons.md \ - -o test/addons/ \ - --type-map doc/type-map.json \ + tools/doc/addon-verify.mjs \ + --input doc/api/addons.md \ + --output test/addons/ \ ) \ [ $$? -eq 0 ] && touch $@; \ fi diff --git a/README.md b/README.md index e651f81939a942..c137f44f4b7a4e 100644 --- a/README.md +++ b/README.md @@ -451,8 +451,6 @@ For information about the governance of the Node.js project, see **Ulises Gascón** <> (he/him) * [vmoroz](https://github.com/vmoroz) - **Vladimir Morozov** <> (he/him) -* [VoltrexKeyva](https://github.com/VoltrexKeyva) - - **Mohammed Keyvanzadeh** <> (he/him) * [watilde](https://github.com/watilde) - **Daijiro Wachi** <> (he/him) * [zcbenz](https://github.com/zcbenz) - @@ -717,6 +715,8 @@ For information about the governance of the Node.js project, see **Vladimir de Turckheim** <> (he/him) * [vkurchatkin](https://github.com/vkurchatkin) - **Vladimir Kurchatkin** <> +* [VoltrexKeyva](https://github.com/VoltrexKeyva) - + **Mohammed Keyvanzadeh** <> (he/him) * [vsemozhetbyt](https://github.com/vsemozhetbyt) - **Vse Mozhet Byt** <> (he/him) * [watson](https://github.com/watson) - @@ -747,8 +747,6 @@ maintaining the Node.js project. **Sangchul Lee** <<1ilsang.dev@gmail.com>> (he/him) * [atlowChemi](https://github.com/atlowChemi) - **Chemi Atlow** <> (he/him) -* [Ayase-252](https://github.com/Ayase-252) - - **Qingyu Deng** <> * [bjohansebas](https://github.com/bjohansebas) - **Sebastian Beltran** <> * [bmuenzenmeyer](https://github.com/bmuenzenmeyer) - @@ -773,16 +771,12 @@ maintaining the Node.js project. **Kevin Eady** <> (he/him) * [marsonya](https://github.com/marsonya) - **Akhil Marsonya** <> (he/him) -* [meixg](https://github.com/meixg) - - **Xuguang Mei** <> (he/him) * [milesguicent](https://github.com/milesguicent) - **Miles Guicent** <> (he/him) * [preveen-stack](https://github.com/preveen-stack) - **Preveen Padmanabhan** <> (he/him) * [RaisinTen](https://github.com/RaisinTen) - **Darshan Sen** <> (he/him) -* [VoltrexKeyva](https://github.com/VoltrexKeyva) - - **Mohammed Keyvanzadeh** <> (he/him) Triagers follow the [Triage Guide](./doc/contributing/issues.md#triaging-a-bug-report) when responding to new issues. diff --git a/SECURITY.md b/SECURITY.md index 540ab0f9dee80d..0e88d7b50702fa 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -213,7 +213,7 @@ then untrusted input must not lead to arbitrary JavaScript code execution. along with anything under the control of the operating system. * The code it is asked to run, including JavaScript, WASM and native code, even if said code is dynamically loaded, e.g., all dependencies installed from the - npm registry. + npm registry or libraries loaded via `node:ffi`. The code run inherits all the privileges of the execution user. * Inputs provided to it by the code it is asked to run, as it is the responsibility of the application to perform the required input validations, diff --git a/benchmark/crypto/kem.js b/benchmark/crypto/kem.js index e03ae65f1926ca..ffdcac6d7fcb0d 100644 --- a/benchmark/crypto/kem.js +++ b/benchmark/crypto/kem.js @@ -54,9 +54,6 @@ const bench = common.createBenchmark(main, { // assess whether mutexes over the key material impact the operation if (p.keyFormat === 'keyObject.unique') return p.mode === 'async-parallel'; - // JWK is not supported for ml-kem for now - if (p.keyFormat === 'jwk') - return !p.keyType.startsWith('ml-'); // raw-public is only supported for encapsulate, not rsa if (p.keyFormat === 'raw-public') return p.keyType !== 'rsa' && p.op === 'encapsulate'; diff --git a/benchmark/misc/webidl-buffer-source.js b/benchmark/misc/webidl-buffer-source.js new file mode 100644 index 00000000000000..56a3b628b98011 --- /dev/null +++ b/benchmark/misc/webidl-buffer-source.js @@ -0,0 +1,54 @@ +'use strict'; + +const common = require('../common.js'); + +const bench = common.createBenchmark(main, { + input: [ + 'arraybuffer', + 'buffer', + 'dataview', + 'uint8array', + 'uint8clampedarray', + 'int8array', + 'uint16array', + 'int16array', + 'uint32array', + 'int32array', + 'float16array', + 'float32array', + 'float64array', + 'bigint64array', + 'biguint64array', + ], + n: [1e6], +}, { flags: ['--expose-internals'] }); + +function main({ n, input }) { + const { converters } = require('internal/webidl'); + + let value; + switch (input) { + case 'arraybuffer': value = new ArrayBuffer(32); break; + case 'buffer': value = Buffer.alloc(32); break; + case 'dataview': value = new DataView(new ArrayBuffer(32)); break; + case 'uint8array': value = new Uint8Array(32); break; + case 'uint8clampedarray': value = new Uint8ClampedArray(32); break; + case 'int8array': value = new Int8Array(32); break; + case 'uint16array': value = new Uint16Array(16); break; + case 'int16array': value = new Int16Array(16); break; + case 'uint32array': value = new Uint32Array(8); break; + case 'int32array': value = new Int32Array(8); break; + case 'float16array': value = new Float16Array(16); break; + case 'float32array': value = new Float32Array(8); break; + case 'float64array': value = new Float64Array(4); break; + case 'bigint64array': value = new BigInt64Array(4); break; + case 'biguint64array': value = new BigUint64Array(4); break; + } + + const opts = { prefix: 'test', context: 'test' }; + + bench.start(); + for (let i = 0; i < n; i++) + converters.BufferSource(value, opts); + bench.end(n); +} diff --git a/benchmark/url/url-searchparams-mutation.js b/benchmark/url/url-searchparams-mutation.js new file mode 100644 index 00000000000000..cb01793037437e --- /dev/null +++ b/benchmark/url/url-searchparams-mutation.js @@ -0,0 +1,43 @@ +'use strict'; +const common = require('../common.js'); + +const bench = common.createBenchmark(main, { + method: ['set', 'delete'], + type: ['unique', 'duplicates'], + count: [10, 1000], + n: [1e4], +}); + +function buildSeed(type, count) { + const parts = new Array(count); + + if (type === 'duplicates') { + for (let i = 0; i < count; i++) { + parts[i] = `dup=${i}`; + } + } else { + for (let i = 0; i < count; i++) { + parts[i] = `k${i}=${i}`; + } + } + + return new URLSearchParams(parts.join('&')); +} + +function main({ method, type, count, n }) { + const seed = buildSeed(type, count); + const key = type === 'duplicates' ? 'dup' : 'k0'; + const mutate = method === 'set' ? + (params) => params.set(key, 'updated') : + (params) => params.delete(key); + + for (let i = 0; i < 1e3; i++) { + mutate(new URLSearchParams(seed)); + } + + bench.start(); + for (let i = 0; i < n; i++) { + mutate(new URLSearchParams(seed)); + } + bench.end(n); +} diff --git a/common.gypi b/common.gypi index b9baa1f35ed5e1..de828a0f74cc12 100644 --- a/common.gypi +++ b/common.gypi @@ -12,9 +12,11 @@ 'msvs_multi_core_compile': '0', # we do enable multicore compiles, but not using the V8 way 'enable_pgo_generate%': '0', 'enable_pgo_use%': '0', + 'clang_profile_lib%': '', 'python%': 'python', 'node_shared%': 'false', + 'node_enable_experimentals%': 'false', 'force_dynamic_crt%': 0, 'node_use_v8_platform%': 'true', 'node_use_bundled_v8%': 'true', @@ -242,6 +244,65 @@ },], ], },], + ['OS=="win"', { + 'conditions': [ + ['enable_lto=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['-flto=full'], + }, + 'VCLibrarianTool': { + 'AdditionalOptions': ['-flto=full'], + }, + 'VCLinkerTool': { + 'AdditionalOptions': ['-flto=full'], + }, + }, + },], + ['enable_thin_lto=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['-flto=thin'], + }, + 'VCLibrarianTool': { + 'AdditionalOptions': ['-flto=thin'], + }, + 'VCLinkerTool': { + 'AdditionalOptions': ['-flto=thin'], + }, + }, + },], + ], + 'target_conditions': [ + ['_toolset=="target"', { + 'conditions': [ + ['enable_pgo_generate=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['-fprofile-generate'], + }, + 'VCLinkerTool': { + 'AdditionalOptions': [ + '/NODEFAULTLIB:clang_rt.profile.lib', + '"<(clang_profile_lib)"', + ], + }, + }, + },], + ['enable_pgo_use=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['-fprofile-use=$(SolutionDir)node.profdata'], + }, + 'VCLinkerTool': { + 'AdditionalOptions': ['-fprofile-use=$(SolutionDir)node.profdata'], + }, + }, + },], + ], + },], + ], + },], ['OS == "android"', { 'cflags': [ '-fPIC', '-I<(android_ndk_path)/sources/android/cpufeatures' ], 'ldflags': [ '-fPIC' ] @@ -437,6 +498,9 @@ }], # The defines bellow must include all things from the external_v8_defines # list in v8/BUILD.gn. + ['node_enable_experimentals == "true"', { + 'defines': ['EXPERIMENTALS_DEFAULT_VALUE=true'], + }], ['v8_enable_v8_checks == 1', { 'defines': ['V8_ENABLE_CHECKS'], }], diff --git a/configure.py b/configure.py index 5e31fa03558819..c167a0a0b27358 100755 --- a/configure.py +++ b/configure.py @@ -202,14 +202,14 @@ dest="enable_pgo_generate", default=None, help="Enable profiling with pgo of a binary. This feature is only available " - "on linux with gcc and g++ 5.4.1 or newer.") + "on linux with gcc and g++ 5.4.1 or newer and on windows.") parser.add_argument("--enable-pgo-use", action="store_true", dest="enable_pgo_use", default=None, help="Enable use of the profile generated with --enable-pgo-generate. This " - "feature is only available on linux with gcc and g++ 5.4.1 or newer.") + "feature is only available on linux with gcc and g++ 5.4.1 or newer and on windows.") parser.add_argument("--enable-lto", action="store_true", @@ -218,6 +218,13 @@ help="Enable compiling with lto of a binary. This feature is only available " "with gcc 5.4.1+ or clang 3.9.1+.") +parser.add_argument("--enable-thin-lto", + action="store_true", + dest="enable_thin_lto", + default=None, + help="Enable compiling with thin lto of a binary. This feature is only available " + "on windows.") + parser.add_argument("--link-module", action="append", dest="linked_module", @@ -797,6 +804,12 @@ default=None, help='Enable the --trace-maps flag in V8 (use at your own risk)') +parser.add_argument('--enable-all-experimentals', + action='store_true', + dest='enable_all_experimentals', + default=None, + help='Enable all experimental features by default') + parser.add_argument('--experimental-enable-pointer-compression', action='store_true', dest='enable_pointer_compression', @@ -919,7 +932,8 @@ action='store_true', dest='with_ltcg', default=None, - help='Use Link Time Code Generation. This feature is only available on Windows.') + help='Use Thin LTO scoped to node.exe and libnode only. ' + 'This feature is only available on Windows.') parser.add_argument('--write-snapshot-as-array-literals', action='store_true', @@ -1803,6 +1817,7 @@ def configure_node_cctest_sources(o): def configure_node(o): if options.dest_os == 'android': o['variables']['OS'] = 'android' + o['variables']['node_enable_experimentals'] = b(options.enable_all_experimentals) o['variables']['node_prefix'] = options.prefix o['variables']['node_install_npm'] = b(not options.without_npm) o['variables']['node_install_corepack'] = b(options.with_corepack) @@ -1909,9 +1924,9 @@ def configure_node(o): else: o['variables']['node_enable_v8_vtunejit'] = 'false' - if flavor != 'linux' and (options.enable_pgo_generate or options.enable_pgo_use): + if (flavor != 'linux' and flavor != 'win') and (options.enable_pgo_generate or options.enable_pgo_use): raise Exception( - 'The pgo option is supported only on linux.') + 'The pgo option is supported only on linux and windows.') if flavor == 'linux': if options.enable_pgo_generate or options.enable_pgo_use: @@ -1922,21 +1937,55 @@ def configure_node(o): 'The options --enable-pgo-generate and --enable-pgo-use ' f'are supported for gcc and gxx {version_checked_str} or newer only.') - if options.enable_pgo_generate and options.enable_pgo_use: - raise Exception( - 'Only one of the --enable-pgo-generate or --enable-pgo-use options ' - 'can be specified at a time. You would like to use ' - '--enable-pgo-generate first, profile node, and then recompile ' - 'with --enable-pgo-use') + if options.enable_pgo_generate and options.enable_pgo_use: + raise Exception( + 'Only one of the --enable-pgo-generate or --enable-pgo-use options ' + 'can be specified at a time. You would like to use ' + '--enable-pgo-generate first, profile node, and then recompile ' + 'with --enable-pgo-use') o['variables']['enable_pgo_generate'] = b(options.enable_pgo_generate) o['variables']['enable_pgo_use'] = b(options.enable_pgo_use) - if flavor == 'win' and (options.enable_lto): + if flavor == 'win' and (options.enable_pgo_generate or options.enable_pgo_use): + lib_suffix = 'aarch64' if target_arch == 'arm64' else 'x86_64' + lib_name = f'clang_rt.profile-{lib_suffix}.lib' + msvc_dir = target_arch # 'x64' or 'arm64' + + vc_tools_dir = os.environ.get('VCToolsInstallDir', '') + if vc_tools_dir: + clang_profile_lib = os.path.join(vc_tools_dir, 'lib', msvc_dir, lib_name) + if os.path.isfile(clang_profile_lib): + o['variables']['clang_profile_lib'] = clang_profile_lib + else: + raise Exception( + f'PGO profile runtime library not found at {clang_profile_lib}. ' + 'Ensure the ClangCL toolset is installed.') + else: + raise Exception( + 'VCToolsInstallDir not set. Run from a Visual Studio command prompt.') + + if flavor != 'win' and options.enable_thin_lto: raise Exception( - 'Use Link Time Code Generation instead.') + 'Use --enable-lto instead.') + + # LTO mutual exclusion + if flavor == 'win': + lto_options = [] + if options.enable_lto: + lto_options.append('--enable-lto') + if options.enable_thin_lto: + lto_options.append('--enable-thin-lto') + if options.with_ltcg: + lto_options.append('--with-ltcg') + if len(lto_options) > 1: + raise Exception( + f'Only one LTO option can be specified at a time: {", ".join(lto_options)}. ' + 'Use --enable-lto for Full LTO (global), ' + '--enable-thin-lto for Thin LTO (global), ' + 'or --with-ltcg for Thin LTO (scoped to node.exe and libnode).') - if options.enable_lto: + if options.enable_lto and flavor != 'win': gcc_version_checked = (5, 4, 1) clang_version_checked = (3, 9, 1) if not gcc_version_ge(gcc_version_checked) and not clang_version_ge(clang_version_checked): @@ -1947,6 +1996,7 @@ def configure_node(o): f'or clang {clang_version_checked_str}+ only.') o['variables']['enable_lto'] = b(options.enable_lto) + o['variables']['enable_thin_lto'] = b(options.enable_thin_lto) if options.node_use_large_pages or options.node_use_large_pages_script_lld: warn('''The `--use-largepages` and `--use-largepages-script-lld` options diff --git a/deps/openssl/openssl-cli.gypi b/deps/openssl/openssl-cli.gypi index ae74be9a2b17d2..da03938f1d1a92 100644 --- a/deps/openssl/openssl-cli.gypi +++ b/deps/openssl/openssl-cli.gypi @@ -25,5 +25,15 @@ ['enable_lto=="true"', { 'ldflags': [ '-fno-lto' ], }], + ['node_with_ltcg=="true" or enable_lto=="true" or enable_thin_lto=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + 'VCLinkerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + }, + }], ], } diff --git a/deps/openssl/openssl.gyp b/deps/openssl/openssl.gyp index ea3a2dc09ef29b..4d8d16f0e3c838 100644 --- a/deps/openssl/openssl.gyp +++ b/deps/openssl/openssl.gyp @@ -77,6 +77,16 @@ ['enable_lto=="true"', { 'ldflags': [ '-fno-lto' ], }], + ['node_with_ltcg=="true" or enable_lto=="true" or enable_thin_lto=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + 'VCLinkerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + }, + }], ] }, { # openssl-fipsmodule target diff --git a/deps/uv/src/unix/fs.c b/deps/uv/src/unix/fs.c index fc0fe0a269116b..a26f2dc7fa4649 100644 --- a/deps/uv/src/unix/fs.c +++ b/deps/uv/src/unix/fs.c @@ -1324,10 +1324,7 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) { times[1] = src_statsbuf.st_mtim; #endif - if (futimens(dstfd, times) == -1) { - err = UV__ERR(errno); - goto out; - } + (void) futimens(dstfd, times); /* * Change the ownership and permissions of the destination file to match the diff --git a/deps/v8/.clang-format b/deps/v8/.clang-format index d655b2039f8848..370034d67bba3a 100644 --- a/deps/v8/.clang-format +++ b/deps/v8/.clang-format @@ -14,6 +14,11 @@ Macros: - CASE_=case - FOR_WITH_HANDLE_SCOPE(isolate, init, loop_var, limit_check, increment)=for(init; loop_var; increment) - WHILE_WITH_HANDLE_SCOPE(isolate, limit_check)=while(limit_check) + # Make clang-format parse conditions in flag implications as expressions, not type definitions. + - DEFINE_IMPLICATION(cond,flag,value)=if(cond) { flag; }; + - DEFINE_WEAK_IMPLICATION(cond,flag,value)=if(cond) { flag; }; + - DEFINE_VALUE_IMPLICATION(cond,flag,value)=if(cond) { flag = value; }; + - DEFINE_WEAK_VALUE_IMPLICATION(cond,flag,value)=if(cond) { flag = value; }; StatementMacros: - DECL_CAST - DECL_VERIFIER diff --git a/deps/v8/.gitignore b/deps/v8/.gitignore index 388d39863d83cc..240c287763ca99 100644 --- a/deps/v8/.gitignore +++ b/deps/v8/.gitignore @@ -155,6 +155,7 @@ bazel-out bazel-testlogs bazel-v8 launch.json +MODULE.bazel.lock !/third_party/jinja2 !/third_party/markupsafe !/third_party/zlib diff --git a/deps/v8/.gn b/deps/v8/.gn index e13ae6d5ec82da..6ead7196c515cf 100644 --- a/deps/v8/.gn +++ b/deps/v8/.gn @@ -36,6 +36,9 @@ default_args = { # Use Siso instead of Ninja. use_siso = true + + # V8 should stay in C++20 for now. + use_cxx23 = false } # These are the list of GN files that run exec_script. This whitelist exists diff --git a/deps/v8/AUTHORS b/deps/v8/AUTHORS index 96d5b843d2badf..c5ed884e128c21 100644 --- a/deps/v8/AUTHORS +++ b/deps/v8/AUTHORS @@ -272,6 +272,7 @@ Rong Wang Ross Kirsling Ruben Bridgewater Ryan Dahl +Sahil Shaikh Sakthipriyan Vairamani (thefourtheye) Sander Mathijs van Veen Sandro Santilli @@ -340,6 +341,7 @@ Zhao Jiazhong Zhao Qin Zhaojun Meng Zheng Liu +Zhongqi Wang Zhongping Wang 柳荣一 Yang Xiang diff --git a/deps/v8/BUILD.bazel b/deps/v8/BUILD.bazel index 91da3cadbb526a..f28fea4e8aa162 100644 --- a/deps/v8/BUILD.bazel +++ b/deps/v8/BUILD.bazel @@ -163,6 +163,13 @@ v8_flag( default = True, ) +v8_flag( + name = "v8_enable_undefined_double", + default = True, +) + +v8_flag(name = "v8_enable_experimental_tq_to_tsa") + # Default setting for v8_enable_maglev selects.config_setting_group( name = "maglev_by_default", @@ -513,12 +520,12 @@ v8_config( "v8_enable_seeded_array_index_hash": "V8_ENABLE_SEEDED_ARRAY_INDEX_HASH", "v8_jitless": "V8_JITLESS", "v8_enable_vtunejit": "ENABLE_VTUNE_JIT_INTERFACE", + "v8_enable_undefined_double": "V8_ENABLE_UNDEFINED_DOUBLE", }, defines = [ "GOOGLE3", "V8_ADVANCED_BIGINT_ALGORITHMS", "V8_CONCURRENT_MARKING", - "V8_ENABLE_LEAPTIERING", "V8_ENABLE_SPARKPLUG", "V8_ENABLE_EXTENSIBLE_RO_SNAPSHOT", "V8_ENABLE_CONTINUATION_PRESERVED_EMBEDDER_DATA", @@ -529,6 +536,18 @@ v8_config( "V8_ENABLE_CHECKS", ], "//conditions:default": [], + }) + select({ + "@v8//bazel/config:asan_enabled" : [ "V8_USE_ADDRESS_SANITIZER" ], + "//conditions:default": [], + }) + select({ + "@v8//bazel/config:hwasan_enabled" : [ "V8_USE_HWADDRESS_SANITIZER" ], + "//conditions:default": [], + }) + select({ + "@v8//bazel/config:msan_enabled" : [ "V8_USE_MEMORY_SANITIZER" ], + "//conditions:default": [], + }) + select({ + "@v8//bazel/config:ubsan_enabled" : [ "V8_USE_UNDEFINED_BEHAVIOR_SANITIZER" ], + "//conditions:default": [], }) + select( { "@v8//bazel/config:v8_target_ia32": ["V8_TARGET_ARCH_IA32"], @@ -803,6 +822,7 @@ filegroup( "src/base/abort-mode.cc", "src/base/abort-mode.h", "src/base/address-region.h", + "src/base/algorithm.h", "src/base/atomic-utils.h", "src/base/atomicops.h", "src/base/base-export.h", @@ -832,6 +852,7 @@ filegroup( "src/base/file-utils.cc", "src/base/file-utils.h", "src/base/flags.h", + "src/base/float16.h", "src/base/fpu.cc", "src/base/fpu.h", "src/base/functional/bind-internal.h", @@ -849,6 +870,7 @@ filegroup( "src/base/logging.cc", "src/base/logging.h", "src/base/macros.h", + "src/base/memcopy.h", "src/base/memory.h", "src/base/numbers/bignum.cc", "src/base/numbers/bignum.h", @@ -1337,7 +1359,14 @@ filegroup( "src/torque/types.h", "src/torque/utils.cc", "src/torque/utils.h", - ], + ] + select({ + ":is_v8_enable_experimental_tq_to_tsa": [ + "src/torque/ast-visitor.h", + "src/torque/tsa-generator.cc", + "src/torque/tsa-generator.h", + ], + "//conditions:default": [], + }), ) filegroup( @@ -1422,7 +1451,12 @@ filegroup( "src/builtins/builtins-function.cc", "src/builtins/builtins-global.cc", "src/builtins/builtins-internal.cc", + "src/builtins/builtins-iterator.h", + "src/builtins/builtins-iterator-inl.h", "src/builtins/builtins-json.cc", + "src/builtins/builtins-math.cc", + "src/builtins/builtins-math-xsum.h", + "src/builtins/builtins-math-xsum.cc", "src/builtins/builtins-number.cc", "src/builtins/builtins-object.cc", "src/builtins/builtins-promise.h", @@ -1631,6 +1665,7 @@ filegroup( "src/execution/isolate.cc", "src/execution/isolate.h", "src/execution/isolate-data.h", + "src/execution/isolate-data-fields.h", "src/execution/isolate-inl.h", "src/execution/isolate-utils.h", "src/execution/isolate-utils-inl.h", @@ -1707,9 +1742,9 @@ filegroup( "src/heap/base-space.h", "src/heap/base/active-system-pages.cc", "src/heap/base/active-system-pages.h", - "src/heap/memory-chunk-metadata.cc", - "src/heap/memory-chunk-metadata.h", - "src/heap/memory-chunk-metadata-inl.h", + "src/heap/base-page.cc", + "src/heap/base-page.h", + "src/heap/base-page-inl.h", "src/heap/code-range.cc", "src/heap/code-range.h", "src/heap/trusted-range.cc", @@ -1789,9 +1824,9 @@ filegroup( "src/heap/incremental-marking-job.h", "src/heap/index-generator.cc", "src/heap/index-generator.h", - "src/heap/large-page-metadata.cc", - "src/heap/large-page-metadata.h", - "src/heap/large-page-metadata-inl.h", + "src/heap/large-page.cc", + "src/heap/large-page.h", + "src/heap/large-page-inl.h", "src/heap/large-spaces.cc", "src/heap/large-spaces.h", "src/heap/linear-allocation-area.h", @@ -1833,9 +1868,9 @@ filegroup( "src/heap/memory-allocator.h", "src/heap/memory-balancer.cc", "src/heap/memory-balancer.h", - "src/heap/mutable-page-metadata.cc", - "src/heap/mutable-page-metadata.h", - "src/heap/mutable-page-metadata-inl.h", + "src/heap/mutable-page.cc", + "src/heap/mutable-page.h", + "src/heap/mutable-page-inl.h", "src/heap/memory-chunk.cc", "src/heap/memory-chunk.h", "src/heap/memory-chunk-constants.h", @@ -1860,9 +1895,9 @@ filegroup( "src/heap/heap-visitor.cc", "src/heap/heap-visitor.h", "src/heap/heap-visitor-inl.h", - "src/heap/page-metadata.cc", - "src/heap/page-metadata.h", - "src/heap/page-metadata-inl.h", + "src/heap/normal-page.cc", + "src/heap/normal-page.h", + "src/heap/normal-page-inl.h", "src/heap/paged-spaces.cc", "src/heap/paged-spaces.h", "src/heap/paged-spaces-inl.h", @@ -2210,6 +2245,8 @@ filegroup( "src/objects/hole.h", "src/objects/trusted-object.h", "src/objects/trusted-object-inl.h", + "src/objects/trusted-pointer.h", + "src/objects/trusted-pointer-inl.h", "src/objects/option-utils.cc", "src/objects/option-utils.h", "src/objects/ordered-hash-table.cc", @@ -2390,6 +2427,10 @@ filegroup( "src/regexp/regexp.h", "src/regexp/regexp-ast.cc", "src/regexp/regexp-ast.h", + "src/regexp/regexp-ast-printer.cc", + "src/regexp/regexp-ast-printer.h", + "src/regexp/regexp-bytecode-analysis.cc", + "src/regexp/regexp-bytecode-analysis.h", "src/regexp/regexp-bytecode-generator.cc", "src/regexp/regexp-bytecode-generator.h", "src/regexp/regexp-bytecode-generator-inl.h", @@ -2411,6 +2452,8 @@ filegroup( "src/regexp/regexp-error.cc", "src/regexp/regexp-error.h", "src/regexp/regexp-flags.h", + "src/regexp/regexp-graph-printer.cc", + "src/regexp/regexp-graph-printer.h", "src/regexp/regexp-interpreter.cc", "src/regexp/regexp-interpreter.h", "src/regexp/regexp-macro-assembler.cc", @@ -2419,8 +2462,12 @@ filegroup( "src/regexp/regexp-macro-assembler-tracer.cc", "src/regexp/regexp-macro-assembler-tracer.h", "src/regexp/regexp-nodes.h", + "src/regexp/regexp-node-printer.cc", + "src/regexp/regexp-node-printer.h", "src/regexp/regexp-parser.cc", "src/regexp/regexp-parser.h", + "src/regexp/regexp-printer.cc", + "src/regexp/regexp-printer.h", "src/regexp/regexp-result-vector.cc", "src/regexp/regexp-result-vector.h", "src/regexp/regexp-stack.cc", @@ -2470,6 +2517,8 @@ filegroup( "src/runtime/runtime-weak-refs.cc", "src/sandbox/bounded-size.h", "src/sandbox/bounded-size-inl.h", + "src/sandbox/bytecode-verifier.h", + "src/sandbox/bytecode-verifier.cc", "src/sandbox/check.h", "src/sandbox/external-pointer.h", "src/sandbox/external-pointer-inl.h", @@ -2497,6 +2546,8 @@ filegroup( "src/sandbox/code-sandboxing-mode.h", "src/sandbox/compactible-external-entity-table-inl.h", "src/sandbox/compactible-external-entity-table.h", + "src/sandbox/external-strings-cage.cc", + "src/sandbox/external-strings-cage.h", "src/sandbox/isolate.h", "src/sandbox/isolate-inl.h", "src/sandbox/indirect-pointer.h", @@ -2509,6 +2560,8 @@ filegroup( "src/sandbox/hardware-support.h", "src/sandbox/sandbox.cc", "src/sandbox/sandbox.h", + "src/sandbox/sandboxable-thread.cc", + "src/sandbox/sandboxable-thread.h", "src/sandbox/sandbox-malloc.h", "src/sandbox/sandboxed-pointer.h", "src/sandbox/sandboxed-pointer-inl.h", @@ -2565,6 +2618,7 @@ filegroup( "src/snapshot/startup-serializer.h", "src/strings/char-predicates.h", "src/strings/char-predicates-inl.h", + "src/strings/owning-external-string-resource.h", "src/strings/string-builder.cc", "src/strings/string-builder.h", "src/strings/string-builder-inl.h", @@ -2590,6 +2644,7 @@ filegroup( "src/tasks/task-utils.cc", "src/tasks/task-utils.h", "src/torque/runtime-macro-shims.h", + "src/tracing/perfetto-sdk.h", "src/tracing/trace-event.cc", "src/tracing/trace-event.h", "src/tracing/trace-event-no-perfetto.h", @@ -2613,7 +2668,6 @@ filegroup( "src/utils/identity-map.h", "src/utils/locked-queue.h", "src/utils/locked-queue-inl.h", - "src/utils/memcopy.cc", "src/utils/memcopy.h", "src/utils/output-stream.cc", "src/utils/output-stream.h", @@ -2902,6 +2956,7 @@ filegroup( "//conditions:default": [], }) + select({ ":enable_maglev": [ + "src/maglev/hamt.h", "src/maglev/maglev-assembler-inl.h", "src/maglev/maglev-assembler.h", "src/maglev/maglev-basic-block.h", @@ -2925,12 +2980,15 @@ filegroup( "src/maglev/maglev-ir-inl.h", "src/maglev/maglev-ir.h", "src/maglev/maglev-kna-processor.h", + "src/maglev/maglev-node-type.h", "src/maglev/maglev-phi-representation-selector.h", "src/maglev/maglev-truncation.h", "src/maglev/maglev-pipeline-statistics.h", "src/maglev/maglev-post-hoc-optimizations-processors.h", "src/maglev/maglev-pre-regalloc-codegen-processors.h", + "src/maglev/maglev-range.h", "src/maglev/maglev-range-analysis.h", + "src/maglev/maglev-range-verification.h", "src/maglev/maglev-reducer-inl.h", "src/maglev/maglev-reducer.h", "src/maglev/maglev-regalloc-data.h", @@ -2954,9 +3012,11 @@ filegroup( "src/maglev/maglev-inlining.cc", "src/maglev/maglev-interpreter-frame-state.cc", "src/maglev/maglev-ir.cc", + "src/maglev/maglev-node-type.cc", "src/maglev/maglev-phi-representation-selector.cc", "src/maglev/maglev-truncation.cc", "src/maglev/maglev-pipeline-statistics.cc", + "src/maglev/maglev-range-verification.cc", "src/maglev/maglev-regalloc.cc", "src/maglev/maglev.cc", ], @@ -3032,12 +3092,13 @@ filegroup( "src/wasm/code-space-access.h", "src/wasm/compilation-environment.h", "src/wasm/compilation-environment-inl.h", + "src/wasm/compilation-hints-generation.cc", + "src/wasm/compilation-hints-generation.h", "src/wasm/constant-expression.cc", "src/wasm/constant-expression.h", "src/wasm/constant-expression-interface.cc", "src/wasm/constant-expression-interface.h", "src/wasm/decoder.h", - "src/wasm/float16.h", "src/wasm/function-body-decoder.cc", "src/wasm/function-body-decoder.h", "src/wasm/function-body-decoder-impl.h", @@ -3103,7 +3164,11 @@ filegroup( "src/wasm/wasm-features.cc", "src/wasm/wasm-features.h", "src/wasm/wasm-import-wrapper-cache.cc", + "src/wasm/wasm-stack-wrapper-cache.cc", + "src/wasm/wasm-wrapper-cache.cc", "src/wasm/wasm-import-wrapper-cache.h", + "src/wasm/wasm-stack-wrapper-cache.h", + "src/wasm/wasm-wrapper-cache.h", "src/wasm/wasm-init-expr.h", "src/wasm/wasm-js.cc", "src/wasm/wasm-js.h", @@ -3135,6 +3200,7 @@ filegroup( "src/wasm/wrappers.cc", "src/wasm/wrappers.h", "src/wasm/wrappers-inl.h", + "src/wasm/wasm-wrapper-cache-inl.h", "third_party/utf8-decoder/generalized-utf8-decoder.h", ], "//conditions:default": [], @@ -3254,8 +3320,8 @@ filegroup( "src/compiler/backend/spill-placer.cc", "src/compiler/backend/spill-placer.h", "src/compiler/backend/unwinding-info-writer.h", - "src/compiler/basic-block-instrumentor.cc", - "src/compiler/basic-block-instrumentor.h", + "src/compiler/basic-block-call-graph-profiler.cc", + "src/compiler/basic-block-call-graph-profiler.h", "src/compiler/branch-elimination.cc", "src/compiler/branch-elimination.h", "src/compiler/bytecode-analysis.cc", @@ -3509,6 +3575,7 @@ filegroup( "src/compiler/turboshaft/late-load-elimination-reducer.h", "src/compiler/turboshaft/layered-hash-map.h", "src/compiler/turboshaft/load-store-simplification-reducer.h", + "src/compiler/turboshaft/load-store-verification-reducer.h", "src/compiler/turboshaft/loop-finder.cc", "src/compiler/turboshaft/loop-finder.h", "src/compiler/turboshaft/loop-peeling-phase.cc", @@ -3521,6 +3588,7 @@ filegroup( "src/compiler/turboshaft/machine-lowering-phase.cc", "src/compiler/turboshaft/machine-lowering-phase.h", "src/compiler/turboshaft/machine-lowering-reducer-inl.h", + "src/compiler/turboshaft/maglev-assert-types-reducer.h", "src/compiler/turboshaft/turbolev-early-lowering-reducer-inl.h", "src/compiler/turboshaft/turbolev-frontend-pipeline.cc", "src/compiler/turboshaft/turbolev-frontend-pipeline.h", @@ -3714,6 +3782,7 @@ filegroup( # include them explicitely when Maglev is disabled. ":enable_maglev": [], "//conditions:default": [ + "src/maglev/hamt.h", "src/maglev/maglev-basic-block.h", "src/maglev/maglev-code-gen-state.h", "src/maglev/maglev-code-gen-state-inl.h", @@ -3744,7 +3813,12 @@ filegroup( "src/maglev/maglev-ir.h", "src/maglev/maglev-ir-inl.h", "src/maglev/maglev-kna-processor.h", + "src/maglev/maglev-node-type.cc", + "src/maglev/maglev-node-type.h", + "src/maglev/maglev-range.h", "src/maglev/maglev-range-analysis.h", + "src/maglev/maglev-range-verification.cc", + "src/maglev/maglev-range-verification.h", "src/maglev/maglev-reducer-inl.h", "src/maglev/maglev-reducer.h", "src/maglev/maglev-register-frame-array.h", @@ -3806,6 +3880,7 @@ filegroup( "src/builtins/builtins-string-gen.cc", "src/builtins/builtins-string-gen.h", "src/builtins/builtins-string-tsa.cc", + "src/builtins/builtins-string-tsa-inl.h", "src/builtins/builtins-typed-array-gen.cc", "src/builtins/builtins-typed-array-gen.h", "src/builtins/builtins-utils-gen.h", @@ -4394,6 +4469,8 @@ filegroup( "src/d8/d8-platforms.h", "src/d8/d8-posix.cc", "src/d8/d8-test.cc", + "src/d8/hardware-watchpoints.cc", + "src/d8/hardware-watchpoints.h", ], ) diff --git a/deps/v8/BUILD.gn b/deps/v8/BUILD.gn index 75034b69a8a648..e81430fbc39392 100644 --- a/deps/v8/BUILD.gn +++ b/deps/v8/BUILD.gn @@ -9,7 +9,6 @@ import("//build/config/dcheck_always_on.gni") import("//build/config/host_byteorder.gni") import("//build/config/mips.gni") import("//build/config/riscv.gni") -import("//build/config/rust.gni") import("//build/config/sanitizers/sanitizers.gni") import("//build_overrides/build.gni") import("//third_party/icu/config.gni") @@ -21,6 +20,10 @@ if (is_clang) { import("//build/config/clang/clang.gni") } +if (v8_enable_temporal_support) { + import("//build/config/rust.gni") +} + if (is_ios) { import("//build/config/apple/mobile_config.gni") # For `target_platform`. } @@ -68,6 +71,9 @@ declare_args() { # Sets -DENABLE_DISASSEMBLER. v8_enable_disassembler = "" + # Sets -DV8_ENABLE_REGEXP_DIAGNOSTICS. + v8_enable_regexp_diagnostics = !build_with_chromium + # Sets the number of internal fields on promise objects. v8_promise_internal_field_count = 0 @@ -83,6 +89,9 @@ declare_args() { # Sets -DENABLE_VTUNE_TRACEMARK. v8_enable_vtunetracemark = false + # Sets -DV8_ENABLE_APX_F. + v8_enable_apx_f = false + # Sets -DENABLE_HUGEPAGE v8_enable_hugepage = false @@ -456,8 +465,11 @@ declare_args() { # Use the experimental TSA-based definition for some builtins. v8_enable_experimental_tsa_builtins = false + # Use the experimental TSA backend for Torque. + v8_enable_experimental_tq_to_tsa = false + # Use the encoding of undefined in double values. - v8_enable_undefined_double = false + v8_enable_undefined_double = true v8_dcheck_always_on = dcheck_always_on @@ -481,9 +493,6 @@ declare_args() { # usages. The plugin is not exhaustive but covers most common known pitfalls. use_cppgc_clang_plugin = true - # Deinterleaving load support. - v8_enable_wasm_deinterleaved_mem_ops = false - # Expose F.p.caller and .arguments as own properties. v8_function_arguments_caller_are_own_props = false @@ -492,6 +501,12 @@ declare_args() { # Enable seeded array index hash. v8_enable_seeded_array_index_hash = false + + # add instrumentation for Dumpling differential fuzzing + v8_dumpling = false + + # Enable compilation of riscv32. + v8_riscv_enable_deprecated_riscv32 = false } # Derived defaults. @@ -564,7 +579,6 @@ if (v8_multi_arch_build && rebase_path(get_label_info(":d8", "root_out_dir"), root_build_dir) == "clang_x64_fuzzer_experiments") { v8_enable_pointer_compression = !v8_enable_pointer_compression - v8_enable_undefined_double = !v8_enable_undefined_double v8_lower_limits_mode = !v8_lower_limits_mode } @@ -601,16 +615,18 @@ if (v8_enable_external_code_space == "") { v8_enable_pointer_compression && v8_enable_pointer_compression_shared_cage && (v8_current_cpu == "x64" || v8_current_cpu == "arm64" || - v8_current_cpu == "loong64") + v8_current_cpu == "loong64" || v8_current_cpu == "riscv64") } if (v8_enable_sparkplug == "") { v8_enable_sparkplug = !v8_jitless } if (v8_enable_maglev == "") { - v8_enable_maglev = v8_enable_turbofan && - (v8_current_cpu == "arm" || v8_current_cpu == "x64" || - v8_current_cpu == "arm64" || v8_current_cpu == "s390x" || - v8_current_cpu == "ppc64" || v8_current_cpu == "riscv64") + v8_enable_maglev = + v8_enable_turbofan && + (v8_current_cpu == "arm" || v8_current_cpu == "x64" || + v8_current_cpu == "arm64" || v8_current_cpu == "s390x" || + v8_current_cpu == "ppc64" || v8_current_cpu == "riscv64" || + v8_current_cpu == "loong64") } assert(v8_enable_turbofan || !v8_enable_maglev, "Maglev is not available when Turbofan is disabled.") @@ -742,6 +758,10 @@ if (v8_enable_webassembly && v8_current_cpu == "arm64") { v8_enable_wasm_deinterleaved_mem_ops = true } +if (v8_enable_experimental_tq_to_tsa) { + v8_enable_experimental_tsa_builtins = true +} + assert(!v8_disable_write_barriers || v8_enable_single_generation, "Disabling write barriers works only with single generation") @@ -981,6 +1001,8 @@ external_v8_defines = [ "V8_DEPRECATION_WARNINGS", "V8_IMMINENT_DEPRECATION_WARNINGS", "V8_USE_PERFETTO", + "V8_USE_PERFETTO_JSON_EXPORT", + "V8_USE_PERFETTO_SDK", "V8_MAP_PACKING", "V8_IS_TSAN", "V8_ENABLE_DIRECT_HANDLE", @@ -993,6 +1015,8 @@ external_v8_defines = [ "V8_TARGET_OS_MACOS", "V8_TARGET_OS_WIN", "V8_TARGET_OS_CHROMEOS", + "V8_TARGET_ARCH_ARM64", + "V8_TARGET_ARCH_PPC64", ] enabled_external_v8_defines = [ @@ -1029,6 +1053,12 @@ if (v8_imminent_deprecation_warnings) { if (v8_use_perfetto) { enabled_external_v8_defines += [ "V8_USE_PERFETTO" ] } +if (v8_use_perfetto_json_export) { + enabled_external_v8_defines += [ "V8_USE_PERFETTO_JSON_EXPORT" ] +} +if (v8_use_perfetto_sdk) { + enabled_external_v8_defines += [ "V8_USE_PERFETTO_SDK" ] +} if (v8_enable_map_packing) { enabled_external_v8_defines += [ "V8_MAP_PACKING" ] } @@ -1069,6 +1099,18 @@ if (target_os == "android") { enabled_external_v8_defines += [ "V8_TARGET_OS_CHROMEOS" ] } +# Some V8_TARGET_ARCH_ defines that affect Api constants (see usages of +# V8_TARGET_ARCH_* in v8-internal.h). +# The target architecture may differ from host one e.g. in mksnapshot or in +# msan builds. +# TODO(ishell): support all target archs and add V8_HAVE_TARGET_ARCH similar +# to V8_TARGET_OS_* and V8_HAVE_TARGET_OS defines. +if (v8_current_cpu == "arm64") { + enabled_external_v8_defines += [ "V8_TARGET_ARCH_ARM64" ] +} else if (v8_current_cpu == "ppc64") { + enabled_external_v8_defines += [ "V8_TARGET_ARCH_PPC64" ] +} + disabled_external_v8_defines = external_v8_defines - enabled_external_v8_defines # Put defines that are used in public headers here; public headers are @@ -1179,8 +1221,7 @@ config("features") { if (v8_enable_pointer_compression) { if (v8_enable_pointer_compression_shared_cage) { defines += [ - # TODO(442942399): Re-enable after bug has been addressed. - # "V8_CONTIGUOUS_COMPRESSED_RO_SPACE", + "V8_CONTIGUOUS_COMPRESSED_RO_SPACE", "V8_CONTIGUOUS_COMPRESSED_RO_SPACE_SIZE_MB=${v8_contiguous_compressed_ro_space_size_mb}", ] } else { @@ -1215,6 +1256,9 @@ config("features") { if (v8_enable_vtunetracemark) { defines += [ "ENABLE_VTUNE_TRACEMARK" ] } + if (v8_enable_apx_f) { + defines += [ "V8_ENABLE_APX_F" ] + } if (v8_enable_hugepage) { defines += [ "ENABLE_HUGEPAGE" ] } @@ -1245,6 +1289,9 @@ config("features") { if (v8_enable_trace_feedback_updates) { defines += [ "V8_TRACE_FEEDBACK_UPDATES" ] } + if (v8_enable_regexp_diagnostics) { + defines += [ "V8_ENABLE_REGEXP_DIAGNOSTICS" ] + } if (v8_enable_test_features) { defines += [ "V8_ENABLE_ALLOCATION_TIMEOUT" ] defines += [ "V8_ENABLE_FORCE_SLOW_PATH" ] @@ -1332,6 +1379,12 @@ config("features") { if (v8_fuzzilli) { defines += [ "V8_FUZZILLI" ] } + if (v8_dumpling) { + defines += [ "V8_DUMPLING" ] + } + if (v8_riscv_enable_deprecated_riscv32) { + defines += [ "V8_RISCV_ENABLE_DEPRECATED_RISCV32" ] + } if (v8_enable_fuzztest) { defines += [ "V8_ENABLE_FUZZTEST" ] } @@ -1435,9 +1488,6 @@ config("features") { if (v8_enable_wasm_simd256_revec) { defines += [ "V8_ENABLE_WASM_SIMD256_REVEC" ] } - if (v8_enable_wasm_deinterleaved_mem_ops) { - defines += [ "V8_ENABLE_WASM_DEINTERLEAVED_MEM_OPS" ] - } if (v8_enable_maglev_graph_printer) { defines += [ "V8_ENABLE_MAGLEV_GRAPH_PRINTER" ] } @@ -1462,10 +1512,12 @@ config("features") { if (v8_enable_experimental_tsa_builtins) { defines += [ "V8_ENABLE_EXPERIMENTAL_TSA_BUILTINS" ] } + if (v8_enable_experimental_tq_to_tsa) { + defines += [ "V8_ENABLE_EXPERIMENTAL_TQ_TO_TSA" ] + } if (v8_enable_undefined_double) { defines += [ "V8_ENABLE_UNDEFINED_DOUBLE" ] } - defines += [ "V8_ENABLE_LEAPTIERING" ] if (v8_enable_partition_alloc) { defines += [ "V8_ENABLE_PARTITION_ALLOC" ] } @@ -1492,24 +1544,16 @@ config("toolchain") { ldflags = [] if (v8_current_cpu == "arm") { - defines += [ "V8_TARGET_ARCH_ARM" ] - if (arm_version >= 7) { - defines += [ "CAN_USE_ARMV7_INSTRUCTIONS" ] - } - if (arm_fpu == "vfpv3-d16") { - defines += [ "CAN_USE_VFP3_INSTRUCTIONS" ] - } else if (arm_fpu == "vfpv3") { - defines += [ - "CAN_USE_VFP3_INSTRUCTIONS", - "CAN_USE_VFP32DREGS", - ] - } else if (arm_fpu == "neon") { - defines += [ - "CAN_USE_VFP3_INSTRUCTIONS", - "CAN_USE_VFP32DREGS", - "CAN_USE_NEON", - ] - } + defines += [ + "V8_TARGET_ARCH_ARM", + + # TODO(arm): Now that we always require v7+ and neon, we can simplify the + # code and then stop defining these. + "CAN_USE_ARMV7_INSTRUCTIONS", + "CAN_USE_VFP3_INSTRUCTIONS", + "CAN_USE_VFP32DREGS", + "CAN_USE_NEON", + ] # TODO(infra): Add support for arm_test_noprobe. @@ -1576,14 +1620,14 @@ config("toolchain") { defines += [ "V8_TARGET_ARCH_S390X" ] if (host_byteorder == "little") { defines += [ "V8_TARGET_ARCH_S390X_LE_SIM" ] - } else if (current_os != "zos") { - cflags += [ "-march=z196" ] + } else if (!v8_target_is_simulator && current_os != "zos") { + cflags += [ "-march=z14" ] } } if (v8_current_cpu == "ppc64") { defines += [ "V8_TARGET_ARCH_PPC64" ] cflags += [ "-ffp-contract=off" ] - if (current_os == "aix" and !is_clang) { + if (current_os == "aix" && !is_clang) { cflags += [ # Work around AIX ceil, trunc and round oddities. "-mcpu=power5+", @@ -1592,18 +1636,26 @@ config("toolchain") { # Work around AIX assembler popcntb bug. "-mno-popcntb", ] + } else if (!v8_target_is_simulator) { + cflags += [ "-mcpu=pwr9" ] } } - # Under simulator build, compiler will not provide __riscv_xlen. Define here if (v8_current_cpu == "riscv64" || v8_current_cpu == "riscv32") { if (v8_target_is_simulator) { - defines += [ "_RISCV_TARGET_SIMULATOR" ] + defines += [ "RISCV_TARGET_SIMULATOR" ] } if (riscv_use_rvv || v8_target_is_simulator) { defines += [ "CAN_USE_RVV_INSTRUCTIONS" ] defines += [ "RVV_VLEN=${riscv_rvv_vlen}" ] } + + if (riscv_use_zicfiss) { + defines += [ "V8_ENABLE_RISCV_SHADOW_STACK" ] + } + if (riscv_use_zicond) { + defines += [ "__riscv_zicond" ] + } if (riscv_use_zba) { defines += [ "__riscv_zba" ] } @@ -1613,10 +1665,19 @@ config("toolchain") { if (riscv_use_zbs) { defines += [ "__riscv_zbs" ] } + defines += [ "RISCV_CODE_ALIGNMENT=${riscv_code_alignment}" ] + defines += + [ "RISCV_CONSTANT_POOL_ALIGNMENT=${riscv_constant_pool_alignment}" ] } + if (v8_current_cpu == "riscv64") { defines += [ "V8_TARGET_ARCH_RISCV64" ] - defines += [ "__riscv_xlen=64" ] + + # When building the simulator, the compiler does not provide __riscv_xlen, + # so we explicitly define it here. + if (v8_target_is_simulator) { + defines += [ "__riscv_xlen=64" ] + } if (!is_clang) { cflags += [ "-ffp-contract=off" ] } @@ -1627,7 +1688,12 @@ config("toolchain") { if (v8_current_cpu == "riscv32") { defines += [ "V8_TARGET_ARCH_RISCV32" ] - defines += [ "__riscv_xlen=32" ] + + # When building the simulator, the compiler does not provide __riscv_xlen, + # so we explicitly define it here. + if (v8_target_is_simulator) { + defines += [ "__riscv_xlen=32" ] + } } if (v8_current_cpu == "x86") { @@ -2232,6 +2298,75 @@ if (v8_enable_webassembly) { ] } +if (v8_enable_experimental_tq_to_tsa) { + template("run_torque_to_tsa") { + if (target_name == "") { + suffix = "" + } else { + suffix = "_$target_name" + } + + toolchain = invoker.toolchain + + action("run_torque_to_tsa" + suffix) { + visibility = [ + ":*", + "test/cctest/:*", + "tools/debug_helper/:*", + "tools/gcmole/:*", + ] + + deps = [ ":torque($toolchain)" ] + + if (is_msan) { + configs = [ + "//third_party/instrumented_libs:msan_runtime_libs($host_toolchain)", + ] + deps += [ "//third_party/instrumented_libs:ld-linux($host_toolchain)" ] + } + + script = "tools/run.py" + + sources = torque_files + + destination_folder = "$target_gen_dir/torque-generated$suffix" + + outputs = [] + + foreach(file, torque_files) { + filetq = string_replace(file, ".tq", "-tq") + outputs += [ + "$destination_folder/$filetq-tsa.cc", + "$destination_folder/$filetq-tsa.h", + ] + } + + args = [ + "./" + rebase_path(get_label_info(":torque($toolchain)", + "root_out_dir") + "/torque", + root_build_dir), + "-o", + rebase_path("$destination_folder", root_build_dir), + "-v8-root", + rebase_path(".", root_build_dir), + "-output-tsa", + ] + if (v8_annotate_torque_ir) { + args += [ "-annotate-ir" ] + } + if (defined(invoker.args)) { + args += invoker.args + } + args += torque_files + } + } + + # Default run_torque_to_tsa action + run_torque_to_tsa("") { + toolchain = v8_generator_toolchain + } +} + # Template for running torque # When building with v8_verify_torque_generation_invariance=true we need # to be able to run torque for both 32 and 64 bits in the same build @@ -2396,6 +2531,9 @@ v8_source_set("torque_generated_initializers") { ":v8_maybe_temporal", ":v8_tracing", ] + if (v8_enable_experimental_tq_to_tsa) { + deps += [ ":run_torque_to_tsa" ] + } public_deps = [ ":torque_runtime_support", @@ -2415,6 +2553,12 @@ v8_source_set("torque_generated_initializers") { "$target_gen_dir/torque-generated/$filetq-csa.cc", "$target_gen_dir/torque-generated/$filetq-csa.h", ] + if (v8_enable_experimental_tq_to_tsa) { + sources += [ + "$target_gen_dir/torque-generated/$filetq-tsa.cc", + "$target_gen_dir/torque-generated/$filetq-tsa.h", + ] + } } configs = [ ":internal_config" ] @@ -2443,6 +2587,9 @@ v8_source_set("torque_generated_definitions") { ":v8_maybe_temporal", ":v8_tracing", ] + if (v8_enable_experimental_tq_to_tsa) { + deps += [ ":run_torque_to_tsa" ] + } public_deps = [ ":v8_abseil", @@ -2560,7 +2707,7 @@ template("run_mksnapshot") { "root_out_dir") + "/mksnapshot", root_build_dir), "--turbo_instruction_scheduling", - "--stress-turbo-late-spilling", + "--turbo-always-optimize-spills", # In cross builds, the snapshot may be generated for both the host and # target toolchains. The same host binary is used to generate both, so @@ -3090,6 +3237,7 @@ v8_source_set("v8_initializers") { "src/builtins/builtins-sharedarraybuffer-gen.cc", "src/builtins/builtins-string-gen.cc", "src/builtins/builtins-string-gen.h", + "src/builtins/builtins-string-tsa-inl.h", "src/builtins/builtins-string-tsa.cc", "src/builtins/builtins-typed-array-gen.cc", "src/builtins/builtins-typed-array-gen.h", @@ -3146,6 +3294,9 @@ v8_source_set("v8_initializers") { } else if (v8_current_cpu == "arm64") { sources += [ "src/wasm/interpreter/arm64/interpreter-builtins-arm64.cc" ] + } else if (v8_current_cpu == "riscv64") { + sources += + [ "src/wasm/interpreter/riscv/interpreter-builtins-riscv.cc" ] } } } @@ -3463,6 +3614,9 @@ v8_header_set("v8_internal_headers") { "src/builtins/builtins-descriptors.h", "src/builtins/builtins-effects-analyzer.h", "src/builtins/builtins-inl.h", + "src/builtins/builtins-iterator-inl.h", + "src/builtins/builtins-iterator.h", + "src/builtins/builtins-math-xsum.h", "src/builtins/builtins-promise.h", "src/builtins/builtins-utils-inl.h", "src/builtins/builtins-utils.h", @@ -3561,7 +3715,7 @@ v8_header_set("v8_internal_headers") { "src/compiler/backend/register-allocator.h", "src/compiler/backend/spill-placer.h", "src/compiler/backend/unwinding-info-writer.h", - "src/compiler/basic-block-instrumentor.h", + "src/compiler/basic-block-call-graph-profiler.h", "src/compiler/branch-elimination.h", "src/compiler/bytecode-analysis.h", "src/compiler/bytecode-graph-builder.h", @@ -3705,6 +3859,7 @@ v8_header_set("v8_internal_headers") { "src/compiler/turboshaft/late-load-elimination-reducer.h", "src/compiler/turboshaft/layered-hash-map.h", "src/compiler/turboshaft/load-store-simplification-reducer.h", + "src/compiler/turboshaft/load-store-verification-reducer.h", "src/compiler/turboshaft/loop-finder.h", "src/compiler/turboshaft/loop-peeling-phase.h", "src/compiler/turboshaft/loop-peeling-reducer.h", @@ -3713,6 +3868,7 @@ v8_header_set("v8_internal_headers") { "src/compiler/turboshaft/machine-lowering-phase.h", "src/compiler/turboshaft/machine-lowering-reducer-inl.h", "src/compiler/turboshaft/machine-optimization-reducer.h", + "src/compiler/turboshaft/maglev-assert-types-reducer.h", "src/compiler/turboshaft/memory-optimization-reducer.h", "src/compiler/turboshaft/operation-matcher.h", "src/compiler/turboshaft/operations.h", @@ -3810,6 +3966,7 @@ v8_header_set("v8_internal_headers") { "src/execution/frames.h", "src/execution/futex-emulation.h", "src/execution/interrupts-scope.h", + "src/execution/isolate-data-fields.h", "src/execution/isolate-data.h", "src/execution/isolate-inl.h", "src/execution/isolate-utils-inl.h", @@ -3855,6 +4012,8 @@ v8_header_set("v8_internal_headers") { "src/heap/allocation-result.h", "src/heap/allocation-stats.h", "src/heap/array-buffer-sweeper.h", + "src/heap/base-page-inl.h", + "src/heap/base-page.h", "src/heap/base-space.h", "src/heap/code-range.h", "src/heap/code-stats.h", @@ -3905,8 +4064,8 @@ v8_header_set("v8_internal_headers") { "src/heap/incremental-marking-job.h", "src/heap/incremental-marking.h", "src/heap/index-generator.h", - "src/heap/large-page-metadata-inl.h", - "src/heap/large-page-metadata.h", + "src/heap/large-page-inl.h", + "src/heap/large-page.h", "src/heap/large-spaces.h", "src/heap/linear-allocation-area.h", "src/heap/list.h", @@ -3937,8 +4096,6 @@ v8_header_set("v8_internal_headers") { "src/heap/memory-chunk-constants.h", "src/heap/memory-chunk-inl.h", "src/heap/memory-chunk-layout.h", - "src/heap/memory-chunk-metadata-inl.h", - "src/heap/memory-chunk-metadata.h", "src/heap/memory-chunk.h", "src/heap/memory-measurement-inl.h", "src/heap/memory-measurement.h", @@ -3947,14 +4104,15 @@ v8_header_set("v8_internal_headers") { "src/heap/minor-gc-job.h", "src/heap/minor-mark-sweep-inl.h", "src/heap/minor-mark-sweep.h", - "src/heap/mutable-page-metadata-inl.h", - "src/heap/mutable-page-metadata.h", + "src/heap/mutable-page-inl.h", + "src/heap/mutable-page.h", "src/heap/new-spaces-inl.h", "src/heap/new-spaces.h", + "src/heap/normal-page-inl.h", + "src/heap/normal-page.h", "src/heap/object-lock-inl.h", "src/heap/object-lock.h", "src/heap/object-stats.h", - "src/heap/page-metadata.h", "src/heap/paged-spaces-inl.h", "src/heap/paged-spaces.h", "src/heap/parallel-work-item.h", @@ -4256,6 +4414,8 @@ v8_header_set("v8_internal_headers") { "src/objects/transitions.h", "src/objects/trusted-object-inl.h", "src/objects/trusted-object.h", + "src/objects/trusted-pointer-inl.h", + "src/objects/trusted-pointer.h", "src/objects/turbofan-types-inl.h", "src/objects/turbofan-types.h", "src/objects/turboshaft-types-inl.h", @@ -4310,6 +4470,7 @@ v8_header_set("v8_internal_headers") { "src/regexp/experimental/experimental-interpreter.h", "src/regexp/experimental/experimental.h", "src/regexp/regexp-ast.h", + "src/regexp/regexp-bytecode-analysis.h", "src/regexp/regexp-bytecode-generator-inl.h", "src/regexp/regexp-bytecode-generator.h", "src/regexp/regexp-bytecode-iterator-inl.h", @@ -4319,12 +4480,10 @@ v8_header_set("v8_internal_headers") { "src/regexp/regexp-bytecodes.h", "src/regexp/regexp-code-generator.h", "src/regexp/regexp-compiler.h", - "src/regexp/regexp-dotprinter.h", "src/regexp/regexp-error.h", "src/regexp/regexp-flags.h", "src/regexp/regexp-interpreter.h", "src/regexp/regexp-macro-assembler-arch.h", - "src/regexp/regexp-macro-assembler-tracer.h", "src/regexp/regexp-macro-assembler.h", "src/regexp/regexp-nodes.h", "src/regexp/regexp-parser.h", @@ -4344,6 +4503,7 @@ v8_header_set("v8_internal_headers") { "src/runtime/runtime.h", "src/sandbox/bounded-size-inl.h", "src/sandbox/bounded-size.h", + "src/sandbox/bytecode-verifier.h", "src/sandbox/check.h", "src/sandbox/code-entrypoint-tag.h", "src/sandbox/code-pointer-inl.h", @@ -4363,6 +4523,7 @@ v8_header_set("v8_internal_headers") { "src/sandbox/external-pointer-table-inl.h", "src/sandbox/external-pointer-table.h", "src/sandbox/external-pointer.h", + "src/sandbox/external-strings-cage.h", "src/sandbox/hardware-support.h", "src/sandbox/indirect-pointer-inl.h", "src/sandbox/indirect-pointer-tag.h", @@ -4373,6 +4534,7 @@ v8_header_set("v8_internal_headers") { "src/sandbox/js-dispatch-table.h", "src/sandbox/sandbox-malloc.h", "src/sandbox/sandbox.h", + "src/sandbox/sandboxable-thread.h", "src/sandbox/sandboxed-pointer-inl.h", "src/sandbox/sandboxed-pointer.h", "src/sandbox/tagged-payload.h", @@ -4407,6 +4569,7 @@ v8_header_set("v8_internal_headers") { "src/snapshot/startup-serializer.h", "src/strings/char-predicates-inl.h", "src/strings/char-predicates.h", + "src/strings/owning-external-string-resource.h", "src/strings/string-builder-inl.h", "src/strings/string-builder.h", "src/strings/string-case.h", @@ -4468,6 +4631,17 @@ v8_header_set("v8_internal_headers") { sources += [ "src/snapshot/snapshot-compression.h" ] } + if (v8_enable_regexp_diagnostics) { + sources += [ + "src/regexp/regexp-ast-printer.h", + "src/regexp/regexp-dotprinter.h", + "src/regexp/regexp-graph-printer.h", + "src/regexp/regexp-macro-assembler-tracer.h", + "src/regexp/regexp-node-printer.h", + "src/regexp/regexp-printer.h", + ] + } + if (v8_enable_temporal_support) { sources += [ "src/objects/js-temporal-objects-inl.h", @@ -4482,6 +4656,7 @@ v8_header_set("v8_internal_headers") { "src/tracing/code-data-source.h", "src/tracing/code-trace-context.h", "src/tracing/perfetto-logger.h", + "src/tracing/perfetto-sdk.h", "src/tracing/perfetto-utils.h", ] } @@ -4497,6 +4672,7 @@ v8_header_set("v8_internal_headers") { if (v8_enable_maglev) { sources += [ + "src/maglev/hamt.h", "src/maglev/maglev-assembler-inl.h", "src/maglev/maglev-assembler.h", "src/maglev/maglev-basic-block.h", @@ -4521,10 +4697,14 @@ v8_header_set("v8_internal_headers") { "src/maglev/maglev-ir.h", "src/maglev/maglev-kna-processor.h", "src/maglev/maglev-known-node-aspects.h", + "src/maglev/maglev-node-type.h", "src/maglev/maglev-phi-representation-selector.h", "src/maglev/maglev-pipeline-statistics.h", "src/maglev/maglev-post-hoc-optimizations-processors.h", "src/maglev/maglev-pre-regalloc-codegen-processors.h", + "src/maglev/maglev-range-analysis.h", + "src/maglev/maglev-range-verification.h", + "src/maglev/maglev-range.h", "src/maglev/maglev-reducer-inl.h", "src/maglev/maglev-reducer.h", "src/maglev/maglev-regalloc-data.h", @@ -4545,6 +4725,8 @@ v8_header_set("v8_internal_headers") { sources += [ "src/maglev/s390/maglev-assembler-s390-inl.h" ] } else if (v8_current_cpu == "ppc64") { sources += [ "src/maglev/ppc/maglev-assembler-ppc-inl.h" ] + } else if (v8_current_cpu == "loong64") { + sources += [ "src/maglev/loong64/maglev-assembler-loong64-inl.h" ] } } @@ -4601,10 +4783,10 @@ v8_header_set("v8_internal_headers") { "src/wasm/code-space-access.h", "src/wasm/compilation-environment-inl.h", "src/wasm/compilation-environment.h", + "src/wasm/compilation-hints-generation.h", "src/wasm/constant-expression-interface.h", "src/wasm/constant-expression.h", "src/wasm/decoder.h", - "src/wasm/float16.h", "src/wasm/function-body-decoder-impl.h", "src/wasm/function-body-decoder.h", "src/wasm/function-compiler.h", @@ -4659,10 +4841,13 @@ v8_header_set("v8_internal_headers") { "src/wasm/wasm-opcodes.h", "src/wasm/wasm-result.h", "src/wasm/wasm-serialization.h", + "src/wasm/wasm-stack-wrapper-cache.h", "src/wasm/wasm-subtyping.h", "src/wasm/wasm-tier.h", "src/wasm/wasm-tracing.h", "src/wasm/wasm-value.h", + "src/wasm/wasm-wrapper-cache-inl.h", + "src/wasm/wasm-wrapper-cache.h", "src/wasm/well-known-imports.h", "src/wasm/wrappers-inl.h", "src/wasm/wrappers.h", @@ -5034,6 +5219,11 @@ v8_header_set("v8_internal_headers") { "src/baseline/riscv/baseline-compiler-riscv-inl.h", ] } + + if (riscv_use_zicfiss) { + sources += [ "src/execution/riscv/shadow-stack-riscv.h" ] + } + if (v8_enable_webassembly) { # Trap handling is enabled on riscv64 Linux and in simulators on # x64 on Linux. @@ -5124,6 +5314,9 @@ v8_header_set("v8_internal_headers") { ":v8_maybe_temporal", "//third_party/simdutf", ] + if (v8_enable_experimental_tq_to_tsa) { + deps += [ ":run_torque_to_tsa" ] + } } v8_compiler_sources = [ @@ -5144,7 +5337,7 @@ v8_compiler_sources = [ "src/compiler/backend/register-allocator-verifier.cc", "src/compiler/backend/register-allocator.cc", "src/compiler/backend/spill-placer.cc", - "src/compiler/basic-block-instrumentor.cc", + "src/compiler/basic-block-call-graph-profiler.cc", "src/compiler/branch-elimination.cc", "src/compiler/bytecode-analysis.cc", "src/compiler/bytecode-graph-builder.cc", @@ -5300,7 +5493,9 @@ if (!v8_enable_maglev) { "src/maglev/maglev-interpreter-frame-state.cc", "src/maglev/maglev-ir.cc", "src/maglev/maglev-known-node-aspects.cc", + "src/maglev/maglev-node-type.cc", "src/maglev/maglev-phi-representation-selector.cc", + "src/maglev/maglev-range-verification.cc", "src/maglev/maglev-truncation.cc", ] } @@ -5437,6 +5632,9 @@ v8_source_set("v8_compiler_for_mksnapshot_source_set") { ":v8_maybe_icu", ":v8_tracing", ] + if (v8_enable_experimental_tq_to_tsa) { + public_deps += [ ":run_torque_to_tsa" ] + } deps = [ ":v8_base_without_compiler", @@ -5477,6 +5675,9 @@ v8_source_set("v8_compiler") { ":v8_maybe_icu", ":v8_tracing", ] + if (v8_enable_experimental_tq_to_tsa) { + public_deps += [ ":run_torque_to_tsa" ] + } deps = [ ":v8_base_without_compiler", @@ -5561,6 +5762,8 @@ v8_source_set("v8_base_without_compiler") { "src/builtins/builtins-internal.cc", "src/builtins/builtins-intl.cc", "src/builtins/builtins-json.cc", + "src/builtins/builtins-math-xsum.cc", + "src/builtins/builtins-math.cc", "src/builtins/builtins-number.cc", "src/builtins/builtins-object.cc", "src/builtins/builtins-reflect.cc", @@ -5674,6 +5877,7 @@ v8_source_set("v8_base_without_compiler") { "src/handles/traced-handles.cc", "src/heap/allocation-observer.cc", "src/heap/array-buffer-sweeper.cc", + "src/heap/base-page.cc", "src/heap/code-range.cc", "src/heap/code-stats.cc", "src/heap/collection-barrier.cc", @@ -5704,7 +5908,7 @@ v8_source_set("v8_base_without_compiler") { "src/heap/incremental-marking-job.cc", "src/heap/incremental-marking.cc", "src/heap/index-generator.cc", - "src/heap/large-page-metadata.cc", + "src/heap/large-page.cc", "src/heap/large-spaces.cc", "src/heap/local-factory.cc", "src/heap/local-heap.cc", @@ -5716,17 +5920,16 @@ v8_source_set("v8_base_without_compiler") { "src/heap/marking.cc", "src/heap/memory-allocator.cc", "src/heap/memory-balancer.cc", - "src/heap/memory-chunk-metadata.cc", "src/heap/memory-chunk.cc", "src/heap/memory-measurement.cc", "src/heap/memory-pool.cc", "src/heap/memory-reducer.cc", "src/heap/minor-gc-job.cc", "src/heap/minor-mark-sweep.cc", - "src/heap/mutable-page-metadata.cc", + "src/heap/mutable-page.cc", "src/heap/new-spaces.cc", + "src/heap/normal-page.cc", "src/heap/object-stats.cc", - "src/heap/page-metadata.cc", "src/heap/paged-spaces.cc", "src/heap/pretenuring-handler.cc", "src/heap/read-only-heap.cc", @@ -5897,6 +6100,7 @@ v8_source_set("v8_base_without_compiler") { "src/regexp/experimental/experimental-interpreter.cc", "src/regexp/experimental/experimental.cc", "src/regexp/regexp-ast.cc", + "src/regexp/regexp-bytecode-analysis.cc", "src/regexp/regexp-bytecode-generator.cc", "src/regexp/regexp-bytecode-iterator.cc", "src/regexp/regexp-bytecode-peephole.cc", @@ -5904,10 +6108,8 @@ v8_source_set("v8_base_without_compiler") { "src/regexp/regexp-code-generator.cc", "src/regexp/regexp-compiler-tonode.cc", "src/regexp/regexp-compiler.cc", - "src/regexp/regexp-dotprinter.cc", "src/regexp/regexp-error.cc", "src/regexp/regexp-interpreter.cc", - "src/regexp/regexp-macro-assembler-tracer.cc", "src/regexp/regexp-macro-assembler.cc", "src/regexp/regexp-parser.cc", "src/regexp/regexp-result-vector.cc", @@ -5946,12 +6148,15 @@ v8_source_set("v8_base_without_compiler") { "src/runtime/runtime-typedarray.cc", "src/runtime/runtime-weak-refs.cc", "src/runtime/runtime.cc", + "src/sandbox/bytecode-verifier.cc", "src/sandbox/code-pointer-table.cc", "src/sandbox/cppheap-pointer-table.cc", "src/sandbox/external-pointer-table.cc", + "src/sandbox/external-strings-cage.cc", "src/sandbox/hardware-support.cc", "src/sandbox/js-dispatch-table.cc", "src/sandbox/sandbox.cc", + "src/sandbox/sandboxable-thread.cc", "src/sandbox/testing.cc", "src/sandbox/trusted-pointer-scope.cc", "src/sandbox/trusted-pointer-table.cc", @@ -5995,7 +6200,6 @@ v8_source_set("v8_base_without_compiler") { "src/utils/detachable-vector.cc", "src/utils/hex-format.cc", "src/utils/identity-map.cc", - "src/utils/memcopy.cc", "src/utils/ostreams.cc", "src/utils/output-stream.cc", "src/utils/sha-256.cc", @@ -6012,6 +6216,17 @@ v8_source_set("v8_base_without_compiler") { sources += [ "src/snapshot/snapshot-compression.cc" ] } + if (v8_enable_regexp_diagnostics) { + sources += [ + "src/regexp/regexp-ast-printer.cc", + "src/regexp/regexp-dotprinter.cc", + "src/regexp/regexp-graph-printer.cc", + "src/regexp/regexp-macro-assembler-tracer.cc", + "src/regexp/regexp-node-printer.cc", + "src/regexp/regexp-printer.cc", + ] + } + if (v8_enable_temporal_support) { sources += [ "src/builtins/builtins-temporal.cc", @@ -6044,8 +6259,10 @@ v8_source_set("v8_base_without_compiler") { "src/maglev/maglev-interpreter-frame-state.cc", "src/maglev/maglev-ir.cc", "src/maglev/maglev-known-node-aspects.cc", + "src/maglev/maglev-node-type.cc", "src/maglev/maglev-phi-representation-selector.cc", "src/maglev/maglev-pipeline-statistics.cc", + "src/maglev/maglev-range-verification.cc", "src/maglev/maglev-regalloc.cc", "src/maglev/maglev-truncation.cc", "src/maglev/maglev.cc", @@ -6080,6 +6297,11 @@ v8_source_set("v8_base_without_compiler") { "src/maglev/ppc/maglev-assembler-ppc.cc", "src/maglev/ppc/maglev-ir-ppc.cc", ] + } else if (v8_current_cpu == "loong64") { + sources += [ + "src/maglev/loong64/maglev-assembler-loong64.cc", + "src/maglev/loong64/maglev-ir-loong64.cc", + ] } } @@ -6111,6 +6333,7 @@ v8_source_set("v8_base_without_compiler") { "src/wasm/basic-block-calculator.cc", "src/wasm/canonical-types.cc", "src/wasm/code-space-access.cc", + "src/wasm/compilation-hints-generation.cc", "src/wasm/constant-expression-interface.cc", "src/wasm/constant-expression.cc", "src/wasm/function-body-decoder.cc", @@ -6145,8 +6368,10 @@ v8_source_set("v8_base_without_compiler") { "src/wasm/wasm-opcodes.cc", "src/wasm/wasm-result.cc", "src/wasm/wasm-serialization.cc", + "src/wasm/wasm-stack-wrapper-cache.cc", "src/wasm/wasm-subtyping.cc", "src/wasm/wasm-tracing.cc", + "src/wasm/wasm-wrapper-cache.cc", "src/wasm/well-known-imports.cc", ] if (v8_wasm_random_fuzzers) { @@ -6407,6 +6632,9 @@ v8_source_set("v8_base_without_compiler") { if (current_cpu == "x64" && is_linux) { sources += [ "src/trap-handler/handler-outside-simulator.cc" ] } + if (riscv_use_zicfiss) { + sources += [ "src/execution/riscv/shadow-stack-riscv.cc" ] + } } } else if (v8_current_cpu == "riscv32") { sources += [ @@ -6490,6 +6718,9 @@ v8_source_set("v8_base_without_compiler") { ":v8_maybe_icu", "//third_party/simdutf", ] + if (v8_enable_experimental_tq_to_tsa) { + public_deps += [ ":run_torque_to_tsa" ] + } if (v8_fuzzilli) { sources += [ @@ -6500,6 +6731,15 @@ v8_source_set("v8_base_without_compiler") { ] } + if (v8_dumpling) { + sources += [ + "src/dumpling/dumpling-manager.cc", + "src/dumpling/dumpling-manager.h", + "src/dumpling/object-dumping.cc", + "src/dumpling/object-dumping.h", + ] + } + if (v8_enable_i18n_support) { deps += [ ":run_gen-regexp-special-case" ] sources += [ "$target_gen_dir/src/regexp/special-case.cc" ] @@ -6634,6 +6874,14 @@ v8_source_set("torque_base") { "src/torque/utils.h", ] + if (v8_enable_experimental_tq_to_tsa) { + sources += [ + "src/torque/ast-visitor.h", + "src/torque/tsa-generator.cc", + "src/torque/tsa-generator.h", + ] + } + deps = [ ":v8_flags", ":v8_shared_internal_headers", @@ -6701,6 +6949,7 @@ v8_component("v8_libbase") { "src/base/abort-mode.cc", "src/base/abort-mode.h", "src/base/address-region.h", + "src/base/algorithm.h", "src/base/atomic-utils.h", "src/base/atomicops.h", "src/base/base-export.h", @@ -6730,6 +6979,7 @@ v8_component("v8_libbase") { "src/base/file-utils.cc", "src/base/file-utils.h", "src/base/flags.h", + "src/base/float16.h", "src/base/fpu.cc", "src/base/fpu.h", "src/base/free_deleter.h", @@ -6747,6 +6997,7 @@ v8_component("v8_libbase") { "src/base/logging.cc", "src/base/logging.h", "src/base/macros.h", + "src/base/memcopy.h", "src/base/memory.h", "src/base/numbers/bignum-dtoa.cc", "src/base/numbers/bignum-dtoa.h", @@ -7438,6 +7689,7 @@ v8_source_set("cppgc_base") { if (v8_use_perfetto) { sources += [ + "src/tracing/perfetto-sdk.h", "src/tracing/trace-categories.cc", "src/tracing/trace-categories.h", ] @@ -8006,6 +8258,8 @@ v8_executable("d8") { "src/d8/d8-test.cc", "src/d8/d8.cc", "src/d8/d8.h", + "src/d8/hardware-watchpoints.cc", + "src/d8/hardware-watchpoints.h", ] configs = [ @@ -8309,6 +8563,9 @@ if (v8_enable_webassembly) { ":v8_shared_internal_headers", ":v8_tracing", ] + if (v8_enable_experimental_tq_to_tsa) { + deps += [ ":run_torque_to_tsa" ] + } public_deps = [ ":v8_abseil", @@ -8409,6 +8666,9 @@ group("v8_generated_cc_files") { ":run_torque", "src/inspector:v8_generated_cc_files", ] + if (v8_enable_experimental_tq_to_tsa) { + deps += [ ":run_torque_to_tsa" ] + } } # Protobuf targets, used only when building outside of chromium. diff --git a/deps/v8/COMMON_OWNERS b/deps/v8/COMMON_OWNERS index 1a14c2458d6d0d..87e36677744ae4 100644 --- a/deps/v8/COMMON_OWNERS +++ b/deps/v8/COMMON_OWNERS @@ -8,6 +8,7 @@ dinfuehr@chromium.org dlehmann@chromium.org dmercadier@chromium.org ecmziegler@chromium.org +emaxx@google.com evih@chromium.org fgm@chromium.org gdeepti@chromium.org diff --git a/deps/v8/DEPS b/deps/v8/DEPS index 86740bd8ebbf1d..170b52a281c5b1 100644 --- a/deps/v8/DEPS +++ b/deps/v8/DEPS @@ -6,6 +6,7 @@ use_relative_paths = True gclient_gn_args_file = 'build/config/gclient_args.gni' gclient_gn_args = [ + 'checkout_src_internal', ] vars = { @@ -58,6 +59,9 @@ vars = { # Used for downloading the Fuchsia SDK without running hooks. 'checkout_fuchsia_no_hooks': False, + # V8 doesn't need src_internal, but some shared GN files use this variable. + 'checkout_src_internal': False, + # reclient CIPD package version 'reclient_version': 're_client_version:0.185.0.db415f21-gomaip', @@ -75,53 +79,41 @@ vars = { 'build_with_chromium': False, # GN CIPD package version. - 'gn_version': 'git_revision:07d3c6f4dc290fae5ca6152ebcb37d6815c411ab', + 'gn_version': 'git_revision:103f8b437f5e791e0aef9d5c372521a5d675fabb', # ninja CIPD package version # https://chrome-infra-packages.appspot.com/p/infra/3pp/tools/ninja 'ninja_version': 'version:3@1.12.1.chromium.4', # siso CIPD package version - 'siso_version': 'git_revision:0915813c4c786240e12d03aa3018c02bab4df14f', + 'siso_version': 'git_revision:dc540b292de5e5cbfc88bbdd15b30b1634979823', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Fuchsia sdk # and whatever else without interference from each other. - 'fuchsia_version': 'version:29.20251023.3.1', + 'fuchsia_version': 'version:30.20251218.4.1', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling partition_alloc_version # and whatever else without interference from each other. - 'partition_alloc_version': 'db8446987dfff3cfc0c100b7d58e6a404ef639eb', + 'partition_alloc_version': '936619c71ecb17c0e2482cf86be3f3f417b2f683', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling android_sdk_build-tools_version # and whatever else without interference from each other. - 'android_sdk_build-tools_version': 'y3EsZLg4bxPmpW0oYsAHylywNyMnIwPS3kh1VbQLAFAC', + 'android_sdk_build-tools_version': '-jLl4Ibk_WmgTsZaP-ueQwZDhBwkWf5BsQ4UNrkzXF0C', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling android_sdk_emulator_version # and whatever else without interference from each other. 'android_sdk_emulator_version': '9lGp8nTUCRRWGMnI_96HcKfzjnxEJKUcfvfwmA3wXNkC', # Three lines of non-changing comments so that - # the commit queue can handle CLs rolling android_sdk_extras_version - # and whatever else without interference from each other. - 'android_sdk_extras_version': 'bY55nDqO6FAm6FkGIj09sh2KW9oqAkCGKjYok5nUvBMC', - # Three lines of non-changing comments so that - # the commit queue can handle CLs rolling android_sdk_patcher_version - # and whatever else without interference from each other. - 'android_sdk_patcher_version': 'I6FNMhrXlpB-E1lOhMlvld7xt9lBVNOO83KIluXDyA0C', - # Three lines of non-changing comments so that # the commit queue can handle CLs rolling android_sdk_platform-tools_version # and whatever else without interference from each other. 'android_sdk_platform-tools_version': 'qTD9QdBlBf3dyHsN1lJ0RH6AhHxR42Hmg2Ih-Vj4zIEC', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling android_sdk_platforms_version # and whatever else without interference from each other. - 'android_sdk_platforms_version': '_YHemUrK49JrE7Mctdf5DDNOHu1VKBx_PTcWnZ-cbOAC', - # Three lines of non-changing comments so that - # the commit queue can handle CLs rolling android_sdk_sources_version - # and whatever else without interference from each other. - 'android_sdk_sources_version': 'qfTSF99e29-w3eIVPpfcif0Em5etyvxuicTDTntWHQMC', + 'android_sdk_platforms_version': 'gxwLT70eR_ObwZJzKK8UIS-N549yAocNTmc0JHgO7gUC', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling android_sdk_tools-lint_version # and whatever else without interference from each other. @@ -130,9 +122,9 @@ vars = { deps = { 'build': - Var('chromium_url') + '/chromium/src/build.git' + '@' + '81330a6e45719ab8c0d22e5548afbedb5482b068', + Var('chromium_url') + '/chromium/src/build.git' + '@' + '483cecced32ce8b098d65eb08eb77925afa90bec', 'buildtools': - Var('chromium_url') + '/chromium/src/buildtools.git' + '@' + '723d636763a2635105e0e912bae25f662b2acaa8', + Var('chromium_url') + '/chromium/src/buildtools.git' + '@' + '6a18683f555b4ac8b05ac8395c29c84483ac9588', 'buildtools/linux64': { 'packages': [ { @@ -178,7 +170,7 @@ deps = { 'test/mozilla/data': Var('chromium_url') + '/v8/deps/third_party/mozilla-tests.git' + '@' + 'f6c578a10ea707b1a8ab0b88943fe5115ce2b9be', 'test/test262/data': - Var('chromium_url') + '/external/github.com/tc39/test262.git' + '@' + 'd2940bdbb0e28fd002ec31b89f8182bbf63da092', + Var('chromium_url') + '/external/github.com/tc39/test262.git' + '@' + 'dd6138f9bc1aa2c3ba9cbf54452049b9a92c4e13', 'third_party/android_platform': { 'url': Var('chromium_url') + '/chromium/src/third_party/android_platform.git' + '@' + 'e3919359f2387399042d31401817db4a02d756ec', 'condition': 'checkout_android', @@ -186,33 +178,21 @@ deps = { 'third_party/android_sdk/public': { 'packages': [ { - 'package': 'chromium/third_party/android_sdk/public/build-tools/36.0.0', + 'package': 'chromium/third_party/android_sdk/public/build-tools/36.1.0', 'version': Var('android_sdk_build-tools_version'), }, { 'package': 'chromium/third_party/android_sdk/public/emulator', 'version': Var('android_sdk_emulator_version'), }, - { - 'package': 'chromium/third_party/android_sdk/public/extras', - 'version': Var('android_sdk_extras_version'), - }, - { - 'package': 'chromium/third_party/android_sdk/public/patcher', - 'version': Var('android_sdk_patcher_version'), - }, { 'package': 'chromium/third_party/android_sdk/public/platform-tools', 'version': Var('android_sdk_platform-tools_version'), }, { - 'package': 'chromium/third_party/android_sdk/public/platforms/android-36', + 'package': 'chromium/third_party/android_sdk/public/platforms/android-36.1', 'version': Var('android_sdk_platforms_version'), }, - { - 'package': 'chromium/third_party/android_sdk/public/sources/android-30', - 'version': Var('android_sdk_sources_version'), - }, { 'package': 'chromium/third_party/android_sdk/public/cmdline-tools', 'version': Var('android_sdk_cmdline-tools_version'), @@ -232,7 +212,7 @@ deps = { 'dep_type': 'cipd', }, 'third_party/catapult': { - 'url': Var('chromium_url') + '/catapult.git' + '@' + '4daf7b2cead7fb17ec3da4eedb30215e3bd3674a', + 'url': Var('chromium_url') + '/catapult.git' + '@' + 'ef2533747d649df4cba6756528f68751b8523f81', 'condition': 'checkout_android', }, 'third_party/clang-format/script': @@ -246,19 +226,19 @@ deps = { 'condition': 'checkout_android', }, 'third_party/depot_tools': - Var('chromium_url') + '/chromium/tools/depot_tools.git' + '@' + '8a1ec6a0213ae033f6749f261e1c528488349991', + Var('chromium_url') + '/chromium/tools/depot_tools.git' + '@' + '284c5ccb591c3de4e9f71be4a4beb5d1916d5383', 'third_party/dragonbox/src': - Var('chromium_url') + '/external/github.com/jk-jeon/dragonbox.git' + '@' + '6c7c925b571d54486b9ffae8d9d18a822801cbda', + Var('chromium_url') + '/external/github.com/jk-jeon/dragonbox.git' + '@' + 'beeeef91cf6fef89a4d4ba5e95d47ca64ccb3a44', 'third_party/fp16/src': Var('chromium_url') + '/external/github.com/Maratyszcza/FP16.git' + '@' + '3d2de1816307bac63c16a297e8c4dc501b4076df', 'third_party/fast_float/src': Var('chromium_url') + '/external/github.com/fastfloat/fast_float.git' + '@' + 'cb1d42aaa1e14b09e1452cfdef373d051b8c02a4', 'third_party/fuchsia-gn-sdk': { - 'url': Var('chromium_url') + '/chromium/src/third_party/fuchsia-gn-sdk.git' + '@' + '99294ee55f28f8ae5a3552f4c435528e4c1686b6', + 'url': Var('chromium_url') + '/chromium/src/third_party/fuchsia-gn-sdk.git' + '@' + '947109b3f1f40fb060e7c91df049ee53fe89d573', 'condition': 'checkout_fuchsia', }, 'third_party/simdutf': - Var('chromium_url') + '/chromium/src/third_party/simdutf' + '@' + 'acd71a451c1bcb808b7c3a77e0242052909e381e', + Var('chromium_url') + '/chromium/src/third_party/simdutf' + '@' + '93b35aec29256f705c97f675fe4623578bd7a395', # Exists for rolling the Fuchsia SDK. Check out of the SDK should always # rely on the hook running |update_sdk.py| script below. 'third_party/fuchsia-sdk/sdk': { @@ -272,21 +252,21 @@ deps = { 'dep_type': 'cipd', }, 'third_party/google_benchmark_chrome': { - 'url': Var('chromium_url') + '/chromium/src/third_party/google_benchmark.git' + '@' + 'fa1929c5500ccfc01852ba50ff9258303e93601e', + 'url': Var('chromium_url') + '/chromium/src/third_party/google_benchmark.git' + '@' + 'abeba5d5e6db5bdf85261045e148f1db3fdc40ad', }, 'third_party/google_benchmark_chrome/src': { - 'url': Var('chromium_url') + '/external/github.com/google/benchmark.git' + '@' + '761305ec3b33abf30e08d50eb829e19a802581cc', + 'url': Var('chromium_url') + '/external/github.com/google/benchmark.git' + '@' + '188e8278990a9069ffc84441cb5a024fd0bede37', }, 'third_party/fuzztest': - Var('chromium_url') + '/chromium/src/third_party/fuzztest.git' + '@' + 'aa6ba9074b8d66a2e2853a0a0992c25966022e13', + Var('chromium_url') + '/chromium/src/third_party/fuzztest.git' + '@' + '3c8b741ed69e60949a481e3ff86c7933f65cfc2d', 'third_party/fuzztest/src': - Var('chromium_url') + '/external/github.com/google/fuzztest.git' + '@' + '7940ee9a7ebce6419c6391eef8b289524b16f198', + Var('chromium_url') + '/external/github.com/google/fuzztest.git' + '@' + '54dfec04d5c9ad1f22b08002ab6a5e2d0de77671', 'third_party/googletest/src': - Var('chromium_url') + '/external/github.com/google/googletest.git' + '@' + 'b2b9072ecbe874f5937054653ef8f2731eb0f010', + Var('chromium_url') + '/external/github.com/google/googletest.git' + '@' + '4fe3307fb2d9f86d19777c7eb0e4809e9694dde7', 'third_party/highway/src': Var('chromium_url') + '/external/github.com/google/highway.git' + '@' + '84379d1c73de9681b54fbe1c035a23c7bd5d272d', 'third_party/icu': - Var('chromium_url') + '/chromium/deps/icu.git' + '@' + 'f27805b7d7d8618fa73ce89e9d28e0a8b2216fec', + Var('chromium_url') + '/chromium/deps/icu.git' + '@' + 'a86a32e67b8d1384b33f8fa48c83a6079b86f8cd', 'third_party/instrumented_libs': { 'url': Var('chromium_url') + '/chromium/third_party/instrumented_libraries.git' + '@' + '69015643b3f68dbd438c010439c59adc52cac808', 'condition': 'checkout_instrumented_libraries', @@ -302,179 +282,179 @@ deps = { 'third_party/jsoncpp/source': Var('chromium_url') + '/external/github.com/open-source-parsers/jsoncpp.git'+ '@' + '42e892d96e47b1f6e29844cc705e148ec4856448', 'third_party/libc++/src': - Var('chromium_url') + '/external/github.com/llvm/llvm-project/libcxx.git' + '@' + 'cdb24138c1591d12b07d5147825ec7dfeb495276', + Var('chromium_url') + '/external/github.com/llvm/llvm-project/libcxx.git' + '@' + '7ab65651aed6802d2599dcb7a73b1f82d5179d05', 'third_party/libc++abi/src': - Var('chromium_url') + '/external/github.com/llvm/llvm-project/libcxxabi.git' + '@' + 'a02fa0058d8d52aca049868d229808a3e5dadbad', + Var('chromium_url') + '/external/github.com/llvm/llvm-project/libcxxabi.git' + '@' + '8f11bb1d4438d0239d0dfc1bd9456a9f31629dda', 'third_party/libpfm4': Var('chromium_url') + '/chromium/src/third_party/libpfm4.git' + '@' + '25c29f04c9127e1ca09e6c1181f74850aa7f118b', 'third_party/libpfm4/src': Var('chromium_url') + '/external/git.code.sf.net/p/perfmon2/libpfm4.git' + '@' + '964baf9d35d5f88d8422f96d8a82c672042e7064', 'third_party/libunwind/src': - Var('chromium_url') + '/external/github.com/llvm/llvm-project/libunwind.git' + '@' + '61ba011ba3c4ed238af93ebad476d3ab5a2fb5ab', + Var('chromium_url') + '/external/github.com/llvm/llvm-project/libunwind.git' + '@' + 'ba19d93d6d4f467fba11ff20fe2fc7c056f79345', 'third_party/llvm-libc/src': - Var('chromium_url') + '/external/github.com/llvm/llvm-project/libc.git' + '@' + 'db35841a6fcbeee98e4d7fe6ba3df1a876a18a62', + Var('chromium_url') + '/external/github.com/llvm/llvm-project/libc.git' + '@' + 'e81e859cfb7e78e70a58c3bfce859c509f45e1da', 'third_party/llvm-build/Release+Asserts': { 'dep_type': 'gcs', 'bucket': 'chromium-browser-clang', 'objects': [ { - 'object_name': 'Linux_x64/clang-llvmorg-22-init-8940-g4d4cb757-84.tar.xz', - 'sha256sum': 'f6a487ffd0e56ba7a39b063d85d1f8ff7846514f50635785730cffb7368872ce', - 'size_bytes': 55669844, - 'generation': 1759771493989631, + 'object_name': 'Linux_x64/clang-llvmorg-23-init-484-gf646b915-1.tar.xz', + 'sha256sum': '1c3c056427ab0db261c54c8fdf7c8404ff55e3de3e550520bcb1e1660ca05aad', + 'size_bytes': 57489092, + 'generation': 1768590901063677, 'condition': 'host_os == "linux"', }, { - 'object_name': 'Linux_x64/clang-tidy-llvmorg-22-init-8940-g4d4cb757-84.tar.xz', - 'sha256sum': '4fc7aacf4c25e50a25a941f1186a9e042ae26a2c5c698f359907798fa68106c8', - 'size_bytes': 14053336, - 'generation': 1759771494041411, + 'object_name': 'Linux_x64/clang-tidy-llvmorg-23-init-484-gf646b915-1.tar.xz', + 'sha256sum': 'e3f568bd57c7ab199eb384153eea8cbe3c0e0604b2d8bbb158985647709a9a9c', + 'size_bytes': 14391456, + 'generation': 1768590901188932, 'condition': 'host_os == "linux" and checkout_clang_tidy', }, { - 'object_name': 'Linux_x64/clangd-llvmorg-22-init-8940-g4d4cb757-84.tar.xz', - 'sha256sum': '238897cb0b55ffcb7f6b8f6a10055e44e05023642441a800895704ced91d37d1', - 'size_bytes': 14197108, - 'generation': 1759771494144266, + 'object_name': 'Linux_x64/clangd-llvmorg-23-init-484-gf646b915-1.tar.xz', + 'sha256sum': '8762f3c6102eae568e3ca7a287e774514846f2bb2feda4cf7dc1c33d9f5f1c8d', + 'size_bytes': 14588900, + 'generation': 1768590901246745, 'condition': 'host_os == "linux" and checkout_clangd', }, { - 'object_name': 'Linux_x64/llvm-code-coverage-llvmorg-22-init-8940-g4d4cb757-84.tar.xz', - 'sha256sum': '2c5b0bf210ca982d8ec37cacf3d06d9c45bd6e68b33dcaabce0d108d6c266a36', - 'size_bytes': 2272128, - 'generation': 1759771494296549, + 'object_name': 'Linux_x64/llvm-code-coverage-llvmorg-23-init-484-gf646b915-1.tar.xz', + 'sha256sum': '4705feac592251ad0e2e1c41c855a6ecdc728908cbb459d4f68ca57f16bc4c5e', + 'size_bytes': 2321652, + 'generation': 1768590901407256, 'condition': 'host_os == "linux" and checkout_clang_coverage_tools', }, { - 'object_name': 'Linux_x64/llvmobjdump-llvmorg-22-init-8940-g4d4cb757-84.tar.xz', - 'sha256sum': 'fd644634db56977b072d951f26571ac41c9c298bf5989e99efeb150ee8427364', - 'size_bytes': 5666140, - 'generation': 1759771494159187, + 'object_name': 'Linux_x64/llvmobjdump-llvmorg-23-init-484-gf646b915-1.tar.xz', + 'sha256sum': '39d29ab3e708bcb6485181ac03123cfa3bac5b2365d5c441ab0cf5e7b25354b6', + 'size_bytes': 5802908, + 'generation': 1768590901316435, 'condition': '(checkout_linux or checkout_mac or checkout_android) and host_os == "linux"', }, { - 'object_name': 'Mac/clang-llvmorg-22-init-8940-g4d4cb757-84.tar.xz', - 'sha256sum': '44811b6ed6868142c088807f6bcc0d08811a7b11d3f2bc2124c45868037e8cc3', - 'size_bytes': 53583464, - 'generation': 1759771495565305, + 'object_name': 'Mac/clang-llvmorg-23-init-484-gf646b915-1.tar.xz', + 'sha256sum': '9aff2c8f9d941be0667dc3ad3d4c3591dccd70f7a3b8e80396a623364e752eeb', + 'size_bytes': 54613288, + 'generation': 1768590902935296, 'condition': 'host_os == "mac" and host_cpu == "x64"', }, { - 'object_name': 'Mac/clang-mac-runtime-library-llvmorg-22-init-8940-g4d4cb757-84.tar.xz', - 'sha256sum': '8a2e16410bede5d52c77a012f182dde2350b05e647f7c1acaf7823ce816b4422', - 'size_bytes': 1005144, - 'generation': 1759771503758969, + 'object_name': 'Mac/clang-mac-runtime-library-llvmorg-23-init-484-gf646b915-1.tar.xz', + 'sha256sum': '4f953afe4edebb54912719b437c78842978b0205792c069aa7529fd369d900be', + 'size_bytes': 1011040, + 'generation': 1768590912304306, 'condition': 'checkout_mac and not host_os == "mac"', }, { - 'object_name': 'Mac/clang-tidy-llvmorg-22-init-8940-g4d4cb757-84.tar.xz', - 'sha256sum': '66633fe8846fddeda86b5ee992b945939bfe46567c9c685900c39531d22ce5cf', - 'size_bytes': 14133312, - 'generation': 1759771495642847, + 'object_name': 'Mac/clang-tidy-llvmorg-23-init-484-gf646b915-1.tar.xz', + 'sha256sum': '62033f6ff5c1ea0a18ae581edfd1178f50ede19d84675eafb640d752e26b60ae', + 'size_bytes': 14444752, + 'generation': 1768590903234647, 'condition': 'host_os == "mac" and host_cpu == "x64" and checkout_clang_tidy', }, { - 'object_name': 'Mac/clangd-llvmorg-22-init-8940-g4d4cb757-84.tar.xz', - 'sha256sum': '44088b951aa7ddc96c0f32703b076311a7e7b803b3adfe0bfe9725f78c4fab29', - 'size_bytes': 15627392, - 'generation': 1759771495653658, + 'object_name': 'Mac/clangd-llvmorg-23-init-484-gf646b915-1.tar.xz', + 'sha256sum': '4a0365772b4eb7fe12fe595355e645f12c72255bf3f869941f2b4e5c5e2b76da', + 'size_bytes': 16398188, + 'generation': 1768590903283692, 'condition': 'host_os == "mac" and host_cpu == "x64" and checkout_clangd', }, { - 'object_name': 'Mac/llvm-code-coverage-llvmorg-22-init-8940-g4d4cb757-84.tar.xz', - 'sha256sum': '79d62c78d256a508a0f3dbe59aa0fdf0391a9d462bf74e56adc1dee82efa83ac', - 'size_bytes': 2321940, - 'generation': 1759771495825689, + 'object_name': 'Mac/llvm-code-coverage-llvmorg-23-init-484-gf646b915-1.tar.xz', + 'sha256sum': '1c307bb206c5fc1e79deec1d12ce4ead53fa41574729deaa6d2b3a67f9540710', + 'size_bytes': 2352620, + 'generation': 1768590903522922, 'condition': 'host_os == "mac" and host_cpu == "x64" and checkout_clang_coverage_tools', }, { - 'object_name': 'Mac/llvmobjdump-llvmorg-22-init-8940-g4d4cb757-84.tar.xz', - 'sha256sum': 'a10d075e19e7b614ffd8c5a65f04fbd45011ec74c735dda89f0b3780ab397329', - 'size_bytes': 5567160, - 'generation': 1759771495741126, + 'object_name': 'Mac/llvmobjdump-llvmorg-23-init-484-gf646b915-1.tar.xz', + 'sha256sum': '83767300f93707ba9a40d3d7e14f1149aaf587d5c1e3ae243be8742a008e5052', + 'size_bytes': 5682364, + 'generation': 1768590903341073, 'condition': 'host_os == "mac" and host_cpu == "x64"', }, { - 'object_name': 'Mac_arm64/clang-llvmorg-22-init-8940-g4d4cb757-84.tar.xz', - 'sha256sum': 'c97e4f62cdd77edf725ccbf4cd63b589302605bf643c871f83214f39e629b2ea', - 'size_bytes': 44593804, - 'generation': 1759771504972271, + 'object_name': 'Mac_arm64/clang-llvmorg-23-init-484-gf646b915-1.tar.xz', + 'sha256sum': '5475c6c38a6199276ff173665a27ab608aadb3118ac34f1a75391ee6dc226798', + 'size_bytes': 45585568, + 'generation': 1768590913838191, 'condition': 'host_os == "mac" and host_cpu == "arm64"', }, { - 'object_name': 'Mac_arm64/clang-tidy-llvmorg-22-init-8940-g4d4cb757-84.tar.xz', - 'sha256sum': '3a0eb0fb3a4633c8b4b143e826c5476c41cdd6bd0db8e93a74bbee6520b02b79', - 'size_bytes': 12136348, - 'generation': 1759771505073378, + 'object_name': 'Mac_arm64/clang-tidy-llvmorg-23-init-484-gf646b915-1.tar.xz', + 'sha256sum': '9893644917db71d520832ea9e276a98e2051acf53458efc6834973e076c6d36e', + 'size_bytes': 12429560, + 'generation': 1768590913986547, 'condition': 'host_os == "mac" and host_cpu == "arm64" and checkout_clang_tidy', }, { - 'object_name': 'Mac_arm64/clangd-llvmorg-22-init-8940-g4d4cb757-84.tar.xz', - 'sha256sum': '2a5dc1f385bacd25b974b8aa15c57008e33bc384521e2d705a940acbb3292356', - 'size_bytes': 12479180, - 'generation': 1759771505148040, + 'object_name': 'Mac_arm64/clangd-llvmorg-23-init-484-gf646b915-1.tar.xz', + 'sha256sum': 'e67cfb9503c442bd29642b3a42b36c283346371b8143c81b895684df0fb09e69', + 'size_bytes': 12817084, + 'generation': 1768590914107805, 'condition': 'host_os == "mac" and host_cpu == "arm64" and checkout_clangd', }, { - 'object_name': 'Mac_arm64/llvm-code-coverage-llvmorg-22-init-8940-g4d4cb757-84.tar.xz', - 'sha256sum': '00bf0f82ca9aff15f32e7f0cf7e7b25d36a5a672a1a9bc345c1b7e140a478f93', - 'size_bytes': 1948520, - 'generation': 1759771505303586, + 'object_name': 'Mac_arm64/llvm-code-coverage-llvmorg-23-init-484-gf646b915-1.tar.xz', + 'sha256sum': '5f17c695f24eadcc51edc720a5b0b2f2cc36413552038d93ae2bf361667d780a', + 'size_bytes': 1978756, + 'generation': 1768590914351005, 'condition': 'host_os == "mac" and host_cpu == "arm64" and checkout_clang_coverage_tools', }, { - 'object_name': 'Mac_arm64/llvmobjdump-llvmorg-22-init-8940-g4d4cb757-84.tar.xz', - 'sha256sum': '7aa959752d6beafc74129e4822912021f855584e55a55600044f1d42b889f8b0', - 'size_bytes': 5292960, - 'generation': 1759771505201957, + 'object_name': 'Mac_arm64/llvmobjdump-llvmorg-23-init-484-gf646b915-1.tar.xz', + 'sha256sum': '5fd7fc88f00197a710aac7dd00a2b6947d5231c53a472ed38e1af0b9088cefc3', + 'size_bytes': 5418172, + 'generation': 1768590914206466, 'condition': 'host_os == "mac" and host_cpu == "arm64"', }, { - 'object_name': 'Win/clang-llvmorg-22-init-8940-g4d4cb757-84.tar.xz', - 'sha256sum': 'fc756186dea61e700bd0f885b585050d9356bbd7f942dafae25d38eef4671adf', - 'size_bytes': 47657436, - 'generation': 1759771514781908, + 'object_name': 'Win/clang-llvmorg-23-init-484-gf646b915-1.tar.xz', + 'sha256sum': 'de6622ec6d9d22b00316c47b2eb59e6cb7dbcb2d5b59f04f18c94714d0b35066', + 'size_bytes': 48839256, + 'generation': 1768590925760667, 'condition': 'host_os == "win"', }, { - 'object_name': 'Win/clang-tidy-llvmorg-22-init-8940-g4d4cb757-84.tar.xz', - 'sha256sum': 'f7ecd7e8d555e8622e0096ea1aca3ddb3fb4e89e91228c3c87289a4b8ca7919c', - 'size_bytes': 14016476, - 'generation': 1759771514824669, + 'object_name': 'Win/clang-tidy-llvmorg-23-init-484-gf646b915-1.tar.xz', + 'sha256sum': 'bf6f38e7f5d06c8c3eff9ba85df1ddd1006828cc72b638d22a3e7562507a8a51', + 'size_bytes': 14353272, + 'generation': 1768590926006423, 'condition': 'host_os == "win" and checkout_clang_tidy', }, { - 'object_name': 'Win/clang-win-runtime-library-llvmorg-22-init-8940-g4d4cb757-84.tar.xz', - 'sha256sum': '0a426702c9e0f92ea27f9611a1665cc5df9a58820360d3fa6a4026b9a0e5120f', - 'size_bytes': 2501292, - 'generation': 1759771523074183, + 'object_name': 'Win/clang-win-runtime-library-llvmorg-23-init-484-gf646b915-1.tar.xz', + 'sha256sum': 'ad0dc3c686f63f40a35bd7a10f90935d08bcd9d1f23549c87cbdeb8cb503250c', + 'size_bytes': 2540656, + 'generation': 1768590935148812, 'condition': 'checkout_win and not host_os == "win"', }, { - 'object_name': 'Win/clangd-llvmorg-22-init-8940-g4d4cb757-84.tar.xz', - 'sha256sum': 'b172d0246511cdeffbc5a4fa44ad402a6b9eacd9d3e2e77d88a9965f80d344d5', - 'size_bytes': 14364312, - 'generation': 1759771514873065, + 'object_name': 'Win/clangd-llvmorg-23-init-484-gf646b915-1.tar.xz', + 'sha256sum': 'bcf731c6ce9050067212fc4164893ce768809d73732459f2c1a0ddb8f124f5f2', + 'size_bytes': 14736740, + 'generation': 1768590926034327, 'condition': 'host_os == "win" and checkout_clangd', }, { - 'object_name': 'Win/llvm-code-coverage-llvmorg-22-init-8940-g4d4cb757-84.tar.xz', - 'sha256sum': 'b70528795afd95729971b74939e512c638a8a93fd1ee1c9205a6240f7af28802', - 'size_bytes': 2368144, - 'generation': 1759771515105244, + 'object_name': 'Win/llvm-code-coverage-llvmorg-23-init-484-gf646b915-1.tar.xz', + 'sha256sum': 'bf86ab13e378a70f953d014cc7a37714dbaca0d8002cd638dd4d88df08231910', + 'size_bytes': 2416448, + 'generation': 1768590926285418, 'condition': 'host_os == "win" and checkout_clang_coverage_tools', }, { - 'object_name': 'Win/llvmobjdump-llvmorg-22-init-8940-g4d4cb757-84.tar.xz', - 'sha256sum': '94c068f109e220e028a38f5beced7d6acd67725fc0b1da9fa8ed1b959f12d799', - 'size_bytes': 5673824, - 'generation': 1759771514962844, + 'object_name': 'Win/llvmobjdump-llvmorg-23-init-484-gf646b915-1.tar.xz', + 'sha256sum': '3ee3ece3cf0afa3536c39b15f818e5eab1ac4408cd31ad0ad82414d3a8aa1eca', + 'size_bytes': 5796552, + 'generation': 1768590926109316, 'condition': '(checkout_linux or checkout_mac or checkout_android) and host_os == "win"', }, ], }, 'third_party/logdog/logdog': - Var('chromium_url') + '/infra/luci/luci-py/client/libs/logdog' + '@' + '0b2078a90f7a638d576b3a7c407d136f2fb62399', + Var('chromium_url') + '/infra/luci/luci-py/client/libs/logdog' + '@' + '62fe96d7fd97a62f21a4665d2e71f69e9eedb04e', 'third_party/markupsafe': Var('chromium_url') + '/chromium/src/third_party/markupsafe.git' + '@' + '4256084ae14175d38a3ff7d739dca83ae49ccec6', 'third_party/ninja': { @@ -492,56 +472,56 @@ deps = { 'condition': 'not build_with_chromium', }, 'third_party/perfetto': - Var('android_url') + '/platform/external/perfetto.git' + '@' + '40b529923598b739b2892a536a7692eedbed5685', + Var('chromium_url') + '/external/github.com/google/perfetto.git' + '@' + '1d9994a93c6ada2fb261dc72984fa07683a6c86e', 'third_party/protobuf': - Var('chromium_url') + '/chromium/src/third_party/protobuf.git' + '@' + 'fcb7931541e4fe633b796db3f3e6f54c2dd297a8', + Var('chromium_url') + '/chromium/src/third_party/protobuf.git' + '@' + 'ddf513ccaeed6b1316bc2029b10f4436ad297324', 'third_party/re2/src': - Var('chromium_url') + '/external/github.com/google/re2.git' + '@' + '61c4644171ee6b480540bf9e569cba06d9090b4b', + Var('chromium_url') + '/external/github.com/google/re2.git' + '@' + '972a15cedd008d846f1a39b2e88ce48d7f166cbd', 'third_party/requests': { 'url': Var('chromium_url') + '/external/github.com/kennethreitz/requests.git' + '@' + 'c7e0fc087ceeadb8b4c84a0953a422c474093d6d', 'condition': 'checkout_android', }, 'tools/rust': - Var('chromium_url') + '/chromium/src/tools/rust' + '@' + '12557fcc00d7e94caa5e270d7343b566e48a68ae', + Var('chromium_url') + '/chromium/src/tools/rust' + '@' + 'd2fadac996be252abe5077583a42970163e9f2e7', 'tools/win': - Var('chromium_url') + '/chromium/src/tools/win' + '@' + '24494b071e019a2baea4355d9870ffc5fc0bbafe', + Var('chromium_url') + '/chromium/src/tools/win' + '@' + 'baacfc6d5986b07abe0503216b491e234b94ba79', 'third_party/rust': - Var('chromium_url') + '/chromium/src/third_party/rust' + '@' + '4d93511ebaceb09ebdd83c8876a4a936b75fa04d', + Var('chromium_url') + '/chromium/src/third_party/rust' + '@' + '30eb036e9b2f181dda31bde6f20d2a4983e380b9', 'third_party/rust-toolchain': { 'dep_type': 'gcs', 'bucket': 'chromium-browser-clang', 'objects': [ { - 'object_name': 'Linux_x64/rust-toolchain-15283f6fe95e5b604273d13a428bab5fc0788f5a-1-llvmorg-22-init-8940-g4d4cb757.tar.xz', - 'sha256sum': '2bdaea0b11cb11a8f2f4dcb79b0dbb4bf38e2bd22479ff8014f55b9b6890e135', - 'size_bytes': 142044388, - 'generation': 1758743116775859, + 'object_name': 'Linux_x64/rust-toolchain-d2015e2359d5d0b154c2b192d4039f9b5711fcdc-4-llvmorg-23-init-484-gf646b915.tar.xz', + 'sha256sum': 'd31bf65845992926dc6dedce43348e48fd9140f5339c678974481a1e3b356da1', + 'size_bytes': 267004352, + 'generation': 1769079787385484, 'condition': 'host_os == "linux"', }, { - 'object_name': 'Mac/rust-toolchain-15283f6fe95e5b604273d13a428bab5fc0788f5a-1-llvmorg-22-init-8940-g4d4cb757.tar.xz', - 'sha256sum': '351347e1930a900c63b3953cdb10775b73572c6145e389f3820ba920816d46ca', - 'size_bytes': 135891820, - 'generation': 1758743118329536, + 'object_name': 'Mac/rust-toolchain-d2015e2359d5d0b154c2b192d4039f9b5711fcdc-4-llvmorg-23-init-484-gf646b915.tar.xz', + 'sha256sum': '0677ec808549ab845bfdbe4ff6072d41c8b4f2c3321c68a5bf95b2577372fe34', + 'size_bytes': 254470068, + 'generation': 1769079789279846, 'condition': 'host_os == "mac" and host_cpu == "x64"', }, { - 'object_name': 'Mac_arm64/rust-toolchain-15283f6fe95e5b604273d13a428bab5fc0788f5a-1-llvmorg-22-init-8940-g4d4cb757.tar.xz', - 'sha256sum': '33d6b8cf4fc6617aa98888a46bc1dbef29ae9a9ebd01c3f248ef8c08ec5f198b', - 'size_bytes': 123302332, - 'generation': 1758743119839246, + 'object_name': 'Mac_arm64/rust-toolchain-d2015e2359d5d0b154c2b192d4039f9b5711fcdc-4-llvmorg-23-init-484-gf646b915.tar.xz', + 'sha256sum': '5977efd99f758004492f7e75879db85642c37b3882d610092200e850475b9042', + 'size_bytes': 238094236, + 'generation': 1769079791037848, 'condition': 'host_os == "mac" and host_cpu == "arm64"', }, { - 'object_name': 'Win/rust-toolchain-15283f6fe95e5b604273d13a428bab5fc0788f5a-1-llvmorg-22-init-8940-g4d4cb757.tar.xz', - 'sha256sum': '4f6dfa230e5d401bf9aadd804142b412467177b17d50a3f52a8c69c1957aa2db', - 'size_bytes': 199998880, - 'generation': 1758743121322555, + 'object_name': 'Win/rust-toolchain-d2015e2359d5d0b154c2b192d4039f9b5711fcdc-4-llvmorg-23-init-484-gf646b915.tar.xz', + 'sha256sum': '0e4bd819cae4aa322a6f8667e3ff157adfb5eadb9d0daf615d5ff8202c530e6e', + 'size_bytes': 402509464, + 'generation': 1769079792946462, 'condition': 'host_os == "win"', }, ], }, - 'third_party/siso': { + 'third_party/siso/cipd': { 'packages': [ { 'package': 'build/siso/${{platform}}', @@ -552,13 +532,13 @@ deps = { 'condition': 'not build_with_chromium and host_cpu != "s390x" and host_os != "zos" and host_cpu != "ppc64"', }, 'third_party/zlib': - Var('chromium_url') + '/chromium/src/third_party/zlib.git'+ '@' + '85f05b0835f934e52772efc308baa80cdd491838', + Var('chromium_url') + '/chromium/src/third_party/zlib.git'+ '@' + '980253c1cc835c893c57b5cfc10c5b942e10bc46', 'tools/clang': - Var('chromium_url') + '/chromium/src/tools/clang.git' + '@' + '97f0845783b3d8ebca7541afb46ec53c3f4bd3ac', + Var('chromium_url') + '/chromium/src/tools/clang.git' + '@' + 'd651bc848c45c945ecbc0c1a372b0b781e47c991', 'tools/protoc_wrapper': Var('chromium_url') + '/chromium/src/tools/protoc_wrapper.git' + '@' + '3438d4183bfc7c0d6850e8b970204cc8189f0323', 'third_party/abseil-cpp': { - 'url': Var('chromium_url') + '/chromium/src/third_party/abseil-cpp.git' + '@' + '3fb321d9764442ceaf2e17b6e68ab6b6836bc78a', + 'url': Var('chromium_url') + '/chromium/src/third_party/abseil-cpp.git' + '@' + '6d5ac0f7d3f0af5d13b78044fc31c793aa3549f8', 'condition': 'not build_with_chromium', }, 'third_party/zoslib': { @@ -590,6 +570,7 @@ include_rules = [ '+absl/container/flat_hash_set.h', '+absl/container/btree_map.h', '+absl/functional/overload.h', + '+absl/numeric/int128.h', '+absl/status', '+absl/strings/str_format.h', '+absl/synchronization/mutex.h', diff --git a/deps/v8/MODULE.bazel b/deps/v8/MODULE.bazel index 3370e06bc077b9..7d7ba53b579605 100644 --- a/deps/v8/MODULE.bazel +++ b/deps/v8/MODULE.bazel @@ -3,10 +3,10 @@ module( version = "0.0.0", ) -bazel_dep(name = "bazel_skylib", version = "1.7.1") -bazel_dep(name = "rules_cc", version = "0.1.2") +bazel_dep(name = "bazel_skylib", version = "1.8.1") +bazel_dep(name = "rules_cc", version = "0.2.0") bazel_dep(name = "rules_python", version = "1.0.0") -bazel_dep(name = "platforms", version = "0.0.11") +bazel_dep(name = "platforms", version = "1.0.0") bazel_dep(name = "abseil-cpp", version = "20250814.0") bazel_dep(name = "highway", version = "1.2.0") @@ -15,6 +15,178 @@ pip.parse( hub_name = "v8_python_deps", python_version = "3.11", requirements_lock = "//:bazel/requirements.txt", - extra_pip_args = ["--require-hashes"], + extra_pip_args = [ + "--require-hashes", + "--index-url=https://pypi.org/simple", + ], ) use_repo(pip, "v8_python_deps") + +# Define the local LLVM toolchain repository +llvm_toolchain_repository = use_repo_rule("//bazel/toolchain:llvm_repository.bzl", "llvm_toolchain_repository") + +llvm_toolchain_repository( + name = "llvm_toolchain", + path = "third_party/llvm-build/Release+Asserts", + config_file_content = """ +load("@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl", "feature", "flag_group", "flag_set", "tool_path") + +def _impl(ctx): + tool_paths = [ + tool_path(name = "gcc", path = "bin/clang"), + tool_path(name = "ld", path = "bin/lld"), + tool_path(name = "ar", path = "bin/llvm-ar"), + tool_path(name = "cpp", path = "bin/clang++"), + tool_path(name = "gcov", path = "/bin/false"), + tool_path(name = "nm", path = "bin/llvm-nm"), + tool_path(name = "objdump", path = "bin/llvm-objdump"), + tool_path(name = "strip", path = "bin/llvm-strip"), + ] + + features = [ + feature( + name = "default_compile_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + "c-compile", + "c++-compile", + "c++-header-parsing", + "c++-module-compile", + "c++-module-codegen", + "linkstamp-compile", + "assemble", + "preprocess-assemble", + ], + flag_groups = [ + flag_group( + flags = [ + "--sysroot={WORKSPACE_ROOT}/build/linux/debian_bullseye_amd64-sysroot", + "-nostdinc++", + "-isystem", + "{WORKSPACE_ROOT}/buildtools/third_party/libc++", + "-isystem", + "{WORKSPACE_ROOT}/third_party/libc++/src/include", + "-isystem", + "{WORKSPACE_ROOT}/third_party/libc++abi/src/include", + "-isystem", + "{WORKSPACE_ROOT}/third_party/libc++/src/src", + "-isystem", + "{WORKSPACE_ROOT}/third_party/llvm-libc/src", + "-D_LIBCPP_HARDENING_MODE_DEFAULT=_LIBCPP_HARDENING_MODE_NONE", + "-DLIBC_NAMESPACE=__llvm_libc_cr", + ], + ), + ], + ), + ], + ), + feature( + name = "default_linker_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + "c++-link-executable", + "c++-link-dynamic-library", + "c++-link-nodeps-dynamic-library", + ], + flag_groups = [ + flag_group( + flags = [ + "--sysroot={WORKSPACE_ROOT}/build/linux/debian_bullseye_amd64-sysroot", + "-fuse-ld=lld", + "-lm", + "-lpthread", + ], + ), + ], + ), + ], + ), + ] + + return cc_common.create_cc_toolchain_config_info( + ctx = ctx, + features = features, + cxx_builtin_include_directories = [ + "{WORKSPACE_ROOT}/buildtools/third_party/libc++", + "{WORKSPACE_ROOT}/third_party/libc++/src/include", + "{WORKSPACE_ROOT}/third_party/libc++abi/src/include", + "{WORKSPACE_ROOT}/third_party/libc++/src/src", + "{WORKSPACE_ROOT}/third_party/llvm-libc/src", + "{WORKSPACE_ROOT}/third_party/llvm-build/Release+Asserts/lib/clang/22/include", + "{WORKSPACE_ROOT}/third_party/llvm-build/Release+Asserts/lib/clang/23/include", + "{WORKSPACE_ROOT}/build/linux/debian_bullseye_amd64-sysroot/usr/include", + "{WORKSPACE_ROOT}/build/linux/debian_bullseye_amd64-sysroot/usr/local/include", + ], + toolchain_identifier = "local_clang", + host_system_name = "local", + target_system_name = "local", + target_cpu = "k8", + target_libc = "unknown", + compiler = "clang", + abi_version = "unknown", + abi_libc_version = "unknown", + tool_paths = tool_paths, + ) + +cc_toolchain_config = rule( + implementation = _impl, + attrs = {}, + provides = [CcToolchainConfigInfo], +) +""", + build_file_content = """ +load(":cc_toolchain_config.bzl", "cc_toolchain_config") + +package(default_visibility = ["//visibility:public"]) + +filegroup( + name = "all_files", + srcs = glob(["**/*"]), +) + +filegroup(name = "empty") + +cc_toolchain_config(name = "k8_toolchain_config") + +cc_toolchain( + name = "k8_toolchain", + all_files = ":all_files", + ar_files = ":all_files", + compiler_files = ":all_files", + dwp_files = ":empty", + linker_files = ":all_files", + objcopy_files = ":all_files", + strip_files = ":all_files", + supports_param_files = 0, + toolchain_config = ":k8_toolchain_config", + toolchain_identifier = "local_clang", +) + +toolchain( + name = "cc_toolchain_k8", + exec_compatible_with = [ + "@platforms//cpu:x86_64", + "@platforms//os:linux", + ], + target_compatible_with = [ + "@platforms//cpu:x86_64", + "@platforms//os:linux", + ], + toolchain = ":k8_toolchain", + toolchain_type = "@bazel_tools//tools/cpp:toolchain_type", +) +""", +) + +register_toolchains("@llvm_toolchain//:cc_toolchain_k8") + +# Define local repository for libc++ from third_party sources +libcxx_repository = use_repo_rule("//bazel/toolchain:libcxx_repository.bzl", "libcxx_repository") + +libcxx_repository( + name = "libcxx", +) diff --git a/deps/v8/WATCHLISTS b/deps/v8/WATCHLISTS index 0e9e78b120c9eb..db847a31279e54 100644 --- a/deps/v8/WATCHLISTS +++ b/deps/v8/WATCHLISTS @@ -60,6 +60,9 @@ 'arm': { 'filepath': '/arm/', }, + 'riscv': { + 'filepath': '/riscv/', + }, 'merges': { 'filepath': '.', }, @@ -111,6 +114,7 @@ 'WATCHLISTS': { 'maglev': [ + 'dmercadier+watch@chromium.org', 'leszeks+watch@chromium.org', 'verwaest+watch@chromium.org', 'victorgomes+watch@chromium.org', @@ -140,6 +144,9 @@ 'v8-ppc-ports@googlegroups.com', 'v8-risc-v-ports@chromium.org', ], + 'riscv': [ + 'v8-risc-v-ports@chromium.org', + ], 'merges': [ # Only enabled on branches created with tools/release/create_release.py 'v8-merges@googlegroups.com', diff --git a/deps/v8/bazel/config/BUILD.bazel b/deps/v8/bazel/config/BUILD.bazel index c81f2782031269..17e379b8e27baa 100644 --- a/deps/v8/bazel/config/BUILD.bazel +++ b/deps/v8/bazel/config/BUILD.bazel @@ -357,3 +357,28 @@ selects.config_setting_group( ":is_fastbuild", ], ) + +# To build with sanitizers in the command line, define the local +# variable SANITIZER. +# Eg: bazel build --copt=-fsanitize=address --linkopt=-fsanitize=address \ +# --define=SANITIZER=asan ... + +config_setting( + name = "asan_enabled", + values = {"define": "SANITIZER=asan"}, +) + +config_setting( + name = "hwasan_enabled", + values = {"define": "SANITIZER=hwasan"}, +) + +config_setting( + name = "msan_enabled", + values = {"define": "SANITIZER=msan"}, +) + +config_setting( + name = "ubsan_enabled", + values = {"define": "SANITIZER=ubsan"}, +) diff --git a/deps/v8/bazel/defs.bzl b/deps/v8/bazel/defs.bzl index a7e1e2af786ce4..9648e4a541f99f 100644 --- a/deps/v8/bazel/defs.bzl +++ b/deps/v8/bazel/defs.bzl @@ -97,7 +97,7 @@ v8_config = rule( def _default_args(): return struct( - deps = [":define_flags"], + deps = [":define_flags", "@libcxx//:libc++"], defines = select({ "@v8//bazel/config:is_windows": [ "UNICODE", @@ -111,6 +111,7 @@ def _default_args(): "@v8//bazel/config:is_posix": [ "-fPIC", "-fno-strict-aliasing", + "-fconstexpr-steps=2000000", "-Werror", "-Wextra", "-Wno-unneeded-internal-declaration", diff --git a/deps/v8/bazel/toolchain/BUILD.bazel b/deps/v8/bazel/toolchain/BUILD.bazel new file mode 100644 index 00000000000000..497f126c4c3af3 --- /dev/null +++ b/deps/v8/bazel/toolchain/BUILD.bazel @@ -0,0 +1,2 @@ +package(default_visibility = ["//visibility:public"]) +exports_files(["llvm_repository.bzl"]) diff --git a/deps/v8/bazel/toolchain/libcxx_repository.bzl b/deps/v8/bazel/toolchain/libcxx_repository.bzl new file mode 100644 index 00000000000000..a7d5f11053dd33 --- /dev/null +++ b/deps/v8/bazel/toolchain/libcxx_repository.bzl @@ -0,0 +1,99 @@ +"""Repository rule for building libc++ from third_party sources.""" + +def _libcxx_repository_impl(ctx): + # Find the workspace root + workspace_root = ctx.path(Label("@//:BUILD.bazel")).dirname + + # Symlink the source directories + ctx.symlink(workspace_root.get_child("third_party").get_child("libc++"), "libc++") + ctx.symlink(workspace_root.get_child("third_party").get_child("libc++abi"), "libc++abi") + ctx.symlink(workspace_root.get_child("third_party").get_child("llvm-libc"), "llvm-libc") + ctx.symlink(workspace_root.get_child("buildtools").get_child("third_party").get_child("libc++"), "buildtools_libc++") + + # Get the external repository path for include flags + # In bzlmod, repo names may have prefixes, so we need to determine the actual path + repo_path = "external/" + ctx.name + + # Create the BUILD file + # NOTE: We don't use 'includes' attribute here because it creates relative paths + # that conflict with the toolchain's absolute paths, breaking #include_next. + # The toolchain provides the libc++ include paths via -isystem flags. + build_content = ''' +package(default_visibility = ["//visibility:public"]) + +LIBCXX_COPTS = [ + "-std=c++23", + "-fPIC", + "-fstrict-aliasing", + "-fexceptions", + "-frtti", + "-D_LIBCPP_BUILDING_LIBRARY", + "-D_LIBCPP_HARDENING_MODE_DEFAULT=_LIBCPP_HARDENING_MODE_NONE", + "-DLIBC_NAMESPACE=__llvm_libc_cr", +] + +cc_library( + name = "libc++abi", + srcs = glob([ + "libc++abi/src/src/*.cpp", + "libc++abi/src/src/*.h", + "libc++abi/src/src/demangle/*.h", + ], exclude = [ + # Exclude files not needed for Linux build + "libc++abi/src/src/cxa_noexception.cpp", + "libc++abi/src/src/stdlib_new_delete.cpp", + ]), + hdrs = glob([ + "libc++abi/src/include/**/*.h", + "libc++/src/include/**/*", + "libc++/src/src/include/*.h", + "libc++abi/src/src/demangle/*.def", + "buildtools_libc++/__config_site", + "buildtools_libc++/__assertion_handler", + "llvm-libc/src/**/*.h", + ]), + copts = LIBCXX_COPTS + [ + "-DLIBCXXABI_SILENT_TERMINATE", + "-iquote", "{REPO_PATH}/libc++abi/src/src", + "-iquote", "{REPO_PATH}/libc++abi/src/src/demangle", + ], + linkstatic = True, +) + +cc_library( + name = "libc++", + srcs = glob([ + "libc++/src/src/*.cpp", + "libc++/src/src/*.h", + "libc++/src/src/filesystem/*.cpp", + "libc++/src/src/filesystem/*.h", + "libc++/src/src/ryu/*.cpp", + "libc++/src/src/include/*.h", + "libc++/src/src/include/ryu/*.h", + ]) + glob(["libc++/src/src/support/**/*.ipp"], allow_empty = True), + hdrs = glob([ + "libc++/src/include/**/*", + "buildtools_libc++/__config_site", + "buildtools_libc++/__assertion_handler", + "llvm-libc/src/**/*.h", + ]), + copts = LIBCXX_COPTS + [ + "-DLIBCXX_BUILDING_LIBCXXABI", + "-iquote", "{REPO_PATH}/libc++/src/src", + "-iquote", "{REPO_PATH}/libc++/src/src/filesystem", + ], + linkopts = [ + "-lpthread", + "-lm", + ], + deps = [":libc++abi"], + linkstatic = True, +) +'''.format(REPO_PATH=repo_path) + ctx.file("BUILD.bazel", build_content) + +libcxx_repository = repository_rule( + implementation = _libcxx_repository_impl, + local = True, + configure = True, +) diff --git a/deps/v8/bazel/toolchain/llvm_repository.bzl b/deps/v8/bazel/toolchain/llvm_repository.bzl new file mode 100644 index 00000000000000..76670a5e69b657 --- /dev/null +++ b/deps/v8/bazel/toolchain/llvm_repository.bzl @@ -0,0 +1,40 @@ +def _llvm_toolchain_impl(ctx): + # Find the workspace root by resolving a label in the main repository + # This assumes the main repo is "@" or the default. + workspace_root = ctx.path(Label("@//:BUILD.bazel")).dirname + + # Construct the path to the LLVM build directory within the workspace + # ctx.attr.path should be relative to the workspace root, + # e.g. "third_party/llvm-build/Release+Asserts" + # We manually split and traverse because get_child handles one component. + llvm_path = workspace_root + for component in ctx.attr.path.split("/"): + llvm_path = llvm_path.get_child(component) + + # Symlink top-level directories and files + items = ["bin", "lib", "include", "share"] + for item in items: + # Check if the item exists before symlinking to avoid errors + src_path = llvm_path.get_child(item) + if src_path.exists: + ctx.symlink(src_path, item) + + # Create the config file + config_content = ctx.attr.config_file_content.replace("{WORKSPACE_ROOT}", str(workspace_root)) + ctx.file("cc_toolchain_config.bzl", config_content) + + # Create the BUILD file + ctx.file("BUILD.bazel", ctx.attr.build_file_content) + +llvm_toolchain_repository = repository_rule( + implementation = _llvm_toolchain_impl, + attrs = { + "path": attr.string(mandatory = True, + doc = "Path to the LLVM install directory relative to workspace root"), + "build_file_content": attr.string(mandatory = True, doc = "Content of the BUILD file"), + "config_file_content": attr.string(mandatory = True, + doc = "Content of the cc_toolchain_config.bzl file"), + }, + local = True, + configure = True, # Indicates this rule depends on system configuration/files +) diff --git a/deps/v8/gni/v8.gni b/deps/v8/gni/v8.gni index d198859576a4bd..e29a7c5de3c4cc 100644 --- a/deps/v8/gni/v8.gni +++ b/deps/v8/gni/v8.gni @@ -61,8 +61,7 @@ declare_args() { # is still not accessible unless --harmony-temporal is enabled at runtime) # # Furthermore, some architectures don't have Rust toolchains in Chromium - v8_enable_temporal_support = !(defined(build_with_node) && build_with_node) && - target_cpu != "ppc64" && target_cpu != "s390x" + v8_enable_temporal_support = !(defined(build_with_node) && build_with_node) # Use static libraries instead of source_sets. v8_static_library = false @@ -80,6 +79,12 @@ declare_args() { # Implement tracing using Perfetto (https://perfetto.dev). v8_use_perfetto = false + # Use Perfetto JSON Export. + v8_use_perfetto_json_export = "" + + # Use Perfetto SDK headers. + v8_use_perfetto_sdk = false + # Override global symbol level setting for v8. v8_symbol_level = symbol_level @@ -225,7 +230,7 @@ declare_args() { # When `v8_enable_pointer_compression_shared_cage` RO space is placed into a # contiguous area at the front of the cage. In case RO allocations fails this # size needs to be adjusted. - v8_contiguous_compressed_ro_space_size_mb = 8 + v8_contiguous_compressed_ro_space_size_mb = 16 # Change code emission and runtime features to be CET shadow-stack compliant # (incomplete and experimental). @@ -269,12 +274,25 @@ if (v8_enable_backtrace == "") { v8_enable_backtrace = is_debug && !v8_optimized_debug } +if (v8_use_perfetto_sdk) { + # When using perfetto SDK, we also need to enable perfetto support. + v8_use_perfetto = true +} + # Chromium is configured to use the perfetto client library, v8 should also # use perfetto for tracing. -if (build_with_chromium) { +if (build_with_chromium && !use_fuzzing_engine) { v8_use_perfetto = true } +# Enable JSON export by default when using perfetto but not the SDK. +if (v8_use_perfetto_json_export == "") { + v8_use_perfetto_json_export = v8_use_perfetto && !v8_use_perfetto_sdk +} +assert( + !(v8_use_perfetto_sdk && v8_use_perfetto_json_export), + "Perfetto JSON Export is not available when building with the Perfetto SDK.") + # Includes profiles to optimize builtins if # * it is a Chromium build, and # * Chromium builds with optimization. @@ -305,7 +323,8 @@ if (v8_enable_pointer_compression == "") { # Windows, Linux, MacOS and tvOS. is_drumbrake_supported = v8_enable_webassembly && v8_enable_pointer_compression && - (v8_current_cpu == "x64" || v8_current_cpu == "arm64") && + (v8_current_cpu == "x64" || v8_current_cpu == "arm64" || + v8_current_cpu == "riscv64") && (target_os == "win" || target_os == "linux" || target_os == "mac" || target_os == "ios") diff --git a/deps/v8/include/cppgc/allocation.h b/deps/v8/include/cppgc/allocation.h index a7955fd1016e8e..450db00479e87a 100644 --- a/deps/v8/include/cppgc/allocation.h +++ b/deps/v8/include/cppgc/allocation.h @@ -50,18 +50,17 @@ class MakeGarbageCollectedTraitInternal { protected: static inline void MarkObjectAsFullyConstructed(const void* payload) { // See api_constants for an explanation of the constants. - std::atomic* atomic_mutable_bitfield = - reinterpret_cast*>( - const_cast(reinterpret_cast( - reinterpret_cast(payload) - - api_constants::kFullyConstructedBitFieldOffsetFromPayload))); + std::atomic_ref atomic_mutable_bitfield( + *const_cast(reinterpret_cast( + reinterpret_cast(payload) - + api_constants::kFullyConstructedBitFieldOffsetFromPayload))); // It's safe to split use load+store here (instead of a read-modify-write // operation), since it's guaranteed that this 16-bit bitfield is only // modified by a single thread. This is cheaper in terms of code bloat (on // ARM) and performance. - uint16_t value = atomic_mutable_bitfield->load(std::memory_order_relaxed); + uint16_t value = atomic_mutable_bitfield.load(std::memory_order_relaxed); value |= api_constants::kFullyConstructedBitMask; - atomic_mutable_bitfield->store(value, std::memory_order_release); + atomic_mutable_bitfield.store(value, std::memory_order_release); } // Dispatch based on compile-time information. diff --git a/deps/v8/include/cppgc/internal/api-constants.h b/deps/v8/include/cppgc/internal/api-constants.h index f3b0d8571d3b9d..73b955d7053ae1 100644 --- a/deps/v8/include/cppgc/internal/api-constants.h +++ b/deps/v8/include/cppgc/internal/api-constants.h @@ -28,7 +28,7 @@ constexpr size_t kGB = kMB * 1024; static constexpr size_t kFullyConstructedBitFieldOffsetFromPayload = 2 * sizeof(uint16_t); // Mask for in-construction bit. -static constexpr uint16_t kFullyConstructedBitMask = uint16_t{1}; +static constexpr uint16_t kFullyConstructedBitMask = uint16_t{1} << 15; static constexpr size_t kPageSizeBits = 17; static constexpr size_t kPageSize = size_t{1} << kPageSizeBits; diff --git a/deps/v8/include/libplatform/v8-tracing.h b/deps/v8/include/libplatform/v8-tracing.h index 2271729240b9cb..9d05949c38efac 100644 --- a/deps/v8/include/libplatform/v8-tracing.h +++ b/deps/v8/include/libplatform/v8-tracing.h @@ -14,9 +14,11 @@ #include "v8-platform.h" // NOLINT(build/include_directory) namespace perfetto { +#if defined(V8_USE_PERFETTO_JSON_EXPORT) namespace trace_processor { class TraceProcessorStorage; } +#endif // defined(V8_USE_PERFETTO_JSON_EXPORT) class TracingSession; } @@ -231,6 +233,12 @@ class V8_PLATFORM_EXPORT TraceConfig { #define V8_PLATFORM_NON_EXPORTED_BASE(code) code #endif // defined(_MSC_VER) +/** + * V8 Tracing controller default implementation. + * + * Will become obsolete in Perfetto build + * (v8_use_perfetto_json_export = true). + */ class V8_PLATFORM_EXPORT TracingController : public V8_PLATFORM_NON_EXPORTED_BASE(v8::TracingController) { public: @@ -307,10 +315,13 @@ class V8_PLATFORM_EXPORT TracingController std::unique_ptr trace_config_; std::atomic_bool recording_{false}; -#if defined(V8_USE_PERFETTO) - std::ostream* output_stream_ = nullptr; +#if defined(V8_USE_PERFETTO_JSON_EXPORT) std::unique_ptr trace_processor_; +#endif + +#if defined(V8_USE_PERFETTO) + std::ostream* output_stream_ = nullptr; TraceEventListener* listener_for_testing_ = nullptr; std::unique_ptr tracing_session_; #else // !defined(V8_USE_PERFETTO) diff --git a/deps/v8/include/v8-array-buffer.h b/deps/v8/include/v8-array-buffer.h index 3e64ece5debda3..4b55c445376530 100644 --- a/deps/v8/include/v8-array-buffer.h +++ b/deps/v8/include/v8-array-buffer.h @@ -77,6 +77,11 @@ class V8_EXPORT BackingStore : public v8::internal::BackingStoreBase { */ bool IsShared() const; + /** + * Indicates whether the backing store is immutable. + */ + bool IsImmutable() const; + /** * Indicates whether the backing store was created for a resizable ArrayBuffer * or a growable SharedArrayBuffer, and thus may be resized by user JavaScript @@ -328,6 +333,11 @@ class V8_EXPORT ArrayBuffer : public Object { */ bool WasDetached() const; + /** + * Returns true if this ArrayBuffer is immutable. + */ + bool IsImmutable() const; + /** * Detaches this ArrayBuffer and all its views (typed arrays). * Detaching sets the byte length of the buffer and all typed arrays to zero, diff --git a/deps/v8/include/v8-callbacks.h b/deps/v8/include/v8-callbacks.h index 850b7ccbd4f210..e5eba5a203b8bc 100644 --- a/deps/v8/include/v8-callbacks.h +++ b/deps/v8/include/v8-callbacks.h @@ -260,6 +260,15 @@ enum class CrashKeyId { using AddCrashKeyCallback = void (*)(CrashKeyId id, const std::string& value); +// --- CrashKeyString Callbacks --- +using CrashKey = void*; +enum class CrashKeySize { Size32, Size64, Size256, Size1024 }; + +using AllocateCrashKeyStringCallback = + std::function; +using SetCrashKeyStringCallback = + std::function; + // --- Enter/Leave Script Callback --- using BeforeCallEnteredCallback = void (*)(Isolate*); using CallCompletedCallback = void (*)(Isolate*); diff --git a/deps/v8/include/v8-context.h b/deps/v8/include/v8-context.h index 1d52f5bf40652e..21137f628f2def 100644 --- a/deps/v8/include/v8-context.h +++ b/deps/v8/include/v8-context.h @@ -276,6 +276,20 @@ class V8_EXPORT Context : public Data { * Gets the embedder data with the given index, which must have been set by a * previous call to SetEmbedderData with the same index. */ + V8_INLINE Local GetEmbedderDataV2(int index); + + /** + * Sets the embedder data with the given index, growing the data as + * needed. Note that index 0 currently has a special meaning for Chrome's + * debugger. + */ + void SetEmbedderDataV2(int index, Local value); + + /** + * Gets the embedder data with the given index, which must have been set by a + * previous call to SetEmbedderData with the same index. + */ + V8_DEPRECATE_SOON("Use GetEmbedderDataV2 instead") V8_INLINE Local GetEmbedderData(int index); /** @@ -291,6 +305,7 @@ class V8_EXPORT Context : public Data { * needed. Note that index 0 currently has a special meaning for Chrome's * debugger. */ + V8_DEPRECATE_SOON("Use SetEmbedderDataV2 instead") void SetEmbedderData(int index, Local value); /** @@ -304,7 +319,7 @@ class V8_EXPORT Context : public Data { V8_INLINE void* GetAlignedPointerFromEmbedderData(int index, EmbedderDataTypeTag tag); - V8_DEPRECATE_SOON( + V8_DEPRECATED( "Use GetAlignedPointerFromEmbedderData with EmbedderDataTypeTag " "parameter instead.") V8_INLINE void* GetAlignedPointerFromEmbedderData(Isolate* isolate, @@ -313,7 +328,7 @@ class V8_EXPORT Context : public Data { kEmbedderDataTypeTagDefault); } - V8_DEPRECATE_SOON( + V8_DEPRECATED( "Use GetAlignedPointerFromEmbedderData with EmbedderDataTypeTag " "parameter instead.") V8_INLINE void* GetAlignedPointerFromEmbedderData(int index) { @@ -329,7 +344,7 @@ class V8_EXPORT Context : public Data { * index, growing the data as needed. Note that index 0 currently has a * special meaning for Chrome's debugger. */ - V8_DEPRECATE_SOON( + V8_DEPRECATED( "Use SetAlignedPointerInEmbedderData with EmbedderDataTypeTag parameter " "instead.") void SetAlignedPointerInEmbedderData(int index, void* value) { @@ -453,6 +468,7 @@ class V8_EXPORT Context : public Data { internal::ValueHelper::InternalRepresentationType GetDataFromSnapshotOnce( size_t index); Local SlowGetEmbedderData(int index); + Local SlowGetEmbedderDataV2(int index); void* SlowGetAlignedPointerFromEmbedderData(int index, EmbedderDataTypeTag tag); }; @@ -471,7 +487,7 @@ Local Context::GetEmbedderData(int index) { A value = I::ReadRawField(embedder_data, value_offset); #ifdef V8_COMPRESS_POINTERS // We read the full pointer value and then decompress it in order to avoid - // dealing with potential endiannes issues. + // dealing with potential endianness issues. value = I::DecompressTaggedField(embedder_data, static_cast(value)); #endif @@ -482,6 +498,29 @@ Local Context::GetEmbedderData(int index) { #endif } +V8_INLINE Local Context::GetEmbedderDataV2(int index) { +#ifndef V8_ENABLE_CHECKS + using A = internal::Address; + using I = internal::Internals; + A ctx = internal::ValueHelper::ValueAsAddress(this); + A embedder_data = + I::ReadTaggedPointerField(ctx, I::kNativeContextEmbedderDataOffset); + int value_offset = + I::kEmbedderDataArrayHeaderSize + (I::kEmbedderDataSlotSize * index); + A value = I::ReadRawField(embedder_data, value_offset); +#ifdef V8_COMPRESS_POINTERS + // We read the full pointer value and then decompress it in order to avoid + // dealing with potential endianness issues. + value = I::DecompressTaggedField(embedder_data, static_cast(value)); +#endif + + auto* isolate = I::GetCurrentIsolate(); + return Local::New(isolate, value); +#else + return SlowGetEmbedderDataV2(index); +#endif +} + void* Context::GetAlignedPointerFromEmbedderData(Isolate* isolate, int index, EmbedderDataTypeTag tag) { #if !defined(V8_ENABLE_CHECKS) diff --git a/deps/v8/include/v8-data.h b/deps/v8/include/v8-data.h index bf40b746adf887..7be431b31921a7 100644 --- a/deps/v8/include/v8-data.h +++ b/deps/v8/include/v8-data.h @@ -77,7 +77,8 @@ class V8_EXPORT Data { class V8_EXPORT FixedArray : public Data { public: int Length() const; - Local Get(Local context, int i) const; + + Local Get(int i) const; V8_INLINE static FixedArray* Cast(Data* data) { #ifdef V8_ENABLE_CHECKS diff --git a/deps/v8/include/v8-debug.h b/deps/v8/include/v8-debug.h index 0aff8b9e675e8a..620467a72695e4 100644 --- a/deps/v8/include/v8-debug.h +++ b/deps/v8/include/v8-debug.h @@ -136,6 +136,11 @@ class V8_EXPORT StackTrace { kDetailed = kOverview | kIsEval | kIsConstructor | kScriptNameOrSourceURL }; + struct ScriptIdAndContext { + int id; + v8::Local context; + }; + /** * Returns the (unique) ID of this stack trace. */ @@ -174,15 +179,30 @@ class V8_EXPORT StackTrace { static Local CurrentScriptNameOrSourceURL(Isolate* isolate); /** - * Returns the first valid script id at the top of - * the JS stack. The returned value is Message::kNoScriptIdInfo if no id - * was found. + * Returns the first valid script id at the top of the JS stack. The returned + * value is Message::kNoScriptIdInfo if no id was found. * * This method is equivalent to calling StackTrace::CurrentStackTrace and * walking the resulting frames from the beginning until a non-empty id is * found. The difference is that this method won't allocate a stack trace. */ static int CurrentScriptId(Isolate* isolate); + + /** + * Writes up to the first `frame_data.size()` valid script ids and function + * contexts at the top of the JS stack into the given span. Returns a span + * sized to the number of frames worth of data written. It's similar to the + * CurrentStackTrace method but doesn't allocate a stack trace. Further, it + * skips frames that don't have valid script ids or function contexts. The + * final difference is that the script id written for evals or regexp is that + * of the script that ran eval() or regexp, not the current context. + * + * WARNING: This is an unfinished experimental feature. Semantics and + * implementation may change frequently. + */ + static v8::MemorySpan + CurrentScriptIdsAndContexts(Isolate* isolate, + v8::MemorySpan frame_data); }; } // namespace v8 diff --git a/deps/v8/include/v8-exception.h b/deps/v8/include/v8-exception.h index 5441a0ab6a403c..f240d9a609e92b 100644 --- a/deps/v8/include/v8-exception.h +++ b/deps/v8/include/v8-exception.h @@ -276,15 +276,18 @@ class V8_EXPORT TryCatch { void ResetInternal(); + // Helper methods for internal::Isolate. + bool capture_message() const; + void set_can_continue(bool value); + bool rethrow() const; + void set_rethrow(bool value); + internal::Isolate* i_isolate_; TryCatch* next_; void* exception_; void* message_obj_; internal::Address js_stack_comparable_address_; - bool is_verbose_ : 1; - bool can_continue_ : 1; - bool capture_message_ : 1; - bool rethrow_ : 1; + uint8_t flags_; friend class internal::Isolate; friend class internal::ThreadLocalTop; diff --git a/deps/v8/include/v8-extension.h b/deps/v8/include/v8-extension.h index 0705e2afbb8708..d0056cb286330d 100644 --- a/deps/v8/include/v8-extension.h +++ b/deps/v8/include/v8-extension.h @@ -33,7 +33,6 @@ class V8_EXPORT Extension { } const char* name() const { return name_; } - size_t source_length() const { return source_length_; } const String::ExternalOneByteStringResource* source() const { return source_; } @@ -48,7 +47,6 @@ class V8_EXPORT Extension { private: const char* name_; - size_t source_length_; // expected to initialize before source_ String::ExternalOneByteStringResource* source_; int dep_count_; const char** deps_; diff --git a/deps/v8/include/v8-external.h b/deps/v8/include/v8-external.h index c3feb1daad7e5f..da43c42e350ade 100644 --- a/deps/v8/include/v8-external.h +++ b/deps/v8/include/v8-external.h @@ -28,26 +28,44 @@ constexpr ExternalPointerTypeTag kExternalPointerTypeTagDefault = 0; */ class V8_EXPORT External : public Value { public: - V8_DEPRECATE_SOON("Use the version with the type tag.") + V8_DEPRECATED("Use the version with the type tag.") static Local New(Isolate* isolate, void* value) { return New(isolate, value, kExternalPointerTypeTagDefault); } + /** + * Creates a new External object. + * + * \param isolate The isolate for the external object. + * \param value The C++ pointer value. + * \param tag The type tag of the external pointer. If type tags are not used + * in the embedder, the default value `kExternalPointerTypeTagDefault` can be + * used. + * \return The new External object. + */ static Local New(Isolate* isolate, void* value, ExternalPointerTypeTag tag); - V8_INLINE static External* Cast(Value* value) { + V8_INLINE static External* Cast(Data* value) { #ifdef V8_ENABLE_CHECKS CheckCast(value); #endif return static_cast(value); } - V8_DEPRECATE_SOON("Use the version with the type tag.") + V8_DEPRECATED("Use the version with the type tag.") void* Value() const { return Value(kExternalPointerTypeTagDefault); } + /** + * Returns the value of the external pointer. + * + * \param tag The type tag of the external pointer. If type tags are not used + * in the embedder, the default value `kExternalPointerTypeTagDefault` can be + * used. + * \return The value of the external pointer. + */ void* Value(ExternalPointerTypeTag tag) const; private: - static void CheckCast(v8::Value* obj); + static void CheckCast(v8::Data* obj); }; } // namespace v8 diff --git a/deps/v8/include/v8-function-callback.h b/deps/v8/include/v8-function-callback.h index 0dad94ce22c270..135508ef5b5acc 100644 --- a/deps/v8/include/v8-function-callback.h +++ b/deps/v8/include/v8-function-callback.h @@ -57,6 +57,7 @@ class ReturnValue { V8_INLINE void Set(const Local handle); template V8_INLINE void SetNonEmpty(const Local handle); + // Fast primitive number setters. V8_INLINE void Set(bool value); V8_INLINE void Set(double i); @@ -66,11 +67,13 @@ class ReturnValue { V8_INLINE void Set(uint16_t i); V8_INLINE void Set(uint32_t i); V8_INLINE void Set(uint64_t i); + // Fast JS primitive setters. V8_INLINE void SetNull(); V8_INLINE void SetUndefined(); V8_INLINE void SetFalse(); V8_INLINE void SetEmptyString(); + // Convenience getter for the Isolate. V8_INLINE Isolate* GetIsolate() const; @@ -103,7 +106,7 @@ class ReturnValue { V8_INLINE explicit ReturnValue(internal::Address* slot); // See FunctionCallbackInfo. - static constexpr int kIsolateValueIndex = -2; + static constexpr int kIsolateValueIndex = -1; internal::Address* value_; }; @@ -142,41 +145,75 @@ class FunctionCallbackInfo { friend class internal::CustomArguments; friend class debug::ConsoleCallArguments; friend void internal::PrintFunctionCallbackInfo(void*); + using I = internal::Internals; - // TODO(ishell, http://crbug.com/326505377): in case of non-constructor - // call, don't pass kNewTarget and kUnused. Add IsConstructCall flag to - // kIsolate field. - static constexpr int kUnusedIndex = 0; - static constexpr int kIsolateIndex = 1; - static constexpr int kContextIndex = 2; - static constexpr int kReturnValueIndex = 3; - static constexpr int kTargetIndex = 4; - static constexpr int kNewTargetIndex = 5; - static constexpr int kArgsLength = 6; - - static constexpr int kArgsLengthWithReceiver = kArgsLength + 1; - - // Codegen constants: - static constexpr int kSize = 3 * internal::kApiSystemPointerSize; - static constexpr int kImplicitArgsOffset = 0; - static constexpr int kValuesOffset = - kImplicitArgsOffset + internal::kApiSystemPointerSize; - static constexpr int kLengthOffset = - kValuesOffset + internal::kApiSystemPointerSize; - - static constexpr int kThisValuesIndex = -1; + // Frame block, matches the layout of ApiCallbackExitFrame. + // See ApiCallbackExitFrameConstants. + enum { + // + // Optional frame arguments block (exists only for API_CONSTRUCT_EXIT + // frame). + + // Frame arguments block. + kNewTargetIndex = -1, + + // + // Mandatory part, exists for both API_CALLBACK_EXIT and API_CONSTRUCT_EXIT + // frames. + // + + // Frame arguments block. + kArgcIndex, + + // Regular ExitFrame structure. + kFrameSPIndex, + kFrameTypeIndex, + kFrameConstantPoolIndex, // Optional, see I::kFrameCPSlotCount. + kFrameFPIndex = kFrameConstantPoolIndex + I::kFrameCPSlotCount, + kFramePCIndex, + + // Api arguments block, starts at kFirstArgumentIndex. + kFirstApiArgumentIndex, + kIsolateIndex = kFirstApiArgumentIndex, + kReturnValueIndex, + kContextIndex, + kTargetIndex, + + // JS args block, starts at kFrameFirstImplicitArgsIndex. + kReceiverIndex, + kFirstJSArgumentIndex, + + // Mandatory part includes receiver. + kArgsLength = kReceiverIndex + 1, + // Optional part size (exists only for API_CONSTRUCT_EXIT frame). + kOptionalArgsLength = 1, + + // The length of just Api arguments part. + kApiArgsLength = kReceiverIndex - kFirstApiArgumentIndex, + }; + + static_assert(kArgcIndex == 0); static_assert(ReturnValue::kIsolateValueIndex == kIsolateIndex - kReturnValueIndex); - V8_INLINE FunctionCallbackInfo(internal::Address* implicit_args, - internal::Address* values, int length); + internal::Address* address_of_first_argument() const { + return &values_[kFirstJSArgumentIndex]; + } + + V8_INLINE FunctionCallbackInfo() = default; - // TODO(https://crbug.com/326505377): flatten the v8::FunctionCallbackInfo - // object to avoid indirect loads through values_ and implicit_args_ and - // reduce the number of instructions in the CallApiCallback builtin. - internal::Address* implicit_args_; - internal::Address* values_; - internal::Address length_; + // FunctionCallbackInfo object provides a view of the stack area where the + // data is stored and thus it's not supposed to be copyable/movable. + FunctionCallbackInfo(const FunctionCallbackInfo&) = delete; + FunctionCallbackInfo& operator=(const FunctionCallbackInfo&) = delete; + FunctionCallbackInfo(FunctionCallbackInfo&&) = delete; + FunctionCallbackInfo& operator=(FunctionCallbackInfo&&) = delete; + + // Declare as mutable to let GC modify the contents of the slots even though + // it's not possible to change values via this class. + // Define the array size as 1 to make it clear that we are going to access + // it out-of-bounds from both sides anyway. + mutable internal::Address values_[1]; }; /** @@ -198,72 +235,13 @@ class PropertyCallbackInfo { */ V8_INLINE Local Data() const; - /** - * \return The receiver. In many cases, this is the object on which the - * property access was intercepted. When using - * `Reflect.get`, `Function.prototype.call`, or similar functions, it is the - * object passed in as receiver or thisArg. - * - * \code - * void GetterCallback(Local name, - * const v8::PropertyCallbackInfo& info) { - * auto context = info.GetIsolate()->GetCurrentContext(); - * - * v8::Local a_this = - * info.This() - * ->GetRealNamedProperty(context, v8_str("a")) - * .ToLocalChecked(); - * v8::Local a_holder = - * info.Holder() - * ->GetRealNamedProperty(context, v8_str("a")) - * .ToLocalChecked(); - * - * CHECK(v8_str("r")->Equals(context, a_this).FromJust()); - * CHECK(v8_str("obj")->Equals(context, a_holder).FromJust()); - * - * info.GetReturnValue().Set(name); - * } - * - * v8::Local templ = - * v8::FunctionTemplate::New(isolate); - * templ->InstanceTemplate()->SetHandler( - * v8::NamedPropertyHandlerConfiguration(GetterCallback)); - * LocalContext env; - * env->Global() - * ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local()) - * .ToLocalChecked() - * ->NewInstance(env.local()) - * .ToLocalChecked()) - * .FromJust(); - * - * CompileRun("obj.a = 'obj'; var r = {a: 'r'}; Reflect.get(obj, 'x', r)"); - * \endcode - */ - V8_INLINE Local This() const; - - /** - * \return The object in the prototype chain of the receiver that has the - * interceptor. Suppose you have `x` and its prototype is `y`, and `y` - * has an interceptor. Then `info.This()` is `x` and `info.Holder()` is `y`. - * The Holder() could be a hidden object (the global object, rather - * than the global proxy). - * - * \note For security reasons, do not pass the object back into the runtime. - */ - V8_DEPRECATED( - "V8 will stop providing access to hidden prototype (i.e. " - "JSGlobalObject). Use HolderV2() instead. \n" - "DO NOT try to workaround this by accessing JSGlobalObject via " - "v8::Object::GetPrototype() - it'll be deprecated soon too. \n" - "See http://crbug.com/333672197. ") - V8_INLINE Local Holder() const; - /** * \return The object in the prototype chain of the receiver that has the * interceptor. Suppose you have `x` and its prototype is `y`, and `y` * has an interceptor. Then `info.This()` is `x` and `info.Holder()` is `y`. * In case the property is installed on the global object the Holder() * would return the global proxy. + * TODO(http://crbug.com/333672197): rename back to Holder(). */ V8_INLINE Local HolderV2() const; @@ -278,11 +256,18 @@ class PropertyCallbackInfo { V8_INLINE ReturnValue GetReturnValue() const; /** + * For [[Set]], [[DefineOwnProperty]] and [[Delete]] operations (i.e. + * for setter/definer/deleter callbacks) indicates whether TypeError + * should be thrown upon operation failure. The callback should throw + * TypeError only if it's necessary to provide more details than a default + * error thrown by V8 contains in this case. + * * \return True if the intercepted function should throw if an error occurs. - * Usually, `true` corresponds to `'use strict'`. + * Usually, `true` corresponds to `'use strict'` execution mode. * - * \note Always `false` when intercepting `Reflect.set()` - * independent of the language mode. + * \note Always `false` when the operation was initiated by respecive + * `Reflect` call (i.e. `Reflect.set()`, `Reflect.defineProperty()` and + * `Reflect.deleteProperty()`). */ V8_INLINE bool ShouldThrowOnError() const; @@ -293,22 +278,75 @@ class PropertyCallbackInfo { friend class internal::PropertyCallbackArguments; friend class internal::CustomArguments; friend void internal::PrintPropertyCallbackInfo(void*); + using I = internal::Internals; - static constexpr int kPropertyKeyIndex = 0; - static constexpr int kShouldThrowOnErrorIndex = 1; - static constexpr int kHolderIndex = 2; - static constexpr int kIsolateIndex = 3; - static constexpr int kHolderV2Index = 4; - static constexpr int kReturnValueIndex = 5; - static constexpr int kDataIndex = 6; - static constexpr int kThisIndex = 7; - static constexpr int kArgsLength = 8; + // ShouldThrowOnError() can return true only for setter/definer/deleter + // callbacks which match [[Set]]/[[DefineOwnProperty]]/[[Delete]] + // operations. We detect these operations by return value type - they + // all return boolean value, even though setter/deleter callbacks are + // still using v8::PropertyCallbackInfo. + // TODO(https://crbug.com/348660658): cleanup this, once the callbacks are + // migrated to a new return type. + static constexpr bool HasShouldThrowOnError() { + return std::is_same_v || std::is_same_v; + } - static constexpr int kSize = kArgsLength * internal::kApiSystemPointerSize; + // Indicates whether this is a named accessor/interceptor callback call + // or an indexed one. + V8_INLINE bool IsNamed() const; + + // Frame block, matches the layout of ApiAccessorExitFrame. + // See ApiAccessorExitFrameConstants. + enum { + // Frame arguments block. + kPropertyKeyIndex, + + // Regular ExitFrame structure. + kFrameSPIndex, + kFrameTypeIndex, + kFrameConstantPoolIndex, // Optional, see I::kFrameCPSlotCount. + kFrameFPIndex = kFrameConstantPoolIndex + I::kFrameCPSlotCount, + kFramePCIndex, + + // Other arguments block, starts at kFirstArgumentIndex. + kFirstApiArgumentIndex, + kIsolateIndex = kFirstApiArgumentIndex, + kReturnValueIndex, + kCallbackInfoIndex, + kHolderIndex, + + // + // Optional part, used only by setter/definer/deleter callbacks. + // + kFirstOptionalArgument, + kShouldThrowOnErrorIndex = kFirstOptionalArgument, + + // Used as value handle storage when called via CallApiSetter builtin. + kValueIndex, + + kFullArgsLength, + kMandatoryArgsLength = kFirstOptionalArgument, + kOptionalArgsLength = kFullArgsLength - kFirstOptionalArgument, + + // Various lengths of just Api arguments part. + kMandatoryApiArgsLength = kMandatoryArgsLength - kFirstApiArgumentIndex, + kFullApiArgsLength = kFullArgsLength - kFirstApiArgumentIndex, + }; + + // PropertyCallbackInfo object provides a view of the stack area where the + // data is stored and thus it's not supposed to be copyable/movable. + PropertyCallbackInfo(const PropertyCallbackInfo&) = delete; + PropertyCallbackInfo& operator=(const PropertyCallbackInfo&) = delete; + PropertyCallbackInfo(PropertyCallbackInfo&&) = delete; + PropertyCallbackInfo& operator=(PropertyCallbackInfo&&) = delete; PropertyCallbackInfo() = default; - mutable internal::Address args_[kArgsLength]; + // Declare as mutable to let GC modify the contents of the slots even though + // it's not possible to change values via this class. + // Define the array size as 1 to make it clear that we are going to access + // it out-of-bounds anyway. + mutable internal::Address args_[1]; }; using FunctionCallback = void (*)(const FunctionCallbackInfo& info); @@ -377,25 +415,9 @@ void ReturnValue::SetNonEmpty(const BasicTracedReference& handle) { template template void ReturnValue::Set(const Local handle) { - // "V8_DEPRECATE_SOON" this method if |T| is |void|. -#ifdef V8_IMMINENT_DEPRECATION_WARNINGS - static constexpr bool is_allowed_void = false; - static_assert(!std::is_void_v, - "ReturnValue::Set(const Local) is deprecated. " - "Do nothing to indicate that the operation succeeded or use " - "SetFalse() to indicate that the operation failed (don't " - "forget to handle info.ShouldThrowOnError()). " - "See http://crbug.com/348660658 for details."); -#else - static constexpr bool is_allowed_void = std::is_void_v; -#endif // V8_IMMINENT_DEPRECATION_WARNINGS - static_assert(is_allowed_void || std::is_base_of_v, "type check"); + static_assert(std::is_base_of_v, "type check"); if (V8_UNLIKELY(handle.IsEmpty())) { SetDefaultValue(); - } else if constexpr (is_allowed_void) { - // Simulate old behaviour for "v8::AccessorSetterCallback" for which - // it was possible to set the return value even for ReturnValue. - Set(handle->BooleanValue(GetIsolate())); } else { SetInternal(handle.ptr()); } @@ -404,29 +426,11 @@ void ReturnValue::Set(const Local handle) { template template void ReturnValue::SetNonEmpty(const Local handle) { - // "V8_DEPRECATE_SOON" this method if |T| is |void|. -#ifdef V8_IMMINENT_DEPRECATION_WARNINGS - static constexpr bool is_allowed_void = false; - static_assert(!std::is_void_v, - "ReturnValue::SetNonEmpty(const Local) is deprecated. " - "Do nothing to indicate that the operation succeeded or use " - "SetFalse() to indicate that the operation failed (don't " - "forget to handle info.ShouldThrowOnError()). " - "See http://crbug.com/348660658 for details."); -#else - static constexpr bool is_allowed_void = std::is_void_v; -#endif // V8_IMMINENT_DEPRECATION_WARNINGS - static_assert(is_allowed_void || std::is_base_of_v, "type check"); + static_assert(std::is_base_of_v, "type check"); #ifdef V8_ENABLE_CHECKS internal::VerifyHandleIsNonEmpty(handle.IsEmpty()); #endif // V8_ENABLE_CHECKS - if constexpr (is_allowed_void) { - // Simulate old behaviour for "v8::AccessorSetterCallback" for which - // it was possible to set the return value even for ReturnValue. - Set(handle->BooleanValue(GetIsolate())); - } else { - SetInternal(handle.ptr()); - } + SetInternal(handle.ptr()); } template @@ -611,91 +615,76 @@ void ReturnValue::Set(S* whatever) { static_assert(sizeof(S) < 0, "incompilable to prevent inadvertent misuse"); } -template -FunctionCallbackInfo::FunctionCallbackInfo(internal::Address* implicit_args, - internal::Address* values, - int length) - : implicit_args_(implicit_args), values_(values), length_(length) {} - template Local FunctionCallbackInfo::operator[](int i) const { - // values_ points to the first argument (not the receiver). if (i < 0 || Length() <= i) return Undefined(GetIsolate()); - return Local::FromSlot(values_ + i); + return Local::FromSlot(&values_[kFirstJSArgumentIndex + i]); } template Local FunctionCallbackInfo::This() const { - // values_ points to the first argument (not the receiver). - return Local::FromSlot(values_ + kThisValuesIndex); + return Local::FromSlot(&values_[kReceiverIndex]); } template Local FunctionCallbackInfo::NewTarget() const { - return Local::FromSlot(&implicit_args_[kNewTargetIndex]); + if (IsConstructCall()) { + // Can't use &values_[kNewTargetIndex] because of "array index -1 is + // before the beginning of the array" error. + internal::Address* values = &values_[0]; + return Local::FromSlot(values + kNewTargetIndex); + } + return Undefined(GetIsolate()); } template Local FunctionCallbackInfo::Data() const { - auto target = Local::FromSlot(&implicit_args_[kTargetIndex]); + auto target = Local::FromSlot(&values_[kTargetIndex]); return api_internal::GetFunctionTemplateData(GetIsolate(), target); } template Isolate* FunctionCallbackInfo::GetIsolate() const { - return *reinterpret_cast(&implicit_args_[kIsolateIndex]); + return reinterpret_cast(values_[kIsolateIndex]); } template ReturnValue FunctionCallbackInfo::GetReturnValue() const { - return ReturnValue(&implicit_args_[kReturnValueIndex]); + return ReturnValue(&values_[kReturnValueIndex]); } template bool FunctionCallbackInfo::IsConstructCall() const { - return !NewTarget()->IsUndefined(); + return I::SmiValue(values_[kFrameTypeIndex]) == I::kFrameTypeApiConstructExit; } template int FunctionCallbackInfo::Length() const { - return static_cast(length_); -} - -template -Isolate* PropertyCallbackInfo::GetIsolate() const { - return *reinterpret_cast(&args_[kIsolateIndex]); + return static_cast(values_[kArgcIndex]); } template -Local PropertyCallbackInfo::Data() const { - return Local::FromSlot(&args_[kDataIndex]); +bool PropertyCallbackInfo::IsNamed() const { + return I::SmiValue(args_[kFrameTypeIndex]) == + I::kFrameTypeApiNamedAccessorExit; } template -Local PropertyCallbackInfo::This() const { - return Local::FromSlot(&args_[kThisIndex]); +Isolate* PropertyCallbackInfo::GetIsolate() const { + return *reinterpret_cast(&args_[kIsolateIndex]); } template -Local PropertyCallbackInfo::Holder() const { - return Local::FromSlot(&args_[kHolderIndex]); +Local PropertyCallbackInfo::Data() const { + internal::Address callback_info = args_[kCallbackInfoIndex]; + internal::Address data = + I::ReadTaggedPointerField(callback_info, I::kCallbackInfoDataOffset); + return Local::New(GetIsolate(), data); } -namespace api_internal { -// Returns JSGlobalProxy if holder is JSGlobalObject or unmodified holder -// otherwise. -V8_EXPORT internal::Address ConvertToJSGlobalProxyIfNecessary( - internal::Address holder); -} // namespace api_internal - template Local PropertyCallbackInfo::HolderV2() const { - using I = internal::Internals; - if (!I::HasHeapObjectTag(args_[kHolderV2Index])) { - args_[kHolderV2Index] = - api_internal::ConvertToJSGlobalProxyIfNecessary(args_[kHolderIndex]); - } - return Local::FromSlot(&args_[kHolderV2Index]); + return Local::FromSlot(&args_[kHolderIndex]); } template @@ -705,7 +694,7 @@ ReturnValue PropertyCallbackInfo::GetReturnValue() const { template bool PropertyCallbackInfo::ShouldThrowOnError() const { - using I = internal::Internals; + if constexpr (!HasShouldThrowOnError()) return false; if (args_[kShouldThrowOnErrorIndex] != I::IntegralToSmi(I::kInferShouldThrowMode)) { return args_[kShouldThrowOnErrorIndex] != I::IntegralToSmi(I::kDontThrow); diff --git a/deps/v8/include/v8-function.h b/deps/v8/include/v8-function.h index 9ee77596cd3796..18532519f8b89c 100644 --- a/deps/v8/include/v8-function.h +++ b/deps/v8/include/v8-function.h @@ -31,8 +31,8 @@ class V8_EXPORT Function : public Object { * for a given FunctionCallback. */ static MaybeLocal New( - Local context, FunctionCallback callback, - Local data = Local(), int length = 0, + Local context, FunctionCallback callback, Local data = {}, + int length = 0, ConstructorBehavior behavior = ConstructorBehavior::kAllow, SideEffectType side_effect_type = SideEffectType::kHasSideEffect); diff --git a/deps/v8/include/v8-inspector.h b/deps/v8/include/v8-inspector.h index d1993c0eade0ca..9eb9b2130537a5 100644 --- a/deps/v8/include/v8-inspector.h +++ b/deps/v8/include/v8-inspector.h @@ -10,6 +10,7 @@ #include #include +#include "cppgc/garbage-collected.h" // NOLINT(build/include_directory) #include "v8-isolate.h" // NOLINT(build/include_directory) #include "v8-local-handle.h" // NOLINT(build/include_directory) @@ -415,6 +416,15 @@ class V8_EXPORT V8Inspector { virtual void sendNotification(std::unique_ptr message) = 0; virtual void flushProtocolNotifications() = 0; }; + + class V8_EXPORT ManagedChannel + : public cppgc::GarbageCollected, + public Channel { + public: + virtual ~ManagedChannel() = default; + virtual void Trace(cppgc::Visitor* visitor) const {} + }; + enum ClientTrustLevel { kUntrusted, kFullyTrusted }; enum SessionPauseState { kWaitingForDebugger, kNotWaitingForDebugger }; // TODO(chromium:1352175): remove default value once downstream change lands. @@ -433,6 +443,10 @@ class V8_EXPORT V8Inspector { int contextGroupId, Channel* channel, StringView state, ClientTrustLevel clientTrustLevel, SessionPauseState pauseState) = 0; + virtual std::shared_ptr connectShared( + int contextGroupId, ManagedChannel* channel, StringView state, + ClientTrustLevel clientTrustLevel, SessionPauseState pauseState) = 0; + // API methods. virtual std::unique_ptr createStackTrace( v8::Local) = 0; diff --git a/deps/v8/include/v8-internal.h b/deps/v8/include/v8-internal.h index 8d28f22018609a..fab0f3cc490981 100644 --- a/deps/v8/include/v8-internal.h +++ b/deps/v8/include/v8-internal.h @@ -308,6 +308,13 @@ constexpr size_t kExternalPointerTableReservationSize = 256 * MB; // smaller than the maximum table size even after the C++ compiler multiplies // them by 8 to be used as indexes into a table of 64 bit pointers. constexpr uint32_t kExternalPointerIndexShift = 7; +#elif defined(V8_TARGET_OS_IOS) +// iOS restricts large memory allocations, with 128 MB being the maximum size we +// can configure. If we exceed this, SegmentedTable::Initialize will throw a V8 +// out-of-memory error when running the JetStream benchmark +// (https://browserbench.org/JetStream/). +constexpr size_t kExternalPointerTableReservationSize = 128 * MB; +constexpr uint32_t kExternalPointerIndexShift = 8; #else constexpr size_t kExternalPointerTableReservationSize = 512 * MB; constexpr uint32_t kExternalPointerIndexShift = 6; @@ -431,6 +438,35 @@ constexpr size_t kMaxCppHeapPointers = 0; // the type check to use even fewer instructions (essentially replace a AND + // SUB with a single AND). // +// Tag ranges can also to a limited degree be used for union types. For +// example, with the type graph as above, it would be possible to specify a +// Union(D, E, F) as the tag range [D, F]. However, this only works as long as +// the (otherwise independent) types that form the union have adjacent tags. +// +// +// There are broadly speaking two options for performing the type check when +// given the expected type range and the actual tag of the entry. +// +// The first option is to simply have the equivalent of +// +// CHECK(expected_tag_range.Contains(actual_tag)) +// +// This is nice and simple, and friendly to both the branch-predictor and the +// user/developer as it produces clear error messages. However, this approach +// may result in quite a bit of code being generated, for example for calling +// RuntimeAbort from generated code or similar. +// +// The second option is to generate code such as +// +// if (!expected_tag_range.Contains(actual_tag)) return nullptr; +// +// With this, we are also guaranteed to crash safely when the returned pointer +// is used, but this may result in significantly less code being generated, for +// example because the compiler can implement this with a single conditional +// select in combination with the zero register (e.g. on Arm). +// +// The choice of which approach to use therefore depends on the use case, the +// performance and code size constraints, and the importance of debuggability. template struct TagRange { static_assert(std::is_enum_v && @@ -438,7 +474,16 @@ struct TagRange { "Tag parameter must be an enum with base type uint16_t"); // Construct the inclusive tag range [first, last]. - constexpr TagRange(Tag first, Tag last) : first(first), last(last) {} + constexpr TagRange(Tag first, Tag last) : first(first), last(last) { +#ifdef V8_ENABLE_CHECKS + // This would typically be a DCHECK, but that's not available here. +#if V8_HAS_BUILTIN_UNREACHABLE + if (first > last) __builtin_unreachable(); // Invalid tag range. +#elif defined(_MSC_VER) + if (first > last) __assume(0); // Invalid tag range. +#endif +#endif + } // Construct a tag range consisting of a single tag. // @@ -466,8 +511,8 @@ struct TagRange { // Need to perform the math with uint32_t. Otherwise, the uint16_ts would // be promoted to (signed) int, allowing the compiler to (wrongly) assume // that an underflow cannot happen as that would be undefined behavior. - return static_cast(tag) - first <= - static_cast(last) - first; + return static_cast(tag) - static_cast(first) <= + static_cast(last) - static_cast(first); } constexpr bool Contains(TagRange tag_range) const { @@ -483,9 +528,9 @@ struct TagRange { return (static_cast(first) << 16) | last; } - // Internally we represent tag ranges as half-open ranges [first, last). - const Tag first; - const Tag last; + // Internally we represent tag ranges as closed ranges [first, last]. + Tag first; + Tag last; }; // @@ -616,6 +661,7 @@ enum ExternalPointerTag : uint16_t { kWasmFuncDataTag, kWasmManagedDataTag, kWasmNativeModuleTag, + kBackingStoreTag, kIcuBreakIteratorTag, kIcuUnicodeStringTag, kIcuListFormatterTag, @@ -901,6 +947,9 @@ class Internals { static const int kExternalTwoByteRepresentationTag = 0x02; static const int kExternalOneByteRepresentationTag = 0x0a; + // AccessorInfo::data and InterceptorInfo::data field. + static const int kCallbackInfoDataOffset = 1 * kApiTaggedSize; + static const uint32_t kNumIsolateDataSlots = 4; static const int kStackGuardSize = 8 * kApiSystemPointerSize; static const int kNumberOfBooleanFlags = 6; @@ -915,15 +964,11 @@ class Internals { 2 * kApiSystemPointerSize + 2 * kApiInt32Size; // ExternalPointerTable and TrustedPointerTable layout guarantees. - static const int kExternalPointerTableBasePointerOffset = 0; + static const int kExternalEntityTableBasePointerOffset = 0; static const int kSegmentedTableSegmentPoolSize = 4; - static const int kExternalPointerTableSize = - 4 * kApiSystemPointerSize + - kSegmentedTableSegmentPoolSize * sizeof(uint32_t); - static const int kTrustedPointerTableSize = + static const int kExternalEntityTableSize = 4 * kApiSystemPointerSize + kSegmentedTableSegmentPoolSize * sizeof(uint32_t); - static const int kTrustedPointerTableBasePointerOffset = 0; // IsolateData layout guarantees. static const int kIsolateCageBaseOffset = 0; @@ -967,39 +1012,57 @@ class Internals { static const int kIsolateExternalPointerTableOffset = kIsolateEmbedderDataOffset + kNumIsolateDataSlots * kApiSystemPointerSize; static const int kIsolateSharedExternalPointerTableAddressOffset = - kIsolateExternalPointerTableOffset + kExternalPointerTableSize; + kIsolateExternalPointerTableOffset + kExternalEntityTableSize; static const int kIsolateCppHeapPointerTableOffset = kIsolateSharedExternalPointerTableAddressOffset + kApiSystemPointerSize; #ifdef V8_ENABLE_SANDBOX static const int kIsolateTrustedCageBaseOffset = - kIsolateCppHeapPointerTableOffset + kExternalPointerTableSize; + kIsolateCppHeapPointerTableOffset + kExternalEntityTableSize; static const int kIsolateTrustedPointerTableOffset = kIsolateTrustedCageBaseOffset + kApiSystemPointerSize; static const int kIsolateSharedTrustedPointerTableAddressOffset = - kIsolateTrustedPointerTableOffset + kTrustedPointerTableSize; + kIsolateTrustedPointerTableOffset + kExternalEntityTableSize; static const int kIsolateTrustedPointerPublishingScopeOffset = kIsolateSharedTrustedPointerTableAddressOffset + kApiSystemPointerSize; static const int kIsolateCodePointerTableBaseAddressOffset = kIsolateTrustedPointerPublishingScopeOffset + kApiSystemPointerSize; - static const int kIsolateApiCallbackThunkArgumentOffset = + static const int kIsolateJSDispatchTableOffset = kIsolateCodePointerTableBaseAddressOffset + kApiSystemPointerSize; #else - static const int kIsolateApiCallbackThunkArgumentOffset = - kIsolateCppHeapPointerTableOffset + kExternalPointerTableSize; + static const int kIsolateJSDispatchTableOffset = + kIsolateCppHeapPointerTableOffset + kExternalEntityTableSize; #endif // V8_ENABLE_SANDBOX #else - static const int kIsolateApiCallbackThunkArgumentOffset = + static const int kIsolateJSDispatchTableOffset = kIsolateEmbedderDataOffset + kNumIsolateDataSlots * kApiSystemPointerSize; #endif // V8_COMPRESS_POINTERS - static const int kJSDispatchTableOffset = - kIsolateApiCallbackThunkArgumentOffset + kApiSystemPointerSize; + static const int kIsolateApiCallbackThunkArgumentOffset = + kIsolateJSDispatchTableOffset + kExternalEntityTableSize; static const int kIsolateRegexpExecVectorArgumentOffset = - kJSDispatchTableOffset + kApiSystemPointerSize; + kIsolateApiCallbackThunkArgumentOffset + kApiSystemPointerSize; static const int kContinuationPreservedEmbedderDataOffset = kIsolateRegexpExecVectorArgumentOffset + kApiSystemPointerSize; static const int kIsolateRootsOffset = kContinuationPreservedEmbedderDataOffset + kApiSystemPointerSize; +#if V8_TARGET_ARCH_PPC64 + static constexpr int kFrameCPSlotCount = 1; +#else + static constexpr int kFrameCPSlotCount = 0; +#endif + +#if V8_TARGET_ARCH_ARM64 + // The padding required to keep SP 16-byte aligned. + static constexpr int kSPAlignmentSlotCount = 1; +#else + static constexpr int kSPAlignmentSlotCount = 0; +#endif + + static const int kFrameTypeApiCallExit = 18; + static const int kFrameTypeApiConstructExit = 19; + static const int kFrameTypeApiNamedAccessorExit = 20; + static const int kFrameTypeApiIndexedAccessorExit = 21; + // Assert scopes static const int kDisallowGarbageCollectionAlign = alignof(uint32_t); static const int kDisallowGarbageCollectionSize = sizeof(uint32_t); @@ -1020,13 +1083,9 @@ class Internals { using Tagged_t = uint32_t; struct StaticReadOnlyRoot { #ifdef V8_ENABLE_WEBASSEMBLY - static constexpr Tagged_t kBuildDependentTheHoleValue = 0x20001; + static constexpr Tagged_t kBuildDependentTheHoleValue = 0x2fffd; #else -#ifdef V8_INTL_SUPPORT - static constexpr Tagged_t kBuildDependentTheHoleValue = 0x6581; -#else - static constexpr Tagged_t kBuildDependentTheHoleValue = 0x58d1; -#endif + static constexpr Tagged_t kBuildDependentTheHoleValue = 0xfffd; #endif #define DEF_ROOT(name, value) static constexpr Tagged_t k##name = value; @@ -1267,7 +1326,7 @@ class Internals { V8_INLINE static Address* GetExternalPointerTableBase(v8::Isolate* isolate) { Address addr = reinterpret_cast
(isolate) + kIsolateExternalPointerTableOffset + - kExternalPointerTableBasePointerOffset; + kExternalEntityTableBasePointerOffset; return *reinterpret_cast(addr); } @@ -1276,7 +1335,7 @@ class Internals { Address addr = reinterpret_cast
(isolate) + kIsolateSharedExternalPointerTableAddressOffset; addr = *reinterpret_cast(addr); - addr += kExternalPointerTableBasePointerOffset; + addr += kExternalEntityTableBasePointerOffset; return *reinterpret_cast(addr); } #endif diff --git a/deps/v8/include/v8-isolate.h b/deps/v8/include/v8-isolate.h index 1efaa547bc0ed3..f929f13a4de8ff 100644 --- a/deps/v8/include/v8-isolate.h +++ b/deps/v8/include/v8-isolate.h @@ -357,18 +357,6 @@ class V8_EXPORT Isolate { */ bool allow_atomics_wait = true; - /** - * The following parameters describe the offsets for addressing type info - * for wrapped API objects and are used by the fast C API - * (for details see v8-fast-api-calls.h). - * - * V8_DEPRECATED was applied in v14.3. - */ - V8_DEPRECATED("This field is unused.") - int embedder_wrapper_type_index = -1; - V8_DEPRECATED("This field is unused.") - int embedder_wrapper_object_index = -1; - /** * Callbacks to invoke in case of fatal or OOM errors. */ @@ -1492,6 +1480,13 @@ class V8_EXPORT Isolate { */ void SetAddCrashKeyCallback(AddCrashKeyCallback); + /** + * Enables the host application to provide a mechanism for allocating a new + * crash key and setting/updating values for them. + */ + void SetCrashKeyStringCallbacks(AllocateCrashKeyStringCallback, + SetCrashKeyStringCallback); + /** * Optional notification that the system is running low on memory. * V8 uses these notifications to attempt to free memory. @@ -1553,6 +1548,19 @@ class V8_EXPORT Isolate { */ void SetIsLoading(bool is_loading); + /** + * Optional notification to tell V8 whether the embedder is currently + * handling user input. If the embedder uses this notification, it should + * call SetIsInputHandling(true) when input handling starts, and + * SetIsInputHandling(false) when it ends. + * Calling SetIsInputHandling(true) while handling input, or calling + * SetIsInputHandling(false) while not handling input, both have no effect. + * V8 uses these notifications to guide heuristics. + * This is an unfinished experimental feature. Semantics and implementation + * may change frequently. + */ + void SetIsInputHandling(bool is_input_handling); + /** * Optional notification to tell V8 whether the embedder is currently frozen. * V8 uses these notifications to guide heuristics. diff --git a/deps/v8/include/v8-metrics.h b/deps/v8/include/v8-metrics.h index 1e8a546b8418bb..b67196c3e1be1c 100644 --- a/deps/v8/include/v8-metrics.h +++ b/deps/v8/include/v8-metrics.h @@ -53,6 +53,7 @@ struct GarbageCollectionFullCycle { std::optional priority = std::nullopt; bool reduce_memory = false; bool is_loading = false; + bool is_input_handling = false; GarbageCollectionPhases total; GarbageCollectionPhases total_cpp; GarbageCollectionPhases main_thread; diff --git a/deps/v8/include/v8-object.h b/deps/v8/include/v8-object.h index 65a144474cb095..634aebb56ae5dd 100644 --- a/deps/v8/include/v8-object.h +++ b/deps/v8/include/v8-object.h @@ -163,30 +163,44 @@ enum PropertyAttribute { }; /** - * Accessor[Getter|Setter] are used as callback functions when setting|getting - * a particular data property. See Object::SetNativeDataProperty and + * This callback function is called when getting a particular data property + * (i.e. when performing [[Get]] operation). + * + * The callback returns the result by calling `info.GetReturnValue().Set(..)`. + * + * \param property The name of the property being requested. + * \param info Information about the intercepted request, such as + * isolate, object holding the property, return value. See + * `PropertyCallbackInfo`. + * + * See Object::SetNativeDataProperty and * ObjectTemplate::SetNativeDataProperty methods. */ using AccessorNameGetterCallback = void (*)(Local property, const PropertyCallbackInfo& info); -using AccessorNameSetterCallback = - void (*)(Local property, Local value, - const PropertyCallbackInfo& info); - /** - * Access control specifications. + * This callback function is called when setting a particular data property + * (i.e. when performing [[Set]] operation). + * + * In case of operation failure the callback should + * - call `info.GetReturnValue().Set(false)`, + * - (optionally) upon operation failure and info.ShouldThrowOnError() + * is true (indicating execution in `'use strict'` mode) the callback can + * throw TypeError if the error message needs to include more details than + * a TypeError thrown by V8 in this case. * - * Some accessors should be accessible across contexts. These - * accessors have an explicit access control parameter which specifies - * the kind of cross-context access that should be allowed. + * \param property The name of the property being requested. + * \param info Information about the intercepted request, such as + * isolate, object holding the property, return value, or whether running in + * `'use strict'` mode. See `PropertyCallbackInfo`. * + * See Object::SetNativeDataProperty and + * ObjectTemplate::SetNativeDataProperty methods. */ -enum V8_DEPRECATED( - "This enum is no longer used and will be removed in V8 14.3.") - AccessControl { - DEFAULT V8_ENUM_DEPRECATED("not used") = 0, - }; +using AccessorNameSetterCallback = + void (*)(Local property, Local value, + const PropertyCallbackInfo& info); /** * Property filter bits. They can be or'ed to build a composite filter. @@ -322,9 +336,24 @@ class V8_EXPORT Object : public Value { * Gets the property attributes of a property which can be None or * any combination of ReadOnly, DontEnum and DontDelete. Returns * None when the property doesn't exist. + * + * This method will be deprecated soon, since it doesn't provide a way + * to return "property does not exist" result. Use GetPropertyAttributes with + * PropertyAttribute* instead. */ V8_WARN_UNUSED_RESULT Maybe GetPropertyAttributes( Local context, Local key); + /** + * Gets the property attributes of a property which can be None or + * any combination of ReadOnly, DontEnum and DontDelete. + * + * Returns true and sets *out_attributes if the property exists, false if + * not or empty Maybe if an exception is thrown. In the latter two cases, + * the value of *out_attributes is not modified. + */ + V8_WARN_UNUSED_RESULT Maybe GetPropertyAttributes( + Local context, Local key, + PropertyAttribute* out_attributes); /** * Implements Object.getOwnPropertyDescriptor(O, P), see @@ -517,23 +546,6 @@ class V8_EXPORT Object : public Value { int index, EmbedderDataTypeTag tag); - V8_DEPRECATE_SOON( - "Use GetAlignedPointerFromInternalField with EmbedderDataTypeTag " - "parameter instead.") - V8_INLINE void* GetAlignedPointerFromInternalField(int index) { - return GetAlignedPointerFromInternalField(index, - kEmbedderDataTypeTagDefault); - } - - V8_DEPRECATE_SOON( - "Use GetAlignedPointerFromInternalField with EmbedderDataTypeTag " - "parameter instead.") - V8_INLINE void* GetAlignedPointerFromInternalField(v8::Isolate* isolate, - int index) { - return GetAlignedPointerFromInternalField(isolate, index, - kEmbedderDataTypeTagDefault); - } - /** Same as above, but works for PersistentBase. */ V8_INLINE static void* GetAlignedPointerFromInternalField( const PersistentBase& object, int index, @@ -542,15 +554,6 @@ class V8_EXPORT Object : public Value { index, tag); } - V8_DEPRECATE_SOON( - "Use GetAlignedPointerFromInternalField with EmbedderDataTypeTag " - "parameter instead.") - V8_INLINE static void* GetAlignedPointerFromInternalField( - const PersistentBase& object, int index) { - return object.template value()->GetAlignedPointerFromInternalField( - index); - } - /** Same as above, but works for TracedReference. */ V8_INLINE static void* GetAlignedPointerFromInternalField( const BasicTracedReference& object, int index, @@ -559,15 +562,6 @@ class V8_EXPORT Object : public Value { index, tag); } - V8_DEPRECATE_SOON( - "Use GetAlignedPointerFromInternalField with EmbedderDataTypeTag " - "parameter instead.") - V8_INLINE static void* GetAlignedPointerFromInternalField( - const BasicTracedReference& object, int index) { - return object.template value()->GetAlignedPointerFromInternalField( - index); - } - /** * Sets a 2-byte-aligned native pointer in an internal field. To retrieve such * a field, GetAlignedPointerFromInternalField must be used, everything else @@ -576,19 +570,6 @@ class V8_EXPORT Object : public Value { void SetAlignedPointerInInternalField(int index, void* value, EmbedderDataTypeTag tag); - V8_DEPRECATE_SOON( - "Use SetAlignedPointerInInternalField with EmbedderDataTypeTag parameter " - "instead.") - void SetAlignedPointerInInternalField(int index, void* value) { - SetAlignedPointerInInternalField(index, value, kEmbedderDataTypeTagDefault); - } - - V8_DEPRECATE_SOON( - "Use SetAlignedPointerInInternalField with EmbedderDataTypeTag " - "parameter instead.") - void SetAlignedPointerInInternalFields(int argc, int indices[], - void* values[]); - // Type information for a Wrappable object that got wrapped with // `v8::Object::Wrap()`. struct WrapperTypeInfo { @@ -815,29 +796,17 @@ class V8_EXPORT Object : public Value { * * Prefer using version with Isolate parameter if you have an Isolate, * otherwise use the other one. + * + * The type tag has to match the type tag used for storing the value in the + * embedder field. + * If type tags are not used in the embedder, the default value + * `kEmbedderDataTypeTagDefault` can be used. */ void* GetAlignedPointerFromEmbedderDataInCreationContext( v8::Isolate* isolate, int index, EmbedderDataTypeTag tag); void* GetAlignedPointerFromEmbedderDataInCreationContext( int index, EmbedderDataTypeTag tag); - V8_DEPRECATE_SOON( - "Use GetAlignedPointerFromEmbedderDataInCreationContext with " - "EmbedderDataTypeTag parameter instead.") - void* GetAlignedPointerFromEmbedderDataInCreationContext(v8::Isolate* isolate, - int index) { - return GetAlignedPointerFromEmbedderDataInCreationContext( - isolate, index, kEmbedderDataTypeTagDefault); - } - - V8_DEPRECATE_SOON( - "Use GetAlignedPointerFromEmbedderDataInCreationContext with " - "EmbedderDataTypeTag parameter instead.") - void* GetAlignedPointerFromEmbedderDataInCreationContext(int index) { - return GetAlignedPointerFromEmbedderDataInCreationContext( - index, kEmbedderDataTypeTagDefault); - } - /** * Checks whether a callback is set by the * ObjectTemplate::SetCallAsFunctionHandler method. diff --git a/deps/v8/include/v8-platform.h b/deps/v8/include/v8-platform.h index 8d87fe973bc945..3484e988d9fec1 100644 --- a/deps/v8/include/v8-platform.h +++ b/deps/v8/include/v8-platform.h @@ -316,6 +316,29 @@ class JobTask { virtual size_t GetMaxConcurrency(size_t worker_count) const = 0; }; +// Allows a thread to temporarily boost another thread's priority to match its +// own priority. The priority is reset when the object is destroyed, which must +// happens on the boosted thread. +class ScopedBoostablePriority { + public: + ScopedBoostablePriority() = default; + virtual ~ScopedBoostablePriority() = default; + ScopedBoostablePriority(const ScopedBoostablePriority&) = delete; + ScopedBoostablePriority& operator=(const ScopedBoostablePriority& other) = + delete; + + // Boosts the priority of the thread where this ScopedBoostablePriority was + // created. Can be called from any thread, but requires proper external + // synchronization with the constructor, destructor and any other call to + // BoostPriority/Reset(). If called multiple times, only the first call takes + // effect. + virtual bool BoostPriority() = 0; + + // Resets the priority of the thread where this ScopedBoostablePriority was + // created to its original priority. + virtual void Reset() = 0; +}; + /** * A "blocking call" refers to any call that causes the calling thread to wait * off-CPU. It includes but is not limited to calls that wait on synchronous @@ -369,7 +392,7 @@ class ConvertableToTraceFormat { * * Can be implemented by an embedder to record trace events from V8. * - * Will become obsolete in Perfetto SDK build (v8_use_perfetto = true). + * Will become obsolete in Perfetto build (v8_use_perfetto = true). */ class TracingController { public: @@ -446,6 +469,77 @@ class TracingController { virtual void RemoveTraceStateObserver(TraceStateObserver*) {} }; +// Opaque type representing a handle to a shared memory region. +class SharedMemoryHandle { + public: + // For the handle itself, we use the underlying type (e.g. unsigned int) + // instead of e.g. mach_port_t to avoid pulling in large OS header files into + // this header file. Instead, the users of these routines are expected to + // include the respective OS headers in addition to this one. + +#if V8_OS_DARWIN + // A mach_port_t referencing a memory entry object. + using PlatformHandle = unsigned int; +#elif V8_OS_FUCHSIA + // A zx_handle_t to a VMO. + using PlatformHandle = uint32_t; +#elif V8_OS_WIN + // A Windows HANDLE to a file mapping object. + using PlatformHandle = void*; +#else + // A file descriptor. + using PlatformHandle = int; +#endif + + static constexpr SharedMemoryHandle FromPlatformHandle( + PlatformHandle handle) { + return SharedMemoryHandle(handle); + } + + PlatformHandle GetPlatformHandle() const { return handle_; } + + private: + SharedMemoryHandle() = delete; + explicit constexpr SharedMemoryHandle(PlatformHandle handle) + : handle_(handle) {} + + PlatformHandle handle_; +}; + +#define DEFINE_SHARED_MEMORY_HANDLE_WRAPPERS(Wrap, Unwrap) \ + V8_DEPRECATE_SOON("Use SharedMemoryHandle::FromPlatformHandle instead") \ + inline SharedMemoryHandle Wrap(SharedMemoryHandle::PlatformHandle handle) { \ + return SharedMemoryHandle::FromPlatformHandle(handle); \ + } \ + V8_DEPRECATE_SOON("Use SharedMemoryHandle::GetPlatformHandle instead") \ + inline SharedMemoryHandle::PlatformHandle Unwrap( \ + SharedMemoryHandle handle) { \ + return handle.GetPlatformHandle(); \ + } + +#if V8_OS_DARWIN +DEFINE_SHARED_MEMORY_HANDLE_WRAPPERS(SharedMemoryHandleFromMachMemoryEntry, + MachMemoryEntryFromSharedMemoryHandle) +#elif V8_OS_FUCHSIA +DEFINE_SHARED_MEMORY_HANDLE_WRAPPERS(SharedMemoryHandleFromVMO, + VMOFromSharedMemoryHandle) +#elif V8_OS_WIN +DEFINE_SHARED_MEMORY_HANDLE_WRAPPERS(SharedMemoryHandleFromFileMapping, + FileMappingFromSharedMemoryHandle) +#else +DEFINE_SHARED_MEMORY_HANDLE_WRAPPERS(SharedMemoryHandleFromFileDescriptor, + FileDescriptorFromSharedMemoryHandle) +#endif + +#undef DEFINE_SHARED_MEMORY_HANDLE_WRAPPERS + +// TODO(https://crbug.com/463925491): Remove this type alias once Chromium's +// "gin" V8 binding migrates off it. +using PlatformSharedMemoryHandle = std::optional; +V8_DEPRECATE_SOON("Use std::nullopt instead") +static constexpr PlatformSharedMemoryHandle kInvalidSharedMemoryHandle = + std::nullopt; + /** * A V8 memory page allocator. * @@ -703,67 +797,44 @@ class ThreadIsolatedAllocator { virtual int Pkey() const { return -1; } }; -// Opaque type representing a handle to a shared memory region. -using PlatformSharedMemoryHandle = intptr_t; -static constexpr PlatformSharedMemoryHandle kInvalidSharedMemoryHandle = -1; - -// Conversion routines from the platform-dependent shared memory identifiers -// into the opaque PlatformSharedMemoryHandle type. These use the underlying -// types (e.g. unsigned int) instead of the typedef'd ones (e.g. mach_port_t) -// to avoid pulling in large OS header files into this header file. Instead, -// the users of these routines are expected to include the respecitve OS -// headers in addition to this one. -#if V8_OS_DARWIN -// Convert between a shared memory handle and a mach_port_t referencing a memory -// entry object. -inline PlatformSharedMemoryHandle SharedMemoryHandleFromMachMemoryEntry( - unsigned int port) { - return static_cast(port); -} -inline unsigned int MachMemoryEntryFromSharedMemoryHandle( - PlatformSharedMemoryHandle handle) { - return static_cast(handle); -} -#elif V8_OS_FUCHSIA -// Convert between a shared memory handle and a zx_handle_t to a VMO. -inline PlatformSharedMemoryHandle SharedMemoryHandleFromVMO(uint32_t handle) { - return static_cast(handle); -} -inline uint32_t VMOFromSharedMemoryHandle(PlatformSharedMemoryHandle handle) { - return static_cast(handle); -} -#elif V8_OS_WIN -// Convert between a shared memory handle and a Windows HANDLE to a file mapping -// object. -inline PlatformSharedMemoryHandle SharedMemoryHandleFromFileMapping( - void* handle) { - return reinterpret_cast(handle); -} -inline void* FileMappingFromSharedMemoryHandle( - PlatformSharedMemoryHandle handle) { - return reinterpret_cast(handle); +/** + * Possible permissions for memory pages. + */ +enum class PagePermissions { + kNoAccess = 0, + kRead = 1, + kWrite = 2, + kExecute = 4, + kReadWrite = kRead | kWrite, + kReadExecute = kRead | kExecute, + kWriteExecute = kWrite | kExecute, + kReadWriteExecute = kRead | kWrite | kExecute, +}; + +inline constexpr PagePermissions operator|(PagePermissions lhs, + PagePermissions rhs) { + return static_cast(static_cast(lhs) | + static_cast(rhs)); } -#else -// Convert between a shared memory handle and a file descriptor. -inline PlatformSharedMemoryHandle SharedMemoryHandleFromFileDescriptor(int fd) { - return static_cast(fd); + +inline constexpr PagePermissions operator&(PagePermissions lhs, + PagePermissions rhs) { + return static_cast(static_cast(lhs) & + static_cast(rhs)); } -inline int FileDescriptorFromSharedMemoryHandle( - PlatformSharedMemoryHandle handle) { - return static_cast(handle); + +inline PagePermissions& operator|=(PagePermissions& lhs, PagePermissions rhs) { + lhs = lhs | rhs; + return lhs; } -#endif /** - * Possible permissions for memory pages. + * Helper routine to determine whether one set of page permissions (the lhs) is + * a subset of another one (the rhs). */ -enum class PagePermissions { - kNoAccess, - kRead, - kReadWrite, - kReadWriteExecute, - kReadExecute, -}; +inline constexpr bool IsSubset(PagePermissions lhs, PagePermissions rhs) { + return (lhs & rhs) == lhs; +} /** * Class to manage a virtual memory address space. @@ -970,7 +1041,16 @@ class VirtualAddressSpace { */ virtual V8_WARN_UNUSED_RESULT Address AllocateSharedPages(Address hint, size_t size, PagePermissions permissions, - PlatformSharedMemoryHandle handle, uint64_t offset) = 0; + SharedMemoryHandle handle, uint64_t offset) = 0; + + // TODO(https://crbug.com/463925491): Remove me once API users change from + // PlatformSharedMemoryHandle to SharedMemoryHandle. + V8_DEPRECATE_SOON("Use AllocateSharedPages() with SharedMemoryHandle") + V8_WARN_UNUSED_RESULT Address AllocateSharedPages( + Address hint, size_t size, PagePermissions permissions, + std::optional handle, uint64_t offset) { + return AllocateSharedPages(hint, size, permissions, *handle, offset); + } /** * Frees previously allocated shared pages. @@ -1048,7 +1128,7 @@ class VirtualAddressSpace { Address hint, size_t size, size_t alignment, PagePermissions max_page_permissions, std::optional key = std::nullopt, - PlatformSharedMemoryHandle handle = kInvalidSharedMemoryHandle) = 0; + std::optional handle = std::nullopt) = 0; // // TODO(v8) maybe refactor the methods below before stabilizing the API. For @@ -1101,6 +1181,22 @@ class VirtualAddressSpace { virtual V8_WARN_UNUSED_RESULT bool DecommitPages(Address address, size_t size) = 0; + /** + * Sets a name for the address space. + * + * This is mostly useful for debugging tools. If supported by the system, the + * name will for example show up in /proc/$pid/maps next to the virtual + * address reservation: + * + * 2ae700000000-2ae700010000 r--p 00000000 00:00 0 [anon:foo-bar] + * + * \param name The name of the address space. The name must only contain + * alphanumeric characters or dashes. + * + * \returns true on success, false otherwise. + */ + virtual bool SetName(const std::string& name) { return false; } + private: const size_t page_size_; const size_t allocation_granularity_; @@ -1343,6 +1439,14 @@ class Platform { return CreateJobImpl(priority, std::move(job_task), location); } + /** + * Instantiates a ScopedBoostablePriority to boost a thread's priority. + */ + virtual std::unique_ptr + CreateBoostablePriorityScope() { + return nullptr; + } + /** * Instantiates a ScopedBlockingCall to annotate a scope that may/will block. */ diff --git a/deps/v8/include/v8-primitive.h b/deps/v8/include/v8-primitive.h index 87215cf6ece31d..457a8838461faa 100644 --- a/deps/v8/include/v8-primitive.h +++ b/deps/v8/include/v8-primitive.h @@ -199,7 +199,7 @@ class V8_EXPORT String : public Name { * the end of the buffer. If null termination is requested, the output buffer * will always be null terminated even if not all characters fit. In that * case, the capacity must be at least one. The required size of the output - * buffer can be determined using Utf8Length(). + * buffer can be determined using Utf8LengthV2(). * * \param buffer The buffer into which the string will be written. * \param capacity The number of bytes available in the output buffer. diff --git a/deps/v8/include/v8-profiler.h b/deps/v8/include/v8-profiler.h index 61f427ea47c691..7e2d0933e92e68 100644 --- a/deps/v8/include/v8-profiler.h +++ b/deps/v8/include/v8-profiler.h @@ -48,6 +48,21 @@ template class V8_EXPORT std::vector; namespace v8 { +/** + * Identifies which component initiated CPU profiling for proper attribution. + */ +enum class CpuProfileSource : uint8_t { + /** Default value when no explicit source is specified. */ + kUnspecified = 0, + /** Profiling initiated via the DevTools Inspector protocol. */ + kInspector = 1, + /** Profiling initiated by the embedder (e.g., Blink) via self-profiling API. + */ + kSelfProfiling = 2, + /** Profiling initiated internally by V8 (e.g., tracing CPU profiler). */ + kInternal = 3, +}; + struct V8_EXPORT CpuProfileDeoptInfo { /** A pointer to a static string owned by v8. */ const char* deopt_reason; @@ -378,11 +393,13 @@ class V8_EXPORT CpuProfilingOptions { * the profiler's sampling interval. * \param filter_context If specified, profiles will only contain frames * using this context. Other frames will be elided. + * \param profile_source Identifies the source of this CPU profile. */ CpuProfilingOptions( CpuProfilingMode mode = kLeafNodeLineNumbers, unsigned max_samples = kNoSampleLimit, int sampling_interval_us = 0, - MaybeLocal filter_context = MaybeLocal()); + MaybeLocal filter_context = MaybeLocal(), + CpuProfileSource profile_source = CpuProfileSource::kUnspecified); CpuProfilingOptions(CpuProfilingOptions&&) = default; CpuProfilingOptions& operator=(CpuProfilingOptions&&) = default; @@ -390,6 +407,7 @@ class V8_EXPORT CpuProfilingOptions { CpuProfilingMode mode() const { return mode_; } unsigned max_samples() const { return max_samples_; } int sampling_interval_us() const { return sampling_interval_us_; } + CpuProfileSource profile_source() const { return profile_source_; } private: friend class internal::CpuProfile; @@ -401,6 +419,7 @@ class V8_EXPORT CpuProfilingOptions { unsigned max_samples_; int sampling_interval_us_; Global filter_context_; + CpuProfileSource profile_source_; }; /** @@ -1113,7 +1132,7 @@ class V8_EXPORT HeapProfiler { /** * The resolver used by the snapshot generator to get names for V8 objects. */ - V8_DEPRECATE_SOON("Use context_name_resolver callback instead.") + V8_DEPRECATED("Use context_name_resolver callback instead.") ObjectNameResolver* global_object_name_resolver = nullptr; /** * The resolver used by the snapshot generator to get names for v8::Context @@ -1151,7 +1170,7 @@ class V8_EXPORT HeapProfiler { * * \returns the snapshot. */ - V8_DEPRECATE_SOON("Use overload with ContextNameResolver* resolver instead.") + V8_DEPRECATED("Use overload with ContextNameResolver* resolver instead.") const HeapSnapshot* TakeHeapSnapshot( ActivityControl* control, ObjectNameResolver* global_object_name_resolver, bool hide_internals = true, bool capture_numeric_value = false); diff --git a/deps/v8/include/v8-promise.h b/deps/v8/include/v8-promise.h index 8c127c8122a2ec..36412c774d1b51 100644 --- a/deps/v8/include/v8-promise.h +++ b/deps/v8/include/v8-promise.h @@ -19,7 +19,7 @@ class Context; #endif /** - * An instance of the built-in Promise constructor (ES6 draft). + * An instance of the built-in Promise constructor. */ class V8_EXPORT Promise : public Object { public: @@ -65,10 +65,21 @@ class V8_EXPORT Promise : public Object { }; /** - * Register a resolution/rejection handler with a promise. - * The handler is given the respective resolution/rejection value as - * an argument. If the promise is already resolved/rejected, the handler is - * invoked at the end of turn. + * Register a resolution/rejection handler with a promise. The handler is + * given the respective resolution/rejection value as an argument. If the + * promise is already resolved/rejected, the handler is invoked at the end of + * turn. + * + * This performs the PerformPromiseThen abstract operation with a fresh native + * promise as result, rather than the similar Promise.prototype.then + * operation. In particular, it does not do species lookup on the Promise + * constructor, and is therefore guaranteed to return a Promise. + * + * https://tc39.es/ecma262/#sec-performpromisethen + * + * This is consistent with Promise reactions in WebIDL: + * + * https://webidl.spec.whatwg.org/#dfn-perform-steps-once-promise-is-settled */ V8_WARN_UNUSED_RESULT MaybeLocal Catch(Local context, Local handler); diff --git a/deps/v8/include/v8-sandbox.h b/deps/v8/include/v8-sandbox.h index 06d01761798582..1fc79ed5409e91 100644 --- a/deps/v8/include/v8-sandbox.h +++ b/deps/v8/include/v8-sandbox.h @@ -62,41 +62,7 @@ enum class CppHeapPointerTag : uint16_t { kLastTag = 0x7fff, }; -// Convenience struct to represent tag ranges. This is used for type checks -// against supertypes, which cover a range of types (their subtypes). -// Both the lower- and the upper bound are inclusive. In other words, this -// struct represents the range [lower_bound, upper_bound]. -// TODO(saelo): reuse internal::TagRange here. -struct CppHeapPointerTagRange { - constexpr CppHeapPointerTagRange(CppHeapPointerTag lower, - CppHeapPointerTag upper) - : lower_bound(lower), upper_bound(upper) {} - CppHeapPointerTag lower_bound; - CppHeapPointerTag upper_bound; - - // Check whether the tag of the given CppHeapPointerTable entry is within - // this range. This method encodes implementation details of the - // CppHeapPointerTable, which is necessary as it is used by - // ReadCppHeapPointerField below. - // Returns true if the check is successful and the tag of the given entry is - // within this range, false otherwise. - bool CheckTagOf(uint64_t entry) { - // Note: the cast to uint32_t is important here. Otherwise, the uint16_t's - // would be promoted to int in the range check below, which would result in - // undefined behavior (signed integer undeflow) if the actual value is less - // than the lower bound. Then, the compiler would take advantage of the - // undefined behavior and turn the range check into a simple - // `actual_tag <= last_tag` comparison, which is incorrect. - uint32_t actual_tag = static_cast(entry); - // The actual_tag is shifted to the left by one and contains the marking - // bit in the LSB. To ignore that during the type check, simply add one to - // the (shifted) range. - constexpr int kTagShift = internal::kCppHeapPointerTagShift; - uint32_t first_tag = static_cast(lower_bound) << kTagShift; - uint32_t last_tag = (static_cast(upper_bound) << kTagShift) + 1; - return actual_tag >= first_tag && actual_tag <= last_tag; - } -}; +using CppHeapPointerTagRange = internal::TagRange; constexpr CppHeapPointerTagRange kAnyCppHeapPointer( CppHeapPointerTag::kFirstTag, CppHeapPointerTag::kLastTag); @@ -115,16 +81,6 @@ class SandboxHardwareSupport { * hardware permissions to the memory that will be inherited on clone. */ V8_EXPORT static void InitializeBeforeThreadCreation(); - - /** - * Prepares the current thread for executing sandboxed code. - * - * This must be called on newly created threads before they execute any - * sandboxed code (in particular any JavaScript or WebAssembly code). It - * should not be invoked on threads that never execute sandboxed code, - * although it is fine to do so from a security point of view. - */ - V8_EXPORT static void PrepareCurrentThreadForHardwareSandboxing(); }; namespace internal { @@ -133,7 +89,7 @@ namespace internal { V8_INLINE static Address* GetCppHeapPointerTableBase(v8::Isolate* isolate) { Address addr = reinterpret_cast
(isolate) + Internals::kIsolateCppHeapPointerTableOffset + - Internals::kExternalPointerTableBasePointerOffset; + Internals::kExternalEntityTableBasePointerOffset; return *reinterpret_cast(addr); } #endif // V8_COMPRESS_POINTERS @@ -142,9 +98,12 @@ template V8_INLINE static T* ReadCppHeapPointerField(v8::Isolate* isolate, Address heap_object_ptr, int offset, CppHeapPointerTagRange tag_range) { + // This is a specialized version of the the CppHeapPointerTable accessors + // which (1) allows the code to be inlined into the callers for performance + // and (2) is optimized for code size as there are a huge number of callers + // from auto-generated bindings code. + #ifdef V8_COMPRESS_POINTERS - // See src/sandbox/cppheap-pointer-table-inl.h. Logic duplicated here so - // it can be inlined and doesn't require an additional call. const CppHeapPointerHandle handle = Internals::ReadRawField(heap_object_ptr, offset); const uint32_t index = handle >> kExternalPointerIndexShift; @@ -153,9 +112,21 @@ V8_INLINE static T* ReadCppHeapPointerField(v8::Isolate* isolate, reinterpret_cast*>(&table[index]); Address entry = std::atomic_load_explicit(ptr, std::memory_order_relaxed); - Address pointer = entry; - if (V8_LIKELY(tag_range.CheckTagOf(entry))) { - pointer = entry >> kCppHeapPointerPayloadShift; + // Note: the cast to uint32_t is important here. Otherwise, the uint16_t's + // would be promoted to int in the range check below, which would result in + // undefined behavior (signed integer underflow) if the actual value is less + // than the lower bound. Then, the compiler would take advantage of the + // undefined behavior and turn the range check into a simple + // `actual_tag <= last_tag` comparison, which is incorrect. + uint32_t actual_tag = static_cast(entry); + // The actual_tag is shifted to the left by one and contains the marking + // bit in the LSB. To ignore that during the type check, simply add one to + // the (shifted) range. + constexpr int kTagShift = internal::kCppHeapPointerTagShift; + uint32_t first_tag = static_cast(tag_range.first) << kTagShift; + uint32_t last_tag = (static_cast(tag_range.last) << kTagShift) + 1; + if (V8_LIKELY(actual_tag >= first_tag && actual_tag <= last_tag)) { + entry = entry >> kCppHeapPointerPayloadShift; } else { // If the type check failed, we simply return nullptr here. That way: // 1. The null handle always results in nullptr being returned here, which @@ -174,15 +145,25 @@ V8_INLINE static T* ReadCppHeapPointerField(v8::Isolate* isolate, // between returning nullptr or the original entry, since it will // simply compile to a `csel x0, x8, xzr, lo` instead of a // `csel x0, x10, x8, lo` instruction. - pointer = 0; + // 3. The machine code sequence ends up being pretty short, which is + // important here as this code will be inlined into a lot of functions. + entry = 0; } - return reinterpret_cast(pointer); + return reinterpret_cast(entry); #else // !V8_COMPRESS_POINTERS return reinterpret_cast( Internals::ReadRawField
(heap_object_ptr, offset)); #endif // !V8_COMPRESS_POINTERS } +// TODO(saelo): temporary workaround needed to introduce range-based type +// checks for the external pointer table. See comment above +// ExternalPointerCanBeEmpty(ExternalPointerTagRange) function for details. +V8_INLINE static constexpr bool ExternalPointerCanBeEmpty( + CppHeapPointerTagRange tag_range) { + return true; +} + } // namespace internal } // namespace v8 diff --git a/deps/v8/include/v8-script.h b/deps/v8/include/v8-script.h index debe89b50847ea..c3a2274d43334e 100644 --- a/deps/v8/include/v8-script.h +++ b/deps/v8/include/v8-script.h @@ -64,7 +64,13 @@ class V8_EXPORT UnboundScript : public Data { */ Local diff --git a/test/fixtures/wpt/streams/readable-streams/crashtests/garbage-collection.any.js b/test/fixtures/wpt/streams/readable-streams/crashtests/garbage-collection.any.js new file mode 100644 index 00000000000000..6e9d80c41425b1 --- /dev/null +++ b/test/fixtures/wpt/streams/readable-streams/crashtests/garbage-collection.any.js @@ -0,0 +1,38 @@ +// META: global=window,worker +// META: script=/common/gc.js +'use strict'; + +// See https://crbug.com/335506658 for details. +promise_test(async () => { + const closed = new ReadableStream({ + pull(controller) { + controller.enqueue('is there anybody in there?'); + } + }).getReader().closed; + // 3 GCs are actually required to trigger the bug at time of writing. + for (let i = 0; i < 5; ++i) + await garbageCollect(); +}, 'Garbage-collecting a stream along with its reader should not crash'); + +promise_test(async () => { + let reader = new ReadableStream({ + pull() { } + }).getReader(); + const promise = reader.read(); + reader = null; + for (let i = 0; i < 5; ++i) + await garbageCollect(); +}, 'Garbage-collecting a stream with a pending read should not crash'); + +promise_test(async () => { + let reader = new ReadableStream({ + type: "bytes", + pull() { return new Promise(resolve => {}); } + }).getReader({mode: "byob"}); + const promise = reader.read(new Uint8Array(42)); + reader = null; + for (let i = 0; i < 5; ++i) + await garbageCollect(); +}, 'Garbage-collecting a stream with a pending BYOB read should not crash'); + + diff --git a/test/fixtures/wpt/streams/readable-streams/from.any.js b/test/fixtures/wpt/streams/readable-streams/from.any.js index 2a4212ab890606..2b28a05fee398c 100644 --- a/test/fixtures/wpt/streams/readable-streams/from.any.js +++ b/test/fixtures/wpt/streams/readable-streams/from.any.js @@ -150,6 +150,12 @@ const badIterables = [ ['an object with a non-callable @@asyncIterator method', { [Symbol.asyncIterator]: 42 }], + ['an object with an @@iterator method returning a non-object', { + [Symbol.iterator]: () => 42 + }], + ['an object with an @@asyncIterator method returning a non-object', { + [Symbol.asyncIterator]: () => 42 + }], ]; for (const [label, iterable] of badIterables) { @@ -244,6 +250,64 @@ promise_test(async t => { }, `ReadableStream.from: stream errors when next() rejects`); +promise_test(async t => { + const theError = new Error('a unique string'); + + const iterable = { + next() { + throw theError; + }, + [Symbol.asyncIterator]: () => iterable + }; + + const rs = ReadableStream.from(iterable); + const reader = rs.getReader(); + + await Promise.all([ + promise_rejects_exactly(t, theError, reader.read()), + promise_rejects_exactly(t, theError, reader.closed) + ]); + +}, 'ReadableStream.from: stream errors when next() throws synchronously'); + +promise_test(async t => { + + const iterable = { + next() { + return 42; // not a promise or an iterator result + }, + [Symbol.asyncIterator]: () => iterable + }; + + const rs = ReadableStream.from(iterable); + const reader = rs.getReader(); + + await Promise.all([ + promise_rejects_js(t, TypeError, reader.read()), + promise_rejects_js(t, TypeError, reader.closed) + ]); + +}, 'ReadableStream.from: stream errors when next() returns a non-object'); + +promise_test(async t => { + + const iterable = { + next() { + return Promise.resolve(42); // not an iterator result + }, + [Symbol.asyncIterator]: () => iterable + }; + + const rs = ReadableStream.from(iterable); + const reader = rs.getReader(); + + await Promise.all([ + promise_rejects_js(t, TypeError, reader.read()), + promise_rejects_js(t, TypeError, reader.closed) + ]); + +}, 'ReadableStream.from: stream errors when next() fulfills with a non-object'); + promise_test(async t => { const iterable = { @@ -360,6 +424,93 @@ promise_test(async t => { }, `ReadableStream.from: return() is not called when iterator completes normally`); +promise_test(async t => { + + const theError = new Error('a unique string'); + + const iterable = { + next: t.unreached_func('next() should not be called'), + throw: t.unreached_func('throw() should not be called'), + // no return method + [Symbol.asyncIterator]: () => iterable + }; + + const rs = ReadableStream.from(iterable); + const reader = rs.getReader(); + + await Promise.all([ + reader.cancel(theError), + reader.closed + ]); + +}, `ReadableStream.from: cancel() resolves when return() method is missing`); + +promise_test(async t => { + + const theError = new Error('a unique string'); + + const iterable = { + next: t.unreached_func('next() should not be called'), + throw: t.unreached_func('throw() should not be called'), + return: 42, + [Symbol.asyncIterator]: () => iterable + }; + + const rs = ReadableStream.from(iterable); + const reader = rs.getReader(); + + await promise_rejects_js(t, TypeError, reader.cancel(theError), 'cancel() should reject with a TypeError'); + + await reader.closed; + +}, `ReadableStream.from: cancel() rejects when return() is not a method`); + +promise_test(async t => { + + const cancelReason = new Error('cancel reason'); + const rejectError = new Error('reject error'); + + const iterable = { + next: t.unreached_func('next() should not be called'), + throw: t.unreached_func('throw() should not be called'), + async return() { + throw rejectError; + }, + [Symbol.asyncIterator]: () => iterable + }; + + const rs = ReadableStream.from(iterable); + const reader = rs.getReader(); + + await promise_rejects_exactly(t, rejectError, reader.cancel(cancelReason), 'cancel() should reject with error from return()'); + + await reader.closed; + +}, `ReadableStream.from: cancel() rejects when return() rejects`); + +promise_test(async t => { + + const cancelReason = new Error('cancel reason'); + const rejectError = new Error('reject error'); + + const iterable = { + next: t.unreached_func('next() should not be called'), + throw: t.unreached_func('throw() should not be called'), + return() { + throw rejectError; + }, + [Symbol.asyncIterator]: () => iterable + }; + + const rs = ReadableStream.from(iterable); + const reader = rs.getReader(); + + await promise_rejects_exactly(t, rejectError, reader.cancel(cancelReason), 'cancel() should reject with error from return()'); + + await reader.closed; + +}, `ReadableStream.from: cancel() rejects when return() throws synchronously`); + promise_test(async t => { const theError = new Error('a unique string'); diff --git a/test/fixtures/wpt/streams/readable-streams/garbage-collection.any.js b/test/fixtures/wpt/streams/readable-streams/garbage-collection.any.js index 13bd1fb3437877..abcb4cf21b8955 100644 --- a/test/fixtures/wpt/streams/readable-streams/garbage-collection.any.js +++ b/test/fixtures/wpt/streams/readable-streams/garbage-collection.any.js @@ -69,3 +69,22 @@ promise_test(async () => { 'old reader should still be locking the stream even after garbage collection')); }, 'Garbage-collecting a ReadableStreamDefaultReader should not unlock its stream'); + +promise_test(async () => { + + const promise = (() => { + const rs = new ReadableStream({ + pull(controller) { + controller.enqueue('words'); + } + }); + const reader = rs.getReader(); + return reader.read(); + })(); + await garbageCollect(); + const {value, done} = await promise; + // If we get here, the test passed. + assert_equals(value, 'words', 'value should be words'); + assert_false(done, 'we should not be done'); + +}, 'A ReadableStream and its reader should not be garbage collected while there is a read promise pending'); diff --git a/test/fixtures/wpt/streams/readable-streams/templated.any.js b/test/fixtures/wpt/streams/readable-streams/templated.any.js index dc75b805a10d63..4f2440c0880bb7 100644 --- a/test/fixtures/wpt/streams/readable-streams/templated.any.js +++ b/test/fixtures/wpt/streams/readable-streams/templated.any.js @@ -13,7 +13,9 @@ templatedRSEmpty('ReadableStream (empty)', () => { }); templatedRSEmptyReader('ReadableStream (empty) reader', () => { - return streamAndDefaultReader(new ReadableStream()); + const stream = new ReadableStream(); + const reader = stream.getReader(); + return { stream, reader, read: () => reader.read() }; }); templatedRSClosed('ReadableStream (closed via call in start)', () => { @@ -138,6 +140,10 @@ templatedRSTwoChunksClosedReader('ReadableStream (two chunks enqueued, then clos return result; }, chunks); +templatedRSThrowAfterCloseOrError('ReadableStream', (extras) => { + return new ReadableStream({ ...extras }); +}); + function streamAndDefaultReader(stream) { return { stream, reader: stream.getReader() }; } diff --git a/test/fixtures/wpt/streams/resources/rs-test-templates.js b/test/fixtures/wpt/streams/resources/rs-test-templates.js index 25751c477f5dc8..73ef0463768d23 100644 --- a/test/fixtures/wpt/streams/resources/rs-test-templates.js +++ b/test/fixtures/wpt/streams/resources/rs-test-templates.js @@ -200,7 +200,7 @@ self.templatedRSEmptyReader = (label, factory) => { test(() => { - const stream = factory().stream; + const { stream } = factory(); assert_true(stream.locked, 'locked getter should return true'); @@ -208,9 +208,9 @@ self.templatedRSEmptyReader = (label, factory) => { promise_test(t => { - const reader = factory().reader; + const { read } = factory(); - reader.read().then( + read().then( t.unreached_func('read() should not fulfill'), t.unreached_func('read() should not reject') ); @@ -221,14 +221,14 @@ self.templatedRSEmptyReader = (label, factory) => { promise_test(t => { - const reader = factory().reader; + const { read } = factory(); - reader.read().then( + read().then( t.unreached_func('read() should not fulfill'), t.unreached_func('read() should not reject') ); - reader.read().then( + read().then( t.unreached_func('read() should not fulfill'), t.unreached_func('read() should not reject') ); @@ -239,26 +239,24 @@ self.templatedRSEmptyReader = (label, factory) => { test(() => { - const reader = factory().reader; - assert_not_equals(reader.read(), reader.read(), 'the promises returned should be distinct'); + const { read } = factory(); + assert_not_equals(read(), read(), 'the promises returned should be distinct'); }, label + ': read() should return distinct promises each time'); test(() => { - const stream = factory().stream; + const { stream } = factory(); assert_throws_js(TypeError, () => stream.getReader(), 'stream.getReader() should throw a TypeError'); }, label + ': getReader() again on the stream should fail'); promise_test(async t => { - const streamAndReader = factory(); - const stream = streamAndReader.stream; - const reader = streamAndReader.reader; + const { stream, reader, read } = factory(); - const read1 = reader.read(); - const read2 = reader.read(); + const read1 = read(); + const read2 = read(); const closed = reader.closed; reader.releaseLock(); @@ -275,19 +273,19 @@ self.templatedRSEmptyReader = (label, factory) => { promise_test(t => { - const reader = factory().reader; + const { reader, read } = factory(); reader.releaseLock(); return Promise.all([ - promise_rejects_js(t, TypeError, reader.read()), - promise_rejects_js(t, TypeError, reader.read()) + promise_rejects_js(t, TypeError, read()), + promise_rejects_js(t, TypeError, read()) ]); }, label + ': releasing the lock should cause further read() calls to reject with a TypeError'); promise_test(t => { - const reader = factory().reader; + const { reader } = factory(); const closedBefore = reader.closed; reader.releaseLock(); @@ -301,9 +299,7 @@ self.templatedRSEmptyReader = (label, factory) => { test(() => { - const streamAndReader = factory(); - const stream = streamAndReader.stream; - const reader = streamAndReader.reader; + const { stream, reader } = factory(); reader.releaseLock(); assert_false(stream.locked, 'locked getter should return false'); @@ -312,10 +308,10 @@ self.templatedRSEmptyReader = (label, factory) => { promise_test(() => { - const reader = factory().reader; + const { reader, read } = factory(); reader.cancel(); - return reader.read().then(r => { + return read().then(r => { assert_object_equals(r, { value: undefined, done: true }, 'read()ing from the reader should give a done result'); }); @@ -323,7 +319,7 @@ self.templatedRSEmptyReader = (label, factory) => { promise_test(t => { - const stream = factory().stream; + const { stream } = factory(); return promise_rejects_js(t, TypeError, stream.cancel()); }, label + ': canceling via the stream should fail'); @@ -719,3 +715,62 @@ self.templatedRSTeeCancel = (label, factory) => { }, `${label}: erroring a teed stream should properly handle canceled branches`); }; + +self.templatedRSThrowAfterCloseOrError = (label, factory) => { + test(() => {}, 'Running templatedRSThrowAfterCloseOrError with ' + label); + + const theError = new Error('a unique string'); + + promise_test(async t => { + let controller; + const stream = factory({ + start: t.step_func((c) => { + controller = c; + }) + }); + + controller.close(); + + assert_throws_js(TypeError, () => controller.enqueue(new Uint8Array([1]))); + }, `${label}: enqueue() throws after close()`); + + promise_test(async t => { + let controller; + const stream = factory({ + start: t.step_func((c) => { + controller = c; + }) + }); + + controller.enqueue(new Uint8Array([1])); + controller.close(); + + assert_throws_js(TypeError, () => controller.enqueue(new Uint8Array([2]))); + }, `${label}: enqueue() throws after enqueue() and close()`); + + promise_test(async t => { + let controller; + const stream = factory({ + start: t.step_func((c) => { + controller = c; + }) + }); + + controller.error(theError); + + assert_throws_js(TypeError, () => controller.enqueue(new Uint8Array([1]))); + }, `${label}: enqueue() throws after error()`); + + promise_test(async t => { + let controller; + const stream = factory({ + start: t.step_func((c) => { + controller = c; + }) + }); + + controller.error(theError); + + assert_throws_js(TypeError, () => controller.close()); + }, `${label}: close() throws after error()`); +}; diff --git a/test/fixtures/wpt/streams/transferable/WEB_FEATURES.yml b/test/fixtures/wpt/streams/transferable/WEB_FEATURES.yml new file mode 100644 index 00000000000000..4ecacf5edbc7da --- /dev/null +++ b/test/fixtures/wpt/streams/transferable/WEB_FEATURES.yml @@ -0,0 +1,3 @@ +features: +- name: transferable-streams + files: "**" diff --git a/test/fixtures/wpt/streams/transform-streams/WEB_FEATURES.yml b/test/fixtures/wpt/streams/transform-streams/WEB_FEATURES.yml new file mode 100644 index 00000000000000..f39dc16de4c731 --- /dev/null +++ b/test/fixtures/wpt/streams/transform-streams/WEB_FEATURES.yml @@ -0,0 +1,8 @@ +features: +- name: streams + files: + - "*" + - "!cancel.any.js" +- name: transformstream-transformer-cancel + files: + - cancel.any.js diff --git a/test/fixtures/wpt/streams/transform-streams/cancel.any.js b/test/fixtures/wpt/streams/transform-streams/cancel.any.js index 5c7fc4eae5d55b..9b369bfb7c68b4 100644 --- a/test/fixtures/wpt/streams/transform-streams/cancel.any.js +++ b/test/fixtures/wpt/streams/transform-streams/cancel.any.js @@ -113,3 +113,93 @@ promise_test(async t => { const closePromise = ts.readable.cancel(1); await promise_rejects_exactly(t, thrownError, closePromise, 'closePromise should reject with thrownError'); }, 'writable.abort() and readable.cancel() should reject if a transformer.cancel() calls controller.error()'); + +promise_test(async t => { + const cancelReason = new Error('cancel reason'); + let controller; + let cancelPromise; + let flushCalled = false; + const ts = new TransformStream({ + start(c) { + controller = c; + }, + flush() { + flushCalled = true; + cancelPromise = ts.readable.cancel(cancelReason); + }, + cancel: t.unreached_func('cancel should not be called') + }); + await flushAsyncEvents(); // ensure stream is started + await ts.writable.close(); + assert_true(flushCalled, 'flush() was called'); + await cancelPromise; +}, 'readable.cancel() should not call cancel() when flush() is already called from writable.close()'); + +promise_test(async t => { + const cancelReason = new Error('cancel reason'); + const abortReason = new Error('abort reason'); + let cancelCalls = 0; + let controller; + let cancelPromise; + const ts = new TransformStream({ + start(c) { + controller = c; + }, + cancel() { + if (++cancelCalls === 1) { + cancelPromise = ts.readable.cancel(cancelReason); + } + }, + flush: t.unreached_func('flush should not be called') + }); + await flushAsyncEvents(); // ensure stream is started + await ts.writable.abort(abortReason); + assert_equals(cancelCalls, 1); + await cancelPromise; + assert_equals(cancelCalls, 1); +}, 'readable.cancel() should not call cancel() again when already called from writable.abort()'); + +promise_test(async t => { + const cancelReason = new Error('cancel reason'); + let controller; + let closePromise; + let cancelCalled = false; + const ts = new TransformStream({ + start(c) { + controller = c; + }, + cancel() { + cancelCalled = true; + closePromise = ts.writable.close(); + }, + flush: t.unreached_func('flush should not be called') + }); + await flushAsyncEvents(); // ensure stream is started + await ts.readable.cancel(cancelReason); + assert_true(cancelCalled, 'cancel() was called'); + await closePromise; +}, 'writable.close() should not call flush() when cancel() is already called from readable.cancel()'); + +promise_test(async t => { + const cancelReason = new Error('cancel reason'); + const abortReason = new Error('abort reason'); + let cancelCalls = 0; + let controller; + let abortPromise; + const ts = new TransformStream({ + start(c) { + controller = c; + }, + cancel() { + if (++cancelCalls === 1) { + abortPromise = ts.writable.abort(abortReason); + } + }, + flush: t.unreached_func('flush should not be called') + }); + await flushAsyncEvents(); // ensure stream is started + await promise_rejects_exactly(t, abortReason, ts.readable.cancel(cancelReason)); + assert_equals(cancelCalls, 1); + await promise_rejects_exactly(t, abortReason, abortPromise); + assert_equals(cancelCalls, 1); +}, 'writable.abort() should not call cancel() again when already called from readable.cancel()'); diff --git a/test/fixtures/wpt/streams/writable-streams/WEB_FEATURES.yml b/test/fixtures/wpt/streams/writable-streams/WEB_FEATURES.yml new file mode 100644 index 00000000000000..8cf3517baf08a4 --- /dev/null +++ b/test/fixtures/wpt/streams/writable-streams/WEB_FEATURES.yml @@ -0,0 +1,3 @@ +features: +- name: streams + files: "**" diff --git a/test/fixtures/wpt/streams/writable-streams/aborting.any.js b/test/fixtures/wpt/streams/writable-streams/aborting.any.js index 9171dbe158f00c..9d8e80b1de63c5 100644 --- a/test/fixtures/wpt/streams/writable-streams/aborting.any.js +++ b/test/fixtures/wpt/streams/writable-streams/aborting.any.js @@ -1472,16 +1472,96 @@ promise_test(async t => { promise_test(async t => { let ctrl; + let abortPromise; + let abortPromiseFromSignal; + const e1 = SyntaxError(); + const e2 = TypeError(); + const ws = new WritableStream({ + start(c) { ctrl = c; }, + }); + + const writer = ws.getWriter(); + ctrl.signal.addEventListener('abort', () => { + abortPromiseFromSignal = writer.abort(e2); + }); + abortPromise = writer.abort(e1); + assert_true(ctrl.signal.aborted); + + await Promise.all([ + abortPromise, + abortPromiseFromSignal, + promise_rejects_exactly(t, e2, writer.closed, 'closed') + ]); +}, 'recursive abort() call from abort() aborting signal (not started)'); + +promise_test(async t => { + let ctrl; + let abortPromise; + let abortPromiseFromSignal; const e1 = SyntaxError(); const e2 = TypeError(); const ws = new WritableStream({ start(c) { ctrl = c; }, }); + await flushAsyncEvents(); // ensure stream is started + + const writer = ws.getWriter(); + ctrl.signal.addEventListener('abort', () => { + abortPromiseFromSignal = writer.abort(e2); + }); + abortPromise = writer.abort(e1); + assert_true(ctrl.signal.aborted); + + await Promise.all([ + abortPromise, + abortPromiseFromSignal, + promise_rejects_exactly(t, e2, writer.closed, 'closed') + ]); +}, 'recursive abort() call from abort() aborting signal'); + +promise_test(async t => { + let ctrl; + let abortPromise; + let closePromiseFromSignal; + const theError = SyntaxError(); + const ws = new WritableStream({ + start(c) { ctrl = c; }, + }); + + const writer = ws.getWriter(); + ctrl.signal.addEventListener('abort', () => { + closePromiseFromSignal = writer.close(); + }); + abortPromise = writer.abort(theError); + assert_true(ctrl.signal.aborted); + + await Promise.all([ + abortPromise, + promise_rejects_exactly(t, theError, closePromiseFromSignal, 'closed'), + promise_rejects_exactly(t, theError, writer.closed, 'closed') + ]); +}, 'recursive close() call from abort() aborting signal (not started)'); + +promise_test(async t => { + let ctrl; + let abortPromise; + let closePromiseFromSignal; + const theError = SyntaxError(); + const ws = new WritableStream({ + start(c) { ctrl = c; }, + }); + await flushAsyncEvents(); // ensure stream is started const writer = ws.getWriter(); - ctrl.signal.addEventListener('abort', () => writer.abort(e2)); - writer.abort(e1); + ctrl.signal.addEventListener('abort', () => { + closePromiseFromSignal = writer.close(); + }); + abortPromise = writer.abort(theError); assert_true(ctrl.signal.aborted); - await promise_rejects_exactly(t, e2, writer.closed, 'closed'); -}, 'recursive abort() call'); + await Promise.all([ + abortPromise, + closePromiseFromSignal, + writer.closed + ]); +}, 'recursive close() call from abort() aborting signal'); diff --git a/test/fixtures/wpt/streams/writable-streams/close.any.js b/test/fixtures/wpt/streams/writable-streams/close.any.js index 45261e7ca7e01d..3b2c07cc3bfea1 100644 --- a/test/fixtures/wpt/streams/writable-streams/close.any.js +++ b/test/fixtures/wpt/streams/writable-streams/close.any.js @@ -468,3 +468,14 @@ promise_test(t => { return promise_rejects_js(t, TypeError, ws.close(), 'close should reject'); }, 'close() on a stream with a pending close should reject'); + +// See https://github.com/whatwg/streams/issues/1341. +promise_test(async t => { + const ws = new WritableStream(); + const writer = ws.getWriter(); + + await writer.write(1); + await writer.close(); + + return promise_rejects_js(t, TypeError, writer.write(2), 'write should reject'); +}, 'write() on a closed stream should reject'); diff --git a/test/fixtures/wpt/streams/writable-streams/constructor.any.js b/test/fixtures/wpt/streams/writable-streams/constructor.any.js index 0abc7ef545ea0b..bb382a33b8e0c4 100644 --- a/test/fixtures/wpt/streams/writable-streams/constructor.any.js +++ b/test/fixtures/wpt/streams/writable-streams/constructor.any.js @@ -87,6 +87,10 @@ test(() => { new WritableStream(); }, 'WritableStream should be constructible with no arguments'); +test(() => { + assert_throws_js(RangeError, () => new WritableStream({ type: 'bytes' }), 'constructor should throw'); +}, `WritableStream can't be constructed with a defined type`); + test(() => { const underlyingSink = { get start() { throw error1; } }; const queuingStrategy = { highWaterMark: 0, get size() { throw error2; } }; diff --git a/test/fixtures/wpt/streams/writable-streams/crashtests/garbage-collection.any.js b/test/fixtures/wpt/streams/writable-streams/crashtests/garbage-collection.any.js new file mode 100644 index 00000000000000..9f64e9b7a8ac7b --- /dev/null +++ b/test/fixtures/wpt/streams/writable-streams/crashtests/garbage-collection.any.js @@ -0,0 +1,90 @@ +// META: global=window,worker +// META: script=/common/gc.js +'use strict'; + +// See https://crbug.com/390646657 for details. +promise_test(async () => { + const written = new WritableStream({ + write(chunk) { + return new Promise(resolve => {}); + } + }).getWriter().write('just nod if you can hear me'); + for (let i = 0; i < 5; ++i) + await garbageCollect(); +}, 'Garbage-collecting a stream writer with a pending write should not crash'); + +promise_test(async () => { + const closed = new WritableStream({ + write(chunk) { } + }).getWriter().closed; + for (let i = 0; i < 5; ++i) + await garbageCollect(); +}, 'Garbage-collecting a stream writer should not crash with closed promise is retained'); + +promise_test(async () => { + let writer = new WritableStream({ + write(chunk) { return new Promise(resolve => {}); }, + close() { return new Promise(resolve => {}); } + }).getWriter(); + writer.write('is there anyone home?'); + writer.close(); + writer = null; + for (let i = 0; i < 5; ++i) + await garbageCollect(); +}, 'Garbage-collecting a stream writer should not crash with close promise pending'); + +promise_test(async () => { + const ready = new WritableStream({ + write(chunk) { } + }, {highWaterMark: 0}).getWriter().ready; + for (let i = 0; i < 5; ++i) + await garbageCollect(); +}, 'Garbage-collecting a stream writer should not crash when backpressure is being applied'); + +// Repro for https://crbug.com/455800266 +promise_test(async () => { + // This logic is wrapped in a function to make it easy to garbage collect all + // references to the WritableStream. + const createWritableStream = async () => { + const WRITE_COUNT = 2; + let writes_done = 0; + const { promise, resolve } = Promise.withResolvers(); + + const ws = new WritableStream({ + write() { + if (writes_done === WRITE_COUNT) { + // Will never resolve, leaving the write operation pending. + return new Promise(resolve => { }); + } + ++writes_done; + return promise; + } + }); + + const writer = ws.getWriter(); + await writer.ready; + + const writeChunks = () => { + for (let i = 0; i < WRITE_COUNT; ++i) { + const ready = writer.ready; + writer.write("chunk"); + } + }; + + // Apply backpressure. + writeChunks(); + + // Release backpressure. + resolve(); + await writer.ready; + + // Apply backpressure again. + writeChunks(); + }; + + await createWritableStream(); + + for (let i = 0; i < 5; ++i) { + await garbageCollect(); + } +}, "WritableStream should not crash when garbage collected with backpressure"); diff --git a/test/fixtures/wpt/streams/writable-streams/garbage-collection.any.js b/test/fixtures/wpt/streams/writable-streams/garbage-collection.any.js new file mode 100644 index 00000000000000..5e39e7c60d2c33 --- /dev/null +++ b/test/fixtures/wpt/streams/writable-streams/garbage-collection.any.js @@ -0,0 +1,21 @@ +// META: global=window,worker,shadowrealm +// META: script=/common/gc.js +'use strict'; + +promise_test(async () => { + + let written = false; + const promise = (() => { + const rs = new WritableStream({ + write() { + written = true; + } + }); + const writer = rs.getWriter(); + return writer.write('something'); + })(); + await garbageCollect(); + await promise; + assert_true(written); + +}, 'A WritableStream and its writer should not be garbage collected while there is a write promise pending'); diff --git a/test/fixtures/wpt/versions.json b/test/fixtures/wpt/versions.json index d627791ed27e0c..68dde063450d82 100644 --- a/test/fixtures/wpt/versions.json +++ b/test/fixtures/wpt/versions.json @@ -68,7 +68,7 @@ "path": "resources" }, "streams": { - "commit": "bc9dcbbf1a4c2c741ef47f47d6ede6458f40c4a4", + "commit": "f8f26a372f357370846226ff64db1f9cc98a1edb", "path": "streams" }, "url": { diff --git a/test/parallel/test-buffer-indexof.js b/test/parallel/test-buffer-indexof.js index 21fcd78477ab06..2dd62c91e25cf6 100644 --- a/test/parallel/test-buffer-indexof.js +++ b/test/parallel/test-buffer-indexof.js @@ -691,3 +691,38 @@ assert.strictEqual(reallyLong.lastIndexOf(pattern), 0); assert.strictEqual(buf.includes('c'), true); } + +{ + const buf = Buffer.from('abcabc'); + + // Negative end should be treated as 0 (no match possible). + assert.strictEqual(buf.indexOf('a', 0, -1), -1); + assert.strictEqual(buf.indexOf('a', 0, -100), -1); + assert.strictEqual(buf.indexOf(0x61, 0, -1), -1); + assert.strictEqual(buf.lastIndexOf('a', 5, -1), -1); + assert.strictEqual(buf.lastIndexOf(0x61, 5, -1), -1); + assert.strictEqual(buf.includes('a', 0, -1), false); + assert.strictEqual(buf.indexOf(Buffer.from('a'), 0, -1), -1); + assert.strictEqual(buf.lastIndexOf(Buffer.from('a'), 5, -1), -1); + + // End = 0 means empty search range. + assert.strictEqual(buf.indexOf('a', 0, 0), -1); + assert.strictEqual(buf.indexOf(0x61, 0, 0), -1); + assert.strictEqual(buf.lastIndexOf('a', 5, 0), -1); + assert.strictEqual(buf.lastIndexOf(0x61, 5, 0), -1); + + // End greater than buffer length should be clamped. + assert.strictEqual(buf.indexOf('c', 0, 100), 2); + assert.strictEqual(buf.indexOf(0x63, 0, 100), 2); + assert.strictEqual(buf.lastIndexOf('c', 5, 100), 5); + assert.strictEqual(buf.lastIndexOf(0x63, 5, 100), 5); + assert.strictEqual(buf.indexOf(Buffer.from('c'), 0, 100), 2); + + // Empty needle with end parameter should clamp to search_end. + assert.strictEqual(buf.indexOf('', 0, 3), 0); + assert.strictEqual(buf.indexOf('', 5, 3), 3); + assert.strictEqual(buf.indexOf(Buffer.from(''), 5, 3), 3); + assert.strictEqual(buf.indexOf('', 0, 0), 0); + assert.strictEqual(buf.lastIndexOf('', 5, 3), 3); + assert.strictEqual(buf.lastIndexOf(Buffer.from(''), 5, 3), 3); +} diff --git a/test/parallel/test-crypto-argon2-job.js b/test/parallel/test-crypto-argon2-job.js new file mode 100644 index 00000000000000..34db5e4660c11c --- /dev/null +++ b/test/parallel/test-crypto-argon2-job.js @@ -0,0 +1,59 @@ +// Flags: --expose-internals --no-warnings +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const { hasOpenSSL } = require('../common/crypto'); + +if (!hasOpenSSL(3, 2)) + common.skip('requires OpenSSL >= 3.2'); + +// Exercises the native Argon2 job directly via internalBinding, bypassing +// the JS validators, to ensure that if invalid parameters ever reach the +// native layer they produce a clean error from the KDF rather than crashing, +// in both sync and async modes. + +const assert = require('node:assert'); +const { internalBinding } = require('internal/test/binding'); +const { + Argon2Job, + kCryptoJobAsync, + kCryptoJobSync, + kTypeArgon2id, +} = internalBinding('crypto'); + +const pass = Buffer.from('password'); +const salt = Buffer.alloc(16, 0x02); +const empty = Buffer.alloc(0); + +// Parameters that OpenSSL's Argon2 KDF rejects. +const badParams = [ + { lanes: 0, keylen: 32, memcost: 16, iter: 1 }, // lanes < 1 + { lanes: 1, keylen: 32, memcost: 0, iter: 1 }, // memcost == 0 + { lanes: 1, keylen: 32, memcost: 16, iter: 0 }, // iter == 0 +]; + +for (const { lanes, keylen, memcost, iter } of badParams) { + { + const job = new Argon2Job( + kCryptoJobSync, pass, salt, lanes, keylen, memcost, iter, + empty, empty, kTypeArgon2id); + const { 0: err, 1: result } = job.run(); + assert.ok(err); + assert.match(err.message, /Argon2 derivation failed/); + assert.strictEqual(result, undefined); + } + + { + const job = new Argon2Job( + kCryptoJobAsync, pass, salt, lanes, keylen, memcost, iter, + empty, empty, kTypeArgon2id); + job.ondone = common.mustCall((err, result) => { + assert.ok(err); + assert.match(err.message, /Argon2 derivation failed/); + assert.strictEqual(result, undefined); + }); + job.run(); + } +} diff --git a/test/parallel/test-crypto-dh-stateless.js b/test/parallel/test-crypto-dh-stateless.js index 86846a613c0a52..9e13c62595ef26 100644 --- a/test/parallel/test-crypto-dh-stateless.js +++ b/test/parallel/test-crypto-dh-stateless.js @@ -7,6 +7,21 @@ const assert = require('assert'); const crypto = require('crypto'); const { hasOpenSSL } = require('../common/crypto'); +// Error code for a key-type mismatch during (EC)DH. The underlying OpenSSL +// error code varies by version, and in OpenSSL 4.0 by platform: some builds +// report a generic internal error instead of a typed key-type mismatch. +// https://github.com/openssl/openssl/issues/30895 +// TODO(panva): Tighten this check once/if fixed. +let keyTypeMismatchCode; +if (hasOpenSSL(4, 0)) { + keyTypeMismatchCode = + /^ERR_OSSL_EVP_(OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE|INTERNAL_ERROR)$/; +} else if (hasOpenSSL(3)) { + keyTypeMismatchCode = 'ERR_OSSL_EVP_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE'; +} else { + keyTypeMismatchCode = 'ERR_OSSL_EVP_DIFFERENT_KEY_TYPES'; +} + assert.throws(() => crypto.diffieHellman(), { name: 'TypeError', code: 'ERR_INVALID_ARG_TYPE', @@ -397,9 +412,7 @@ test(crypto.generateKeyPairSync('x25519'), privateKey: crypto.generateKeyPairSync('x448').privateKey, publicKey: crypto.generateKeyPairSync('x25519').publicKey, }; - testDHError(options, { code: hasOpenSSL(3) ? - 'ERR_OSSL_EVP_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE' : - 'ERR_OSSL_EVP_DIFFERENT_KEY_TYPES' }); + testDHError(options, { code: keyTypeMismatchCode }); } // Test all key encoding formats @@ -541,23 +554,21 @@ for (const { privateKey: alicePriv, publicKey: bobPub } of [ testDHError({ privateKey: privKey(ec256.privateKey), publicKey: pubKey(x25519.publicKey), - }, { code: hasOpenSSL(3) ? - 'ERR_OSSL_EVP_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE' : - 'ERR_OSSL_EVP_DIFFERENT_KEY_TYPES' }); + }, { code: keyTypeMismatchCode }); // Unsupported key type (ed25519) testDHError({ privateKey: privKey(ed25519.privateKey), publicKey: pubKey(ed25519.publicKey), - }, { code: 'ERR_OSSL_EVP_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE' }); + }, { code: hasOpenSSL(4, 0) ? + /^ERR_OSSL_EVP_(OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE|INTERNAL_ERROR)$/ : + 'ERR_OSSL_EVP_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE' }); // Incompatible key types (x448 + x25519) testDHError({ privateKey: privKey(x448.privateKey), publicKey: pubKey(x25519.publicKey), - }, { code: hasOpenSSL(3) ? - 'ERR_OSSL_EVP_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE' : - 'ERR_OSSL_EVP_DIFFERENT_KEY_TYPES' }); + }, { code: keyTypeMismatchCode }); // Zero x25519 public key testDHError({ diff --git a/test/parallel/test-crypto-encap-decap.js b/test/parallel/test-crypto-encap-decap.js index c361fcc12e7b8b..38e24a7341713a 100644 --- a/test/parallel/test-crypto-encap-decap.js +++ b/test/parallel/test-crypto-encap-decap.js @@ -137,15 +137,11 @@ for (const [name, { publicKey: formatKeyAs(keyObjects.publicKey, { format: 'der', type: 'spki' }), privateKey: formatKeyAs(keyObjects.privateKey, { format: 'der', type: 'pkcs8' }) }, - ]; - - // TODO(@panva): ML-KEM does not have a JWK format defined yet, add once standardized - if (!keyObjects.privateKey.asymmetricKeyType.startsWith('ml')) { - keyPairs.push({ + { publicKey: formatKeyAs(keyObjects.publicKey, { format: 'jwk' }), privateKey: formatKeyAs(keyObjects.privateKey, { format: 'jwk' }) - }); - } + }, + ]; if (raw) { const { asymmetricKeyType } = keyObjects.privateKey; diff --git a/test/parallel/test-crypto-keygen.js b/test/parallel/test-crypto-keygen.js index e0515c15776fc6..7911520af34481 100644 --- a/test/parallel/test-crypto-keygen.js +++ b/test/parallel/test-crypto-keygen.js @@ -55,6 +55,20 @@ const { hasOpenSSL3 } = require('../common/crypto'); code: 'ERR_INVALID_ARG_VALUE', message: "The argument 'type' must be a supported key type. Received 'rsa2'" }); + + for (const type of ['toString', 'constructor']) { + assert.throws(() => generateKeyPairSync(type, {}), { + name: 'TypeError', + code: 'ERR_INVALID_ARG_VALUE', + message: `The argument 'type' must be a supported key type. Received '${type}'` + }); + + assert.throws(() => generateKeyPair(type, {}, common.mustNotCall()), { + name: 'TypeError', + code: 'ERR_INVALID_ARG_VALUE', + message: `The argument 'type' must be a supported key type. Received '${type}'` + }); + } } { diff --git a/test/parallel/test-crypto-pqc-key-objects-ml-dsa.js b/test/parallel/test-crypto-pqc-key-objects-ml-dsa.js index ddce614a56ea9b..413adf48686ede 100644 --- a/test/parallel/test-crypto-pqc-key-objects-ml-dsa.js +++ b/test/parallel/test-crypto-pqc-key-objects-ml-dsa.js @@ -26,6 +26,7 @@ for (const [asymmetricKeyType, pubLen] of [ private: fixtures.readKey(getKeyFileName(asymmetricKeyType, 'private'), 'ascii'), private_seed_only: fixtures.readKey(getKeyFileName(asymmetricKeyType, 'private_seed_only'), 'ascii'), private_priv_only: fixtures.readKey(getKeyFileName(asymmetricKeyType, 'private_priv_only'), 'ascii'), + jwk: JSON.parse(fixtures.readKey(`${asymmetricKeyType}.json`)), }; function assertJwk(jwk) { @@ -79,10 +80,6 @@ for (const [asymmetricKeyType, pubLen] of [ key.export({ format: 'der', type: 'pkcs8' }); if (hasSeed) { assert.strictEqual(key.export({ format: 'pem', type: 'pkcs8' }), keys.private_seed_only); - } else { - assert.strictEqual(key.export({ format: 'pem', type: 'pkcs8' }), keys.private_priv_only); - } - if (hasSeed) { const jwk = key.export({ format: 'jwk' }); assertPrivateJwk(jwk); assert.strictEqual(key.equals(createPrivateKey({ format: 'jwk', key: jwk })), true); @@ -97,6 +94,7 @@ for (const [asymmetricKeyType, pubLen] of [ }); assert.strictEqual(importedPriv.equals(key), true); } else { + assert.strictEqual(key.export({ format: 'pem', type: 'pkcs8' }), keys.private_priv_only); assert.throws(() => key.export({ format: 'jwk' }), { code: 'ERR_CRYPTO_OPERATION_FAILED', message: 'key does not have an available seed' }); } @@ -128,21 +126,12 @@ for (const [asymmetricKeyType, pubLen] of [ assert.strictEqual(pubFromPriv.equals(publicKey), true); } } - } -} -{ - const format = 'jwk'; - const jwk = { - kty: 'AKP', - alg: 'ML-DSA-44', - priv: '9_uqvxH0WKJFgfLyse1a1des2bwPgsHctl_jCt5AfEo', - // eslint-disable-next-line @stylistic/js/max-len - pub: 'SXghXj9P-DJ5eznNa_zLJxRxpa0mt86WlIid0EVEv1qraLBkC1UKevSZrjtgo1QUEN0oa3tP-HyYj8Onnc1zEnxsSoeC5A-PgywKgYuZP581wGPS-cbA2-5acsg-YUi_9fDkLR5YOTQQ3Iu952K1m8w0QDIBxZjecm32HgkD56CCC6ZyBOwfx9qcNUeO0aImya1igzL2_LRsqomogl9OuduWhtussAavGlAK7ZR4_4lmyjWcdeIc-z--iy42biV5d_tnopfNTJFlycBKinZu3h0lr4-ldl6apGDIyvSdZulhgj_j6jgEX-AgQZgS93ctx680GvROkBL7YI_3iXW3REWzVgS9HLasagEi2h6-RYQ9RzgUTODbei5fNRj3bNSqr8IKTZ08DCsRasN61TGwE3F7meoauw2NYkV51mhTxIafwhLWJrRA4C09Y-afrOtqk6a7Fiy21ObP95TGujXwThuwQSjKcUzTdCbD94ERhleZLqnPYEpb6_Jcc1OBY3kUJvCjoUhXwbW6PhWr533JDEFHoNCkPfhHS7vVCUFx4mQASkPLBud5arFSZU1uDStuiftJXfnQTWaMoJeA1N6rywB3xwLH__lHZQwEh4KnuYVuCeOMU1t8inuHI4EpZ4iTi2LrL0Cl6HadpHv-GENYwuPDVq9qg2Mo75o1X6wpPSN1J5KUDAATyR_0hurg4A1DlVpVWtykP5YWEmx_g5w4MZfEVwH-JJjEhJRxLKajxrjfG4XlnwwxPTznr1k1Mb7getKbLSbiMA3fvAgl1IjBIB8eFaISauFPpLPSpKHCVZrQYPIKSxMVdlXHgwm3CRrkR29GevCM5iSwRHQK6_HWfIQIlJ8H7uVQqXkNMvNmFldnfi3dj-oY6wMhs1ffP4RAsb5UTljvhJc6GoygBL_b0rv4aKcywDF8wa2P6B8gGFl6cVvWBQmWLJ-HL5RR68J_OmvJUm3PD-wwX3YigStd6thdTNlHOZhl4ysn8ulkFY3Rz2jEBV_nO6EXBLdOmxn_yX77qQ9yPcE64uC8iDTFWpQU1gmOF38od96oYD-T-whVl1NLD2bOvFVdd4UmWpb3Ui8AYzKzFBHNczAogQfplFmr8VABsgtWk8hW8csam70NADWK54SZOPQHeiOt1Mb488OZiDpX0FifEnCac78C_5uEiOPa8FAHpgUJ_XeXg83doDsAvrE1ZkrgFDwzT5pUTLyqh9eI1PAQKCoKmAbofcZM65o_qGvmnpN8fGVudOoHb0_Dqu_E2RBbaLcxsIe5jWmGmth4sb_4ANLFCmtt8T8sDmOdcYtQmhpUzg6rjDqeU76yq6fC15bGjT6Qc-EAgrUftFVwLw2UIGAbHmeAyFbSuJiMaUDJeYoZ3zxoID_DRCP9kN3ty-EQMHM9BZgXlH9dJ8ZUCAeH59h4PinM5LQuiS_kvP1iyT1Be6sYglV2dB7W_AziOcrrBiLfjazbUUpwqLm4_Yt_QwYJrAWfYyFOxkKxkT5qi2c1RNGBtiYjv8X_TRJDg0uxX_Hbq0Pc7yYezFQdFYIlRAvJcnc8PiOfhWtQZpCIYKTskg_Y2UfVvjydXYcGuIA2700PzR9ga-1VR7mv9UOLHe2x4ALiOO7Iz6KgOfVYMJ9dIC7f4HY9nrnLdhfKw5dcIf7RDhDqrkPyz8LLTuAuO-hGSwoP35XFkf0FQ8f1Cg5J_k-S3S2dCj8DXIRLcEJ9Qb5zvDofIPSmNKlvwJkqplDLBWlAow' - }; + // JWK import error tests + const format = 'jwk'; + const jwk = keys.jwk; - if (hasOpenSSL(3, 5)) { - assert.throws(() => createPrivateKey({ format, key: { ...jwk, alg: 'ml-dsa-44' } }), + assert.throws(() => createPrivateKey({ format, key: { ...jwk, alg: asymmetricKeyType } }), { code: 'ERR_CRYPTO_INVALID_JWK' }); assert.throws(() => createPrivateKey({ format, key: { ...jwk, alg: undefined } }), { code: 'ERR_CRYPTO_INVALID_JWK' }); @@ -153,10 +142,10 @@ for (const [asymmetricKeyType, pubLen] of [ assert.throws(() => createPrivateKey({ format, key: { ...jwk, priv: Buffer.alloc(33).toString('base64url') } }), { code: 'ERR_CRYPTO_INVALID_JWK' }); // eslint-disable-next-line @stylistic/js/max-len - assert.throws(() => createPublicKey({ format, key: { kty: jwk.kty, alg: jwk.alg, pub: Buffer.alloc(1313).toString('base64url') } }), + assert.throws(() => createPublicKey({ format, key: { kty: jwk.kty, alg: jwk.alg, pub: Buffer.alloc(pubLen + 1).toString('base64url') } }), { code: 'ERR_CRYPTO_INVALID_JWK' }); - // Importing an ML-DSA private JWK where pub does not match priv should fail. + // Importing a private JWK where pub does not match priv should fail. assert.throws( () => createPrivateKey({ format, @@ -165,43 +154,8 @@ for (const [asymmetricKeyType, pubLen] of [ { code: 'ERR_CRYPTO_INVALID_JWK' } ); - assert.ok(createPrivateKey({ format, key: jwk })); - assert.ok(createPublicKey({ format, key: jwk })); - - // Test vectors from ietf-cose-dilithium - { - for (const jwk of [ - { - kty: 'AKP', - alg: 'ML-DSA-44', - // eslint-disable-next-line @stylistic/js/max-len - pub: 'unH59k4RuutY-pxvu24U5h8YZD2rSVtHU5qRZsoBmBMcRPgmu9VuNOVdteXi1zNIXjnqJg_GAAxepLqA00Vc3lO0bzRIKu39VFD8Lhuk8l0V-cFEJC-zm7UihxiQMMUEmOFxe3x1ixkKZ0jqmqP3rKryx8tSbtcXyfea64QhT6XNje2SoMP6FViBDxLHBQo2dwjRls0k5a-XSQSu2OTOiHLoaWsLe8pQ5FLNfTDqmkrawDEdZyxr3oSWJAsHQxRjcIiVzZuvwxYy1zl2STiP2vy_fTBaPemkleynQzqPg7oPCyXEE8bjnJbrfWkbNNN8438e6tHPIX4l7zTuzz98YPhLjt_d6EBdT4MldsYe-Y4KLyjaGHcAlTkk9oa5RhRwW89T0z_t1DSO3dvfKLUGXh8gd1BD6Fz5MfgpF5NjoafnQEqDjsAAhrCXY4b-Y3yYJEdX4_dp3dRGdHG_rWcPmgX4JG7lCnser4f8QGnDriqiAzJYEXeS8LzUngg_0bx0lqv_KcyU5IaLISFO0xZSU5mmEPvdSoDnyAcV8pV44qhLtAvd29n0ehG259oRihtljTWeiu9V60a1N2tbZVl5mEqSK-6_xZvNYA1TCdzNctvweH24unV7U3wer9XA9Q6kvJWDVJ4oKaQsKMrCSMlteBJMRxWbGK7ddUq6F7GdQw-3j2M-qdJvVKm9UPjY9rc1lPgol25-oJxTu7nxGlbJUH-4m5pevAN6NyZ6lfhbjWTKlxkrEKZvQXs_Yf6cpXEwpI_ZJeriq1UC1XHIpRkDwdOY9MH3an4RdDl2r9vGl_IwlKPNdh_5aF3jLgn7PCit1FNJAwC8fIncAXgAlgcXIpRXdfJk4bBiO89GGccSyDh2EgXYdpG3XvNgGWy7npuSoNTE7WIyblAk13UQuO4sdCbMIuriCdyfE73mvwj15xgb07RZRQtFGlFTmnFcIdZ90zDrWXDbANntv7KCKwNvoTuv64bY3HiGbj-NQ-U9eMylWVpvr4hrXcES8c9K3PqHWADZC0iIOvlzFv4VBoc_wVflcOrL_SIoaNFCNBAZZq-2v5lAgpJTqVOtqJ_HVraoSfcKy5g45p-qULunXj6Jwq21fobQiKubBKKOZwcJFyJD7F4ACKXOrz-HIvSHMCWW_9dVrRuCpJw0s0aVFbRqopDNhu446nqb4_EDYQM1tTHMozPd_jKxRRD0sH75X8ZoToxFSpLBDbtdWcenxj-zBf6IGWfZnmaetjKEBYJWC7QDQx1A91pJVJCEgieCkoIfTqkeQuePpIyu48g2FG3P1zjRF-kumhUTfSjo5qS0YiZQy0E1BMs6M11EvuxXRsHClLHoy5nLYI2Sj4zjVjYyxSHyPRPGGo9hwB34yWxzYNtPPGiqXS_dNCpi_zRZwRY4lCGrQ-hYTEWIK1Dm5OlttvC4_eiQ1dv63NiGkLRJ5kJA3bICN0fzCDY-MBqnd1cWn8YVBijVkgtaoascjL9EywDgJdeHnXK0eeOvUxHHhXJVkNqcibn8O4RQdpVU60TSA-uiu675ytIjcBHC6kTv8A8pmkj_4oypPd-F92YIJC741swkYQoeIHj8rE-ThcMUkF7KqC5VORbZTRp8HsZSqgiJcIPaouuxd1-8Rxrid3fXkE6p8bkrysPYoxWEJgh7ZFsRCPDWX-yTeJwFN0PKFP1j0F6YtlLfK5wv-c4F8ZQHA_-yc_gODicy7KmWDZgbTP07e7gEWzw4MFRrndjbDQ', - priv: 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' - }, - { - kty: 'AKP', - alg: 'ML-DSA-65', - // eslint-disable-next-line @stylistic/js/max-len - pub: 'QksvJn5Y1bO0TXGs_Gpla7JpUNV8YdsciAvPof6rRD8JQquL2619cIq7w1YHj22ZolInH-YsdAkeuUr7m5JkxQqIjg3-2AzV-yy9NmfmDVOevkSTAhnNT67RXbs0VaJkgCufSbzkLudVD-_91GQqVa3mk4aKRgy-wD9PyZpOMLzP-opHXlOVOWZ067galJN1h4gPbb0nvxxPWp7kPN2LDlOzt_tJxzrfvC1PjFQwNSDCm_l-Ju5X2zQtlXyJOTZSLQlCtB2C7jdyoAVwrftUXBFDkisElvgmoKlwBks23fU0tfjhwc0LVWXqhGtFQx8GGBQ-zol3e7P2EXmtIClf4KbgYq5u7Lwu848qwaItyTt7EmM2IjxVth64wHlVQruy3GXnIurcaGb_qWg764qZmteoPl5uAWwuTDX292Sa071S7GfsHFxue5lydxIYvpVUu6dyfwuExEubCovYMfz_LJd5zNTKMMatdbBJg-Qd6JPuXznqc1UYC3CccEXCLTOgg_auB6EUdG0b_cy-5bkEOHm7Wi4SDipGNig_ShzUkkot5qSqPZnd2I9IqqToi_0ep2nYLBB3ny3teW21Qpccoom3aGPt5Zl7fpzhg7Q8zsJ4sQ2SuHRCzgQ1uxYlFx21VUtHAjnFDSoMOkGyo4gH2wcLR7-z59EPPNl51pljyNefgCnMSkjrBPyz1wiET-uqi23f8Bq2TVk1jmUFxOwdfLsU7SIS30WOzvwD_gMDexUFpMlEQyL1-Y36kaTLjEWGCi2tx1FTULttQx5JpryPW6lW5oKw5RMyGpfRliYCiRyQePYqipZGoxOHpvCWhCZIN4meDY7H0RxWWQEpiyCzRQgWkOtMViwao6Jb7wZWbLNMebwLJeQJXWunk-gTEeQaMykVJobwDUiX-E_E7fSybVRTZXherY1jrvZKh8C5Gi5VADg5Vs319uN8-dVILRyOOlvjjxclmsRcn6HEvTvxd9MS7lKm2gI8BXIqhzgnTdqNGwTpmDHPV8hygqJWxWXCltBSSgY6OkGkioMAmXjZjYq_Ya9o6AE7WU_hUdm-wZmQLExwtJWEIBdDxrUxA9L9JL3weNyQtaGItPjXcheZiNBBbJTUxXwIYLnXtT1M0mHzMqGFFWXVKsN_AIdHyv4yDzY9m-tuQRfbQ_2K7r5eDOL1Tj8DZ-s8yXG74MMBqOUvlglJNgNcbuPKLRPbSDoN0E3BYkfeDgiUrXy34a5-vU-PkAWCsgAh539wJUUBxqw90V1Du7eTHFKDJEMSFYwusbPhEX4ZTwoeTHg--8Ysn4HCFWLQ00pfBCteqvMvMflcWwVfTnogcPsJb1bEFVSc3nTzhk6Ln8J-MplyS0Y5mGBEtVko_WlyeFsoDCWj4hqrgU7L-ww8vsCRSQfskH8lodiLzj0xmugiKjWUXbYq98x1zSnB9dmPy5P3UNwwMQdpebtR38N9I-jup4Bzok0-JsaOe7EORZ8ld7kAgDWa4K7BAxjc2eD540Apwxs-VLGFVkXbQgYYeDNG2tW1Xt20-XezJqZVUl6-IZXsqc7DijwNInO3fT5o8ZAcLKUUlzSlEXe8sIlHaxjLoJ-oubRtlKKUbzWOHeyxmYZSxYqQhSQj4sheedGXJEYWJ-Y5DRqB-xpy-cftxL10fdXIUhe1hWFBAoQU3b5xRY8KCytYnfLhsFF4O49xhnax3vuumLpJbCqTXpLureoKg5PvWfnpFPB0P-ZWQN35mBzqbb3ZV6U0rU55DvyXTuiZOK2Z1TxbaAd1OZMmg0cpuzewgueV-Nh_UubIqNto5RXCd7vqgqdXDUKAiWyYegYIkD4wbGMqIjxV8Oo2ggOcSj9UQPS1rD5u0rLckAzsxyty9Q5JsmKa0w8Eh7Jwe4Yob4xPVWWbJfm916avRgzDxXo5gmY7txdGFYHhlolJKdhBU9h6f0gtKEtbiUzhp4IWsqAR8riHQs7lLVEz6P537a4kL1r5FjfDf_yjJDBQmy_kdWMDqaNln-MlKK8eENjUO-qZGy0Ql4bMZtNbHXjfJUuSzapA-RqYfkqSLKgQUOW8NTDKhUk73yqCU3TQqDEKaGAoTsPscyMm7u_8QrvUK8kbc-XnxrWZ0BZJBjdinzh2w-QvjbWQ5mqFp4OMgY94__tIU8vvCUNJiYA1RdyodlfPfH5-avpxOCvBD6C7ZIDyQ-6huGEQEAb6DP8ydWIZQ8xY603DoEKKXkJWcP6CJo3nHFEdj_vcEbDQ-WESDpcQFa1fRIiGuALj-sEWcjGdSHyE8QATOcuWl4TLVzRPKAf4tCXx1zyvhJbXQu0jf0yfzVpOhPun4n-xqK4SxPBCeuJOkQ2VG9jDXWH4pnjbAcrqjveJqVti7huMXTLGuqU2uoihBw6mGqu_WSlOP2-XTEyRyvxbv2t-z9V6GPt1V9ceBukA0oGwtJqgD-q7NXFK8zhw7desI5PZMXf3nuVgbJ3xdvAlzkmm5f9RoqQS6_hqwPQEcclq1MEZ3yML5hc99TDtZWy9gGkhR0Hs3QJxxgP7bEqGFP-HjTPnJsrGaT6TjKP7qCxJlcFKLUr5AU_kxMULeUysWWtSGJ9mpxBvsyW1Juo', - priv: 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' - }, - { - kty: 'AKP', - alg: 'ML-DSA-87', - // eslint-disable-next-line @stylistic/js/max-len - pub: '5F_8jMc9uIXcZi5ioYzY44AylxF_pWWIFKmFtf8dt7Roz8gruSnx2Gt37RT1rhamU2h3LOUZEkEBBeBFaXWukf22Q7US8STV5gvWi4x-Mf4Bx7DcZa5HBQHMVlpuHfz8_RJWVDPEr-3VEYIeLpYQxFJ14oNt7jXO1p1--mcv0eQxi-9etuiX6LRRqiAt7QQrKq73envj9pkUbaIpqL2z_6SWRFln51IXv7yQSPmVZEPYcx-DPrMN4Q2slv_-fPZeoERcPjHoYB4TO-ahAHZP4xluJncmRB8xdR-_mm9YgGRPTnJ15X3isPEF5NsFXVDdHJyTT931NbjeKLDHTARJ8iLNLtC7j7x3XM7oyUBmW0D3EvT34AdQ6eHkzZz_JdGUXD6bylPM1PEu7nWBhW69aPJoRZVuPnvrdh8P51vdMb_i-gGBEzl7OHvVnWKmi4r3-iRauTLmn3eOLO79ITBPu4CZ6hPY6lfBgTGXovda4lEHW1Ha04-FNmnp1fmKNlUJiUGZOhWUhg-6cf5TDuXCn1jyl4r2iMy3Wlg4o1nBEumOJahYOsjawfhh_Vjir7pd5aUuAgkE9bQrwIdONb788-YRloR2jzbgCPBHEhd86-YnYHOB5W6q7hYcFym43lHb3kdNSMxoJJ6icWK4eZPmDITtbMZCPLNnbZ61CyyrWjoEnvExOB1iP6b7y8nbHnzAJeoEGLna0sxszU6V-izsJP7spwMYp1Fxa3IT9j7b9lpjM4NX-Dj5TsBxgiwkhRJIiFEHs9HE6SRnjHYU6hrwOBBGGfKuNylAvs-mninLtf9sPiCke-Sk90usNMEzwApqcGrMxv_T2OT71pqZcE4Sg8hQ2MWNHldTzZWHuDxMNGy5pYE3IT7BCDTGat_iu1xQGo7y7K3Rtnej3xpt64br8HIsT1Aw4g-QGN1bb8U-6iT9kre1tAJf6umW0-SP1MZQ2C261-r5NmOWmFEvJiU9LvaEfIUY6FZcyaVJXG__V83nMjiCxUp9tHCrLa-P_Sv3lPp8aS2ef71TLuzB14gOLKCzIWEovii0qfHRUfrJeAiwvZi3tDphKprIZYEr_qxvR0YCd4QLUqOwh_kWynztwPdo6ivRnqIRVfhLSgTEAArSrgWHFU1WC8Ckd6T5MpqJhN0x6x8qBePZGHAdYwz8qa9h7wiNLFWBrLRj5DmQLl1CVxnpVrjW33MFso4P8n060N4ghdKSSZsZozkNQ5b7O6yajYy-rSp6QpD8msb8oEX5imFKRaOcviQ2D4TRT45HJxKs63Tb9FtT1JoORzfkdv_E1bL3zSR6oYbTt2Stnpz-7kVqc8KR2N45EkFKxDkRw3IXOte0cq81xoU87S_ntf4KiVZaszuqb2XN2SgxnXBl4EDnpehPmqkD92SAlLrQcTaxaSe47G28K-8MwoVt4eeVkj4UEsSfJN7rbCH2yKl2XJx5huDaS0xn2ODQyNRmgk-5I9hXMUiZDNLvEzx4zuyrcu2d0oXFo3ZoUtVFNCB__TQCf2x27ej9GjLXLDAEi7qnl9Xfb94n0IfeVyGte3-j6NP3DWv8OrLiUjNTaLv6Fay1yzfUaU6LI86-Jd6ckloiGhg7kE0_hd-ZKakZxU1vh0Vzc6DW7MFAPky75iCZlDXoBpZjTNGo5HR-mCW_ozblu60U9zZA8bn-voANuu_hYwxh-uY1sHTFZOqp2xicnnMChz_GTm1Je8XCkICYegeiHUryEHA6T6B_L9gW8S_R4ptMD0Sv6b1KHqqKeubwKltCWPUsr2En9iYypnz06DEL5Wp8KMhrLid2AMPpLI0j1CWGJExXHpBWjfIC8vbYH4YKVl-euRo8eDcuKosb5hxUGM9Jvy1siVXUpIKpkZt2YLP5pEBP_EVOoHPh5LJomrLMpORr1wBKbEkfom7npX1g817bK4IeYmZELI8zXUUtUkx3LgNTckwjx90Vt6oVXpFEICIUDF_LAVMUftzz6JUvbwOZo8iAZqcnVslAmRXeY_ZPp5eEHFfHlsb8VQ73Rd_p8XlFf5R1WuWiUGp2TzJ-VQvj3BTdQfOwSxR9RUk4xjqNabLqTFcQ7As246bHJXH6XVnd4DbEIDPfNa8FaWb_DNEgQAiXGqa6n7l7aFq5_6Kp0XeBBM0sOzJt4fy8JC6U0DEcMnWxKFDtMM7q06LubQYFCEEdQ5b1Qh2LbQZ898tegmeF--EZ4F4hvYebZPV8sM0ZcsKBXyCr585qs00PRxr0S6rReekGRBIvXzMojmid3dxc6DPpdV3x5zxlxaIBxO3i_6axknSSdxnS04_bemWqQ3CLf6mpSqfTIQJT1407GB4QINAAC9Ch3AXUR_n1jr64TGWzbIr8uDcnoVCJlOgmlXpmOwubigAzJattbWRi7k4QYBnA3_4QMjt73n2Co4-F_Qh4boYLpmwWG2SwcIw2PeXGr2LY2zwkPR4bcSyx1Z6UK5trQpWlpQCxgsvV_RvGzpN22RtHoihPH74K0cBIzCz7tK-jqeuWl1A7af7KmQ66fpRBr5ykTLOsa17WblkcIB_jDvqKfEcdxhPWJUwmOo4TIQS-xH8arLOy_NQFG2m14_yxwUemXC-QxLUYi6_FIcqwPBKjCdpQtadRdyftQSKO0SP-GxUvamMZzWI780rXuOBkq5kyYLy9QF9bf_-bL6QLpe1WMCQlOeXZaCPoncgYoT0WZ17jB52Xb2lPWsyXYK54npszkbKJ4OIqfvF8xqRXcVe22VwJuqT9Uy4-4KKQgQ7TXla7Gdm2H7mKl8YXQlsGCT2Ypc8O4t0Sfw7qYAuaDGf752Hbm3fl1bupcB2huIPlIaDP6IRR9XvTYIW2flbwYfhKLmoVKnG85uUi2qtqCjPOIuU3-peT0othfmwKQXaoOqO-V4r6wPL1VHxVFtIYmEdVt0RccUOvpOVR_OAHG9uHOzTmueK5557Qxp0ojtZCHyN-hgoMZJLrvdKkTCxPNo2-mZQbHoVh2FnThZ9JbO49dB8lKXP4_MU5xAnjXMgKXtbfI8w6ZWATE_XWgf2VQMUpGp4wpy44yWQTxHxh_4T9540BGwG0FU0bkgrwA_erseGZnepqdmz5_ScCs84O5Xr5MbYhJLCGGxY6O5GqS-ooB2w0Mt87KbbE4bpYje9CAHH8FX3pDrJyLsyasA3zxmk4OmGpG7Z70ofONJtHRe56R5287vFmuazEEutXn81kNzB-3aJT1ga3vnWZw4CSvFKoWYSA7auLgrHSHFZdITfOrgtmQmGbFhM9kSBdY1UCnpzf65oos3PZWRa2twfUxxLAnPNtrxpRGyvtsapw7ljUagZmuyh3hLCjhAxYmnoE1dbyIWvpCqSlEtVjL1yb_nuLEzgvmZuV02fHxGuWgHTOMVGXpf81Rce3eoBK3lapW1wkzezlk3tcA2bZOtA9qbxdsbVR37kemzQ9K1e3Y0OWhtSj', - priv: 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' - }, - ]) { - assert.partialDeepStrictEqual(jwk, createPublicKey({ format, key: jwk }).export({ format: 'jwk' })); - assert.deepStrictEqual(createPrivateKey({ format, key: jwk }).export({ format: 'jwk' }), jwk); - } - - } - } else { - assert.throws(() => createPrivateKey({ format, key: jwk }), - { code: 'ERR_INVALID_ARG_VALUE', message: /Unsupported key type/ }); - assert.throws(() => createPublicKey({ format, key: jwk }), - { code: 'ERR_INVALID_ARG_VALUE', message: /Unsupported key type/ }); + // JWK round-trip + assert.partialDeepStrictEqual(jwk, createPublicKey({ format, key: jwk }).export({ format })); + assert.deepStrictEqual(createPrivateKey({ format, key: jwk }).export({ format }), jwk); } } diff --git a/test/parallel/test-crypto-pqc-key-objects-ml-kem.js b/test/parallel/test-crypto-pqc-key-objects-ml-kem.js index 0f4b226604c965..0c344ed100c2da 100644 --- a/test/parallel/test-crypto-pqc-key-objects-ml-kem.js +++ b/test/parallel/test-crypto-pqc-key-objects-ml-kem.js @@ -18,14 +18,35 @@ function getKeyFileName(type, suffix) { return `${type.replaceAll('-', '_')}_${suffix}.pem`; } -for (const asymmetricKeyType of ['ml-kem-512', 'ml-kem-768', 'ml-kem-1024']) { +for (const [asymmetricKeyType, pubLen] of [ + ['ml-kem-512', 800], ['ml-kem-768', 1184], ['ml-kem-1024', 1568], +]) { const keys = { public: fixtures.readKey(getKeyFileName(asymmetricKeyType, 'public'), 'ascii'), private: fixtures.readKey(getKeyFileName(asymmetricKeyType, 'private'), 'ascii'), private_seed_only: fixtures.readKey(getKeyFileName(asymmetricKeyType, 'private_seed_only'), 'ascii'), private_priv_only: fixtures.readKey(getKeyFileName(asymmetricKeyType, 'private_priv_only'), 'ascii'), + jwk: JSON.parse(fixtures.readKey(`${asymmetricKeyType}.json`)), }; + function assertJwk(jwk) { + assert.strictEqual(jwk.kty, 'AKP'); + assert.strictEqual(jwk.alg, asymmetricKeyType.toUpperCase()); + assert.ok(jwk.pub); + assert.strictEqual(Buffer.from(jwk.pub, 'base64url').byteLength, pubLen); + } + + function assertPublicJwk(jwk) { + assertJwk(jwk); + assert.ok(!jwk.priv); + } + + function assertPrivateJwk(jwk) { + assertJwk(jwk); + assert.ok(jwk.priv); + assert.strictEqual(Buffer.from(jwk.priv, 'base64url').byteLength, 64); + } + function assertKey(key) { assert.deepStrictEqual(key.asymmetricKeyDetails, {}); assert.strictEqual(key.asymmetricKeyType, asymmetricKeyType); @@ -38,12 +59,14 @@ for (const asymmetricKeyType of ['ml-kem-512', 'ml-kem-768', 'ml-kem-1024']) { assert.strictEqual(key.type, 'public'); assert.strictEqual(key.export({ format: 'pem', type: 'spki' }), keys.public); key.export({ format: 'der', type: 'spki' }); - assert.throws(() => key.export({ format: 'jwk' }), - { code: 'ERR_CRYPTO_JWK_UNSUPPORTED_KEY_TYPE', message: 'Unsupported JWK Key Type.' }); + const jwk = key.export({ format: 'jwk' }); + assertPublicJwk(jwk); + assert.strictEqual(key.equals(createPublicKey({ format: 'jwk', key: jwk })), true); // Raw format round-trip const rawPub = key.export({ format: 'raw-public' }); assert(Buffer.isBuffer(rawPub)); + assert.strictEqual(rawPub.byteLength, pubLen); const importedPub = createPublicKey({ key: rawPub, format: 'raw-public', asymmetricKeyType, }); @@ -57,19 +80,24 @@ for (const asymmetricKeyType of ['ml-kem-512', 'ml-kem-768', 'ml-kem-1024']) { key.export({ format: 'der', type: 'pkcs8' }); if (hasSeed) { assert.strictEqual(key.export({ format: 'pem', type: 'pkcs8' }), keys.private_seed_only); + const jwk = key.export({ format: 'jwk' }); + assertPrivateJwk(jwk); + assert.strictEqual(key.equals(createPrivateKey({ format: 'jwk', key: jwk })), true); + assert.ok(createPublicKey({ format: 'jwk', key: jwk })); // Raw seed round-trip const rawSeed = key.export({ format: 'raw-seed' }); assert(Buffer.isBuffer(rawSeed)); + assert.strictEqual(rawSeed.byteLength, 64); const importedPriv = createPrivateKey({ key: rawSeed, format: 'raw-seed', asymmetricKeyType, }); assert.strictEqual(importedPriv.equals(key), true); } else { assert.strictEqual(key.export({ format: 'pem', type: 'pkcs8' }), keys.private_priv_only); + assert.throws(() => key.export({ format: 'jwk' }), + { code: 'ERR_CRYPTO_OPERATION_FAILED', message: 'key does not have an available seed' }); } - assert.throws(() => key.export({ format: 'jwk' }), - { code: 'ERR_CRYPTO_JWK_UNSUPPORTED_KEY_TYPE', message: 'Unsupported JWK Key Type.' }); } if (!hasOpenSSL(3, 5)) { @@ -98,5 +126,36 @@ for (const asymmetricKeyType of ['ml-kem-512', 'ml-kem-768', 'ml-kem-1024']) { assert.strictEqual(pubFromPriv.equals(publicKey), true); } } + + // JWK import error tests + const format = 'jwk'; + const jwk = keys.jwk; + + assert.throws(() => createPrivateKey({ format, key: { ...jwk, alg: asymmetricKeyType } }), + { code: 'ERR_CRYPTO_INVALID_JWK' }); + assert.throws(() => createPrivateKey({ format, key: { ...jwk, alg: undefined } }), + { code: 'ERR_CRYPTO_INVALID_JWK' }); + assert.throws(() => createPrivateKey({ format, key: { ...jwk, pub: undefined } }), + { code: 'ERR_CRYPTO_INVALID_JWK' }); + assert.throws(() => createPrivateKey({ format, key: { ...jwk, priv: undefined } }), + { code: 'ERR_CRYPTO_INVALID_JWK', message: /JWK does not contain private key material/ }); + assert.throws(() => createPrivateKey({ format, key: { ...jwk, priv: Buffer.alloc(65).toString('base64url') } }), + { code: 'ERR_CRYPTO_INVALID_JWK' }); + // eslint-disable-next-line @stylistic/js/max-len + assert.throws(() => createPublicKey({ format, key: { kty: jwk.kty, alg: jwk.alg, pub: Buffer.alloc(pubLen + 1).toString('base64url') } }), + { code: 'ERR_CRYPTO_INVALID_JWK' }); + + // Importing a private JWK where pub does not match priv should fail. + assert.throws( + () => createPrivateKey({ + format, + key: { ...jwk, pub: `${jwk.pub[0] === 'A' ? 'B' : 'A'}${jwk.pub.slice(1)}` }, + }), + { code: 'ERR_CRYPTO_INVALID_JWK' } + ); + + // JWK round-trip + assert.partialDeepStrictEqual(jwk, createPublicKey({ format, key: jwk }).export({ format })); + assert.deepStrictEqual(createPrivateKey({ format, key: jwk }).export({ format }), jwk); } } diff --git a/test/parallel/test-crypto-pqc-key-objects-slh-dsa.js b/test/parallel/test-crypto-pqc-key-objects-slh-dsa.js index fdae27f2da797f..98af15dc795f8b 100644 --- a/test/parallel/test-crypto-pqc-key-objects-slh-dsa.js +++ b/test/parallel/test-crypto-pqc-key-objects-slh-dsa.js @@ -26,8 +26,28 @@ for (const asymmetricKeyType of [ const keys = { public: fixtures.readKey(getKeyFileName(asymmetricKeyType, 'public'), 'ascii'), private: fixtures.readKey(getKeyFileName(asymmetricKeyType, 'private'), 'ascii'), + jwk: JSON.parse(fixtures.readKey(`${asymmetricKeyType}.json`)), }; + function assertJwk(jwk) { + assert.strictEqual(jwk.kty, 'AKP'); + // SLH-DSA algorithm names keep the last character (f/s) lowercase. + const expectedAlg = asymmetricKeyType.slice(0, -1).toUpperCase() + + asymmetricKeyType.slice(-1); + assert.strictEqual(jwk.alg, expectedAlg); + assert.ok(jwk.pub); + } + + function assertPublicJwk(jwk) { + assertJwk(jwk); + assert.ok(!jwk.priv); + } + + function assertPrivateJwk(jwk) { + assertJwk(jwk); + assert.ok(jwk.priv); + } + function assertKey(key) { assert.deepStrictEqual(key.asymmetricKeyDetails, {}); assert.strictEqual(key.asymmetricKeyType, asymmetricKeyType); @@ -40,8 +60,9 @@ for (const asymmetricKeyType of [ assert.strictEqual(key.type, 'public'); assert.strictEqual(key.export({ format: 'pem', type: 'spki' }), keys.public); key.export({ format: 'der', type: 'spki' }); - assert.throws(() => key.export({ format: 'jwk' }), - { code: 'ERR_CRYPTO_JWK_UNSUPPORTED_KEY_TYPE', message: 'Unsupported JWK Key Type.' }); + const jwk = key.export({ format: 'jwk' }); + assertPublicJwk(jwk); + assert.strictEqual(key.equals(createPublicKey({ format: 'jwk', key: jwk })), true); // Raw format round-trip const rawPub = key.export({ format: 'raw-public' }); @@ -58,8 +79,10 @@ for (const asymmetricKeyType of [ assertPublicKey(createPublicKey(key)); key.export({ format: 'der', type: 'pkcs8' }); assert.strictEqual(key.export({ format: 'pem', type: 'pkcs8' }), keys.private); - assert.throws(() => key.export({ format: 'jwk' }), - { code: 'ERR_CRYPTO_JWK_UNSUPPORTED_KEY_TYPE', message: 'Unsupported JWK Key Type.' }); + const jwk = key.export({ format: 'jwk' }); + assertPrivateJwk(jwk); + assert.strictEqual(key.equals(createPrivateKey({ format: 'jwk', key: jwk })), true); + assert.ok(createPublicKey({ format: 'jwk', key: jwk })); // Raw format round-trip const rawPriv = key.export({ format: 'raw-private' }); @@ -86,5 +109,36 @@ for (const asymmetricKeyType of [ assertPublicKey(pubFromPriv); assertPrivateKey(createPrivateKey(keys.private)); assert.strictEqual(pubFromPriv.equals(publicKey), true); + + // JWK import error tests + const format = 'jwk'; + const jwk = keys.jwk; + + assert.throws(() => createPrivateKey({ format, key: { ...jwk, alg: asymmetricKeyType } }), + { code: 'ERR_CRYPTO_INVALID_JWK' }); + assert.throws(() => createPrivateKey({ format, key: { ...jwk, alg: undefined } }), + { code: 'ERR_CRYPTO_INVALID_JWK' }); + assert.throws(() => createPrivateKey({ format, key: { ...jwk, pub: undefined } }), + { code: 'ERR_CRYPTO_INVALID_JWK' }); + assert.throws(() => createPrivateKey({ format, key: { ...jwk, priv: undefined } }), + { code: 'ERR_CRYPTO_INVALID_JWK', message: /JWK does not contain private key material/ }); + assert.throws(() => createPrivateKey({ format, key: { ...jwk, priv: Buffer.alloc(1).toString('base64url') } }), + { code: 'ERR_CRYPTO_INVALID_JWK' }); + // eslint-disable-next-line @stylistic/js/max-len + assert.throws(() => createPublicKey({ format, key: { kty: jwk.kty, alg: jwk.alg, pub: Buffer.alloc(1).toString('base64url') } }), + { code: 'ERR_CRYPTO_INVALID_JWK' }); + + // Importing a private JWK where pub does not match priv should fail. + assert.throws( + () => createPrivateKey({ + format, + key: { ...jwk, pub: `${jwk.pub[0] === 'A' ? 'B' : 'A'}${jwk.pub.slice(1)}` }, + }), + { code: 'ERR_CRYPTO_INVALID_JWK' } + ); + + // JWK round-trip + assert.partialDeepStrictEqual(jwk, createPublicKey({ format, key: jwk }).export({ format })); + assert.deepStrictEqual(createPrivateKey({ format, key: jwk }).export({ format }), jwk); } } diff --git a/test/parallel/test-crypto-pqc-keygen-ml-kem.js b/test/parallel/test-crypto-pqc-keygen-ml-kem.js index adb5fcb56ce6ef..ea3ac9f4dde137 100644 --- a/test/parallel/test-crypto-pqc-keygen-ml-kem.js +++ b/test/parallel/test-crypto-pqc-keygen-ml-kem.js @@ -20,6 +20,23 @@ if (!hasOpenSSL(3, 5)) { } } else { for (const asymmetricKeyType of ['ml-kem-512', 'ml-kem-768', 'ml-kem-1024']) { + + function assertJwk(jwk) { + assert.strictEqual(jwk.kty, 'AKP'); + assert.strictEqual(jwk.alg, asymmetricKeyType.toUpperCase()); + assert.ok(jwk.pub); + } + + function assertPublicJwk(jwk) { + assertJwk(jwk); + assert.ok(!jwk.priv); + } + + function assertPrivateJwk(jwk) { + assertJwk(jwk); + assert.ok(jwk.priv); + } + for (const [publicKeyEncoding, validate] of [ /* eslint-disable node-core/must-call-assert */ [undefined, (publicKey) => { @@ -27,6 +44,7 @@ if (!hasOpenSSL(3, 5)) { assert.strictEqual(publicKey.asymmetricKeyType, asymmetricKeyType); assert.deepStrictEqual(publicKey.asymmetricKeyDetails, {}); }], + [{ format: 'jwk' }, (publicKey) => assertPublicJwk(publicKey)], [{ format: 'pem', type: 'spki' }, (publicKey) => assert.strictEqual(typeof publicKey, 'string')], [{ format: 'der', type: 'spki' }, (publicKey) => assert.strictEqual(Buffer.isBuffer(publicKey), true)], /* eslint-enable node-core/must-call-assert */ @@ -40,6 +58,7 @@ if (!hasOpenSSL(3, 5)) { assert.strictEqual(privateKey.asymmetricKeyType, asymmetricKeyType); assert.deepStrictEqual(privateKey.asymmetricKeyDetails, {}); }], + [{ format: 'jwk' }, (_, privateKey) => assertPrivateJwk(privateKey)], [{ format: 'pem', type: 'pkcs8' }, (_, privateKey) => assert.strictEqual(typeof privateKey, 'string')], [{ format: 'der', type: 'pkcs8' }, (_, privateKey) => assert.strictEqual(Buffer.isBuffer(privateKey), true)], /* eslint-enable node-core/must-call-assert */ diff --git a/test/parallel/test-crypto-pqc-keygen-slh-dsa.js b/test/parallel/test-crypto-pqc-keygen-slh-dsa.js index 6b741e37f0cc11..a8e480818cd134 100644 --- a/test/parallel/test-crypto-pqc-keygen-slh-dsa.js +++ b/test/parallel/test-crypto-pqc-keygen-slh-dsa.js @@ -28,6 +28,26 @@ if (!hasOpenSSL(3, 5)) { 'slh-dsa-sha2-256f', 'slh-dsa-sha2-256s', 'slh-dsa-shake-128f', 'slh-dsa-shake-128s', 'slh-dsa-shake-192f', 'slh-dsa-shake-192s', 'slh-dsa-shake-256f', 'slh-dsa-shake-256s', ]) { + + function assertJwk(jwk) { + assert.strictEqual(jwk.kty, 'AKP'); + // SLH-DSA algorithm names keep the last character (f/s) lowercase. + const expectedAlg = asymmetricKeyType.slice(0, -1).toUpperCase() + + asymmetricKeyType.slice(-1); + assert.strictEqual(jwk.alg, expectedAlg); + assert.ok(jwk.pub); + } + + function assertPublicJwk(jwk) { + assertJwk(jwk); + assert.ok(!jwk.priv); + } + + function assertPrivateJwk(jwk) { + assertJwk(jwk); + assert.ok(jwk.priv); + } + for (const [publicKeyEncoding, validate] of [ /* eslint-disable node-core/must-call-assert */ [undefined, (publicKey) => { @@ -35,6 +55,7 @@ if (!hasOpenSSL(3, 5)) { assert.strictEqual(publicKey.asymmetricKeyType, asymmetricKeyType); assert.deepStrictEqual(publicKey.asymmetricKeyDetails, {}); }], + [{ format: 'jwk' }, (publicKey) => assertPublicJwk(publicKey)], [{ format: 'pem', type: 'spki' }, (publicKey) => assert.strictEqual(typeof publicKey, 'string')], [{ format: 'der', type: 'spki' }, (publicKey) => assert.strictEqual(Buffer.isBuffer(publicKey), true)], ]) { @@ -46,6 +67,7 @@ if (!hasOpenSSL(3, 5)) { assert.strictEqual(privateKey.asymmetricKeyType, asymmetricKeyType); assert.deepStrictEqual(privateKey.asymmetricKeyDetails, {}); }], + [{ format: 'jwk' }, (_, privateKey) => assertPrivateJwk(privateKey)], [{ format: 'pem', type: 'pkcs8' }, (_, privateKey) => assert.strictEqual(typeof privateKey, 'string')], [{ format: 'der', type: 'pkcs8' }, (_, privateKey) => assert.strictEqual(Buffer.isBuffer(privateKey), true)], /* eslint-enable node-core/must-call-assert */ diff --git a/test/parallel/test-debugger-probe-child-inspect-port-zero.js b/test/parallel/test-debugger-probe-child-inspect-port-zero.js index a6f03ad9f41cbc..2abc81441edeb1 100644 --- a/test/parallel/test-debugger-probe-child-inspect-port-zero.js +++ b/test/parallel/test-debugger-probe-child-inspect-port-zero.js @@ -4,9 +4,8 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -const assert = require('assert'); const { spawnSyncAndAssert } = require('../common/child_process'); -const { probeScript } = require('../common/debugger-probe'); +const { assertProbeJson, probeScript } = require('../common/debugger-probe'); spawnSyncAndAssert(process.execPath, [ 'inspect', @@ -18,7 +17,7 @@ spawnSyncAndAssert(process.execPath, [ probeScript, ], { stdout(output) { - assert.deepStrictEqual(JSON.parse(output), { + assertProbeJson(output, { v: 1, probes: [{ expr: 'finalValue', diff --git a/test/parallel/test-debugger-probe-global-option-order.js b/test/parallel/test-debugger-probe-global-option-order.js index 9e247c3213178a..19487c8f623795 100644 --- a/test/parallel/test-debugger-probe-global-option-order.js +++ b/test/parallel/test-debugger-probe-global-option-order.js @@ -4,9 +4,8 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -const assert = require('assert'); const { spawnSyncAndAssert } = require('../common/child_process'); -const { probeScript } = require('../common/debugger-probe'); +const { assertProbeJson, probeScript } = require('../common/debugger-probe'); spawnSyncAndAssert(process.execPath, [ 'inspect', @@ -16,7 +15,7 @@ spawnSyncAndAssert(process.execPath, [ probeScript, ], { stdout(output) { - assert.deepStrictEqual(JSON.parse(output), { + assertProbeJson(output, { v: 1, probes: [{ expr: 'finalValue', diff --git a/test/parallel/test-debugger-probe-json-preview.js b/test/parallel/test-debugger-probe-json-preview.js index 62b9edfe489c4a..853939075aa326 100644 --- a/test/parallel/test-debugger-probe-json-preview.js +++ b/test/parallel/test-debugger-probe-json-preview.js @@ -4,9 +4,11 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -const assert = require('assert'); const { spawnSyncAndAssert } = require('../common/child_process'); -const { probeTypesScript } = require('../common/debugger-probe'); +const { + assertProbeJson, + probeTypesScript, +} = require('../common/debugger-probe'); const location = `${probeTypesScript}:17`; @@ -23,7 +25,7 @@ spawnSyncAndAssert(process.execPath, [ probeTypesScript, ], { stdout(output) { - assert.deepStrictEqual(JSON.parse(output), { + assertProbeJson(output, { v: 1, probes: [ { expr: 'objectValue', target: [probeTypesScript, 17] }, diff --git a/test/parallel/test-debugger-probe-json-special-values.js b/test/parallel/test-debugger-probe-json-special-values.js index ea18a33f1e9ac7..97782c9e314e3d 100644 --- a/test/parallel/test-debugger-probe-json-special-values.js +++ b/test/parallel/test-debugger-probe-json-special-values.js @@ -4,9 +4,11 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -const assert = require('assert'); const { spawnSyncAndAssert } = require('../common/child_process'); -const { probeTypesScript } = require('../common/debugger-probe'); +const { + assertProbeJson, + probeTypesScript, +} = require('../common/debugger-probe'); const location = `${probeTypesScript}:17`; @@ -38,7 +40,7 @@ spawnSyncAndAssert(process.execPath, [ probeTypesScript, ], { stdout(output) { - assert.deepStrictEqual(JSON.parse(output), { + assertProbeJson(output, { v: 1, probes: [ { expr: 'stringValue', target: [probeTypesScript, 17] }, diff --git a/test/parallel/test-debugger-probe-json.js b/test/parallel/test-debugger-probe-json.js index e95b7ffbd72587..be2288ee78ddac 100644 --- a/test/parallel/test-debugger-probe-json.js +++ b/test/parallel/test-debugger-probe-json.js @@ -4,9 +4,8 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -const assert = require('assert'); const { spawnSyncAndAssert } = require('../common/child_process'); -const { probeScript } = require('../common/debugger-probe'); +const { assertProbeJson, probeScript } = require('../common/debugger-probe'); spawnSyncAndAssert(process.execPath, [ 'inspect', @@ -20,7 +19,7 @@ spawnSyncAndAssert(process.execPath, [ probeScript, ], { stdout(output) { - assert.deepStrictEqual(JSON.parse(output), { + assertProbeJson(output, { v: 1, probes: [ { expr: 'index', target: [probeScript, 8] }, diff --git a/test/parallel/test-debugger-probe-miss.js b/test/parallel/test-debugger-probe-miss.js index 7dc52e9a07b0c0..2908b8092aeb7d 100644 --- a/test/parallel/test-debugger-probe-miss.js +++ b/test/parallel/test-debugger-probe-miss.js @@ -4,9 +4,8 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -const assert = require('assert'); const { spawnSyncAndAssert } = require('../common/child_process'); -const { missScript } = require('../common/debugger-probe'); +const { assertProbeJson, missScript } = require('../common/debugger-probe'); spawnSyncAndAssert(process.execPath, [ 'inspect', @@ -16,7 +15,7 @@ spawnSyncAndAssert(process.execPath, [ missScript, ], { stdout(output) { - assert.deepStrictEqual(JSON.parse(output), { + assertProbeJson(output, { v: 1, probes: [{ expr: '42', target: [missScript, 99] }], results: [{ diff --git a/test/parallel/test-debugger-probe-text-special-values.js b/test/parallel/test-debugger-probe-text-special-values.js index 17ff62ef4a086d..3886eb66daed8b 100644 --- a/test/parallel/test-debugger-probe-text-special-values.js +++ b/test/parallel/test-debugger-probe-text-special-values.js @@ -4,9 +4,11 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -const assert = require('assert'); const { spawnSyncAndAssert } = require('../common/child_process'); -const { probeTypesScript } = require('../common/debugger-probe'); +const { + assertProbeText, + probeTypesScript, +} = require('../common/debugger-probe'); const location = `${probeTypesScript}:17`; @@ -37,7 +39,7 @@ spawnSyncAndAssert(process.execPath, [ probeTypesScript, ], { stdout(output) { - assert.strictEqual(output, [ + assertProbeText(output, [ `Hit 1 at ${location}`, ' stringValue = "hello"', `Hit 1 at ${location}`, diff --git a/test/parallel/test-debugger-probe-text.js b/test/parallel/test-debugger-probe-text.js index 75be98611d378b..30e77b25985d16 100644 --- a/test/parallel/test-debugger-probe-text.js +++ b/test/parallel/test-debugger-probe-text.js @@ -4,9 +4,8 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -const assert = require('assert'); const { spawnSyncAndAssert } = require('../common/child_process'); -const { probeScript } = require('../common/debugger-probe'); +const { assertProbeText, probeScript } = require('../common/debugger-probe'); spawnSyncAndAssert(process.execPath, [ 'inspect', @@ -15,10 +14,10 @@ spawnSyncAndAssert(process.execPath, [ probeScript, ], { stdout(output) { - assert.strictEqual(output, - `Hit 1 at ${probeScript}:12\n` + - ' finalValue = 81\n' + - 'Completed'); + assertProbeText(output, + `Hit 1 at ${probeScript}:12\n` + + ' finalValue = 81\n' + + 'Completed'); }, trim: true, }); diff --git a/test/parallel/test-debugger-probe-timeout.js b/test/parallel/test-debugger-probe-timeout.js index d4728407bc5924..fe641f31943af0 100644 --- a/test/parallel/test-debugger-probe-timeout.js +++ b/test/parallel/test-debugger-probe-timeout.js @@ -4,9 +4,8 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -const assert = require('assert'); const { spawnSyncAndExit } = require('../common/child_process'); -const { timeoutScript } = require('../common/debugger-probe'); +const { assertProbeJson, timeoutScript } = require('../common/debugger-probe'); spawnSyncAndExit(process.execPath, [ 'inspect', @@ -19,7 +18,7 @@ spawnSyncAndExit(process.execPath, [ signal: null, status: 1, stdout(output) { - assert.deepStrictEqual(JSON.parse(output), { + assertProbeJson(output, { v: 1, probes: [{ expr: '1', target: [timeoutScript, 99] }], results: [{ diff --git a/test/parallel/test-fs-glob.mjs b/test/parallel/test-fs-glob.mjs index 74791deba373e3..560b4e72e4adec 100644 --- a/test/parallel/test-fs-glob.mjs +++ b/test/parallel/test-fs-glob.mjs @@ -36,6 +36,9 @@ async function setup() { const symlinkTo = resolve(fixtureDir, 'a/symlink/a/b/c'); const symlinkFrom = '../..'; + const followTarget = resolve(fixtureDir, 'follow/target'); + const followLink = resolve(fixtureDir, 'follow/link'); + const followCycle = resolve(fixtureDir, 'follow/cycle'); for (const file of files) { const f = resolve(fixtureDir, file); @@ -44,12 +47,19 @@ async function setup() { await writeFile(f, 'i like tests'); } + await mkdir(followTarget, { recursive: true }); + await writeFile(resolve(followTarget, 'file.txt'), 'follow symlinks'); + if (!common.isWindows) { const d = dirname(symlinkTo); await mkdir(d, { recursive: true }); await symlink(symlinkFrom, symlinkTo, 'dir'); } + const linkType = common.isWindows ? 'junction' : 'dir'; + await symlink(followTarget, followLink, linkType); + await symlink(resolve(fixtureDir, 'follow'), followCycle, linkType); + await Promise.all(['foo', 'bar', 'baz', 'asdf', 'quux', 'qwer', 'rewq'].map(async function(w) { await mkdir(resolve(absDir, w), { recursive: true }); })); @@ -216,6 +226,9 @@ const patterns = { common.isWindows ? null : 'a/symlink', 'a/x', 'a/z', + 'follow/cycle', + 'follow/link', + 'follow/target', ], [`{${absDir}/*,*}`]: [ `${absDir}/asdf`, @@ -226,6 +239,7 @@ const patterns = { `${absDir}/qwer`, `${absDir}/rewq`, 'a', + 'follow', ], 'a/!(symlink)/**': [ 'a/abcdef', @@ -524,6 +538,100 @@ describe('fsPromises glob - exclude', function() { } }); +const followSymlinkPattern = 'follow/**'; +const followSymlinkExpected = [ + 'follow', + 'follow/cycle', + 'follow/link', + 'follow/target', + 'follow/target/file.txt', +].map((item) => item.replaceAll('/', sep)).sort(); +const followSymlinkExpectedWithFollow = [ + ...followSymlinkExpected, + 'follow/link/file.txt'.replaceAll('/', sep), +].sort(); + +const getNestedCycleMatches = (matches) => { + return matches.filter((match) => match.startsWith(`follow${sep}cycle${sep}`)); +}; + +describe('glob - followSymlinks', function() { + const promisified = promisify(glob); + + test('does not follow symlinks by default', async () => { + const actual = (await promisified(followSymlinkPattern, { cwd: fixtureDir })).sort(); + assert.deepStrictEqual(actual, followSymlinkExpected); + }); + + test('follows symlinked directories when enabled', async () => { + const actual = (await promisified(followSymlinkPattern, { + cwd: fixtureDir, + followSymlinks: true, + })).sort(); + assert.deepStrictEqual(actual, followSymlinkExpectedWithFollow); + assert.deepStrictEqual(getNestedCycleMatches(actual), []); + }); +}); + +describe('globSync - followSymlinks', function() { + test('does not follow symlinks by default', () => { + const actual = globSync(followSymlinkPattern, { cwd: fixtureDir }).sort(); + assert.deepStrictEqual(actual, followSymlinkExpected); + }); + + test('validates followSymlinks', () => { + assert.throws(() => { + globSync(followSymlinkPattern, { + cwd: fixtureDir, + followSymlinks: 1, + }); + }, { + code: 'ERR_INVALID_ARG_TYPE', + }); + }); + + test('follows symlinked directories when enabled', () => { + const actual = globSync(followSymlinkPattern, { + cwd: fixtureDir, + followSymlinks: true, + }).sort(); + assert.deepStrictEqual(actual, followSymlinkExpectedWithFollow); + assert.deepStrictEqual(getNestedCycleMatches(actual), []); + }); + + test('supports withFileTypes when following symlinked directories', () => { + const actual = globSync(followSymlinkPattern, { + cwd: fixtureDir, + followSymlinks: true, + withFileTypes: true, + }); + assertDirents(actual); + const normalized = actual.map(normalizeDirent).sort(); + assert.deepStrictEqual(normalized, followSymlinkExpectedWithFollow); + assert.deepStrictEqual(getNestedCycleMatches(normalized), []); + }); +}); + +describe('fsPromises glob - followSymlinks', function() { + test('does not follow symlinks by default', async () => { + const actual = []; + for await (const item of asyncGlob(followSymlinkPattern, { cwd: fixtureDir })) actual.push(item); + actual.sort(); + assert.deepStrictEqual(actual, followSymlinkExpected); + }); + + test('follows symlinked directories when enabled', async () => { + const actual = []; + for await (const item of asyncGlob(followSymlinkPattern, { + cwd: fixtureDir, + followSymlinks: true, + })) actual.push(item); + actual.sort(); + assert.deepStrictEqual(actual, followSymlinkExpectedWithFollow); + assert.deepStrictEqual(getNestedCycleMatches(actual), []); + }); +}); + describe('glob - with restricted directory', function() { test('*', async () => { const restrictedDir = tmpdir.resolve('restricted'); diff --git a/test/parallel/test-http-blank-header.js b/test/parallel/test-http-blank-header.js index 696b16f4995b57..a22f2f32ffcfa3 100644 --- a/test/parallel/test-http-blank-header.js +++ b/test/parallel/test-http-blank-header.js @@ -28,11 +28,10 @@ const net = require('net'); const server = http.createServer(common.mustCall((req, res) => { assert.strictEqual(req.method, 'GET'); assert.strictEqual(req.url, '/blah'); - assert.deepStrictEqual(req.headers, { - host: 'example.org:443', - origin: 'http://example.org', - cookie: '' - }); + assert.deepStrictEqual(req.headers, { __proto__: null, + host: 'example.org:443', + origin: 'http://example.org', + cookie: '' }); })); diff --git a/test/parallel/test-http-client-headers-array.js b/test/parallel/test-http-client-headers-array.js index f5a9430b3261f2..697bc0b4600fa2 100644 --- a/test/parallel/test-http-client-headers-array.js +++ b/test/parallel/test-http-client-headers-array.js @@ -7,12 +7,11 @@ const http = require('http'); function execute(options) { http.createServer(common.mustCall(function(req, res) { - const expectHeaders = { - 'x-foo': 'boom', - 'cookie': 'a=1; b=2; c=3', - 'connection': 'keep-alive', - 'host': 'example.com', - }; + const expectHeaders = { '__proto__': null, + 'x-foo': 'boom', + 'cookie': 'a=1; b=2; c=3', + 'connection': 'keep-alive', + 'host': 'example.com' }; // no Host header when you set headers an array if (!Array.isArray(options.headers)) { diff --git a/test/parallel/test-http-client-request-listeners-leak.js b/test/parallel/test-http-client-request-listeners-leak.js new file mode 100644 index 00000000000000..789ef6ca7e8011 --- /dev/null +++ b/test/parallel/test-http-client-request-listeners-leak.js @@ -0,0 +1,50 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); +const { defaultMaxListeners } = require('events'); + +// Immediately destroying an HTTP request must not leak listeners on the +// freed socket. When sockets are reused via a keep-alive agent leaked +// listeners would accumulate to trigger a MaxListenersExceededWarning. + +// Check we don't get a MaxListenersExceededWarning: +process.on('warning', common.mustNotCall('Unexpected warning emitted')); + +const server = http.createServer(common.mustNotCall()); + +server.listen(0, common.mustCall(() => { + const agent = new http.Agent({ keepAlive: true }); + const port = server.address().port; + + // Count actual socket creations to confirm reuse: + let createSocketCount = 0; + const origCreateSocket = agent.createSocket.bind(agent); + agent.createSocket = function(...args) { + createSocketCount++; + return origCreateSocket(...args); + }; + + function executeHttpGet() { + return new Promise((resolve) => { + const req = http.get({ host: '127.0.0.1', port, agent }); + req.on('error', resolve); + req.on('close', resolve); + req.on('response', common.mustNotCall()); + req.destroy(); + }); + } + + async function main() { + for (let i = 0; i < defaultMaxListeners + 1; i++) { + await executeHttpGet(); + } + + assert.strictEqual(createSocketCount, 1); + + server.close(); + agent.destroy(); + } + + main().then(common.mustCall()); +})); diff --git a/test/parallel/test-http-content-length.js b/test/parallel/test-http-content-length.js index e731b0e8ef92aa..69fa6ae5f657e0 100644 --- a/test/parallel/test-http-content-length.js +++ b/test/parallel/test-http-content-length.js @@ -4,20 +4,17 @@ const assert = require('assert'); const http = require('http'); const Countdown = require('../common/countdown'); -const expectedHeadersMultipleWrites = { - 'connection': 'keep-alive', - 'transfer-encoding': 'chunked', -}; +const expectedHeadersMultipleWrites = { '__proto__': null, + 'connection': 'keep-alive', + 'transfer-encoding': 'chunked' }; -const expectedHeadersEndWithData = { - 'connection': 'keep-alive', - 'content-length': String('hello world'.length), -}; +const expectedHeadersEndWithData = { '__proto__': null, + 'connection': 'keep-alive', + 'content-length': String('hello world'.length) }; -const expectedHeadersEndNoData = { - 'connection': 'keep-alive', - 'content-length': '0', -}; +const expectedHeadersEndNoData = { '__proto__': null, + 'connection': 'keep-alive', + 'content-length': '0' }; const countdown = new Countdown(3, () => server.close()); @@ -62,7 +59,9 @@ server.listen(0, common.mustCall(function() { req.write('hello '); req.end('world'); req.on('response', common.mustCall((res) => { - assert.deepStrictEqual(res.headers, { ...expectedHeadersMultipleWrites, 'keep-alive': 'timeout=1' }); + assert.deepStrictEqual(res.headers, { + '__proto__': null, ...expectedHeadersMultipleWrites, 'keep-alive': 'timeout=1', + }); res.resume(); })); @@ -74,7 +73,9 @@ server.listen(0, common.mustCall(function() { req.removeHeader('Date'); req.end('hello world'); req.on('response', common.mustCall((res) => { - assert.deepStrictEqual(res.headers, { ...expectedHeadersEndWithData, 'keep-alive': 'timeout=1' }); + assert.deepStrictEqual(res.headers, { + '__proto__': null, ...expectedHeadersEndWithData, 'keep-alive': 'timeout=1', + }); res.resume(); })); @@ -86,7 +87,7 @@ server.listen(0, common.mustCall(function() { req.removeHeader('Date'); req.end(); req.on('response', common.mustCall((res) => { - assert.deepStrictEqual(res.headers, { ...expectedHeadersEndNoData, 'keep-alive': 'timeout=1' }); + assert.deepStrictEqual(res.headers, { '__proto__': null, ...expectedHeadersEndNoData, 'keep-alive': 'timeout=1' }); res.resume(); })); diff --git a/test/parallel/test-http-keep-alive-max-requests.js b/test/parallel/test-http-keep-alive-max-requests.js index 558beadfae8b04..c4751b6c05c2e8 100644 --- a/test/parallel/test-http-keep-alive-max-requests.js +++ b/test/parallel/test-http-keep-alive-max-requests.js @@ -10,11 +10,11 @@ const bodySent = 'This is my request'; function assertResponse(headers, body, expectClosed) { if (expectClosed) { assert.match(headers, /Connection: close\r\n/m); - assert.strictEqual(headers.search(/Keep-Alive: timeout=5\r\n/m), -1); + assert.strictEqual(headers.search(/Keep-Alive: timeout=65\r\n/m), -1); assert.match(body, /Hello World!/m); } else { assert.match(headers, /Connection: keep-alive\r\n/m); - assert.match(headers, /Keep-Alive: timeout=5, max=3\r\n/m); + assert.match(headers, /Keep-Alive: timeout=65, max=3\r\n/m); assert.match(body, /Hello World!/m); } } diff --git a/test/parallel/test-http-keep-alive-pipeline-max-requests.js b/test/parallel/test-http-keep-alive-pipeline-max-requests.js index a52b12bb4f935f..446bb3c49796b0 100644 --- a/test/parallel/test-http-keep-alive-pipeline-max-requests.js +++ b/test/parallel/test-http-keep-alive-pipeline-max-requests.js @@ -10,11 +10,11 @@ const bodySent = 'This is my request'; function assertResponse(headers, body, expectClosed) { if (expectClosed) { assert.match(headers, /Connection: close\r\n/m); - assert.strictEqual(headers.search(/Keep-Alive: timeout=5, max=3\r\n/m), -1); + assert.strictEqual(headers.search(/Keep-Alive: timeout=65, max=3\r\n/m), -1); assert.match(body, /Hello World!/m); } else { assert.match(headers, /Connection: keep-alive\r\n/m); - assert.match(headers, /Keep-Alive: timeout=5, max=3\r\n/m); + assert.match(headers, /Keep-Alive: timeout=65, max=3\r\n/m); assert.match(body, /Hello World!/m); } } @@ -76,7 +76,7 @@ server.listen(0, common.mustCall((res) => { assert.match(responseParts[6], /HTTP\/1\.1 503 Service Unavailable/m); assert.match(responseParts[6], /Connection: close\r\n/m); - assert.strictEqual(responseParts[6].search(/Keep-Alive: timeout=5\r\n/m), -1); + assert.strictEqual(responseParts[6].search(/Keep-Alive: timeout=65\r\n/m), -1); assert.strictEqual(responseParts[7].search(/Hello World!/m), -1); socket.end(); diff --git a/test/parallel/test-http-multiple-headers.js b/test/parallel/test-http-multiple-headers.js index 75796e1faa9960..f0dbd5942a9299 100644 --- a/test/parallel/test-http-multiple-headers.js +++ b/test/parallel/test-http-multiple-headers.js @@ -19,13 +19,12 @@ const server = createServer( 'Host', host, 'Transfer-Encoding', 'chunked', ]); - assert.deepStrictEqual(req.headers, { - 'connection': 'close', - 'x-req-a': 'eee, fff, ggg, hhh', - 'x-req-b': 'iii; jjj; kkk; lll', - host, - 'transfer-encoding': 'chunked' - }); + assert.deepStrictEqual(req.headers, { '__proto__': null, + 'connection': 'close', + 'x-req-a': 'eee, fff, ggg, hhh', + 'x-req-b': 'iii; jjj; kkk; lll', + host, + 'transfer-encoding': 'chunked' }); assert.deepStrictEqual(req.headersDistinct, Object.assign({ __proto__: null }, { 'connection': ['close'], 'x-req-a': ['eee', 'fff', 'ggg', 'hhh'], @@ -41,7 +40,7 @@ const server = createServer( 'X-req-Y', 'zzz; www', ]); assert.deepStrictEqual( - req.trailers, { 'x-req-x': 'xxx, yyy', 'x-req-y': 'zzz; www' } + req.trailers, { '__proto__': null, 'x-req-x': 'xxx, yyy', 'x-req-y': 'zzz; www' } ); assert.deepStrictEqual( req.trailersDistinct, @@ -124,14 +123,13 @@ server.listen(0, common.mustCall(() => { 'x-res-d', 'JJJ; KKK; LLL', 'Transfer-Encoding', 'chunked', ]); - assert.deepStrictEqual(res.headers, { - 'x-res-a': 'AAA, BBB, CCC', - 'x-res-b': 'DDD; EEE; FFF; GGG', - 'connection': 'close', - 'x-res-c': 'HHH, III', - 'x-res-d': 'JJJ; KKK; LLL', - 'transfer-encoding': 'chunked' - }); + assert.deepStrictEqual(res.headers, { '__proto__': null, + 'x-res-a': 'AAA, BBB, CCC', + 'x-res-b': 'DDD; EEE; FFF; GGG', + 'connection': 'close', + 'x-res-c': 'HHH, III', + 'x-res-d': 'JJJ; KKK; LLL', + 'transfer-encoding': 'chunked' }); assert.deepStrictEqual(res.headersDistinct, Object.assign({ __proto__: null }, { 'x-res-a': [ 'AAA', 'BBB', 'CCC' ], 'x-res-b': [ 'DDD; EEE; FFF; GGG' ], @@ -149,7 +147,7 @@ server.listen(0, common.mustCall(() => { ]); assert.deepStrictEqual( res.trailers, - { 'x-res-x': 'XXX, YYY', 'x-res-y': 'ZZZ; WWW' } + { '__proto__': null, 'x-res-x': 'XXX, YYY', 'x-res-y': 'ZZZ; WWW' } ); assert.deepStrictEqual( res.trailersDistinct, diff --git a/test/parallel/test-http-outgoing-proto.js b/test/parallel/test-http-outgoing-proto.js index 2d265c7ba64968..fa57bd28a8362c 100644 --- a/test/parallel/test-http-outgoing-proto.js +++ b/test/parallel/test-http-outgoing-proto.js @@ -129,6 +129,16 @@ assert.throws(() => { message: 'Invalid character in trailer content ["404"]' }); +assert.throws(() => { + const outgoingMessage = new OutgoingMessage(); + outgoingMessage.finished = true; + outgoingMessage.addTrailers({ 'x-foo': 'bar' }); +}, { + code: 'ERR_HTTP_HEADERS_SENT', + name: 'Error', + message: 'Cannot set trailing headers after they are sent to the client' +}); + { const outgoingMessage = new OutgoingMessage(); assert.strictEqual(outgoingMessage.destroyed, false); diff --git a/test/parallel/test-http-raw-headers.js b/test/parallel/test-http-raw-headers.js index c6ca5f5c7a2a5a..e03dc4445f18cc 100644 --- a/test/parallel/test-http-raw-headers.js +++ b/test/parallel/test-http-raw-headers.js @@ -36,12 +36,11 @@ http.createServer(common.mustCall(function(req, res) { 'Connection', 'keep-alive', ]; - const expectHeaders = { - 'host': `localhost:${this.address().port}`, - 'transfer-encoding': 'CHUNKED', - 'x-bar': 'yoyoyo', - 'connection': 'keep-alive' - }; + const expectHeaders = { '__proto__': null, + 'host': `localhost:${this.address().port}`, + 'transfer-encoding': 'CHUNKED', + 'x-bar': 'yoyoyo', + 'connection': 'keep-alive' }; const expectRawTrailers = [ 'x-bAr', 'yOyOyOy', @@ -52,7 +51,7 @@ http.createServer(common.mustCall(function(req, res) { 'X-baR', 'OyOyOyO', ]; - const expectTrailers = { 'x-bar': 'yOyOyOy, OyOyOyO, yOyOyOy, OyOyOyO' }; + const expectTrailers = { '__proto__': null, 'x-bar': 'yOyOyOy, OyOyOyO, yOyOyOy, OyOyOyO' }; this.close(); @@ -98,13 +97,12 @@ http.createServer(common.mustCall(function(req, res) { 'Transfer-Encoding', 'chunked', ]; - const expectHeaders = { - 'keep-alive': 'timeout=1', - 'trailer': 'x-foo', - 'date': null, - 'connection': 'keep-alive', - 'transfer-encoding': 'chunked' - }; + const expectHeaders = { '__proto__': null, + 'keep-alive': 'timeout=1', + 'trailer': 'x-foo', + 'date': null, + 'connection': 'keep-alive', + 'transfer-encoding': 'chunked' }; res.rawHeaders[5] = null; res.headers.date = null; assert.deepStrictEqual(res.rawHeaders, expectRawHeaders); @@ -120,7 +118,7 @@ http.createServer(common.mustCall(function(req, res) { 'X-foO', 'OxOxOxO', ]; - const expectTrailers = { 'x-foo': 'xOxOxOx, OxOxOxO, xOxOxOx, OxOxOxO' }; + const expectTrailers = { '__proto__': null, 'x-foo': 'xOxOxOx, OxOxOxO, xOxOxOx, OxOxOxO' }; assert.deepStrictEqual(res.rawTrailers, expectRawTrailers); assert.deepStrictEqual(res.trailers, expectTrailers); diff --git a/test/parallel/test-http-remove-header-stays-removed.js b/test/parallel/test-http-remove-header-stays-removed.js index 301a5f8d8e4cd4..f39fd2480db571 100644 --- a/test/parallel/test-http-remove-header-stays-removed.js +++ b/test/parallel/test-http-remove-header-stays-removed.js @@ -50,7 +50,7 @@ const server = http.createServer(common.mustCall(function(request, response) { server.listen(0, common.mustCall(function() { http.get({ port: this.address().port }, common.mustCall((res) => { assert.strictEqual(res.statusCode, 200); - assert.deepStrictEqual(res.headers, { date: 'coffee o clock' }); + assert.deepStrictEqual(res.headers, { __proto__: null, date: 'coffee o clock' }); let response = ''; res.setEncoding('ascii'); diff --git a/test/parallel/test-http-server-headers-null-proto.js b/test/parallel/test-http-server-headers-null-proto.js new file mode 100644 index 00000000000000..3f279a54c9485a --- /dev/null +++ b/test/parallel/test-http-server-headers-null-proto.js @@ -0,0 +1,106 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); +const net = require('net'); + +// Test 1: req.headers has a null prototype +{ + const server = http.createServer(common.mustCall((req, res) => { + assert.strictEqual(Object.getPrototypeOf(req.headers), null); + res.end(); + })); + + server.listen(0, common.mustCall(() => { + const port = server.address().port; + + const client = net.connect(port, common.mustCall(() => { + client.write( + 'GET / HTTP/1.1\r\n' + + 'Host: localhost\r\n' + + 'Connection: close\r\n' + + '\r\n', + ); + })); + + client.on('end', common.mustCall(() => { + server.close(); + })); + + client.resume(); + })); +} + +// Test 2: req.trailers has a null prototype +{ + const server = http.createServer(common.mustCall((req, res) => { + req.on('end', common.mustCall(() => { + assert.strictEqual(Object.getPrototypeOf(req.trailers), null); + assert.strictEqual(req.trailers['x-client-trailer'], 'bar'); + })); + res.setHeader('Transfer-Encoding', 'chunked'); + res.setHeader('Trailer', 'X-Trailer'); + res.write('Hello'); + res.addTrailers({ + 'X-Trailer': 'test', + }); + res.end(); + })); + + server.listen(0, common.mustCall(() => { + const port = server.address().port; + + const client = net.connect(port, common.mustCall(() => { + client.write( + 'GET / HTTP/1.1\r\n' + + 'Host: localhost\r\n' + + 'Transfer-Encoding: chunked\r\n' + + 'Trailer: X-Client-Trailer\r\n' + + 'Connection: close\r\n' + + '\r\n' + + '0\r\n' + + 'X-Client-Trailer: bar\r\n' + + '\r\n', + ); + })); + + client.on('data', () => {}); + + client.on('end', common.mustCall(() => { + server.close(); + })); + })); +} + +// Test 3: req.headers with __proto__ header (should not pollute prototype) +{ + const server = http.createServer(common.mustCall((req, res) => { + assert.strictEqual(Object.getPrototypeOf(req.headers), null); + // The __proto__ header should be stored as a regular property + assert.strictEqual( + Object.getOwnPropertyDescriptor(req.headers, '__proto__').value, 'test', + ); + res.end(); + })); + + server.listen(0, common.mustCall(() => { + const port = server.address().port; + + const client = net.connect(port, common.mustCall(() => { + client.write( + 'GET / HTTP/1.1\r\n' + + 'Host: localhost\r\n' + + '__proto__: test\r\n' + + 'Connection: close\r\n' + + '\r\n', + ); + })); + + client.on('end', common.mustCall(() => { + server.close(); + })); + + client.resume(); + })); +} diff --git a/test/parallel/test-http-server-keep-alive-defaults.js b/test/parallel/test-http-server-keep-alive-defaults.js index 8c10bb37472887..4487a36b3b6d24 100644 --- a/test/parallel/test-http-server-keep-alive-defaults.js +++ b/test/parallel/test-http-server-keep-alive-defaults.js @@ -9,7 +9,7 @@ const bodySent = 'This is my request'; function assertResponse(headers, body, expectClosed) { assert.match(headers, /Connection: keep-alive\r\n/m); - assert.match(headers, /Keep-Alive: timeout=5\r\n/m); + assert.match(headers, /Keep-Alive: timeout=65\r\n/m); assert.match(body, /Hello World!/m); } diff --git a/test/parallel/test-http-server-keep-alive-max-requests-null.js b/test/parallel/test-http-server-keep-alive-max-requests-null.js index 6a9b06960cf6f0..eedac5ad8bfe37 100644 --- a/test/parallel/test-http-server-keep-alive-max-requests-null.js +++ b/test/parallel/test-http-server-keep-alive-max-requests-null.js @@ -9,7 +9,7 @@ const bodySent = 'This is my request'; function assertResponse(headers, body, expectClosed) { assert.match(headers, /Connection: keep-alive\r\n/m); - assert.match(headers, /Keep-Alive: timeout=5\r\n/m); + assert.match(headers, /Keep-Alive: timeout=65\r\n/m); assert.match(body, /Hello World!/m); } diff --git a/test/parallel/test-http-upgrade-agent.js b/test/parallel/test-http-upgrade-agent.js index d2969522433db1..3ec774a2a795b4 100644 --- a/test/parallel/test-http-upgrade-agent.js +++ b/test/parallel/test-http-upgrade-agent.js @@ -76,7 +76,7 @@ server.listen(0, '127.0.0.1', common.mustCall(function() { assert.strictEqual(recvData.toString(), 'nurtzo'); })); - const expectedHeaders = { 'hello': 'world', + const expectedHeaders = { '__proto__': null, 'hello': 'world', 'connection': 'upgrade', 'upgrade': 'websocket' }; assert.deepStrictEqual(expectedHeaders, res.headers); diff --git a/test/parallel/test-http-upgrade-client.js b/test/parallel/test-http-upgrade-client.js index fbeaf08c2c5582..caa0d4baa41ed5 100644 --- a/test/parallel/test-http-upgrade-client.js +++ b/test/parallel/test-http-upgrade-client.js @@ -85,7 +85,8 @@ server.listen(0, common.mustCall(function() { const expectedHeaders = { hello: 'world', connection: 'upgrade', - upgrade: 'websocket' + upgrade: 'websocket', + __proto__: null }; assert.deepStrictEqual(res.headers, expectedHeaders); socket.end(); diff --git a/test/parallel/test-http-upgrade-server-with-body-and-extras.mjs b/test/parallel/test-http-upgrade-server-with-body-and-extras.mjs index 32de17ab7a57a7..bcd24faadb8054 100644 --- a/test/parallel/test-http-upgrade-server-with-body-and-extras.mjs +++ b/test/parallel/test-http-upgrade-server-with-body-and-extras.mjs @@ -24,7 +24,7 @@ server.on('upgrade', common.mustCall(function(req, socket, upgradeHead) { req.on('end', common.mustCall(() => { assert.strictEqual(reqBodyLength, EXPECTED_BODY_LENGTH); - assert.deepStrictEqual(req.trailers, { 'extra-data': 'abc' }); + assert.deepStrictEqual(req.trailers, { '__proto__': null, 'extra-data': 'abc' }); // Defer upgrade stream read slightly to make sure it doesn't start // streaming along with the request body, until we actually read it: diff --git a/test/parallel/test-http2-compat-serverrequest-trailers.js b/test/parallel/test-http2-compat-serverrequest-trailers.js index 620ae69029a375..bfa06231b8c9d7 100644 --- a/test/parallel/test-http2-compat-serverrequest-trailers.js +++ b/test/parallel/test-http2-compat-serverrequest-trailers.js @@ -22,6 +22,7 @@ server.listen(0, common.mustCall(function() { request.on('data', common.mustCallAtLeast((chunk) => data += chunk)); request.on('end', common.mustCall(() => { const trailers = request.trailers; + assert.strictEqual(Object.getPrototypeOf(trailers), null); for (const [name, value] of Object.entries(expectedTrailers)) { assert.strictEqual(trailers[name], value); } diff --git a/test/parallel/test-internal-webidl-buffer-source.js b/test/parallel/test-internal-webidl-buffer-source.js new file mode 100644 index 00000000000000..2fb529edcde1b7 --- /dev/null +++ b/test/parallel/test-internal-webidl-buffer-source.js @@ -0,0 +1,208 @@ +// Flags: --expose-internals +'use strict'; + +require('../common'); +const assert = require('assert'); +const { test } = require('node:test'); + +const { converters } = require('internal/webidl'); + +const TYPED_ARRAY_CTORS = [ + Uint8Array, Int8Array, Uint8ClampedArray, + Uint16Array, Int16Array, + Uint32Array, Int32Array, + Float16Array, Float32Array, Float64Array, + BigInt64Array, BigUint64Array, +]; + +test('BufferSource accepts ArrayBuffer', () => { + const ab = new ArrayBuffer(8); + assert.strictEqual(converters.BufferSource(ab), ab); +}); + +test('BufferSource accepts all TypedArray kinds', () => { + for (const Ctor of TYPED_ARRAY_CTORS) { + const ta = new Ctor(4); + assert.strictEqual(converters.BufferSource(ta), ta); + } +}); + +test('BufferSource accepts Buffer', () => { + const buf = Buffer.alloc(8); + assert.strictEqual(converters.BufferSource(buf), buf); +}); + +test('BufferSource accepts DataView', () => { + const dv = new DataView(new ArrayBuffer(8)); + assert.strictEqual(converters.BufferSource(dv), dv); +}); + +test('BufferSource accepts ArrayBuffer subclass instance', () => { + class MyAB extends ArrayBuffer {} + const sub = new MyAB(8); + assert.strictEqual(converters.BufferSource(sub), sub); +}); + +test('BufferSource accepts TypedArray with null prototype', () => { + const ta = new Uint8Array(4); + Object.setPrototypeOf(ta, null); + assert.strictEqual(converters.BufferSource(ta), ta); +}); + +test('BufferSource accepts DataView with null prototype', () => { + const dv = new DataView(new ArrayBuffer(4)); + Object.setPrototypeOf(dv, null); + assert.strictEqual(converters.BufferSource(dv), dv); +}); + +test('BufferSource accepts ArrayBuffer with null prototype', () => { + const ab = new ArrayBuffer(4); + Object.setPrototypeOf(ab, null); + assert.strictEqual(converters.BufferSource(ab), ab); +}); + +test('BufferSource rejects SharedArrayBuffer', () => { + assert.throws( + () => converters.BufferSource(new SharedArrayBuffer(4)), + { code: 'ERR_INVALID_ARG_TYPE' }, + ); +}); + +test('BufferSource rejects SAB-backed TypedArray', () => { + const view = new Uint8Array(new SharedArrayBuffer(4)); + assert.throws( + () => converters.BufferSource(view), + { code: 'ERR_INVALID_ARG_TYPE' }, + ); +}); + +test('BufferSource rejects SAB-backed DataView', () => { + const dv = new DataView(new SharedArrayBuffer(4)); + assert.throws( + () => converters.BufferSource(dv), + { code: 'ERR_INVALID_ARG_TYPE' }, + ); +}); + +test('BufferSource rejects SAB view whose buffer prototype was reassigned', () => { + const sab = new SharedArrayBuffer(4); + Object.setPrototypeOf(sab, ArrayBuffer.prototype); + const view = new Uint8Array(sab); + assert.throws( + () => converters.BufferSource(view), + { code: 'ERR_INVALID_ARG_TYPE' }, + ); +}); + +test('BufferSource accepts a detached ArrayBuffer', () => { + const ab = new ArrayBuffer(8); + structuredClone(ab, { transfer: [ab] }); + assert.strictEqual(ab.byteLength, 0); + assert.strictEqual(converters.BufferSource(ab), ab); +}); + +test('BufferSource rejects objects with a forged @@toStringTag', () => { + const fake = { [Symbol.toStringTag]: 'Uint8Array' }; + assert.throws( + () => converters.BufferSource(fake), + { code: 'ERR_INVALID_ARG_TYPE' }, + ); +}); + +for (const value of [null, undefined, 0, 1, 1n, '', 'x', true, Symbol('s'), [], + {}, () => {}]) { + test(`BufferSource rejects ${typeof value} ${String(value)}`, () => { + assert.throws( + () => converters.BufferSource(value), + { code: 'ERR_INVALID_ARG_TYPE' }, + ); + }); +} + +test('ArrayBufferView accepts all TypedArray kinds', () => { + for (const Ctor of TYPED_ARRAY_CTORS) { + const ta = new Ctor(4); + assert.strictEqual(converters.ArrayBufferView(ta), ta); + } +}); + +test('ArrayBufferView accepts DataView', () => { + const dv = new DataView(new ArrayBuffer(8)); + assert.strictEqual(converters.ArrayBufferView(dv), dv); +}); + +test('ArrayBufferView accepts TypedArray subclass instance', () => { + class MyU8 extends Uint8Array {} + const sub = new MyU8(4); + assert.strictEqual(converters.ArrayBufferView(sub), sub); +}); + +test('ArrayBufferView accepts TypedArray with null prototype', () => { + const ta = new Uint8Array(4); + Object.setPrototypeOf(ta, null); + assert.strictEqual(converters.ArrayBufferView(ta), ta); +}); + +test('ArrayBufferView accepts DataView with null prototype', () => { + const dv = new DataView(new ArrayBuffer(4)); + Object.setPrototypeOf(dv, null); + assert.strictEqual(converters.ArrayBufferView(dv), dv); +}); + +test('ArrayBufferView rejects raw ArrayBuffer', () => { + assert.throws( + () => converters.ArrayBufferView(new ArrayBuffer(4)), + { code: 'ERR_INVALID_ARG_TYPE' }, + ); +}); + +test('ArrayBufferView rejects raw SharedArrayBuffer', () => { + assert.throws( + () => converters.ArrayBufferView(new SharedArrayBuffer(4)), + { code: 'ERR_INVALID_ARG_TYPE' }, + ); +}); + +test('ArrayBufferView rejects SAB-backed TypedArray', () => { + const view = new Uint8Array(new SharedArrayBuffer(4)); + assert.throws( + () => converters.ArrayBufferView(view), + { code: 'ERR_INVALID_ARG_TYPE' }, + ); +}); + +test('ArrayBufferView rejects SAB-backed DataView', () => { + const dv = new DataView(new SharedArrayBuffer(4)); + assert.throws( + () => converters.ArrayBufferView(dv), + { code: 'ERR_INVALID_ARG_TYPE' }, + ); +}); + +test('ArrayBufferView rejects SAB view whose buffer prototype was reassigned', () => { + const sab = new SharedArrayBuffer(4); + Object.setPrototypeOf(sab, ArrayBuffer.prototype); + const view = new Uint8Array(sab); + assert.throws( + () => converters.ArrayBufferView(view), + { code: 'ERR_INVALID_ARG_TYPE' }, + ); +}); + +test('ArrayBufferView rejects objects with a forged @@toStringTag', () => { + const fake = { [Symbol.toStringTag]: 'Uint8Array' }; + assert.throws( + () => converters.ArrayBufferView(fake), + { code: 'ERR_INVALID_ARG_TYPE' }, + ); +}); + +for (const value of [null, undefined, 0, 1, 1n, '', 'x', true, Symbol('s'), [], + {}, () => {}]) { + test(`ArrayBufferView rejects ${typeof value} ${String(value)}`, () => { + assert.throws( + () => converters.ArrayBufferView(value), + { code: 'ERR_INVALID_ARG_TYPE' }, + ); + }); +} diff --git a/test/parallel/test-mock-timers-abortsignal-timeout.js b/test/parallel/test-mock-timers-abortsignal-timeout.js new file mode 100644 index 00000000000000..242cb3093756e1 --- /dev/null +++ b/test/parallel/test-mock-timers-abortsignal-timeout.js @@ -0,0 +1,21 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const { mock } = require('node:test'); + +const originalAbortSignalTimeout = AbortSignal.timeout; + +mock.timers.enable({ apis: ['AbortSignal.timeout'] }); + +const signal = AbortSignal.timeout(50); +assert.strictEqual(signal.aborted, false); + +mock.timers.tick(49); +assert.strictEqual(signal.aborted, false); + +mock.timers.tick(1); +assert.strictEqual(signal.aborted, true); + +mock.timers.reset(); +assert.strictEqual(AbortSignal.timeout, originalAbortSignalTimeout); diff --git a/test/parallel/test-runner-mock-timers.js b/test/parallel/test-runner-mock-timers.js index 17602e4eb90163..09075cf3a58fa8 100644 --- a/test/parallel/test-runner-mock-timers.js +++ b/test/parallel/test-runner-mock-timers.js @@ -266,6 +266,32 @@ describe('Mock Timers Test Suite', () => { assert.deepStrictEqual(fn.mock.calls[0].arguments, args); }); + it('should expose Timeout.prototype[Symbol.dispose]', (t) => { + t.mock.timers.enable({ apis: ['setTimeout'] }); + const fn = t.mock.fn(); + const timeout = globalThis.setTimeout(fn, 2000); + + assert.strictEqual(typeof timeout[Symbol.dispose], 'function'); + + timeout[Symbol.dispose](); + t.mock.timers.tick(2000); + + assert.strictEqual(fn.mock.callCount(), 0); + }); + + it('should expose Timeout.prototype.close()', (t) => { + t.mock.timers.enable({ apis: ['setTimeout'] }); + const fn = t.mock.fn(); + const timeout = globalThis.setTimeout(fn, 2000); + + assert.strictEqual(typeof timeout.close, 'function'); + assert.strictEqual(timeout.close(), timeout); + + t.mock.timers.tick(2000); + + assert.strictEqual(fn.mock.callCount(), 0); + }); + it('should keep setTimeout working if timers are disabled', (t, done) => { const now = Date.now(); const timeout = 2; diff --git a/test/parallel/test-runner-test-rerun-failures.js b/test/parallel/test-runner-test-rerun-failures.js index 4a9ea5d4ac0bee..585d5f25f04a59 100644 --- a/test/parallel/test-runner-test-rerun-failures.js +++ b/test/parallel/test-runner-test-rerun-failures.js @@ -27,6 +27,13 @@ const expectedStateFile = [ 'test/fixtures/test-runner/rerun.js:44:3': { passed_on_attempt: 0, name: 'passed on first attempt' }, 'test/fixtures/test-runner/rerun.js:47:3': { passed_on_attempt: 0, name: 'a' }, 'test/fixtures/test-runner/rerun.js:43:1': { passed_on_attempt: 0, name: 'describe rerun' }, + 'test/fixtures/test-runner/rerun.js:59:7': { passed_on_attempt: 0, name: 'shared sub A' }, + 'test/fixtures/test-runner/rerun.js:60:7': { passed_on_attempt: 0, name: 'shared sub B' }, + 'test/fixtures/test-runner/rerun.js:65:3': { passed_on_attempt: 0, name: 'first caller' }, + 'test/fixtures/test-runner/rerun.js:59:7:(1)': { passed_on_attempt: 0, name: 'shared sub A' }, + 'test/fixtures/test-runner/rerun.js:60:7:(1)': { passed_on_attempt: 0, name: 'shared sub B' }, + 'test/fixtures/test-runner/rerun.js:66:3': { passed_on_attempt: 0, name: 'second caller' }, + 'test/fixtures/test-runner/rerun.js:64:1': { passed_on_attempt: 0, name: 'rerun with ambiguous shared helper' }, }, { 'test/fixtures/test-runner/rerun.js:9:1': { passed_on_attempt: 0, name: 'ok' }, @@ -38,11 +45,17 @@ const expectedStateFile = [ 'test/fixtures/test-runner/rerun.js:39:1': { passed_on_attempt: 0, name: 'nested ambiguous (expectedAttempts=0)' }, 'test/fixtures/test-runner/rerun.js:30:16:(1)': { passed_on_attempt: 0, name: '2 levels deep' }, 'test/fixtures/test-runner/rerun.js:35:13:(1)': { passed_on_attempt: 0, name: 'ok' }, - 'test/fixtures/test-runner/rerun.js:43:1': { passed_on_attempt: 0, name: 'describe rerun' }, - 'test/fixtures/test-runner/rerun.js:44:3': { passed_on_attempt: 0, name: 'passed on first attempt' }, 'test/fixtures/test-runner/rerun.js:45:13': { passed_on_attempt: 0, name: 'nested' }, - 'test/fixtures/test-runner/rerun.js:45:13:(1)': { passed_on_attempt: 1, name: 'nested' }, + 'test/fixtures/test-runner/rerun.js:44:3': { passed_on_attempt: 0, name: 'passed on first attempt' }, 'test/fixtures/test-runner/rerun.js:47:3': { passed_on_attempt: 0, name: 'a' }, + 'test/fixtures/test-runner/rerun.js:43:1': { passed_on_attempt: 0, name: 'describe rerun' }, + 'test/fixtures/test-runner/rerun.js:59:7': { passed_on_attempt: 0, name: 'shared sub A' }, + 'test/fixtures/test-runner/rerun.js:60:7': { passed_on_attempt: 0, name: 'shared sub B' }, + 'test/fixtures/test-runner/rerun.js:65:3': { passed_on_attempt: 0, name: 'first caller' }, + 'test/fixtures/test-runner/rerun.js:59:7:(1)': { passed_on_attempt: 0, name: 'shared sub A' }, + 'test/fixtures/test-runner/rerun.js:60:7:(1)': { passed_on_attempt: 0, name: 'shared sub B' }, + 'test/fixtures/test-runner/rerun.js:66:3': { passed_on_attempt: 0, name: 'second caller' }, + 'test/fixtures/test-runner/rerun.js:64:1': { passed_on_attempt: 0, name: 'rerun with ambiguous shared helper' }, }, { 'test/fixtures/test-runner/rerun.js:3:1': { passed_on_attempt: 2, name: 'should fail on first two attempts' }, @@ -53,15 +66,21 @@ const expectedStateFile = [ 'test/fixtures/test-runner/rerun.js:29:13': { passed_on_attempt: 0, name: 'nested' }, 'test/fixtures/test-runner/rerun.js:35:13': { passed_on_attempt: 0, name: 'ok' }, 'test/fixtures/test-runner/rerun.js:39:1': { passed_on_attempt: 0, name: 'nested ambiguous (expectedAttempts=0)' }, - 'test/fixtures/test-runner/rerun.js:29:13:(1)': { passed_on_attempt: 2, name: 'nested' }, 'test/fixtures/test-runner/rerun.js:30:16:(1)': { passed_on_attempt: 0, name: '2 levels deep' }, + 'test/fixtures/test-runner/rerun.js:29:13:(1)': { passed_on_attempt: 2, name: 'nested' }, 'test/fixtures/test-runner/rerun.js:35:13:(1)': { passed_on_attempt: 0, name: 'ok' }, 'test/fixtures/test-runner/rerun.js:40:1': { passed_on_attempt: 2, name: 'nested ambiguous (expectedAttempts=1)' }, - 'test/fixtures/test-runner/rerun.js:43:1': { passed_on_attempt: 0, name: 'describe rerun' }, - 'test/fixtures/test-runner/rerun.js:44:3': { passed_on_attempt: 0, name: 'passed on first attempt' }, 'test/fixtures/test-runner/rerun.js:45:13': { passed_on_attempt: 0, name: 'nested' }, - 'test/fixtures/test-runner/rerun.js:45:13:(1)': { passed_on_attempt: 1, name: 'nested' }, + 'test/fixtures/test-runner/rerun.js:44:3': { passed_on_attempt: 0, name: 'passed on first attempt' }, 'test/fixtures/test-runner/rerun.js:47:3': { passed_on_attempt: 0, name: 'a' }, + 'test/fixtures/test-runner/rerun.js:43:1': { passed_on_attempt: 0, name: 'describe rerun' }, + 'test/fixtures/test-runner/rerun.js:59:7': { passed_on_attempt: 0, name: 'shared sub A' }, + 'test/fixtures/test-runner/rerun.js:60:7': { passed_on_attempt: 0, name: 'shared sub B' }, + 'test/fixtures/test-runner/rerun.js:65:3': { passed_on_attempt: 0, name: 'first caller' }, + 'test/fixtures/test-runner/rerun.js:59:7:(1)': { passed_on_attempt: 0, name: 'shared sub A' }, + 'test/fixtures/test-runner/rerun.js:60:7:(1)': { passed_on_attempt: 0, name: 'shared sub B' }, + 'test/fixtures/test-runner/rerun.js:66:3': { passed_on_attempt: 0, name: 'second caller' }, + 'test/fixtures/test-runner/rerun.js:64:1': { passed_on_attempt: 0, name: 'rerun with ambiguous shared helper' }, }, ]; @@ -81,26 +100,26 @@ test('test should pass on third rerun', async () => { let { code, stdout, signal } = await common.spawnPromisified(process.execPath, args); assert.strictEqual(code, 1); assert.strictEqual(signal, null); - assert.match(stdout, /pass 11/); + assert.match(stdout, /pass 17/); assert.match(stdout, /fail 4/); - assert.match(stdout, /suites 1/); + assert.match(stdout, /suites 2/); assert.deepStrictEqual(await getStateFile(), expectedStateFile.slice(0, 1)); ({ code, stdout, signal } = await common.spawnPromisified(process.execPath, args)); assert.strictEqual(code, 1); assert.strictEqual(signal, null); - assert.match(stdout, /pass 13/); + assert.match(stdout, /pass 18/); assert.match(stdout, /fail 3/); - assert.match(stdout, /suites 1/); + assert.match(stdout, /suites 2/); assert.deepStrictEqual(await getStateFile(), expectedStateFile.slice(0, 2)); ({ code, stdout, signal } = await common.spawnPromisified(process.execPath, args)); assert.strictEqual(code, 0); assert.strictEqual(signal, null); - assert.match(stdout, /pass 18/); + assert.match(stdout, /pass 21/); assert.match(stdout, /fail 0/); - assert.match(stdout, /suites 1/); + assert.match(stdout, /suites 2/); assert.deepStrictEqual(await getStateFile(), expectedStateFile); }); @@ -110,32 +129,32 @@ test('test should pass on third rerun with `--test`', async () => { let { code, stdout, signal } = await common.spawnPromisified(process.execPath, args); assert.strictEqual(code, 1); assert.strictEqual(signal, null); - assert.match(stdout, /pass 11/); + assert.match(stdout, /pass 17/); assert.match(stdout, /fail 4/); - assert.match(stdout, /suites 1/); + assert.match(stdout, /suites 2/); assert.deepStrictEqual(await getStateFile(), expectedStateFile.slice(0, 1)); ({ code, stdout, signal } = await common.spawnPromisified(process.execPath, args)); assert.strictEqual(code, 1); assert.strictEqual(signal, null); - assert.match(stdout, /pass 13/); + assert.match(stdout, /pass 18/); assert.match(stdout, /fail 3/); - assert.match(stdout, /suites 1/); + assert.match(stdout, /suites 2/); assert.deepStrictEqual(await getStateFile(), expectedStateFile.slice(0, 2)); ({ code, stdout, signal } = await common.spawnPromisified(process.execPath, args)); assert.strictEqual(code, 0); assert.strictEqual(signal, null); - assert.match(stdout, /pass 18/); + assert.match(stdout, /pass 21/); assert.match(stdout, /fail 0/); - assert.match(stdout, /suites 1/); + assert.match(stdout, /suites 2/); assert.deepStrictEqual(await getStateFile(), expectedStateFile); }); test('using `run` api', async () => { let stream = run({ files: [fixture], rerunFailuresFilePath: stateFile }); - stream.on('test:pass', common.mustCall(12)); + stream.on('test:pass', common.mustCall(19)); stream.on('test:fail', common.mustCall(4)); // eslint-disable-next-line no-unused-vars @@ -145,7 +164,7 @@ test('using `run` api', async () => { stream = run({ files: [fixture], rerunFailuresFilePath: stateFile }); - stream.on('test:pass', common.mustCall(14)); + stream.on('test:pass', common.mustCall(20)); stream.on('test:fail', common.mustCall(3)); // eslint-disable-next-line no-unused-vars @@ -155,7 +174,7 @@ test('using `run` api', async () => { stream = run({ files: [fixture], rerunFailuresFilePath: stateFile }); - stream.on('test:pass', common.mustCall(19)); + stream.on('test:pass', common.mustCall(23)); stream.on('test:fail', common.mustNotCall()); // eslint-disable-next-line no-unused-vars diff --git a/test/parallel/test-stream-duplex-from.js b/test/parallel/test-stream-duplex-from.js index 631b3586b36191..e12599fed17c14 100644 --- a/test/parallel/test-stream-duplex-from.js +++ b/test/parallel/test-stream-duplex-from.js @@ -401,3 +401,20 @@ function makeATestWritableStream(writeFunc) { assert.strictEqual(d.writable, false); })); } + +// When the readable side errors, the error must propagate to the writable side. +{ + const expectedErr = new Error('readable error'); + const r = new Readable({ read() {} }); + const w = new Writable({ + write(chunk, encoding, callback) { callback(); }, + }); + const d = Duplex.from({ readable: r, writable: w }); + d.on('error', common.mustCall((err) => { + assert.strictEqual(err, expectedErr); + })); + w.on('error', common.mustCall((err) => { + assert.strictEqual(err, expectedErr); + })); + r.destroy(expectedErr); +} diff --git a/test/parallel/test-tls-alert-handling.js b/test/parallel/test-tls-alert-handling.js index e0ed4db98fbdfe..c319e766ce8a02 100644 --- a/test/parallel/test-tls-alert-handling.js +++ b/test/parallel/test-tls-alert-handling.js @@ -97,11 +97,11 @@ function sendBADTLSRecord() { // Different OpenSSL versions send different TLS alerts when the peer // receives an invalid record on an established connection. assert.match(err.code, - /ERR_SSL_(TLSV1_ALERT_PROTOCOL_VERSION|TLSV1_ALERT_RECORD_OVERFLOW|SSL\/TLS_ALERT_UNEXPECTED_MESSAGE)/); + /ERR_SSL_(TLSV1_ALERT_PROTOCOL_VERSION|TLSV1_ALERT_RECORD_OVERFLOW|(SSL\/)?TLS_ALERT_UNEXPECTED_MESSAGE)/); assert.strictEqual(err.library, 'SSL routines'); if (!hasOpenSSL3 && !process.features.openssl_is_boringssl) assert.strictEqual(err.function, 'ssl3_read_bytes'); assert.match(err.reason, - /tlsv1[\s_]alert[\s_]protocol[\s_]version|tlsv1[\s_]alert[\s_]record[\s_]overflow|ssl\/tls[\s_]alert[\s_]unexpected[\s_]message/i); + /tlsv1[\s_]alert[\s_]protocol[\s_]version|tlsv1[\s_]alert[\s_]record[\s_]overflow|(ssl\/)?tls[\s_]alert[\s_]unexpected[\s_]message/i); })); } diff --git a/test/parallel/test-tls-client-getephemeralkeyinfo.js b/test/parallel/test-tls-client-getephemeralkeyinfo.js index 0f132c565e4400..19728e3733d868 100644 --- a/test/parallel/test-tls-client-getephemeralkeyinfo.js +++ b/test/parallel/test-tls-client-getephemeralkeyinfo.js @@ -70,7 +70,10 @@ function test(size, type, name, cipher) { test(undefined, undefined, undefined, 'AES256-SHA256'); test('auto', 'DH', undefined, 'DHE-RSA-AES256-GCM-SHA384'); -if (!hasOpenSSL(3, 2)) { +if (hasOpenSSL(4, 0)) { + // OpenSSL 4.0 implements RFC 7919 FFDHE negotiation for TLS 1.2 and + // always selects FFDHE-2048 regardless of the server-supplied dhparam. +} else if (!hasOpenSSL(3, 2)) { test(1024, 'DH', undefined, 'DHE-RSA-AES256-GCM-SHA384'); } else { test(3072, 'DH', undefined, 'DHE-RSA-AES256-GCM-SHA384'); diff --git a/test/parallel/test-tls-client-mindhsize.js b/test/parallel/test-tls-client-mindhsize.js index 778e4b710b4e92..cd7b16ea566fe8 100644 --- a/test/parallel/test-tls-client-mindhsize.js +++ b/test/parallel/test-tls-client-mindhsize.js @@ -13,6 +13,7 @@ const secLevel = require('internal/crypto/util').getOpenSSLSecLevel(); const assert = require('assert'); const tls = require('tls'); const fixtures = require('../common/fixtures'); +const { hasOpenSSL } = require('../common/crypto'); const key = fixtures.readKey('agent2-key.pem'); const cert = fixtures.readKey('agent2-cert.pem'); @@ -24,7 +25,7 @@ function loadDHParam(n) { return fixtures.readKey(`dh${n}.pem`); } -function test(size, err, next) { +function test(size, err, next, minDHSizeOverride) { const options = { key: key, cert: cert, @@ -46,7 +47,7 @@ function test(size, err, next) { // so that it fails when it makes a connection to the tls // server where is too small. This depends on the openssl // security level - const minDHSize = (secLevel > 1) ? 3072 : 2048; + const minDHSize = minDHSizeOverride ?? ((secLevel > 1) ? 3072 : 2048); const client = tls.connect({ minDHSize: minDHSize, port: this.address().port, @@ -84,7 +85,12 @@ function testDHE3072() { test(3072, false, null); } -if (secLevel > 1) { +if (hasOpenSSL(4, 0)) { + // OpenSSL 4.0 implements RFC 7919 FFDHE negotiation for TLS 1.2 and + // ignores the server-supplied dhparam in favor of FFDHE-2048. The 3072 + // success case is therefore replaced by a 2048 success case. + testDHE2048(true, () => test(2048, false, null, 2048)); +} else if (secLevel > 1) { // Minimum size for OpenSSL security level 2 and above is 2048 by default testDHE2048(true, testDHE3072); } else { diff --git a/test/parallel/test-tls-dhe.js b/test/parallel/test-tls-dhe.js index 89cda5f52a6b31..03750bc206adbe 100644 --- a/test/parallel/test-tls-dhe.js +++ b/test/parallel/test-tls-dhe.js @@ -28,6 +28,7 @@ if (!common.hasCrypto) { const { opensslCli, + hasOpenSSL, } = require('../common/crypto'); // OpenSSL has a set of security levels which affect what algorithms @@ -104,9 +105,15 @@ function testCustomParam(keylen, expectedCipher) { } (async () => { - // By default, DHE is disabled while ECDHE is enabled. + // By default, DHE is disabled while ECDHE is enabled. OpenSSL 4.0 + // implements RFC 7919 FFDHE negotiation for TLS 1.2 which enables DHE + // (with FFDHE-2048) even without a server-supplied dhparam. for (const dhparam of [undefined, null]) { - await test(dhparam, null, ecdheCipher); + if (hasOpenSSL(4, 0)) { + await test(dhparam, 2048, dheCipher); + } else { + await test(dhparam, null, ecdheCipher); + } } // The DHE parameters selected by OpenSSL depend on the strength of the @@ -124,14 +131,24 @@ function testCustomParam(keylen, expectedCipher) { // Custom DHE parameters are supported (but discouraged). // 1024 is disallowed at security level 2 and above so use 3072 instead - // for higher security levels + // for higher security levels. + // OpenSSL 4.0 implements RFC 7919 FFDHE negotiation for TLS 1.2 and + // ignores the server-supplied dhparam in favor of FFDHE-2048, so the + // negotiated key length is always 2048. if (secLevel < 2) { await testCustomParam(1024, dheCipher); + } else if (hasOpenSSL(4, 0)) { + await test(loadDHParam(3072), 2048, dheCipher); } else { await testCustomParam(3072, dheCipher); } await testCustomParam(2048, dheCipher); - // Invalid DHE parameters are discarded. ECDHE remains enabled. - await testCustomParam('error', ecdheCipher); + // Invalid DHE parameters are discarded. Prior to OpenSSL 4.0 this + // disabled DHE and ECDHE was negotiated; since 4.0, FFDHE-2048 is used. + if (hasOpenSSL(4, 0)) { + await test(loadDHParam('error'), 2048, dheCipher); + } else { + await testCustomParam('error', ecdheCipher); + } })().then(common.mustCall()); diff --git a/test/parallel/test-tls-ecdh-multiple.js b/test/parallel/test-tls-ecdh-multiple.js index 957f8e0407a6de..ee52f288610956 100644 --- a/test/parallel/test-tls-ecdh-multiple.js +++ b/test/parallel/test-tls-ecdh-multiple.js @@ -8,7 +8,7 @@ if (!common.hasCrypto) { common.skip('missing crypto'); } -const { opensslCli } = require('../common/crypto'); +const { opensslCli, hasOpenSSL } = require('../common/crypto'); const crypto = require('crypto'); if (!opensslCli) { @@ -24,11 +24,17 @@ function loadPEM(n) { return fixtures.readKey(`${n}.pem`); } +// OpenSSL 4.0 disables support for deprecated elliptic curves from RFC 8422 +// (including secp256k1) by default. +const ecdhCurve = hasOpenSSL(4, 0) ? + 'prime256v1:secp521r1' : + 'secp256k1:prime256v1:secp521r1'; + const options = { key: loadPEM('agent2-key'), cert: loadPEM('agent2-cert'), ciphers: '-ALL:ECDHE-RSA-AES128-SHA256', - ecdhCurve: 'secp256k1:prime256v1:secp521r1', + ecdhCurve, maxVersion: 'TLSv1.2', }; @@ -60,6 +66,11 @@ const server = tls.createServer(options, (conn) => { unsupportedCurves.push('brainpoolP256r1'); } + // Deprecated RFC 8422 curves are disabled by default in OpenSSL 4.0. + if (hasOpenSSL(4, 0)) { + unsupportedCurves.push('secp256k1'); + } + unsupportedCurves.forEach((ecdhCurve) => { assert.throws(() => tls.createServer({ ecdhCurve }), /Error: Failed to set ECDH curve/); diff --git a/test/parallel/test-tls-error-stack.js b/test/parallel/test-tls-error-stack.js index 36deb05b511234..02021b060ecb83 100644 --- a/test/parallel/test-tls-error-stack.js +++ b/test/parallel/test-tls-error-stack.js @@ -11,6 +11,9 @@ const tls = require('tls'); assert.throws(() => { tls.createSecureContext({ clientCertEngine: 'x' }); }, (err) => { + if (err.code === 'ERR_CRYPTO_CUSTOM_ENGINE_NOT_SUPPORTED') + common.skip('OpenSSL dropped engine support'); + return err.name === 'Error' && /could not load the shared library/.test(err.message) && Array.isArray(err.opensslErrorStack) && diff --git a/test/parallel/test-tls-ocsp-callback.js b/test/parallel/test-tls-ocsp-callback.js index 3a2d8e45f772ac..3a54ca84eb89b8 100644 --- a/test/parallel/test-tls-ocsp-callback.js +++ b/test/parallel/test-tls-ocsp-callback.js @@ -29,6 +29,7 @@ if (!common.hasCrypto) { const crypto = require('crypto'); const tls = require('tls'); const fixtures = require('../common/fixtures'); +const { hasOpenSSL } = require('../common/crypto'); const assert = require('assert'); @@ -90,7 +91,10 @@ function test(testOptions, cb) { client.on('OCSPResponse', common.mustCall((resp) => { if (testOptions.response) { - assert.strictEqual(resp.toString(), testOptions.response); + if (Buffer.isBuffer(testOptions.response)) + assert.deepStrictEqual(resp, testOptions.response); + else + assert.strictEqual(resp.toString(), testOptions.response); client.destroy(); } else { assert.strictEqual(resp, null); @@ -103,10 +107,27 @@ function test(testOptions, cb) { })); } +// OpenSSL 3.6+ validates that the value passed to +// SSL_set_tlsext_status_ocsp_resp parses as DER, so the test responses need +// to be valid DER-encoded OCSPResponse values. +// Minimal OCSPResponse is SEQUENCE { ENUMERATED responseStatus } where +// 0 = successful and 1 = malformedRequest. +const response1 = Buffer.from([0x30, 0x03, 0x0a, 0x01, 0x00]); +const response2 = Buffer.from([0x30, 0x03, 0x0a, 0x01, 0x01]); + test({ ocsp: true, response: false }); -test({ ocsp: true, response: 'hello world' }); +test({ ocsp: true, response: response1 }); test({ ocsp: false }); if (!crypto.getFips()) { - test({ ocsp: true, response: 'hello pfx', pfx: pfx, passphrase: 'sample' }); + test({ ocsp: true, response: response2, pfx: pfx, passphrase: 'sample' }); +} + +// Older OpenSSL versions accept arbitrary bytes (not just DER) as the OCSP +// response, so additionally exercise the string path there. +if (!hasOpenSSL(3, 6)) { + test({ ocsp: true, response: 'hello world' }); + if (!crypto.getFips()) { + test({ ocsp: true, response: 'hello pfx', pfx: pfx, passphrase: 'sample' }); + } } diff --git a/test/parallel/test-tls-set-ciphers-error.js b/test/parallel/test-tls-set-ciphers-error.js index 0df5a9288de1df..3cfc8c391bf7d5 100644 --- a/test/parallel/test-tls-set-ciphers-error.js +++ b/test/parallel/test-tls-set-ciphers-error.js @@ -7,12 +7,13 @@ if (!common.hasCrypto) const assert = require('assert'); const tls = require('tls'); const fixtures = require('../common/fixtures'); +const { hasOpenSSL } = require('../common/crypto'); { const options = { key: fixtures.readKey('agent2-key.pem'), cert: fixtures.readKey('agent2-cert.pem'), - ciphers: 'aes256-sha' + ciphers: 'DES-CBC-SHA' }; assert.throws(() => tls.createServer(options, common.mustNotCall()), /no[_ ]cipher[_ ]match/i); @@ -23,3 +24,19 @@ const fixtures = require('../common/fixtures'); assert.throws(() => tls.createServer(options, common.mustNotCall()), /no[_ ]cipher[_ ]match/i); } + +// Cipher name matching is case-sensitive prior to OpenSSL 4.0, and +// case-insensitive starting with OpenSSL 4.0. +{ + const options = { + key: fixtures.readKey('agent2-key.pem'), + cert: fixtures.readKey('agent2-cert.pem'), + ciphers: 'aes256-sha', + }; + if (hasOpenSSL(4, 0)) { + tls.createServer(options).close(); + } else { + assert.throws(() => tls.createServer(options, common.mustNotCall()), + /no[_ ]cipher[_ ]match/i); + } +} diff --git a/test/parallel/test-webcrypto-deduplicate-usages.js b/test/parallel/test-webcrypto-deduplicate-usages.js new file mode 100644 index 00000000000000..e30dbe7887166e --- /dev/null +++ b/test/parallel/test-webcrypto-deduplicate-usages.js @@ -0,0 +1,568 @@ +'use strict'; + +// Regression test for https://github.com/nodejs/node/issues/62899 +// SubtleCrypto.generateKey(), SubtleCrypto.importKey(), and +// KeyObject.prototype.toCryptoKey() should produce CryptoKey values whose +// `usages` have been de-duplicated and returned in a canonical order. +// The same applies to `key_ops` on JWK exports of extractable keys. + +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { createSecretKey } = require('crypto'); +const { hasOpenSSL } = require('../common/crypto'); +const { subtle } = globalThis.crypto; + +function assertSameSet(actual, expected, msg) { + if (msg === undefined) { + assert.deepStrictEqual(actual, expected); + } else { + assert.deepStrictEqual(actual, expected, msg); + } +} + +{ + const tests = []; + + // Symmetric keys (single CryptoKey result). Inputs are deliberately in + // non-canonical order so the test exercises the canonical re-ordering. + const symmetric = [ + { algorithm: { name: 'HMAC', hash: 'SHA-256' }, + usages: ['verify', 'sign', 'verify', 'sign'], + expected: ['sign', 'verify'] }, + { algorithm: { name: 'AES-CTR', length: 128 }, + usages: ['wrapKey', 'decrypt', 'encrypt', 'unwrapKey', 'wrapKey', 'encrypt'], + expected: ['encrypt', 'decrypt', 'wrapKey', 'unwrapKey'] }, + { algorithm: { name: 'AES-CBC', length: 128 }, + usages: ['encrypt', 'encrypt'], + expected: ['encrypt'] }, + { algorithm: { name: 'AES-GCM', length: 128 }, + usages: ['decrypt', 'encrypt', 'decrypt'], + expected: ['encrypt', 'decrypt'] }, + ]; + + if (!process.features.openssl_is_boringssl) { + symmetric.push({ + algorithm: { name: 'AES-KW', length: 128 }, + usages: ['wrapKey', 'unwrapKey', 'wrapKey', 'unwrapKey'], + expected: ['wrapKey', 'unwrapKey'], + }); + } else { + common.printSkipMessage('AES-KW is not supported in BoringSSL'); + } + + if (hasOpenSSL(3)) { + symmetric.push({ + algorithm: { name: 'AES-OCB', length: 128 }, + usages: ['decrypt', 'encrypt', 'decrypt', 'encrypt'], + expected: ['encrypt', 'decrypt'], + }); + symmetric.push({ + algorithm: { name: 'KMAC128', length: 128 }, + usages: ['verify', 'sign', 'verify', 'sign'], + expected: ['sign', 'verify'], + }); + } else { + common.printSkipMessage('AES-OCB and KMAC require OpenSSL >= 3'); + } + + if (!process.features.openssl_is_boringssl) { + symmetric.push({ + algorithm: { name: 'ChaCha20-Poly1305' }, + usages: ['wrapKey', 'decrypt', 'encrypt', 'unwrapKey', 'wrapKey', 'encrypt'], + expected: ['encrypt', 'decrypt', 'wrapKey', 'unwrapKey'], + }); + } else { + common.printSkipMessage('ChaCha20-Poly1305 is not supported in BoringSSL'); + } + + for (const { algorithm, usages, expected } of symmetric) { + tests.push((async () => { + const key = await subtle.generateKey(algorithm, true, usages); + assertSameSet(key.usages, expected, + `generateKey ${algorithm.name}`); + assert.strictEqual(key.usages.length, expected.length, + `generateKey ${algorithm.name} usage count`); + })()); + } + + // Asymmetric keys (CryptoKeyPair result). Duplicates across the input + // must not produce duplicates on either the public or private key. + const asymmetric = [ + { algorithm: { name: 'RSA-OAEP', modulusLength: 2048, + publicExponent: new Uint8Array([1, 0, 1]), hash: 'SHA-256' }, + usages: ['wrapKey', 'unwrapKey', 'decrypt', 'encrypt', + 'unwrapKey', 'wrapKey', 'decrypt', 'encrypt'], + publicExpected: ['encrypt', 'wrapKey'], + privateExpected: ['decrypt', 'unwrapKey'] }, + { algorithm: { name: 'RSA-PSS', modulusLength: 2048, + publicExponent: new Uint8Array([1, 0, 1]), hash: 'SHA-256' }, + usages: ['verify', 'sign', 'verify', 'sign'], + publicExpected: ['verify'], + privateExpected: ['sign'] }, + { algorithm: { name: 'ECDSA', namedCurve: 'P-256' }, + usages: ['verify', 'sign', 'verify', 'sign', 'verify'], + publicExpected: ['verify'], + privateExpected: ['sign'] }, + { algorithm: { name: 'ECDH', namedCurve: 'P-256' }, + usages: ['deriveBits', 'deriveKey', 'deriveBits', 'deriveKey'], + publicExpected: [], + privateExpected: ['deriveKey', 'deriveBits'] }, + { algorithm: { name: 'Ed25519' }, + usages: ['verify', 'sign', 'verify', 'sign'], + publicExpected: ['verify'], + privateExpected: ['sign'] }, + { algorithm: { name: 'X25519' }, + usages: ['deriveBits', 'deriveKey', 'deriveBits', 'deriveKey'], + publicExpected: [], + privateExpected: ['deriveKey', 'deriveBits'] }, + ]; + + if (hasOpenSSL(3, 5)) { + asymmetric.push({ + algorithm: { name: 'ML-DSA-65' }, + usages: ['verify', 'sign', 'verify', 'sign'], + publicExpected: ['verify'], + privateExpected: ['sign'], + }); + asymmetric.push({ + algorithm: { name: 'ML-KEM-768' }, + usages: ['decapsulateBits', 'encapsulateBits', 'decapsulateKey', + 'encapsulateKey', 'decapsulateBits', 'encapsulateBits'], + publicExpected: ['encapsulateKey', 'encapsulateBits'], + privateExpected: ['decapsulateKey', 'decapsulateBits'], + }); + } else { + common.printSkipMessage('ML-DSA and ML-KEM require OpenSSL >= 3.5'); + } + + for (const { algorithm, usages, publicExpected, privateExpected } of asymmetric) { + tests.push((async () => { + const { publicKey, privateKey } = + await subtle.generateKey(algorithm, true, usages); + assertSameSet(publicKey.usages, publicExpected, + `generateKey ${algorithm.name} publicKey`); + assert.strictEqual(publicKey.usages.length, publicExpected.length); + assertSameSet(privateKey.usages, privateExpected, + `generateKey ${algorithm.name} privateKey`); + assert.strictEqual(privateKey.usages.length, privateExpected.length); + })()); + } + + Promise.all(tests).then(common.mustCall()); +} + +{ + const tests = []; + + // Symmetric raw imports. + const rawSymmetric = [ + { algorithm: { name: 'AES-CBC' }, keyData: new Uint8Array(16), + usages: ['decrypt', 'encrypt', 'decrypt', 'encrypt'], + expected: ['encrypt', 'decrypt'] }, + { algorithm: { name: 'AES-CTR' }, keyData: new Uint8Array(16), + usages: ['wrapKey', 'encrypt', 'wrapKey', 'encrypt'], + expected: ['encrypt', 'wrapKey'] }, + { algorithm: { name: 'AES-GCM' }, keyData: new Uint8Array(16), + usages: ['decrypt', 'encrypt', 'decrypt'], + expected: ['encrypt', 'decrypt'] }, + { algorithm: { name: 'HMAC', hash: 'SHA-256' }, keyData: new Uint8Array(32), + usages: ['verify', 'sign', 'verify', 'sign'], + expected: ['sign', 'verify'] }, + ]; + + if (!process.features.openssl_is_boringssl) { + rawSymmetric.push({ + algorithm: { name: 'AES-KW' }, keyData: new Uint8Array(16), + usages: ['wrapKey', 'unwrapKey', 'wrapKey'], + expected: ['wrapKey', 'unwrapKey'], + }); + } else { + common.printSkipMessage('AES-KW is not supported in BoringSSL'); + } + + if (hasOpenSSL(3)) { + // KMAC does not support `raw` format, only `raw-secret` and `jwk`. + tests.push((async () => { + const key = await subtle.importKey( + 'raw-secret', new Uint8Array(16), { name: 'KMAC128' }, true, + ['verify', 'sign', 'verify', 'sign']); + assertSameSet(key.usages, ['sign', 'verify'], + 'importKey raw-secret KMAC128'); + assert.strictEqual(key.usages.length, 2); + })()); + + tests.push((async () => { + const jwk = { + kty: 'oct', + k: Buffer.from(new Uint8Array(16)).toString('base64url'), + alg: 'K128', + }; + const key = await subtle.importKey( + 'jwk', jwk, { name: 'KMAC128' }, true, + ['verify', 'sign', 'verify', 'sign']); + assertSameSet(key.usages, ['sign', 'verify'], + 'importKey jwk KMAC128'); + assert.strictEqual(key.usages.length, 2); + })()); + } else { + common.printSkipMessage('AES-OCB and KMAC require OpenSSL >= 3'); + } + + for (const { algorithm, keyData, usages, expected } of rawSymmetric) { + tests.push((async () => { + const key = await subtle.importKey('raw', keyData, algorithm, true, usages); + assertSameSet(key.usages, expected, + `importKey raw ${algorithm.name}`); + assert.strictEqual(key.usages.length, expected.length); + })()); + } + + // Generic secret keys (HKDF, PBKDF2) - importGenericSecretKey path. + // These are not extractable. + for (const name of ['HKDF', 'PBKDF2']) { + tests.push((async () => { + const key = await subtle.importKey( + 'raw', + new Uint8Array(16), + name, + false, + ['deriveBits', 'deriveKey', 'deriveBits', 'deriveKey']); + assertSameSet(key.usages, ['deriveKey', 'deriveBits'], + `importKey raw ${name}`); + assert.strictEqual(key.usages.length, 2); + })()); + } + + // Argon2 - also via importGenericSecretKey, deriveBits-only. + // Argon2 only supports raw-secret import. + if (hasOpenSSL(3, 2)) { + tests.push((async () => { + const key = await subtle.importKey( + 'raw-secret', + new Uint8Array(16), + 'Argon2id', + false, + ['deriveBits', 'deriveBits']); + assertSameSet(key.usages, ['deriveBits'], + 'importKey raw-secret Argon2id'); + assert.strictEqual(key.usages.length, 1); + })()); + } else { + common.printSkipMessage('Argon2 requires OpenSSL >= 3.2'); + } + + // JWK symmetric import. + tests.push((async () => { + const jwk = { + kty: 'oct', + k: 'AAAAAAAAAAAAAAAAAAAAAA', + alg: 'A128CBC', + }; + const key = await subtle.importKey('jwk', jwk, { name: 'AES-CBC' }, true, + ['decrypt', 'encrypt', 'decrypt']); + assertSameSet(key.usages, ['encrypt', 'decrypt'], + 'importKey jwk AES-CBC'); + assert.strictEqual(key.usages.length, 2); + })()); + + // Asymmetric import via JWK - RSA, ECDSA, Ed25519. + tests.push((async () => { + // Generate, export, re-import with duplicate usages. + const { privateKey } = await subtle.generateKey( + { name: 'RSA-PSS', modulusLength: 2048, + publicExponent: new Uint8Array([1, 0, 1]), hash: 'SHA-256' }, + true, ['sign', 'verify']); + const jwk = await subtle.exportKey('jwk', privateKey); + const imported = await subtle.importKey( + 'jwk', jwk, + { name: 'RSA-PSS', hash: 'SHA-256' }, + true, + ['sign', 'sign', 'sign']); + assert.deepStrictEqual(imported.usages, ['sign']); + })()); + + tests.push((async () => { + const { privateKey } = await subtle.generateKey( + { name: 'ECDSA', namedCurve: 'P-256' }, + true, ['sign', 'verify']); + const jwk = await subtle.exportKey('jwk', privateKey); + const imported = await subtle.importKey( + 'jwk', jwk, + { name: 'ECDSA', namedCurve: 'P-256' }, + true, + ['sign', 'sign']); + assert.deepStrictEqual(imported.usages, ['sign']); + })()); + + tests.push((async () => { + const { privateKey } = await subtle.generateKey( + { name: 'Ed25519' }, true, ['sign', 'verify']); + const pkcs8 = await subtle.exportKey('pkcs8', privateKey); + const imported = await subtle.importKey( + 'pkcs8', pkcs8, + { name: 'Ed25519' }, + true, + ['sign', 'sign', 'sign']); + assert.deepStrictEqual(imported.usages, ['sign']); + })()); + + if (hasOpenSSL(3, 5)) { + // ML-DSA JWK roundtrip. + tests.push((async () => { + const { privateKey } = await subtle.generateKey( + { name: 'ML-DSA-65' }, true, ['sign', 'verify']); + const jwk = await subtle.exportKey('jwk', privateKey); + const imported = await subtle.importKey( + 'jwk', jwk, { name: 'ML-DSA-65' }, true, + ['sign', 'sign', 'sign']); + assert.deepStrictEqual(imported.usages, ['sign']); + })()); + + // ML-KEM JWK roundtrip. + tests.push((async () => { + const { privateKey } = await subtle.generateKey( + { name: 'ML-KEM-768' }, true, + ['decapsulateKey', 'decapsulateBits']); + const jwk = await subtle.exportKey('jwk', privateKey); + const imported = await subtle.importKey( + 'jwk', jwk, { name: 'ML-KEM-768' }, true, + ['decapsulateBits', 'decapsulateKey', + 'decapsulateBits', 'decapsulateKey']); + assert.deepStrictEqual(imported.usages, + ['decapsulateKey', 'decapsulateBits']); + })()); + } else { + common.printSkipMessage('ML-DSA and ML-KEM require OpenSSL >= 3.5'); + } + + // Spki import of RSA public key. + tests.push((async () => { + const { publicKey } = await subtle.generateKey( + { name: 'RSA-OAEP', modulusLength: 2048, + publicExponent: new Uint8Array([1, 0, 1]), hash: 'SHA-256' }, + true, ['encrypt', 'decrypt', 'wrapKey', 'unwrapKey']); + const spki = await subtle.exportKey('spki', publicKey); + const imported = await subtle.importKey( + 'spki', spki, + { name: 'RSA-OAEP', hash: 'SHA-256' }, + true, + ['wrapKey', 'encrypt', 'wrapKey', 'encrypt']); + assertSameSet(imported.usages, ['encrypt', 'wrapKey']); + assert.strictEqual(imported.usages.length, 2); + })()); + + // ChaCha20-Poly1305 raw-secret import. + if (!process.features.openssl_is_boringssl) { + tests.push((async () => { + const key = await subtle.importKey( + 'raw-secret', + new Uint8Array(32), + { name: 'ChaCha20-Poly1305' }, + true, + ['decrypt', 'encrypt', 'decrypt', 'encrypt']); + assertSameSet(key.usages, ['encrypt', 'decrypt']); + assert.strictEqual(key.usages.length, 2); + })()); + } else { + common.printSkipMessage('ChaCha20-Poly1305 is not supported in BoringSSL'); + } + + // AES-OCB raw-secret import. + if (hasOpenSSL(3)) { + tests.push((async () => { + const key = await subtle.importKey( + 'raw-secret', + new Uint8Array(16), + { name: 'AES-OCB' }, + true, + ['decrypt', 'encrypt', 'decrypt', 'encrypt']); + assertSameSet(key.usages, ['encrypt', 'decrypt']); + assert.strictEqual(key.usages.length, 2); + })()); + } else { + common.printSkipMessage('AES-OCB requires OpenSSL >= 3'); + } + + Promise.all(tests).then(common.mustCall()); +} + +{ + const tests = []; + + // Symmetric: HMAC, AES-*, HKDF, PBKDF2 + tests.push((async () => { + const keyObject = createSecretKey(new Uint8Array(32)); + const key = keyObject.toCryptoKey( + { name: 'HMAC', hash: 'SHA-256' }, + true, + ['verify', 'sign', 'verify', 'sign']); + assertSameSet(key.usages, ['sign', 'verify']); + assert.strictEqual(key.usages.length, 2); + })()); + + tests.push((async () => { + const keyObject = createSecretKey(new Uint8Array(16)); + const key = keyObject.toCryptoKey( + { name: 'AES-GCM' }, + true, + ['decrypt', 'encrypt', 'decrypt']); + assertSameSet(key.usages, ['encrypt', 'decrypt']); + assert.strictEqual(key.usages.length, 2); + })()); + + tests.push((async () => { + const keyObject = createSecretKey(new Uint8Array(32)); + const key = keyObject.toCryptoKey( + 'HKDF', + false, + ['deriveBits', 'deriveKey', 'deriveBits']); + assertSameSet(key.usages, ['deriveKey', 'deriveBits']); + assert.strictEqual(key.usages.length, 2); + })()); + + Promise.all(tests).then(common.mustCall()); +} + +{ + (async () => { + const key = await subtle.generateKey( + { name: 'AES-CTR', length: 128 }, + true, + ['wrapKey', 'encrypt', 'decrypt', 'encrypt', 'wrapKey', 'unwrapKey']); + // Regardless of the input order, de-duplicated usages are returned in + // a canonical order. + assert.deepStrictEqual( + key.usages, + ['encrypt', 'decrypt', 'wrapKey', 'unwrapKey']); + })().then(common.mustCall()); +} + +// Exported JWK `key_ops` must also be de-duplicated. +{ + const tests = []; + + const jwkVectors = [ + { algorithm: { name: 'HMAC', hash: 'SHA-256' }, + usages: ['verify', 'sign', 'verify', 'sign'], + expected: ['sign', 'verify'] }, + { algorithm: { name: 'AES-CBC', length: 128 }, + usages: ['decrypt', 'encrypt', 'decrypt', 'encrypt'], + expected: ['encrypt', 'decrypt'] }, + { algorithm: { name: 'AES-GCM', length: 128 }, + usages: ['decrypt', 'encrypt', 'decrypt'], + expected: ['encrypt', 'decrypt'] }, + ]; + + if (!process.features.openssl_is_boringssl) { + jwkVectors.push({ + algorithm: { name: 'AES-KW', length: 128 }, + usages: ['wrapKey', 'unwrapKey', 'wrapKey', 'unwrapKey'], + expected: ['wrapKey', 'unwrapKey'], + }); + } else { + common.printSkipMessage('AES-KW is not supported in BoringSSL'); + } + + if (hasOpenSSL(3)) { + jwkVectors.push({ + algorithm: { name: 'AES-OCB', length: 128 }, + usages: ['decrypt', 'encrypt', 'decrypt', 'encrypt'], + expected: ['encrypt', 'decrypt'], + }); + jwkVectors.push({ + algorithm: { name: 'KMAC128', length: 128 }, + usages: ['verify', 'sign', 'verify', 'sign'], + expected: ['sign', 'verify'], + }); + } else { + common.printSkipMessage('AES-OCB and KMAC require OpenSSL >= 3'); + } + + for (const { algorithm, usages, expected } of jwkVectors) { + tests.push((async () => { + const key = await subtle.generateKey(algorithm, true, usages); + const jwk = await subtle.exportKey('jwk', key); + assertSameSet(jwk.key_ops, expected, + `jwk key_ops for ${algorithm.name}`); + assert.strictEqual(jwk.key_ops.length, expected.length, + `jwk key_ops length for ${algorithm.name}`); + })()); + } + + const jwkPairVectors = [ + { algorithm: { name: 'RSA-OAEP', modulusLength: 2048, + publicExponent: new Uint8Array([1, 0, 1]), hash: 'SHA-256' }, + usages: ['wrapKey', 'unwrapKey', 'decrypt', 'encrypt', + 'unwrapKey', 'wrapKey', 'decrypt', 'encrypt'], + publicExpected: ['encrypt', 'wrapKey'], + privateExpected: ['decrypt', 'unwrapKey'] }, + { algorithm: { name: 'RSA-PSS', modulusLength: 2048, + publicExponent: new Uint8Array([1, 0, 1]), hash: 'SHA-256' }, + usages: ['verify', 'sign', 'verify', 'sign'], + publicExpected: ['verify'], + privateExpected: ['sign'] }, + { algorithm: { name: 'ECDSA', namedCurve: 'P-256' }, + usages: ['verify', 'sign', 'verify', 'sign', 'verify'], + publicExpected: ['verify'], + privateExpected: ['sign'] }, + { algorithm: { name: 'ECDH', namedCurve: 'P-256' }, + usages: ['deriveBits', 'deriveKey', 'deriveBits', 'deriveKey'], + publicExpected: undefined, + privateExpected: ['deriveKey', 'deriveBits'] }, + { algorithm: { name: 'Ed25519' }, + usages: ['verify', 'sign', 'verify', 'sign'], + publicExpected: ['verify'], + privateExpected: ['sign'] }, + { algorithm: { name: 'X25519' }, + usages: ['deriveBits', 'deriveKey', 'deriveBits', 'deriveKey'], + publicExpected: undefined, + privateExpected: ['deriveKey', 'deriveBits'] }, + ]; + + if (hasOpenSSL(3, 5)) { + jwkPairVectors.push({ + algorithm: { name: 'ML-DSA-65' }, + usages: ['verify', 'sign', 'verify', 'sign'], + publicExpected: ['verify'], + privateExpected: ['sign'], + }); + jwkPairVectors.push({ + algorithm: { name: 'ML-KEM-768' }, + usages: ['decapsulateBits', 'encapsulateBits', 'decapsulateKey', + 'encapsulateKey', 'decapsulateBits', 'encapsulateBits'], + publicExpected: ['encapsulateKey', 'encapsulateBits'], + privateExpected: ['decapsulateKey', 'decapsulateBits'], + }); + } else { + common.printSkipMessage('ML-DSA and ML-KEM require OpenSSL >= 3.5'); + } + + for (const { algorithm, usages, publicExpected, privateExpected } of jwkPairVectors) { + tests.push((async () => { + const { publicKey, privateKey } = + await subtle.generateKey(algorithm, true, usages); + const publicJwk = await subtle.exportKey('jwk', publicKey); + const privateJwk = await subtle.exportKey('jwk', privateKey); + + if (publicExpected === undefined) { + // Empty public-key usages result in an empty `key_ops`. + assert.deepStrictEqual(publicJwk.key_ops, [], + `jwk key_ops for ${algorithm.name} publicKey`); + } else { + assertSameSet(publicJwk.key_ops, publicExpected, + `jwk key_ops for ${algorithm.name} publicKey`); + assert.strictEqual(publicJwk.key_ops.length, publicExpected.length); + } + + assertSameSet(privateJwk.key_ops, privateExpected, + `jwk key_ops for ${algorithm.name} privateKey`); + assert.strictEqual(privateJwk.key_ops.length, privateExpected.length); + })()); + } + + Promise.all(tests).then(common.mustCall()); +} diff --git a/test/parallel/test-webcrypto-export-import-ml-dsa.js b/test/parallel/test-webcrypto-export-import-ml-dsa.js index f522b2cd5066e7..7030c452003c63 100644 --- a/test/parallel/test-webcrypto-export-import-ml-dsa.js +++ b/test/parallel/test-webcrypto-export-import-ml-dsa.js @@ -25,46 +25,18 @@ function toDer(pem) { return Buffer.alloc(Buffer.byteLength(der, 'base64'), der, 'base64'); } -/* eslint-disable @stylistic/js/max-len */ -const keyData = { - 'ML-DSA-44': { - pkcs8_seed_only: toDer(fixtures.readKey(getKeyFileName('ml-dsa-44', 'private_seed_only'), 'ascii')), - pkcs8: toDer(fixtures.readKey(getKeyFileName('ml-dsa-44', 'private'), 'ascii')), - pkcs8_priv_only: toDer(fixtures.readKey(getKeyFileName('ml-dsa-44', 'private_priv_only'), 'ascii')), - spki: toDer(fixtures.readKey(getKeyFileName('ml-dsa-44', 'public'), 'ascii')), - jwk: { - kty: 'AKP', - alg: 'ML-DSA-44', - pub: 'fYmD1Rx_jkoW9KG7Bs_5zyYEiWEZs15tYBxNdKq9NircZnvZBwwwaGbj0UsxJNc4Dyfp2IFAZZPO3rFCSUdpXHPrGRHwIVMzwiwfu2V7V02xoheW4mrkPThA3JRJSmNdsx6YGu37MaeJkIk6AlUexo46JfGrkRXZp_IyZxiL_L2dPrfwx-32j7WFI5sBadp7cDWfNkJjdQwW4puTe5Rw7h16GHb-DMOAKpfeMHujh7IYHuLCU6lVi90j1m8Ru0dxdmeQ1eY1vDnO7fNQKfzOLhpUNnj7BBZ24GTqFc-SN5HDCSCsSGKScTYYBwiSVTdSGG1GNqIiN2FgE4z1Jj6JFVB_OIUnl4sKbb3m8kB0BwtUPbkC0FVokGRUEGt6ba1Pc_IMpB5Gs3g9PFREI_C9o1yVW3NS2PzH_Vk4Tpf0N1K1kzIK_3IqekLfyqXmVDNsOovsS7Sw9TdmdWUNGRmhXFKRkex5VjpMIx7OwBGsYJCc4FhauWdrVtbkvHGggSpsla73ZcA4Vzh7aq47LMv0KS2YLp-DMn7SEohPHGg74118eLLn88yptxwtwt1dBFj8BKUfPrytuN1EIRQy34hwbkBLN9wDqhgn3Z3fvksRvmgN_4ZQ8YjeD-H3OFh5WJ_Rd66wHSl-YFat-_JF4UPcdlkNUbxPvDi5VL909Pe3VlwEZhT5otdtXQX4U3dUfqWKEh2kN0Q2lo8wbf3OMmBOFTfyX0eYa_5088ZnJvvliefn-TCDyc6WlcZrNqwBOF8N8-IN3b_8RPq-RuV8-mK-M83Hi4ElQB7Z44eZMmfUwFrozEG4Wq2K6MwQ_edG4dWeUVMCloTpGDFOtlLQlDoAN4m_sS2Lbwm_3ra29noUcK8_j10yy-hENE2Yluh1pIL-GoWZj3uYO-rEKVbszaagdE0DJ_uQcHUdNnBHKn64-cQ6xihXzxaeHx9OxkWWMKbzLtKpuYDK_X7EVvm8YTjl_oTsr2SWT2usjNJko32DhRV-OXLKKHo5FJpCy2bGFLXGG26CglUvgZQ2dyXiWeGVNKffOv1cQ5R_RlU2MpLiZ1bigy9hh4lu_XAHLfjQfhf71jeMuF4nEBWV-YOAjDTaDB2hcGqv_XcGXcmLWHqOWgc5Mb6lkb2zYs_oyOskmyFx6C0P7UrV8kCiN4zbuTqZNdNjlWL_QJUmU3vk6CpNa0XN1M3sLjZpOEsaqgRVPLcIDH-juVhyWiymuxe-8yNCOFSKxhscew08EQ9DEckP_iIA8qU2gcreHtvAS5VA5Emz1K2ypYe6oS3ogP-CX4nOAEfvjsb1HHJoclgiwjL1BtCLFgOE-0vn1M-nVOE6WbHGHoNKMJMHP2a3HQC7DmDfSOw5P6Cj5X7QVqhCY6tAGZWEPu3hUssp7K5UJePEdBn_LrErt4ucyXW6y1PAA2Fn8EuHaRyf2ggibDGnzq8E15m_R4LMvZAuGR0bN9jBTlm_x4ZQMqFwKkIdllkN1QTErazOyNsgU6fhA_20h5EIYT6-LqXr_Otj3Kp8MkJB9c3XNGoo5sbHTQCt0VNOHoxCFP_swiAJLtm743eOsI1M6naWLIqPagSCioosAvJYowypJQGvM-N3hBu8KUr0f911KRN7WqTAXTOHZ_vvTqcWKet0dFdh1EHuP3TrU8hSMciaphGvuK93T3gaWuJ6lcCkQndWvEo9S6FQB7eLU_ALKOQ3ROybUUkXgfyTkWDPxbHdeJCgMRv6Ig1PShPyxYb4ig', - priv: '273AhMPiZWLlSQCY41yi1fMj6xavGH0btB23zMhI1uY', - }, - }, - 'ML-DSA-65': { - pkcs8_seed_only: toDer(fixtures.readKey(getKeyFileName('ml-dsa-65', 'private_seed_only'), 'ascii')), - pkcs8: toDer(fixtures.readKey(getKeyFileName('ml-dsa-65', 'private'), 'ascii')), - pkcs8_priv_only: toDer(fixtures.readKey(getKeyFileName('ml-dsa-65', 'private_priv_only'), 'ascii')), - spki: toDer(fixtures.readKey(getKeyFileName('ml-dsa-65', 'public'), 'ascii')), - jwk: { - kty: 'AKP', - alg: 'ML-DSA-65', - pub: 'hxPP5LvG83t2fJyfA1TUssJK_ydrzryrCHGZuKFxmnl5Y3sxHRCPW_JpHEoiIgR6kgELnwibZnueax1zFerTOTA7o0NwXHFiaEB-8AmqJI93DkvtbUOSTCixa3admQBKW_PtgMCVtaEVuuvCuOEFhOyuZkyfvnpBwUKOkz3t-O1wpgrSmf-rdPXOEv8YcsSn-xfLYPSLzPCnt7gnIX_fwtkgnXjref-QqjFKlKZE2e7MkmHeViJ4iGy78r3UzVhBHsmFGC0ZNc8-iT3muH5Sn0SXmNq-F2EoerWLIAsPxL2KE6UrqPAwTbHn1B5sAGWvhsVVLlFPI1s1JLVLBNRJ5vhif525xNIpMAMuAZrteD827pve3zQo9_GHjWgykj9VzM9PEcVmVqxZ5u41kUXsM4PWZF29Oh2sYsmJ2LdiJ9RcA91vRLG2DqEYm-V5JwIz8uxL17DUsEC7zYthvtqGASq05CbfPTBev33rQUv4H0Etz99U89WooTk0FisHDz1uEUilU_VY1tN5byIDitXNf0jnz3SIHDUUZARn7ll0YwO0jtksT68sQW3Liy6Exhlp1td0so2qZUrbVZasjyCOVuibwbwvrdpP3QRsoG5UqkAqk8Rm2iCpdQSg87pswOscgA8AC8TczGHNfXc9PqzAmbsEPKvmZuE60HLGzqpRqFULf3nyYUQUqbdmKJsKQ29LXeDVbyy3-fkTUDuYqNC2tBY7PkzHJSA9Z4hDC_BHEFxcelibScSNyf7y4lDVWnuJMXpQ0WRh3UkUPa007IerhixwxvBvFXQR-ytYinixvjirlcEF1wQI1DzE8KjOXYYuFPS4Yl8HeZHQ-64Q0RuxlKIRP3YvjZWh4IDVvEVs5ZLzZPbE3Twe0N5a7iCu0BzZWTeHNbcMoViFyJTpec2w3vVHeI4PJB-5HeI8xuh-9y8ytTau8QtMe4thoROoajizDQLrkw3e6ryJJ3R84i0oni4vmZWyLDilwcLqPOkQJCIDMjq7exdmVX5t3DtAW4F6Coz0z3sf7tGlSMVxA7izCoVbG0y2_l1P2h7fWBuEPT7PWlMdPqu9Pj5jqXY6jJ0nkaR_pp7dDhO1HKae5edcBYunHZqVQQjRZ_DvKzbPrDk5t6Xq9fdSkiAeP3B4qn5uU-nx7OaX7DRoVEnbbiEDynIRPSEY-Ts3alPJtBv8zuzaGNyX05Z9MyZ0w-VlC-WxOBdVEsIAp_4uJ3kQ3UsfE9DLJH8WPuDI4t4i2VnNNyFlI0XSUocc_0rWgqp2I1UzSzkVbklwkuFywPI645u4G2XAlfdd_wpjFGC-IUPXgpeSfspPwW15sBP-ITS-gwtvfzQVLpRS0euzN97xo_GMhNPZ4bW-YyZt8z_R8bsQ8ktfoP-5RUV-yzYDt0tA01QJsZdBLf5J_H7qP8l4c8V4hPe_CFL032obbxmAnVPAP69u2SaMBlL8azjk4wGVFQpQp1JqMJCao2W8ZImCVegkPZxhGbx0nkgVfyFx4ihMeDNM288JbGC4CGON8C02Q84rQzhwzZE83Y9rSe1Bb0fUMHMu6ihD5jLdeltuBL4ZdJlKgL24KZK5o5pq4_l9SyzGAjB1KAQnClNOB1SxV89CtILu-65wb17s0z3qw2-NF0B6UVlGQFebjbSyLQv2ARaETh_8cBiPugVMgBIV3K1KBwNyWejyI1ZDCssvIZHJCF2SRW3HmJerTiB23eGHFYKSLdxW7LEzoHIc2xZEc3pwR43gavjeoL0pNc-HNFV_c19wiH7Tnw3IHld_FfTqAIPnqKMNIY7D_D0DmFNTOdnzcipqKxUB0Avc-wr8Fz0gjeRpLH2iDSCJWtvWjoeYvHTktGsblDAM5j9xznwEvZfQvj8fTUnFxl3clkD6e9V1jrDQDkXfOtl-bDIv9PtMwamfJFu-z2ubF-gKytUewPNo10uhwr2TDNdUayCZDR2T3HoRLN8goIw2bFoPJ98LoPcSukEvKABjH0DiHNeqFELNZPx_uCx5N-YFkUZxHWA1QUoGhqQ3REtcT3c-SZf_TDFOPvws6bmwt4lcWpLmubOAJLFt6J8m8HCkVUshRdFzvHQm_0JEvA3JtyXZzvsPUv5njdk0nxTZktvsnqX054RQk5x8U-lBY-bK3uMOoFnHju45LMoHCUgGJi22eUm7nLGZEh84ZAbNlPLXpfavXvPJh21OW5EOAeuQ-yWNHY2xbmAiHNnb-J2VpZc1Vy82sxn8umFtKduuQuIQMOsf4qHqj5MzDY_1NjrM4Wm7XAiLC4MpQ22w9PWNQXSZWvo2fj8WUnfEibpgyRkoD2P25GRQqsRJ3-Ykl5bm_2Vfe6i3oXHOwQwZwKGXfAqXyo4iU1UI7e-qC4sj5U64oB_A_NSBaJJrZoQ2fVeGTnFxA4QMMoWCT0VlwBXK0B3jht8Xal3WcI-i9ctQB1-GrmwwgG2ttePHt1IKy69bSZE3FLkFicaHg6VxypG6ef8rVsmMrfpTATOnF5_iEaLNY9428HHGW0iz4vXwaE-MkYy7NK2KMPFiCB0ec9OjIROwayK4LREv4qknWHnVQRSm25Rr9DcVFXKj16Au7X1hv7TuVH7h25U', - priv: '1X9VEr_iXMRwBvnSytEmHrtA-DpD6FWAUqMrDNlJVBg', - }, - }, - 'ML-DSA-87': { - pkcs8_seed_only: toDer(fixtures.readKey(getKeyFileName('ml-dsa-87', 'private_seed_only'), 'ascii')), - pkcs8: toDer(fixtures.readKey(getKeyFileName('ml-dsa-87', 'private'), 'ascii')), - pkcs8_priv_only: toDer(fixtures.readKey(getKeyFileName('ml-dsa-87', 'private_priv_only'), 'ascii')), - spki: toDer(fixtures.readKey(getKeyFileName('ml-dsa-87', 'public'), 'ascii')), - jwk: { - kty: 'AKP', - alg: 'ML-DSA-87', - pub: 'DZXqaBATRN0GRtigxzkLxp7C9fFYxI7Gl-tdfXqJHbzVCTTvRfRwZcu3YmpsUYXBdX2pVsQ51QlqxMslKBRmfNCanBLcfd57qoEIb0K6GIKZGHxlsr9aXNjEGcKMo0ICon0LYTvTWrl72Oz-2yEA_abPK3_dBUFGAYQ6kOQhAHcT1CMmTTck23PnEd5WUpYfZOA9giFX9dNVrrdFWczj_vDOty81ObNKsxfVWT1nG7c60UCJxb2c2tMrBx3rp7Hfc_aOg54W5KHocJi0Eai0ok4buySTe0UCSCUTkeoCcdiABgOFBiXRYzpm3Lz4uot6hSgFpuh67fE9Zpgtn64vfI-O1mgcPrPPpd3yA92Jrq-dvXuM55w1RmA_hha3U5Sh2vm0tD1U57q945UppFReIv_8NAKBkxQ_vHil7ySm-m7IAM-sTUY86_IqMZqisxoz7Ff7ZR2vIiUm3-L0ow4B8uPsCv2ZlUoVXvMF6XQiOHsgqgP1rfH8DmfmPFudwiXrAW6wEmi10skPmkN92aC3TPG6nmaNryQ7f8J82yVmGxW9U7zMbg21qNkRGBi_1YwEt6D8V2pUWv5U1a4p4-Ma0f4uQG4g0odM-WGomlh7pZWZf3sffiPXk9wBrGisxtCJuaB5vtkheWxpEfWqnhc3QdOWfrsRg6P1h7M95SNVW0U8A38wwrqPOpzEnckCVdCrZz2b2KVln6a4twfINg1-3lEZR4rkEmTaTYlLFlXzbRFWBBPGATxeRxhQ_9N5VhHi7STWPFD5HIJyVqz436bbVvM6Py_oldT_xt_tWlPc0w4Pesy2CgaCPlJCnx6cjEg_sRBUcRkBoHqa7aZj4JFFm9bzEaiJ2MKfkHVT4xdbEimMHsD0HkIQpg5-zoB2Jsqgc6Qi3L57hZi-Q1V0G2lmdZ5WZkQ2m5hxle4hHtAmghgynK2p0qzDWHScxHcdd2sInYqQgMYbnvs04YYKWpIfndCUBs9q_EONN5tn8gfSwHEKlQ-KpplEL5kc-99h2uaZsxRlJOF6_z8EZ-aKaKY8jvoAV0g4kZJH5UKy_MkBiva6r-zXUmo88qJjXQatOOSdfvJUTiZiSfcpBQqF9SSDD9WWsgInaOCKO_fAFf9fuacXDMEj0esUx1YrVEe_77S5uObg-UrK405U1JhKJJvd7o8xQKxenv5BJdsbbyYQDbSSe9BrCqeHEgmfRHTXdSvl_3QOP0Ej-dT8YJJHZ1lrujU7Zg5f5Kg99tU5GdLMbHX2kt4F2a0NX09HikEemvUg4NLPhjOihfVkChr-zdF69nfsnTiaQrMgpIcl9jttN99_8Gju-LU8OWbb92m9RLxAUFP115v22f77YPoILm92IjMZMkxEhGneoclWhnudkyR7YoTBjCnT5b7AC9_05uls637FmVf7Ck8-MF4gLil3dstXi4g24bitYhxxqWwiqF4vsDouSGUnuKCMwx3TLsII_xk77TjpQP4vpLdYM3tn94AVlTMMhnI-OZkVJk-_mIbywCwRHlQb5nzVCc0BWlM1kb9PJys2IfciS8LWEoxeq9moDX5w72yJKoLN3CWpD3VdJAiW79zUaySw-IeW0XaHnlze5fYnOozG8lIeyQ9sMZasMiFovGnR3b7jyMtA38U33v16fouWuBILOu0m_QOpRDI9i3rjRM6hdC48zCtNSzc1_1VPYkWDSFK1oVAjdd8-2rjyqdPeUwnqD26VA9_d3R7x8ThrazdbRC8U1hr9jpqNHuZ4LGYu3Ui8wB-lSt9QMaHz517MY_zBEoNGyvbQWtlM7mvLu12KoMM7nvGrPJnvD-HmxTqsVQolD8_lIV5ao72yiKDpArVr6RuV4PpI0j_Wy4-yDCuwBW0gjnB9GvCwOTeByYXJT6Ul7dgHck4BbF3IyFgvmY--ceWr5mBrbAC9LJP_4Wf5O6ul3hFrhiG6zSV4zzBYLnEwfW6LNLEZjZKgmBYiC5s1xlxYWDdcQ5FGmLQ9uEDkr4VItXQWvIIdeBQPyujxmd965Mig9-Sa7SCyV_3wH8fQnGlvU-jJMGL0zvzB2gcu7hMLMagUBj1AKXj-UxpbX1i95f2TOiZDwMeCCszgvCjQ21XKg07TBXrrOiFcgcADgdo-HJr7O1T3ozOIulq1PDM7QZH6i3wDD0j3b0NCdqKCWqhfLXz2-FszyUHmA_GCzOLVzrLT2DcGWIcQbkvF0yZPgTyqKArKa8qytOerdH6oCJ0bRl96855sMVjuUdyVLX7XW_rVTwsOwV0gVAx8SrzovtDFeHRNl7BQKMsyQ1BjWu25jqKJ598vAi3LCZv0kMdiC24qPdgZU4e2aUkco11EnD6nJgdqsVFxufCl4BD_D9g5Wy42fJt4ZgNPAcbUf341KERyReeBEQj-qlPB3IUTIXcJw68GScebhxb0W_tGKMBC6-ip4QfNW7UTxUxVxmCV7h0yRnBBlkuUR1eYQwWRmEPjKd3dLHvgHtr266NQmE1tnKtJlsdPKb0ztrI9vogsENsgGNFQ2tHoeX8vqxcagGznlPVPfc3DlqjBSeTFQaPWvmCQHVKgxkbvffKzFQyFvXEqt6bGGtkwBoRJ_IIwtSeWQ2nFPBe3rlyKrtSnQFIMibJbbYvPVE03Cld9R61r-GGDSQz4aXekLzePEVwnxpe4mWJGco7ctQyE73PekL1uo2g0bRK-KgaE878OiLRBo5T7c633xEf2hMy9532M2GVdTZuoE0LL-wpAh9GmNdvJZc7g2sINvwZi778v2WHcYEKqXvdmrX-Shyh3QkzgIGZrDzM4UlxxUWaXfZ0Z6PNguk7Jafqf3xuUe9Z8zfAJl5c_VA3k8dn7IRg99hRsh-TGBCzqzgjJq4p4XMWP2QuxFTGSgHRe2GSCFzd5-lPjrj76ZyT3MPUxQe_bV2VE-Oys3MT-VkCCM8jFCANXdrfltG6jSiUZ2uJUWNqdNnxPglmmyrgff_m-5CyIRWYXQsIdZGspqdjzb4F6RbBKfL2PQlM6zUfo9JNmE8YQq815Nxkex8vDOrImnew312fZA6rRjr9_uE4lEbw3U7PFlCKBUPvPnsdgedjKYhiS0xU6iS1NDKOvYhcrkCkiU67EmFD4U0-OCv9Kpbb5bIxTJuv405NxJBElAMVI-ya0ns7D4-xUPn05E7PhtGZT0eHwItjT6omThTsTHwB_bQqYfNrjrObO1l1go2hQ-cUadZYsG5l47CB5RlFhANtaC8tiq4KJi48TmEEApB_0VwOI4EmI7SR0oaqx3HRXZfeGevCx2yC9aCYM4HqcyqP2g_1HwsOYzwq4XDEbK5Yl1dtYABxPoo7t8FBq2sSmfrBJWFv_nvreb_DPwbfoSeCy9knqvOktSQRrPMmo-nNGpandBvjmrjSk3EdeziAP7XNre5I-bn_2voDxkzGFtUM-wzlL379ASRGej8FkNWaOyqGP6Anq5PSJ', - priv: 'LZSOlEPbU9S5_mSsMULffTyxZu6qKEOQ1nfEi2NCscg', - } - }, -}; -/* eslint-enable @stylistic/js/max-len */ +const keyData = {}; + +for (const name of ['ML-DSA-44', 'ML-DSA-65', 'ML-DSA-87']) { + const lcName = name.toLowerCase(); + keyData[name] = { + pkcs8_seed_only: toDer(fixtures.readKey(getKeyFileName(lcName, 'private_seed_only'), 'ascii')), + pkcs8: toDer(fixtures.readKey(getKeyFileName(lcName, 'private'), 'ascii')), + pkcs8_priv_only: toDer(fixtures.readKey(getKeyFileName(lcName, 'private_priv_only'), 'ascii')), + spki: toDer(fixtures.readKey(getKeyFileName(lcName, 'public'), 'ascii')), + jwk: JSON.parse(fixtures.readKey(`${lcName}.json`)), + }; +} const testVectors = [ { diff --git a/test/parallel/test-webcrypto-export-import-ml-kem.js b/test/parallel/test-webcrypto-export-import-ml-kem.js index 50f3444ce87899..1da0218f258e3c 100644 --- a/test/parallel/test-webcrypto-export-import-ml-kem.js +++ b/test/parallel/test-webcrypto-export-import-ml-kem.js @@ -25,29 +25,18 @@ function toDer(pem) { return Buffer.alloc(Buffer.byteLength(der, 'base64'), der, 'base64'); } -const keyData = { - 'ML-KEM-512': { - pkcs8_seed_only: toDer(fixtures.readKey(getKeyFileName('ml-kem-512', 'private_seed_only'), 'ascii')), - pkcs8: toDer(fixtures.readKey(getKeyFileName('ml-kem-512', 'private'), 'ascii')), - pkcs8_priv_only: toDer(fixtures.readKey(getKeyFileName('ml-kem-512', 'private_priv_only'), 'ascii')), - spki: toDer(fixtures.readKey(getKeyFileName('ml-kem-512', 'public'), 'ascii')), - pub_len: 800, - }, - 'ML-KEM-768': { - pkcs8_seed_only: toDer(fixtures.readKey(getKeyFileName('ml-kem-768', 'private_seed_only'), 'ascii')), - pkcs8: toDer(fixtures.readKey(getKeyFileName('ml-kem-768', 'private'), 'ascii')), - pkcs8_priv_only: toDer(fixtures.readKey(getKeyFileName('ml-kem-768', 'private_priv_only'), 'ascii')), - spki: toDer(fixtures.readKey(getKeyFileName('ml-kem-768', 'public'), 'ascii')), - pub_len: 1184, - }, - 'ML-KEM-1024': { - pkcs8_seed_only: toDer(fixtures.readKey(getKeyFileName('ml-kem-1024', 'private_seed_only'), 'ascii')), - pkcs8: toDer(fixtures.readKey(getKeyFileName('ml-kem-1024', 'private'), 'ascii')), - pkcs8_priv_only: toDer(fixtures.readKey(getKeyFileName('ml-kem-1024', 'private_priv_only'), 'ascii')), - spki: toDer(fixtures.readKey(getKeyFileName('ml-kem-1024', 'public'), 'ascii')), - pub_len: 1568, - }, -}; +const keyData = {}; + +for (const name of ['ML-KEM-512', 'ML-KEM-768', 'ML-KEM-1024']) { + const lcName = name.toLowerCase(); + keyData[name] = { + pkcs8_seed_only: toDer(fixtures.readKey(getKeyFileName(lcName, 'private_seed_only'), 'ascii')), + pkcs8: toDer(fixtures.readKey(getKeyFileName(lcName, 'private'), 'ascii')), + pkcs8_priv_only: toDer(fixtures.readKey(getKeyFileName(lcName, 'private_priv_only'), 'ascii')), + spki: toDer(fixtures.readKey(getKeyFileName(lcName, 'public'), 'ascii')), + jwk: JSON.parse(fixtures.readKey(`${lcName}.json`)), + }; +} const testVectors = [ { @@ -212,7 +201,8 @@ async function testImportPkcs8MismatchedSeed({ name, privateUsages }, extractabl } async function testImportRawPublic({ name, publicUsages }, extractable) { - const pub = keyData[name].spki.subarray(-keyData[name].pub_len); + const jwk = keyData[name].jwk; + const pub = Buffer.from(jwk.pub, 'base64url'); const publicKey = await subtle.importKey( 'raw-public', @@ -255,7 +245,8 @@ async function testImportRawPublic({ name, publicUsages }, extractable) { } async function testImportRawSeed({ name, privateUsages }, extractable) { - const seed = keyData[name].pkcs8_seed_only.subarray(-64); + const jwk = keyData[name].jwk; + const seed = Buffer.from(jwk.priv, 'base64url'); const privateKey = await subtle.importKey( 'raw-seed', @@ -285,6 +276,184 @@ async function testImportRawSeed({ name, privateUsages }, extractable) { { message: 'Invalid keyData' }); } +async function testImportJwk({ name, publicUsages, privateUsages }, extractable) { + + const jwk = keyData[name].jwk; + + const tests = [ + subtle.importKey( + 'jwk', + { + kty: jwk.kty, + alg: jwk.alg, + pub: jwk.pub, + }, + { name }, + extractable, publicUsages), + subtle.importKey( + 'jwk', + jwk, + { name }, + extractable, + privateUsages), + ]; + + const [ + publicKey, + privateKey, + ] = await Promise.all(tests); + + assert.strictEqual(publicKey.type, 'public'); + assert.strictEqual(privateKey.type, 'private'); + assert.strictEqual(publicKey.extractable, extractable); + assert.strictEqual(privateKey.extractable, extractable); + assert.deepStrictEqual(publicKey.usages, publicUsages); + assert.deepStrictEqual(privateKey.usages, privateUsages); + assert.strictEqual(publicKey.algorithm.name, name); + assert.strictEqual(privateKey.algorithm.name, name); + assert.strictEqual(privateKey.algorithm, privateKey.algorithm); + assert.strictEqual(privateKey.usages, privateKey.usages); + assert.strictEqual(publicKey.algorithm, publicKey.algorithm); + assert.strictEqual(publicKey.usages, publicKey.usages); + + if (extractable) { + // Test the round trip + const [ + pubJwk, + pvtJwk, + ] = await Promise.all([ + subtle.exportKey('jwk', publicKey), + subtle.exportKey('jwk', privateKey), + ]); + + assert.deepStrictEqual(pubJwk.key_ops, publicUsages); + assert.strictEqual(pubJwk.ext, true); + assert.strictEqual(pubJwk.kty, 'AKP'); + assert.strictEqual(pubJwk.pub, jwk.pub); + + assert.deepStrictEqual(pvtJwk.key_ops, privateUsages); + assert.strictEqual(pvtJwk.ext, true); + assert.strictEqual(pvtJwk.kty, 'AKP'); + assert.strictEqual(pvtJwk.pub, jwk.pub); + assert.strictEqual(pvtJwk.priv, jwk.priv); + + assert.strictEqual(pubJwk.alg, jwk.alg); + assert.strictEqual(pvtJwk.alg, jwk.alg); + } else { + await assert.rejects( + subtle.exportKey('jwk', publicKey), { + message: /key is not extractable/, + name: 'InvalidAccessError', + }); + await assert.rejects( + subtle.exportKey('jwk', privateKey), { + message: /key is not extractable/, + name: 'InvalidAccessError', + }); + } + + await assert.rejects( + subtle.importKey( + 'jwk', + { ...jwk, use: 'sig' }, + { name }, + extractable, + privateUsages), + { message: 'Invalid JWK "use" Parameter' }); + + await assert.rejects( + subtle.importKey( + 'jwk', + { ...jwk, pub: undefined }, + { name }, + extractable, + privateUsages), + { message: 'Invalid keyData' }); + + await assert.rejects( + subtle.importKey( + 'jwk', + { ...jwk, priv: 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' }, // Public vs private mismatch + { name }, + extractable, + privateUsages), + { message: 'Invalid keyData' }); + + await assert.rejects( + subtle.importKey( + 'jwk', + { ...jwk, kty: 'OKP' }, + { name }, + extractable, + privateUsages), + { message: 'Invalid JWK "kty" Parameter' }); + + await assert.rejects( + subtle.importKey( + 'jwk', + { ...jwk }, + { name }, + extractable, + publicUsages), // Invalid for a private key + { message: /Unsupported key usage/ }); + + await assert.rejects( + subtle.importKey( + 'jwk', + { ...jwk, ext: false }, + { name }, + true, + privateUsages), + { message: 'JWK "ext" Parameter and extractable mismatch' }); + + await assert.rejects( + subtle.importKey( + 'jwk', + { ...jwk, priv: undefined }, + { name }, + extractable, + privateUsages), // Invalid for a public key + { message: /Unsupported key usage/ }); + + for (const alg of [undefined, name === 'ML-KEM-512' ? 'ML-KEM-1024' : 'ML-KEM-512']) { + await assert.rejects( + subtle.importKey( + 'jwk', + { kty: jwk.kty, pub: jwk.pub, alg }, + { name }, + extractable, + publicUsages), + { message: 'JWK "alg" Parameter and algorithm name mismatch' }); + + await assert.rejects( + subtle.importKey( + 'jwk', + { ...jwk, alg }, + { name }, + extractable, + privateUsages), + { message: 'JWK "alg" Parameter and algorithm name mismatch' }); + } + + await assert.rejects( + subtle.importKey( + 'jwk', + { ...jwk }, + { name }, + extractable, + [/* empty usages */]), + { name: 'SyntaxError', message: 'Usages cannot be empty when importing a private key.' }); + + await assert.rejects( + subtle.importKey( + 'jwk', + { kty: jwk.kty, /* missing pub */ alg: jwk.alg }, + { name }, + extractable, + publicUsages), + { name: 'DataError', message: 'Invalid keyData' }); +} + (async function() { const tests = []; for (const vector of testVectors) { @@ -294,6 +463,7 @@ async function testImportRawSeed({ name, privateUsages }, extractable) { tests.push(testImportPkcs8SeedOnly(vector, extractable)); tests.push(testImportPkcs8PrivOnly(vector, extractable)); tests.push(testImportPkcs8MismatchedSeed(vector, extractable)); + tests.push(testImportJwk(vector, extractable)); tests.push(testImportRawSeed(vector, extractable)); tests.push(testImportRawPublic(vector, extractable)); } @@ -303,7 +473,7 @@ async function testImportRawSeed({ name, privateUsages }, extractable) { (async function() { const alg = 'ML-KEM-512'; - const pub = keyData[alg].spki.subarray(-keyData[alg].pub_len); + const pub = Buffer.from(keyData[alg].jwk.pub, 'base64url'); await assert.rejects(subtle.importKey('raw', pub, alg, false, []), { name: 'NotSupportedError', message: 'Unable to import ML-KEM-512 using raw format', @@ -321,3 +491,16 @@ async function testImportRawSeed({ name, privateUsages }, extractable) { }); } })().then(common.mustCall()); + +// Regression test: JWK `key_ops` validation must recognize ML-KEM operations +// (encapsulateKey, encapsulateBits, decapsulateKey, decapsulateBits) so that +// duplicate entries are rejected +(async function() { + for (const op of ['encapsulateKey', 'encapsulateBits', + 'decapsulateKey', 'decapsulateBits']) { + const jwk = { ...keyData['ML-KEM-768'].jwk, key_ops: [op, op] }; + await assert.rejects( + subtle.importKey('jwk', jwk, { name: 'ML-KEM-768' }, true, [op]), + { name: 'DataError', message: /Duplicate key operation/ }); + } +})().then(common.mustCall()); diff --git a/test/parallel/test-webcrypto-keygen.js b/test/parallel/test-webcrypto-keygen.js index 1baf2baee9952c..e57c34436578ab 100644 --- a/test/parallel/test-webcrypto-keygen.js +++ b/test/parallel/test-webcrypto-keygen.js @@ -853,8 +853,8 @@ if (hasOpenSSL(3, 5)) { const kTests = ['ML-KEM-512', 'ML-KEM-768', 'ML-KEM-1024']; const tests = kTests.map((name) => test(name, - ['decapsulateBits', 'decapsulateKey'], - ['encapsulateBits', 'encapsulateKey'])); + ['decapsulateKey', 'decapsulateBits'], + ['encapsulateKey', 'encapsulateBits'])); Promise.all(tests).then(common.mustCall()); } diff --git a/test/parallel/test-webcrypto-util.js b/test/parallel/test-webcrypto-util.js index 89d8575e20ddbd..8e139f4d5a87ec 100644 --- a/test/parallel/test-webcrypto-util.js +++ b/test/parallel/test-webcrypto-util.js @@ -9,6 +9,7 @@ const assert = require('assert'); const { normalizeAlgorithm, + validateKeyOps, } = require('internal/crypto/util'); { @@ -49,3 +50,12 @@ const { assert.strictEqual(normalized.name, 'ECDSA'); assert.strictEqual(nameReadCount, 1); } + +{ + for (const ops of [ + ['sign', 'toString', 'constructor'], + ['sign', '__proto__', 'constructor'], + ]) { + validateKeyOps(ops); + } +} diff --git a/test/parallel/test-webstream-readable-from.js b/test/parallel/test-webstream-readable-from.js index 470ee5d60d76e2..0a0c6943c72556 100644 --- a/test/parallel/test-webstream-readable-from.js +++ b/test/parallel/test-webstream-readable-from.js @@ -7,3 +7,22 @@ assert.throws( () => ReadableStream.from({}), { code: 'ERR_ARG_NOT_ITERABLE', name: 'TypeError' }, ); + +const invalidIterators = [ + { [Symbol.iterator]: () => 42 }, + { [Symbol.asyncIterator]: () => 42 }, +]; + +for (const iterable of invalidIterators) { + assert.throws( + () => ReadableStream.from(iterable), + { code: 'ERR_INVALID_STATE', name: 'TypeError' }, + ); +} + +function functionIterator() {} + +// doesNotThrow +ReadableStream.from({ + [Symbol.iterator]: () => functionIterator, +}); diff --git a/test/parallel/test-whatwg-webstreams-transform-stream-members.js b/test/parallel/test-whatwg-webstreams-transform-stream-members.js new file mode 100644 index 00000000000000..90a935683053d4 --- /dev/null +++ b/test/parallel/test-whatwg-webstreams-transform-stream-members.js @@ -0,0 +1,19 @@ +'use strict'; + +require('../common'); +const assert = require('node:assert'); + +const combinations = [ + ((t) => [t, t.readable])(new TransformStream()), + ((t) => [t.readable, t])(new TransformStream()), + ((t) => [t, t.writable])(new TransformStream()), + ((t) => [t.writable, t])(new TransformStream()), +]; + +for (const combination of combinations) { + assert.throws(() => structuredClone(combination, { transfer: combination }), { + constructor: DOMException, + name: 'DataCloneError', + code: 25, + }); +} diff --git a/test/pummel/test-crypto-argon2-nonblocking-constructor.js b/test/pummel/test-crypto-argon2-nonblocking-constructor.js new file mode 100644 index 00000000000000..37cb5363d43c02 --- /dev/null +++ b/test/pummel/test-crypto-argon2-nonblocking-constructor.js @@ -0,0 +1,81 @@ +// Flags: --expose-internals --no-warnings +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const { hasOpenSSL } = require('../common/crypto'); + +if (!hasOpenSSL(3, 2)) + common.skip('requires OpenSSL >= 3.2'); + +// Regression test for https://github.com/nodejs/node/issues/62861. +// `AdditionalConfig` used to invoke the full Argon2 KDF synchronously inside +// `new Argon2Job(...)` for the purpose of getting a parameter validation +// error. +// +// The fix removes the redundant KDF call from the constructor. This test +// asserts that the constructor returns in a small fraction of the time a +// full sync job takes. Pre-fix the constructor ran the KDF, and job.run() +// then ran it a second time in DeriveBits; post-fix the constructor does +// no KDF work at all. + +const assert = require('node:assert'); +const { internalBinding } = require('internal/test/binding'); +const { + Argon2Job, + kCryptoJobAsync, + kCryptoJobSync, + kTypeArgon2id, +} = internalBinding('crypto'); + +const pass = Buffer.from('password'); +const salt = Buffer.alloc(16, 0x02); +const empty = Buffer.alloc(0); + +// Tuned so a single-threaded Argon2id derivation is expensive enough that +// scheduler/GC noise is negligible compared to it. +const kdfArgs = [ + pass, + salt, + 1, // lanes + 32, // keylen + 65536, // memcost + 20, // iter + empty, // secret + empty, // associatedData + kTypeArgon2id, +]; + +// For each mode, measure the constructor and the derivation separately and +// assert that the constructor is a small fraction of the derivation. Pre-fix +// the constructor ran a full KDF, so ctorNs was comparable to runNs. Post-fix +// the constructor does no KDF work and must be orders of magnitude faster. +// +// For async mode the derivation happens on the thread pool, so runNs is +// measured from the start of run() until ondone fires. + +// Sync: run() derives on the main thread and returns when done. +{ + const ctorStart = process.hrtime.bigint(); + const job = new Argon2Job(kCryptoJobSync, ...kdfArgs); + const ctorNs = process.hrtime.bigint() - ctorStart; + const runStart = process.hrtime.bigint(); + const { 0: err } = job.run(); + const runNs = process.hrtime.bigint() - runStart; + assert.strictEqual(err, undefined); + assert.ok(ctorNs * 10n < runNs); +} + +// Async: run() dispatches to the thread pool; measure until ondone fires. +{ + const ctorStart = process.hrtime.bigint(); + const job = new Argon2Job(kCryptoJobAsync, ...kdfArgs); + const ctorNs = process.hrtime.bigint() - ctorStart; + const runStart = process.hrtime.bigint(); + job.ondone = common.mustSucceed(() => { + const runNs = process.hrtime.bigint() - runStart; + assert.ok(ctorNs * 10n < runNs); + }); + job.run(); +} diff --git a/test/wasm-allocation/test-wasm-allocation-memory64.js b/test/wasm-allocation/test-wasm-allocation-memory64.js index 46c7c34273c7b5..d43366ee5b4f80 100644 --- a/test/wasm-allocation/test-wasm-allocation-memory64.js +++ b/test/wasm-allocation/test-wasm-allocation-memory64.js @@ -4,13 +4,19 @@ // wasm memory64 with guard regions reserves 16GB of virtual address space. 'use strict'; -require('../common'); +const common = require('../common'); const assert = require('assert'); +const { isMainThread } = require('worker_threads'); // The first allocation should succeed. const first = new WebAssembly.Memory({ address: 'i64', initial: 10n, maximum: 100n }); assert(first); +if (!isMainThread) { + // https://github.com/nodejs/node/issues/62870 + common.skip('Workers terminate instead of throwing'); +} + // Subsequent allocations should eventually fail due to running out of // virtual address space. memory64 reserves 16GB per allocation (vs 8GB for // memory32), so the limit is reached even faster. diff --git a/test/wasm-allocation/test-wasm-allocation.js b/test/wasm-allocation/test-wasm-allocation.js index 5f2da9284180e4..cd74d0252078cb 100644 --- a/test/wasm-allocation/test-wasm-allocation.js +++ b/test/wasm-allocation/test-wasm-allocation.js @@ -4,13 +4,19 @@ // wasm memory32 with guard regions reserves 8GB of virtual address space. 'use strict'; -require('../common'); +const common = require('../common'); const assert = require('assert'); +const { isMainThread } = require('worker_threads'); // The first allocation should succeed. const first = new WebAssembly.Memory({ initial: 10, maximum: 100 }); assert(first); +if (!isMainThread) { + // https://github.com/nodejs/node/issues/62870 + common.skip('Workers terminate instead of throwing'); +} + // Subsequent allocations should eventually fail due to running out of // virtual address space. assert.throws(() => { diff --git a/test/wpt/status/streams.json b/test/wpt/status/streams.json index 222a68014f4af6..2268ffcc87ac91 100644 --- a/test/wpt/status/streams.json +++ b/test/wpt/status/streams.json @@ -41,15 +41,15 @@ "transferable/transfer-with-messageport.window.js": { "skip": "Browser-specific test" }, - "transferable/transform-stream-members.any.js": { + "transform-streams/invalid-realm.tentative.window.js": { + "skip": "Browser-specific test" + }, + "writable-streams/aborting.any.js": { "fail": { + "note": "Recursive abort() call from within an abort algorithm triggers ERR_INTERNAL_ASSERTION", "expected": [ - "Transferring [object TransformStream],[object ReadableStream] should fail", - "Transferring [object TransformStream],[object WritableStream] should fail" + "recursive abort() call from abort() aborting signal" ] } - }, - "transform-streams/invalid-realm.tentative.window.js": { - "skip": "Browser-specific test" } } diff --git a/test/wpt/status/wasm/jsapi.json b/test/wpt/status/wasm/jsapi.json index 4a20da7b2cf41c..b2d95388fea39a 100644 --- a/test/wpt/status/wasm/jsapi.json +++ b/test/wpt/status/wasm/jsapi.json @@ -15,41 +15,6 @@ ] } }, - "memory/to-fixed-length-buffer-shared.any.js": { - "fail": { - "expected": [ - "toFixedLengthBuffer caching behavior" - ] - } - }, - "memory/to-fixed-length-buffer.any.js": { - "fail": { - "expected": [ - "API surface", - "toFixedLengthBuffer caching behavior" - ] - } - }, - "memory/to-resizable-buffer-shared.any.js": { - "fail": { - "expected": [ - "toResizableBuffer caching behavior", - "toResizableBuffer max size", - "Resizing a Memory's resizable buffer" - ] - } - }, - "memory/to-resizable-buffer.any.js": { - "fail": { - "expected": [ - "API surface", - "toResizableBuffer caching behavior", - "toResizableBuffer max size", - "Resizing a Memory's resizable buffer", - "Resizable buffers from Memory cannot be detached by JS" - ] - } - }, "module/moduleSource.tentative.any.js": { "fail": { "expected": [ diff --git a/tools/clang-format/package-lock.json b/tools/clang-format/package-lock.json index c951eb9e720d9d..5d03eb31fde171 100644 --- a/tools/clang-format/package-lock.json +++ b/tools/clang-format/package-lock.json @@ -23,10 +23,9 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "license": "MIT", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -194,9 +193,9 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" diff --git a/tools/dep_updaters/update-nixpkgs-pin.sh b/tools/dep_updaters/update-nixpkgs-pin.sh index 6c23905ef0e093..271624a8da0fcb 100755 --- a/tools/dep_updaters/update-nixpkgs-pin.sh +++ b/tools/dep_updaters/update-nixpkgs-pin.sh @@ -5,6 +5,7 @@ set -ex BASE_DIR=$(cd "$(dirname "$0")/../.." && pwd) NIXPKGS_PIN_FILE="$BASE_DIR/tools/nix/pkgs.nix" +OPENSSL_MATRIX_FILE="$BASE_DIR/tools/nix/openssl-matrix.json" NIXPKGS_REPO=$(grep 'repo =' "$NIXPKGS_PIN_FILE" | awk -F'"' '{ print $2 }') CURRENT_VERSION_SHA1=$(grep 'rev =' "$NIXPKGS_PIN_FILE" | awk -F'"' '{ print $2 }') @@ -25,12 +26,14 @@ TMP_FILE=$(mktemp) sed "s/$CURRENT_VERSION_SHA1/$NEW_UPSTREAM_SHA1/;s/$CURRENT_TARBALL_HASH/$NEW_TARBALL_HASH/" "$NIXPKGS_PIN_FILE" > "$TMP_FILE" mv "$TMP_FILE" "$NIXPKGS_PIN_FILE" +"$BASE_DIR/tools/nix/collect-openssl-matrix.sh" | jq . > "$OPENSSL_MATRIX_FILE" + cat -< +// ```cpp +// #include +// ... +// ``` +// +// This produces test/addons/01_worker_support/addon.cc. +// Sections are numbered in order of first appearance. + +import { mkdirSync, writeFileSync } from 'node:fs'; +import { open } from 'node:fs/promises'; +import { join } from 'node:path'; +import { parseArgs } from 'node:util'; + +const { values } = parseArgs({ + options: { + input: { type: 'string' }, + output: { type: 'string' }, + }, +}); + +if (!values.input || !values.output) { + console.error('Usage: addon-verify.mjs --input --output '); + process.exit(1); +} + +const src = await open(values.input, 'r'); + +const MARKER_RE = /^$/; + +const entries = []; +let nextBlockIsAddonVerifyFile = false; +let expectedClosingFenceMarker; +for await (const line of src.readLines()) { + if (expectedClosingFenceMarker) { + // We're inside a Addon snippet + if (line === expectedClosingFenceMarker) { + // End of the snippet + expectedClosingFenceMarker = null; + continue; + } + + entries.at(-1).content += `${line}\n`; + } + if (nextBlockIsAddonVerifyFile) { + if (line) { + expectedClosingFenceMarker = line.replace(/\w/g, ''); + nextBlockIsAddonVerifyFile = false; + } + continue; + } + const match = MARKER_RE.exec(line); + if (match) { + nextBlockIsAddonVerifyFile = true; + const [, dir, name] = match; + entries.push({ dir, name, content: '' }); + } +} + +// Collect files grouped by section directory name. +const sections = Map.groupBy(entries, (e) => e.dir); + +let idx = 0; +for (const [name, files] of sections) { + const dirName = `${String(++idx).padStart(2, '0')}_${name}`; + const dir = join(values.output, dirName); + mkdirSync(dir, { recursive: true }); + + for (const file of files) { + let content = file.content; + if (file.name === 'test.js') { + content = + "'use strict';\n" + + "const common = require('../../common');\n" + + content.replace( + "'./build/Release/addon'", + // eslint-disable-next-line no-template-curly-in-string + '`./build/${common.buildType}/addon`', + ); + } + writeFileSync(join(dir, file.name), content); + } + + // Generate binding.gyp + const names = files.map((f) => f.name); + writeFileSync(join(dir, 'binding.gyp'), JSON.stringify({ + targets: [{ + target_name: 'addon', + sources: names, + includes: ['../common.gypi'], + }], + })); + + console.log(`Generated ${dirName} with files: ${names.join(', ')}`); +} diff --git a/tools/doc/package-lock.json b/tools/doc/package-lock.json index 70e5728c99094b..592efc9b202a8b 100644 --- a/tools/doc/package-lock.json +++ b/tools/doc/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "doc", "dependencies": { - "@node-core/doc-kit": "1.0.2" + "@node-core/doc-kit": "1.3.5" } }, "node_modules/@actions/core": { @@ -57,20 +57,20 @@ } }, "node_modules/@emnapi/core": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.0.tgz", - "integrity": "sha512-0DQ98G9ZQZOxfUcQn1waV2yS8aWdZ6kJMbYCJB3oUBecjWYO1fqJ+a1DRfPF3O5JEkwqwP1A9QEN/9mYm2Yd0w==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", + "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==", "license": "MIT", "optional": true, "dependencies": { - "@emnapi/wasi-threads": "1.2.0", + "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.0.tgz", - "integrity": "sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", + "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", "license": "MIT", "optional": true, "dependencies": { @@ -78,9 +78,9 @@ } }, "node_modules/@emnapi/wasi-threads": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz", - "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", "license": "MIT", "optional": true, "dependencies": { @@ -491,19 +491,21 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz", - "integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", + "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" }, "funding": { "type": "github", "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" } }, "node_modules/@noble/hashes": { @@ -519,33 +521,33 @@ } }, "node_modules/@node-core/doc-kit": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@node-core/doc-kit/-/doc-kit-1.0.2.tgz", - "integrity": "sha512-dYTh29FRH7Qcbz8M1WbAu1tg2YVcmCEdt514QcDqJWsRrYJfvzg10OyDODyEPJfKZ4+OUN/VPi3knGoGfOSNkQ==", + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@node-core/doc-kit/-/doc-kit-1.3.5.tgz", + "integrity": "sha512-4LXLxKHf97HYR+yAheifrkIueCOvh+PnG1pGwZWDr+JG3mS4J3jgP7UzlnH5vvpSTRDbGShU26YsEFazXwhU6Q==", "dependencies": { "@actions/core": "^3.0.0", "@heroicons/react": "^2.2.0", "@minify-html/wasm": "^0.18.1", "@node-core/rehype-shiki": "^1.4.1", - "@node-core/ui-components": "^1.6.1", + "@node-core/ui-components": "^1.6.3", "@orama/orama": "^3.1.18", "@orama/ui": "^1.5.4", "@rollup/plugin-virtual": "^3.0.2", - "@swc/html-wasm": "^1.15.18", + "@swc/html-wasm": "^1.15.21", "acorn": "^8.16.0", "commander": "^14.0.3", - "dedent": "^1.7.1", + "dedent": "^1.7.2", "estree-util-to-js": "^2.0.0", "estree-util-visit": "^2.0.0", "github-slugger": "^2.0.0", - "globals": "^17.3.0", + "glob-parent": "^6.0.2", "hast-util-to-string": "^3.0.1", "hastscript": "^9.0.1", - "lightningcss-wasm": "^1.31.1", + "lightningcss-wasm": "^1.32.0", "mdast-util-slice-markdown": "^2.0.1", "piscina": "^5.1.4", - "preact": "^10.28.4", - "preact-render-to-string": "^6.6.3", + "preact": "^10.29.0", + "preact-render-to-string": "^6.6.7", "reading-time": "^1.5.0", "recma-jsx": "^1.0.1", "rehype-raw": "^7.0.0", @@ -555,9 +557,9 @@ "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remark-stringify": "^11.0.0", - "rolldown": "^1.0.0-rc.6", + "rolldown": "^1.0.0-rc.12", "semver": "^7.7.4", - "shiki": "^4.0.0", + "shiki": "^4.0.2", "tinyglobby": "^0.2.15", "unified": "^11.0.5", "unist-builder": "^4.0.0", @@ -566,13 +568,24 @@ "unist-util-remove": "^4.0.0", "unist-util-select": "^5.1.0", "unist-util-visit": "^5.1.0", - "vfile": "^6.0.3", - "yaml": "^2.8.2" + "yaml": "^2.8.3" }, "bin": { "doc-kit": "bin/cli.mjs" } }, + "node_modules/@node-core/doc-kit/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/@node-core/rehype-shiki": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/@node-core/rehype-shiki/-/rehype-shiki-1.4.1.tgz", @@ -759,9 +772,9 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.120.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.120.0.tgz", - "integrity": "sha512-k1YNu55DuvAip/MGE1FTsIuU3FUCn6v/ujG9V7Nq5Df/kX2CWb13hhwD0lmJGMGqE+bE1MXvv9SZVnMzEXlWcg==", + "version": "0.126.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.126.0.tgz", + "integrity": "sha512-oGfVtjAgwQVVpfBrbtk4e1XDyWHRFta6BS3GWVzrF8xYBT2VGQAk39yJS/wFSMrZqoiCU4oghT3Ch0HaHGIHcQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/Boshen" @@ -2054,9 +2067,9 @@ "license": "MIT" }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0-rc.10", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.10.tgz", - "integrity": "sha512-jOHxwXhxmFKuXztiu1ORieJeTbx5vrTkcOkkkn2d35726+iwhrY1w/+nYY/AGgF12thg33qC3R1LMBF5tHTZHg==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.16.tgz", + "integrity": "sha512-rhY3k7Bsae9qQfOtph2Pm2jZEA+s8Gmjoz4hhmx70K9iMQ/ddeae+xhRQcM5IuVx5ry1+bGfkvMn7D6MJggVSA==", "cpu": [ "arm64" ], @@ -2070,9 +2083,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0-rc.10", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.10.tgz", - "integrity": "sha512-gED05Teg/vtTZbIJBc4VNMAxAFDUPkuO/rAIyyxZjTj1a1/s6z5TII/5yMGZ0uLRCifEtwUQn8OlYzuYc0m70w==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.16.tgz", + "integrity": "sha512-rNz0yK078yrNn3DrdgN+PKiMOW8HfQ92jQiXxwX8yW899ayV00MLVdaCNeVBhG/TbH3ouYVObo8/yrkiectkcQ==", "cpu": [ "arm64" ], @@ -2086,9 +2099,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0-rc.10", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.10.tgz", - "integrity": "sha512-rI15NcM1mA48lqrIxVkHfAqcyFLcQwyXWThy+BQ5+mkKKPvSO26ir+ZDp36AgYoYVkqvMcdS8zOE6SeBsR9e8A==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.16.tgz", + "integrity": "sha512-r/OmdR00HmD4i79Z//xO06uEPOq5hRXdhw7nzkxQxwSavs3PSHa1ijntdpOiZ2mzOQ3fVVu8C1M19FoNM+dMUQ==", "cpu": [ "x64" ], @@ -2102,9 +2115,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0-rc.10", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.10.tgz", - "integrity": "sha512-XZRXHdTa+4ME1MuDVp021+doQ+z6Ei4CCFmNc5/sKbqb8YmkiJdj8QKlV3rCI0AJtAeSB5n0WGPuJWNL9p/L2w==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.16.tgz", + "integrity": "sha512-KcRE5w8h0OnjUatG8pldyD14/CQ5Phs1oxfR+3pKDjboHRo9+MkqQaiIZlZRpsxC15paeXme/I127tUa9TXJ6g==", "cpu": [ "x64" ], @@ -2118,9 +2131,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0-rc.10", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.10.tgz", - "integrity": "sha512-R0SQMRluISSLzFE20sPWYHVmJdDQnRyc/FzSCN72BqQmh2SOZUFG+N3/vBZpR4C6WpEUVYJLrYUXaj43sJsNLA==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.16.tgz", + "integrity": "sha512-bT0guA1bpxEJ/ZhTRniQf7rNF8ybvXOuWbNIeLABaV5NGjx4EtOWBTSRGWFU9ZWVkPOZ+HNFP8RMcBokBiZ0Kg==", "cpu": [ "arm" ], @@ -2134,9 +2147,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0-rc.10", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.10.tgz", - "integrity": "sha512-Y1reMrV/o+cwpduYhJuOE3OMKx32RMYCidf14y+HssARRmhDuWXJ4yVguDg2R/8SyyGNo+auzz64LnPK9Hq6jg==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.16.tgz", + "integrity": "sha512-+tHktCHWV8BDQSjemUqm/Jl/TPk3QObCTIjmdDy/nlupcujZghmKK2962LYrqFpWu+ai01AN/REOH3NEpqvYQg==", "cpu": [ "arm64" ], @@ -2150,9 +2163,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0-rc.10", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.10.tgz", - "integrity": "sha512-vELN+HNb2IzuzSBUOD4NHmP9yrGwl1DVM29wlQvx1OLSclL0NgVWnVDKl/8tEks79EFek/kebQKnNJkIAA4W2g==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.16.tgz", + "integrity": "sha512-3fPzdREH806oRLxpTWW1Gt4tQHs0TitZFOECB2xzCFLPKnSOy90gwA7P29cksYilFO6XVRY1kzga0cL2nRjKPg==", "cpu": [ "arm64" ], @@ -2166,9 +2179,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.0-rc.10", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.10.tgz", - "integrity": "sha512-ZqrufYTgzxbHwpqOjzSsb0UV/aV2TFIY5rP8HdsiPTv/CuAgCRjM6s9cYFwQ4CNH+hf9Y4erHW1GjZuZ7WoI7w==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.16.tgz", + "integrity": "sha512-EKwI1tSrLs7YVw+JPJT/G2dJQ1jl9qlTTTEG0V2Ok/RdOenRfBw2PQdLPyjhIu58ocdBfP7vIRN/pvMsPxs/AQ==", "cpu": [ "ppc64" ], @@ -2182,9 +2195,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.0-rc.10", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.10.tgz", - "integrity": "sha512-gSlmVS1FZJSRicA6IyjoRoKAFK7IIHBs7xJuHRSmjImqk3mPPWbR7RhbnfH2G6bcmMEllCt2vQ/7u9e6bBnByg==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.16.tgz", + "integrity": "sha512-Uknladnb3Sxqu6SEcqBldQyJUpk8NleooZEc0MbRBJ4inEhRYWZX0NJu12vNf2mqAq7gsofAxHrGghiUYjhaLQ==", "cpu": [ "s390x" ], @@ -2198,9 +2211,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0-rc.10", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.10.tgz", - "integrity": "sha512-eOCKUpluKgfObT2pHjztnaWEIbUabWzk3qPZ5PuacuPmr4+JtQG4k2vGTY0H15edaTnicgU428XW/IH6AimcQw==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.16.tgz", + "integrity": "sha512-FIb8+uG49sZBtLTn+zt1AJ20TqVcqWeSIyoVt0or7uAWesgKaHbiBh6OpA/k9v0LTt+PTrb1Lao133kP4uVxkg==", "cpu": [ "x64" ], @@ -2214,9 +2227,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0-rc.10", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.10.tgz", - "integrity": "sha512-Xdf2jQbfQowJnLcgYfD/m0Uu0Qj5OdxKallD78/IPPfzaiaI4KRAwZzHcKQ4ig1gtg1SuzC7jovNiM2TzQsBXA==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.16.tgz", + "integrity": "sha512-RuERhF9/EgWxZEXYWCOaViUWHIboceK4/ivdtQ3R0T44NjLkIIlGIAVAuCddFxsZ7vnRHtNQUrt2vR2n2slB2w==", "cpu": [ "x64" ], @@ -2230,9 +2243,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0-rc.10", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.10.tgz", - "integrity": "sha512-o1hYe8hLi1EY6jgPFyxQgQ1wcycX+qz8eEbVmot2hFkgUzPxy9+kF0u0NIQBeDq+Mko47AkaFFaChcvZa9UX9Q==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.16.tgz", + "integrity": "sha512-mXcXnvd9GpazCxeUCCnZ2+YF7nut+ZOEbE4GtaiPtyY6AkhZWbK70y1KK3j+RDhjVq5+U8FySkKRb/+w0EeUwA==", "cpu": [ "arm64" ], @@ -2246,25 +2259,27 @@ } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0-rc.10", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.10.tgz", - "integrity": "sha512-Ugv9o7qYJudqQO5Y5y2N2SOo6S4WiqiNOpuQyoPInnhVzCY+wi/GHltcLHypG9DEUYMB0iTB/huJrpadiAcNcA==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.16.tgz", + "integrity": "sha512-3Q2KQxnC8IJOLqXmUMoYwyIPZU9hzRbnHaoV3Euz+VVnjZKcY8ktnNP8T9R4/GGQtb27C/UYKABxesKWb8lsvQ==", "cpu": [ "wasm32" ], "license": "MIT", "optional": true, "dependencies": { - "@napi-rs/wasm-runtime": "^1.1.1" + "@emnapi/core": "1.9.2", + "@emnapi/runtime": "1.9.2", + "@napi-rs/wasm-runtime": "^1.1.4" }, "engines": { - "node": ">=14.0.0" + "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0-rc.10", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.10.tgz", - "integrity": "sha512-7UODQb4fQUNT/vmgDZBl3XOBAIOutP5R3O/rkxg0aLfEGQ4opbCgU5vOw/scPe4xOqBwL9fw7/RP1vAMZ6QlAQ==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.16.tgz", + "integrity": "sha512-tj7XRemQcOcFwv7qhpUxMTBbI5mWMlE4c1Omhg5+h8GuLXzyj8HviYgR+bB2DMDgRqUE+jiDleqSCRjx4aYk/Q==", "cpu": [ "arm64" ], @@ -2278,9 +2293,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0-rc.10", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.10.tgz", - "integrity": "sha512-PYxKHMVHOb5NJuDL53vBUl1VwUjymDcYI6rzpIni0C9+9mTiJedvUxSk7/RPp7OOAm3v+EjgMu9bIy3N6b408w==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.16.tgz", + "integrity": "sha512-PH5DRZT+F4f2PTXRXR8uJxnBq2po/xFtddyabTJVJs/ZYVHqXPEgNIr35IHTEa6bpa0Q8Awg+ymkTaGnKITw4g==", "cpu": [ "x64" ], @@ -2294,9 +2309,9 @@ } }, "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.10", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.10.tgz", - "integrity": "sha512-UkVDEFk1w3mveXeKgaTuYfKWtPbvgck1dT8TUG3bnccrH0XtLTuAyfCoks4Q/M5ZGToSVJTIQYCzy2g/atAOeg==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.16.tgz", + "integrity": "sha512-45+YtqxLYKDWQouLKCrpIZhke+nXxhsw+qAHVzHDVwttyBlHNBVs2K25rDXrZzhpTp9w1FlAlvweV1H++fdZoA==", "license": "MIT" }, "node_modules/@rollup/plugin-virtual": { @@ -2436,9 +2451,9 @@ "license": "MIT" }, "node_modules/@swc/html-wasm": { - "version": "1.15.18", - "resolved": "https://registry.npmjs.org/@swc/html-wasm/-/html-wasm-1.15.18.tgz", - "integrity": "sha512-nABVlYRZjfTJA3bTEf7w6Gu8GgRfFJZqTAJ+ehJzwKtCreMy4QFBGiv3KkCjIjxXg+U8qrpnqgo9SjVOq3lPEw==", + "version": "1.15.30", + "resolved": "https://registry.npmjs.org/@swc/html-wasm/-/html-wasm-1.15.30.tgz", + "integrity": "sha512-LDD7vHwXe/xh9Cu5diE+HlKzyB+VyNO9pRuEyRYBqmmalOmONeM/fx3K0SphEcrjpeTnbh3pR4gI3lNz04CMbg==", "license": "Apache-2.0" }, "node_modules/@tailwindcss/node": { @@ -3450,18 +3465,6 @@ "node": ">= 6" } }, - "node_modules/globals": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-17.4.0.tgz", - "integrity": "sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -5309,9 +5312,9 @@ "license": "MIT" }, "node_modules/preact": { - "version": "10.29.0", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.29.0.tgz", - "integrity": "sha512-wSAGyk2bYR1c7t3SZ3jHcM6xy0lcBcDel6lODcs9ME6Th++Dx2KU+6D3HD8wMMKGA8Wpw7OMd3/4RGzYRpzwRg==", + "version": "10.29.1", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.29.1.tgz", + "integrity": "sha512-gQCLc/vWroE8lIpleXtdJhTFDogTdZG9AjMUpVkDf2iTCNwYNWA+u16dL41TqUDJO4gm2IgrcMv3uTpjd4Pwmg==", "license": "MIT", "funding": { "type": "opencollective", @@ -5319,9 +5322,9 @@ } }, "node_modules/preact-render-to-string": { - "version": "6.6.6", - "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-6.6.6.tgz", - "integrity": "sha512-EfqZJytnjJldV+YaaqhthU2oXsEf5e+6rDv957p+zxAvNfFLQOPfvBOTncscQ+akzu6Wrl7s3Pa0LjUQmWJsGQ==", + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-6.6.7.tgz", + "integrity": "sha512-3XdbsX3+vn9dQW+jJI/FsI9rlkgl6dbeUpqLsChak6jp3j3auFqBCkno7VChbMFs5Q8ylBj6DrUkKRwtVN3nvw==", "license": "MIT", "peerDependencies": { "preact": ">=10 || >= 11.0.0-0" @@ -5697,13 +5700,13 @@ } }, "node_modules/rolldown": { - "version": "1.0.0-rc.10", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.10.tgz", - "integrity": "sha512-q7j6vvarRFmKpgJUT8HCAUljkgzEp4LAhPlJUvQhA5LA1SUL36s5QCysMutErzL3EbNOZOkoziSx9iZC4FddKA==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.16.tgz", + "integrity": "sha512-rzi5WqKzEZw3SooTt7cgm4eqIoujPIyGcJNGFL7iPEuajQw7vxMHUkXylu4/vhCkJGXsgRmxqMKXUpT6FEgl0g==", "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.120.0", - "@rolldown/pluginutils": "1.0.0-rc.10" + "@oxc-project/types": "=0.126.0", + "@rolldown/pluginutils": "1.0.0-rc.16" }, "bin": { "rolldown": "bin/cli.mjs" @@ -5712,21 +5715,21 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.0-rc.10", - "@rolldown/binding-darwin-arm64": "1.0.0-rc.10", - "@rolldown/binding-darwin-x64": "1.0.0-rc.10", - "@rolldown/binding-freebsd-x64": "1.0.0-rc.10", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.10", - "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.10", - "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.10", - "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.10", - "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.10", - "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.10", - "@rolldown/binding-linux-x64-musl": "1.0.0-rc.10", - "@rolldown/binding-openharmony-arm64": "1.0.0-rc.10", - "@rolldown/binding-wasm32-wasi": "1.0.0-rc.10", - "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.10", - "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.10" + "@rolldown/binding-android-arm64": "1.0.0-rc.16", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.16", + "@rolldown/binding-darwin-x64": "1.0.0-rc.16", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.16", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.16", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.16", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.16", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.16", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.16", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.16", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.16", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.16", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.16", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.16", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.16" } }, "node_modules/scheduler": { diff --git a/tools/doc/package.json b/tools/doc/package.json index 9d143e9724d8d4..1cccd403bf51d0 100644 --- a/tools/doc/package.json +++ b/tools/doc/package.json @@ -2,6 +2,6 @@ "name": "doc", "private": true, "dependencies": { - "@node-core/doc-kit": "1.0.2" + "@node-core/doc-kit": "1.3.5" } } diff --git a/tools/eslint/package-lock.json b/tools/eslint/package-lock.json index 2d042d6307f9b8..7455b3b9e3c3fe 100644 --- a/tools/eslint/package-lock.json +++ b/tools/eslint/package-lock.json @@ -737,9 +737,9 @@ } }, "node_modules/brace-expansion": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", - "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" diff --git a/tools/icu/icu-generic.gyp b/tools/icu/icu-generic.gyp index f007c65232c0d6..f49b4ddba74a12 100644 --- a/tools/icu/icu-generic.gyp +++ b/tools/icu/icu-generic.gyp @@ -456,6 +456,16 @@ ['enable_lto=="true"', { 'ldflags': [ '-fno-lto' ], }], + ['node_with_ltcg=="true" or enable_lto=="true" or enable_thin_lto=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + 'VCLinkerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + }, + }], ], }, # This tool is used to rebuild res_index.res manifests @@ -473,6 +483,16 @@ ['enable_lto=="true"', { 'ldflags': [ '-fno-lto' ], }], + ['node_with_ltcg=="true" or enable_lto=="true" or enable_thin_lto=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + 'VCLinkerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + }, + }], ], }, # This tool is used to package, unpackage, repackage .dat files @@ -491,6 +511,16 @@ ['enable_lto=="true"', { 'ldflags': [ '-fno-lto' ], }], + ['node_with_ltcg=="true" or enable_lto=="true" or enable_thin_lto=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + 'VCLinkerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + }, + }], ], }, # this is used to convert .dat directly into .obj @@ -508,6 +538,16 @@ ['enable_lto=="true"', { 'ldflags': [ '-fno-lto' ], }], + ['node_with_ltcg=="true" or enable_lto=="true" or enable_thin_lto=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + 'VCLinkerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + }, + }], ], }, ], diff --git a/tools/make-v8.sh b/tools/make-v8.sh index ca3b5729043270..bf74606e1908a3 100755 --- a/tools/make-v8.sh +++ b/tools/make-v8.sh @@ -29,10 +29,16 @@ if [ "$ARCH" = "s390x" ] || [ "$ARCH" = "ppc64le" ]; then # shellcheck disable=SC2154 case "$CXX" in - *clang*) GN_COMPILER_OPTS="is_clang=true clang_base_path=\"/usr\" clang_use_chrome_plugins=false treat_warnings_as_errors=false use_custom_libcxx=false" ;; + *clang*) + CLANG_VERSION=$(clang -dumpversion | cut -d. -f1) + RUST_VERSION=$(rustc --version --verbose | awk '/release:/ {print $2}') + GN_COMPILER_OPTS="is_clang=true clang_base_path=\"/usr\" clang_use_chrome_plugins=false treat_warnings_as_errors=false use_custom_libcxx=false clang_version=\"${CLANG_VERSION}\"" + GN_RUST_ARGS="rustc_version=\"${RUST_VERSION}\" rust_sysroot_absolute=\"/usr\" rust_bindgen_root=\"/usr\"" + export RUSTC_BOOTSTRAP=1 + ;; *) GN_COMPILER_OPTS="treat_warnings_as_errors=false use_custom_libcxx=false" ;; esac - gn gen -v "out.gn/$BUILD_ARCH_TYPE" --args="$GN_COMPILER_OPTS is_component_build=false is_debug=false v8_target_cpu=\"$TARGET_ARCH\" target_cpu=\"$TARGET_ARCH\" v8_enable_backtrace=true $CC_WRAPPER" + gn gen -v "out.gn/$BUILD_ARCH_TYPE" --args="$GN_COMPILER_OPTS is_component_build=false is_debug=false v8_target_cpu=\"$TARGET_ARCH\" target_cpu=\"$TARGET_ARCH\" v8_enable_backtrace=true ${GN_RUST_ARGS} $CC_WRAPPER" ninja -v -C "out.gn/$BUILD_ARCH_TYPE" "${JOBS_ARG}" d8 cctest inspector-test else DEPOT_TOOLS_DIR="$(cd depot_tools && pwd)" diff --git a/tools/nix/collect-openssl-matrix.sh b/tools/nix/collect-openssl-matrix.sh new file mode 100755 index 00000000000000..a2a5d5ec72b948 --- /dev/null +++ b/tools/nix/collect-openssl-matrix.sh @@ -0,0 +1,65 @@ +#!/bin/sh +# +# Emits the JSON source data of OpenSSL releases to test Node.js against with +# shared libraries. +# +# This helper is used by tools/dep_updaters/update-nixpkgs-pin.sh to +# regenerate tools/nix/openssl-matrix.json. +# +# Output (stdout): a JSON array with shape +# [{ "version": "3.6.1", "attr": "openssl_3_6", "continue-on-error": false }, ...] +# +# Usage: ./tools/nix/collect-openssl-matrix.sh + +set -eu + +# Latest OpenSSL major.minor cycle we support +# running tests with. Newer cycles are emitted +# with "continue-on-error": true. +SUPPORTED_OPENSSL_VERSION=4.0 + +here=$(cd -- "$(dirname -- "$0")" && pwd) + +# 1. Enumerate every `openssl_N` / `openssl_N_M` attribute exposed by the +# repo-pinned nixpkgs. `tryEval` skips aliases that raise (e.g. +# `openssl_3_0` → renamed to `openssl_3`) so we only keep attributes +# that resolve to a real derivation with a `.version`. +nix_json=$(nix-instantiate --eval --strict --json -E " + let + pkgs = import $here/pkgs.nix {}; + names = builtins.filter + (n: builtins.match \"openssl_[0-9]+(_[0-9]+)?\" n != null) + (builtins.attrNames pkgs); + safe = builtins.filter (n: + let t = builtins.tryEval pkgs.\${n}; in + t.success && (builtins.tryEval t.value.version).success) names; + in map (n: { attr = n; version = pkgs.\${n}.version; }) safe +") + +# 2. Fetch OpenSSL release versions from endoflife.date, keep entries that +# are either not past EOL or still under extended support, then pick the +# first nix attr whose `.version` starts with the release version +# followed by `.` / letter / end-of-string (so "3.6" matches "3.6.1", +# "1.1.1" matches "1.1.1w", and "1.1" does NOT swallow "1.1.1"). +# Releases without a matching nix attr are dropped. +curl -sf https://endoflife.date/api/openssl.json \ + | jq -c \ + --argjson nix "$nix_json" \ + --arg supported "$SUPPORTED_OPENSSL_VERSION" ' + (now | strftime("%Y-%m-%d")) as $today | + # Compare OpenSSL major.minor cycles as numeric tuples. + def cycle_tuple($v): + ($v | split(".") | map(tonumber)); + [ .[] + | select(.eol == false or .eol > $today or .extendedSupport == true) + | .cycle as $v + | ($nix + | map(select(.version | test("^" + ($v | gsub("\\."; "\\.")) + "([.a-z]|$)"))) + | first) as $m + | select($m != null) + | { + version: $m.version, + attr: $m.attr, + "continue-on-error": (cycle_tuple($v) > cycle_tuple($supported)) + } + ]' diff --git a/tools/nix/openssl-matrix.json b/tools/nix/openssl-matrix.json new file mode 100644 index 00000000000000..ec1597df8cede9 --- /dev/null +++ b/tools/nix/openssl-matrix.json @@ -0,0 +1,27 @@ +[ + { + "version": "4.0.0", + "attr": "openssl_4_0", + "continue-on-error": false + }, + { + "version": "3.6.1", + "attr": "openssl_3_6", + "continue-on-error": false + }, + { + "version": "3.5.5", + "attr": "openssl_3_5", + "continue-on-error": false + }, + { + "version": "3.0.19", + "attr": "openssl_3", + "continue-on-error": false + }, + { + "version": "1.1.1w", + "attr": "openssl_1_1", + "continue-on-error": false + } +] diff --git a/tools/nix/pkgs.nix b/tools/nix/pkgs.nix index f4dab340752819..989c515b31b11f 100644 --- a/tools/nix/pkgs.nix +++ b/tools/nix/pkgs.nix @@ -1,21 +1,10 @@ arg: let repo = "https://github.com/NixOS/nixpkgs"; - rev = "13043924aaa7375ce482ebe2494338e058282925"; + rev = "ab72be9733b41190ea34f1422a3e4e243ede7533"; nixpkgs = import (builtins.fetchTarball { url = "${repo}/archive/${rev}.tar.gz"; - sha256 = "1pbv1c3syp94rh147s2nhbzfcib01blz3s7g290m43s3nk71404z"; + sha256 = "1a720dxki2ymwiwdjn8awgpinigz5wsnxg2mmpy1s2b2wyy3gmz1"; }) arg; in nixpkgs -// { - nixfmt-tree = nixpkgs.nixfmt-tree.overrideAttrs (old: { - patches = (old.patches or [ ]) ++ [ - (nixpkgs.fetchpatch2 { - url = "https://github.com/numtide/treefmt/commit/b96016b4e38ffc76518087b3b0c9bbfa190d5225.patch?full_index=1"; - revert = true; - hash = "sha256-DcxT2OGPX6Kmxhqa56DjZsSh2hoyhPyVmE17ULeryv8="; - }) - ]; - }); -} diff --git a/tools/v8_gypfiles/abseil.gyp b/tools/v8_gypfiles/abseil.gyp index 0d7eb255f85acc..fdc2fae1ab4d75 100644 --- a/tools/v8_gypfiles/abseil.gyp +++ b/tools/v8_gypfiles/abseil.gyp @@ -141,6 +141,8 @@ '<(ABSEIL_ROOT)/absl/debugging/failure_signal_handler.cc', '<(ABSEIL_ROOT)/absl/debugging/internal/address_is_readable.h', '<(ABSEIL_ROOT)/absl/debugging/internal/address_is_readable.cc', + '<(ABSEIL_ROOT)/absl/debugging/internal/borrowed_fixup_buffer.h', + '<(ABSEIL_ROOT)/absl/debugging/internal/borrowed_fixup_buffer.cc', '<(ABSEIL_ROOT)/absl/debugging/internal/bounded_utf8_length_sequence.h', '<(ABSEIL_ROOT)/absl/debugging/internal/decode_rust_punycode.h', '<(ABSEIL_ROOT)/absl/debugging/internal/decode_rust_punycode.cc', @@ -250,6 +252,9 @@ '<(ABSEIL_ROOT)/absl/strings/internal/cordz_update_tracker.h', '<(ABSEIL_ROOT)/absl/strings/internal/damerau_levenshtein_distance.h', '<(ABSEIL_ROOT)/absl/strings/internal/damerau_levenshtein_distance.cc', + '<(ABSEIL_ROOT)/absl/strings/internal/generic_printer.cc', + '<(ABSEIL_ROOT)/absl/strings/internal/generic_printer.h', + '<(ABSEIL_ROOT)/absl/strings/internal/generic_printer_internal.h', '<(ABSEIL_ROOT)/absl/strings/internal/escaping.h', '<(ABSEIL_ROOT)/absl/strings/internal/escaping.cc', '<(ABSEIL_ROOT)/absl/strings/internal/memutil.h', @@ -294,7 +299,6 @@ '<(ABSEIL_ROOT)/absl/strings/str_split.h', '<(ABSEIL_ROOT)/absl/strings/str_split.cc', '<(ABSEIL_ROOT)/absl/strings/string_view.h', - '<(ABSEIL_ROOT)/absl/strings/string_view.cc', '<(ABSEIL_ROOT)/absl/strings/strip.h', '<(ABSEIL_ROOT)/absl/strings/substitute.h', '<(ABSEIL_ROOT)/absl/strings/substitute.cc', @@ -367,7 +371,15 @@ '<(ABSEIL_ROOT)/absl/types/span.h', '<(ABSEIL_ROOT)/absl/types/variant.h', '<(ABSEIL_ROOT)/absl/utility/utility.h', - ] + ], + 'conditions': [ + ['OS=="win"', { + 'sources': [ + '<(ABSEIL_ROOT)/absl/time/internal/cctz/src/time_zone_name_win.h', + '<(ABSEIL_ROOT)/absl/time/internal/cctz/src/time_zone_name_win.cc', + ], + }], + ], }, # abseil ] } diff --git a/tools/v8_gypfiles/d8.gyp b/tools/v8_gypfiles/d8.gyp index f5f8a194318eb5..5b47ebf180dd88 100644 --- a/tools/v8_gypfiles/d8.gyp +++ b/tools/v8_gypfiles/d8.gyp @@ -67,6 +67,16 @@ ['enable_lto=="true"', { 'ldflags': [ '-fno-lto' ], }], + ['node_with_ltcg=="true" or enable_lto=="true" or enable_thin_lto=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + 'VCLinkerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + }, + }], ], }, ], diff --git a/tools/v8_gypfiles/features.gypi b/tools/v8_gypfiles/features.gypi index ed9a5a5c487157..0208ce11983a33 100644 --- a/tools/v8_gypfiles/features.gypi +++ b/tools/v8_gypfiles/features.gypi @@ -435,6 +435,12 @@ }], ['v8_disable_write_barriers==1', { 'defines': ['V8_DISABLE_WRITE_BARRIERS',], + }, { + 'configurations': { + 'Debug': { + 'defines': ['V8_VERIFY_WRITE_BARRIERS'], + }, + }, }], ['v8_enable_atomic_object_field_writes==1', { 'defines': ['V8_ATOMIC_OBJECT_FIELD_WRITES',], diff --git a/tools/v8_gypfiles/v8.gyp b/tools/v8_gypfiles/v8.gyp index e09fcc1ce8c59f..945aa0aa501df1 100644 --- a/tools/v8_gypfiles/v8.gyp +++ b/tools/v8_gypfiles/v8.gyp @@ -285,48 +285,12 @@ 'v8_base_without_compiler', 'v8_initializers', 'v8_maybe_icu', - 'fp16', 'abseil.gyp:abseil', ], 'sources': [ '<(V8_ROOT)/src/init/setup-isolate-full.cc', ], }, # v8_init - { - # This target is used to work around a GCC issue that causes the - # compilation to take several minutes when using -O2 or -O3. - # This is fixed in GCC 13. - 'target_name': 'v8_initializers_slow', - 'type': 'static_library', - 'toolsets': ['host', 'target'], - 'dependencies': [ - 'generate_bytecode_builtins_list', - 'run_torque', - 'fp16', - 'abseil.gyp:abseil', - ], - 'cflags!': ['-O3'], - 'cflags': ['-O1'], - 'sources': [ - '<(SHARED_INTERMEDIATE_DIR)/torque-generated/src/builtins/js-to-wasm-tq-csa.h', - '<(SHARED_INTERMEDIATE_DIR)/torque-generated/src/builtins/js-to-wasm-tq-csa.cc', - '<(SHARED_INTERMEDIATE_DIR)/torque-generated/src/builtins/wasm-to-js-tq-csa.h', - '<(SHARED_INTERMEDIATE_DIR)/torque-generated/src/builtins/wasm-to-js-tq-csa.cc', - ], - 'conditions': [ - ['v8_enable_i18n_support==1', { - 'dependencies': [ - '<(icu_gyp_path):icui18n', - '<(icu_gyp_path):icuuc', - ], - }], - ['v8_enable_temporal_support==1 and node_shared_temporal_capi=="false"', { - 'dependencies': [ - '../../deps/crates/crates.gyp:temporal_capi', - ], - }], - ], - }, # v8_initializers_slow { 'target_name': 'v8_initializers', 'type': 'static_library', @@ -336,7 +300,6 @@ 'v8_base_without_compiler', 'v8_shared_internal_headers', 'v8_pch', - 'fp16', 'abseil.gyp:abseil', ], 'include_dirs': [ @@ -348,16 +311,6 @@ ], 'conditions': [ ['v8_enable_webassembly==1', { - 'dependencies': [ - 'v8_initializers_slow', - ], - # Compiled by v8_initializers_slow target. - 'sources!': [ - '<(SHARED_INTERMEDIATE_DIR)/torque-generated/src/builtins/js-to-wasm-tq-csa.h', - '<(SHARED_INTERMEDIATE_DIR)/torque-generated/src/builtins/js-to-wasm-tq-csa.cc', - '<(SHARED_INTERMEDIATE_DIR)/torque-generated/src/builtins/wasm-to-js-tq-csa.h', - '<(SHARED_INTERMEDIATE_DIR)/torque-generated/src/builtins/wasm-to-js-tq-csa.cc', - ], 'sources': [ '