diff --git a/.eslintignore b/.eslintignore index 8ab4750abd1685..5941496e1a6280 100644 --- a/.eslintignore +++ b/.eslintignore @@ -7,4 +7,5 @@ tools/icu tools/lint-md/lint-md.mjs benchmark/tmp doc/**/*.js +!doc/api_assets/*.js !.eslintrc.js diff --git a/.eslintrc.js b/.eslintrc.js index d44e3f1414f438..ea69a2893ff188 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -260,6 +260,7 @@ module.exports = { 'no-useless-concat': 'error', 'no-useless-constructor': 'error', 'no-useless-return': 'error', + 'no-var': 'error', 'no-void': 'error', 'no-whitespace-before-property': 'error', 'object-curly-newline': 'error', @@ -317,11 +318,28 @@ module.exports = { 'node-core/no-duplicate-requires': 'error', }, globals: { + ByteLengthQueuingStrategy: 'readable', + CompressionStream: 'readable', + CountQueuingStrategy: 'readable', Crypto: 'readable', CryptoKey: 'readable', + DecompressionStream: 'readable', fetch: 'readable', FormData: 'readable', + ReadableStream: 'readable', + ReadableStreamDefaultReader: 'readable', + ReadableStreamBYOBReader: 'readable', + ReadableStreamBYOBRequest: 'readable', + ReadableByteStreamController: 'readable', + ReadableStreamDefaultController: 'readable', Response: 'readable', + TextDecoderStream: 'readable', + TextEncoderStream: 'readable', + TransformStream: 'readable', + TransformStreamDefaultController: 'readable', SubtleCrypto: 'readable', + WritableStream: 'readable', + WritableStreamDefaultWriter: 'readable', + WritableStreamDefaultController: 'readable', }, }; diff --git a/.github/ISSUE_TEMPLATE/2-feature-request.yml b/.github/ISSUE_TEMPLATE/2-feature-request.yml index 60cd6dff5759c7..26a77a3617cbeb 100644 --- a/.github/ISSUE_TEMPLATE/2-feature-request.yml +++ b/.github/ISSUE_TEMPLATE/2-feature-request.yml @@ -10,7 +10,7 @@ body: Please fill in as much of the following form as you're able. For more information on how the project manages feature - requests, see [Feature request management](https://github.com/nodejs/node/blob/HEAD/doc/guides/feature-request-management.md). + requests, see [Feature request management](https://github.com/nodejs/node/blob/HEAD/doc/contributing/feature-request-management.md). - type: textarea attributes: label: What is the problem this feature will solve? diff --git a/.github/label-pr-config.yml b/.github/label-pr-config.yml index 93aeaab0257369..a8d22980f6db18 100644 --- a/.github/label-pr-config.yml +++ b/.github/label-pr-config.yml @@ -85,7 +85,7 @@ subSystemLabels: /^deps\/nghttp2\//: http2 /^deps\/ngtcp2\//: quic, dont-land-on-v14.x, dont-land-on-v12.x /^deps\/nghttp3\//: quic, dont-land-on-v14.x, dont-land-on-v12.x - /^deps\/([^/]+)/: $1 + /^deps\/([^/]+)/: dependencies, $1 ## JS subsystems # Oddities first diff --git a/.github/workflows/authors.yml b/.github/workflows/authors.yml index 75fec53a549438..ffd169df9b6ef5 100644 --- a/.github/workflows/authors.yml +++ b/.github/workflows/authors.yml @@ -11,7 +11,7 @@ jobs: if: github.repository == 'nodejs/node' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: fetch-depth: '0' # This is required to actually get all the authors persist-credentials: false diff --git a/.github/workflows/auto-start-ci.yml b/.github/workflows/auto-start-ci.yml index 2416561ab7ebf3..ed5606a58f13ec 100644 --- a/.github/workflows/auto-start-ci.yml +++ b/.github/workflows/auto-start-ci.yml @@ -36,12 +36,12 @@ jobs: if: needs.get-prs-for-ci.outputs.numbers != '' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: persist-credentials: false - name: Install Node.js - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version: ${{ env.NODE_VERSION }} diff --git a/.github/workflows/build-tarball.yml b/.github/workflows/build-tarball.yml index 38a8922c093a99..8c13541fd37d14 100644 --- a/.github/workflows/build-tarball.yml +++ b/.github/workflows/build-tarball.yml @@ -37,11 +37,11 @@ jobs: if: github.event.pull_request.draft == false runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: persist-credentials: false - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: ${{ env.PYTHON_VERSION }} - name: Environment Information @@ -55,7 +55,7 @@ jobs: mkdir tarballs mv *.tar.gz tarballs - name: Upload tarball artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: tarballs path: tarballs @@ -63,17 +63,17 @@ jobs: needs: build-tarball runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: persist-credentials: false - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: ${{ env.PYTHON_VERSION }} - name: Environment Information run: npx envinfo - name: Download tarball - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v3 with: name: tarballs path: tarballs diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 695675b5994aa2..44d7529b2a2e54 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -36,11 +36,11 @@ jobs: fail-fast: false runs-on: ${{ matrix.windows }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: persist-credentials: false - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: ${{ env.PYTHON_VERSION }} - name: Install deps diff --git a/.github/workflows/close-stale-feature-requests.yml b/.github/workflows/close-stale-feature-requests.yml index db737a8f86231e..c815b9acbaef99 100644 --- a/.github/workflows/close-stale-feature-requests.yml +++ b/.github/workflows/close-stale-feature-requests.yml @@ -15,7 +15,7 @@ env: For more information on how the project manages feature requests, please consult the - [feature request management document](https://github.com/nodejs/node/blob/HEAD/doc/guides/feature-request-management.md). + [feature request management document](https://github.com/nodejs/node/blob/HEAD/doc/contributing/feature-request-management.md). WARN_MESSAGE: > There has been no activity on this feature request for @@ -25,7 +25,7 @@ env: For more information on how the project manages feature requests, please consult the - [feature request management document](https://github.com/nodejs/node/blob/HEAD/doc/guides/feature-request-management.md). + [feature request management document](https://github.com/nodejs/node/blob/HEAD/doc/contributing/feature-request-management.md). # yamllint enable jobs: @@ -36,13 +36,13 @@ jobs: - uses: actions/stale@v4 with: repo-token: ${{ secrets.GITHUB_TOKEN }} - days-before-stale: 1226 + days-before-stale: 180 days-before-close: 30 - stale-issue-label: stalled + stale-issue-label: stale close-issue-message: ${{ env.CLOSE_MESSAGE }} stale-issue-message: ${{ env.WARN_MESSAGE }} only-labels: feature request exempt-pr-labels: never-stale # max requests it will send per run to the GitHub API before it deliberately exits to avoid hitting API rate limits - operations-per-run: 30 + operations-per-run: 500 remove-stale-when-updated: true diff --git a/.github/workflows/commit-lint.yml b/.github/workflows/commit-lint.yml index 56da77c36db733..1863a79ab17750 100644 --- a/.github/workflows/commit-lint.yml +++ b/.github/workflows/commit-lint.yml @@ -1,4 +1,4 @@ -name: First commit message adheres to guidelines at https://goo.gl/p2fr5Q +name: First commit message adheres to guidelines on: [pull_request] @@ -14,13 +14,13 @@ jobs: run: | echo "::set-output name=plusOne::$((${{ github.event.pull_request.commits }} + 1))" echo "::set-output name=minusOne::$((${{ github.event.pull_request.commits }} - 1))" - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: fetch-depth: ${{ steps.nb-of-commits.outputs.plusOne }} persist-credentials: false - run: git reset HEAD^2 - name: Install Node.js - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 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 bbaf67a0e7e779..d8189f85e8a118 100644 --- a/.github/workflows/commit-queue.yml +++ b/.github/workflows/commit-queue.yml @@ -42,7 +42,7 @@ jobs: if: needs.get_mergeable_prs.outputs.numbers != '' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: # Needs the whole git history for ncu to work # See https://github.com/nodejs/node-core-utils/pull/486 @@ -55,7 +55,7 @@ jobs: # Install dependencies - name: Install Node.js - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version: ${{ env.NODE_VERSION }} - name: Install node-core-utils diff --git a/.github/workflows/coverage-linux.yml b/.github/workflows/coverage-linux.yml index 65004c243f3dd5..ddc7be8b507557 100644 --- a/.github/workflows/coverage-linux.yml +++ b/.github/workflows/coverage-linux.yml @@ -33,11 +33,11 @@ jobs: if: github.event.pull_request.draft == false runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: persist-credentials: false - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: ${{ env.PYTHON_VERSION }} - name: Environment Information diff --git a/.github/workflows/coverage-windows.yml b/.github/workflows/coverage-windows.yml index 234719548d0a5b..231f3b1ed7ec13 100644 --- a/.github/workflows/coverage-windows.yml +++ b/.github/workflows/coverage-windows.yml @@ -35,11 +35,11 @@ jobs: if: github.event.pull_request.draft == false runs-on: windows-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: persist-credentials: false - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: ${{ env.PYTHON_VERSION }} - name: Install deps diff --git a/.github/workflows/daily.yml b/.github/workflows/daily.yml index 79294ca966ddef..aabc566f5441fa 100644 --- a/.github/workflows/daily.yml +++ b/.github/workflows/daily.yml @@ -14,11 +14,11 @@ jobs: # not working on gcc-8 and gcc-9 see https://github.com/nodejs/node/issues/38570 container: gcc:11 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: persist-credentials: false - name: Use Node.js ${{ env.NODE_VERSION }} - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version: ${{ env.NODE_VERSION }} - name: Environment Information diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index 9bb872f35384d4..1151c42967807a 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -22,18 +22,18 @@ jobs: if: github.event.pull_request.draft == false runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: persist-credentials: false - name: Use Node.js ${{ env.NODE_VERSION }} - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version: ${{ env.NODE_VERSION }} - name: Environment Information run: npx envinfo - name: Build run: NODE=$(command -v node) make doc-only - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: name: docs path: out/doc diff --git a/.github/workflows/find-inactive-collaborators.yml b/.github/workflows/find-inactive-collaborators.yml index 21f0f0297d5f11..705fa2eb38e3a6 100644 --- a/.github/workflows/find-inactive-collaborators.yml +++ b/.github/workflows/find-inactive-collaborators.yml @@ -16,13 +16,13 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: fetch-depth: 0 persist-credentials: false - name: Use Node.js ${{ env.NODE_VERSION }} - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version: ${{ env.NODE_VERSION }} diff --git a/.github/workflows/find-inactive-tsc.yml b/.github/workflows/find-inactive-tsc.yml index 9276de8b6a5c3a..27568392fbd8bf 100644 --- a/.github/workflows/find-inactive-tsc.yml +++ b/.github/workflows/find-inactive-tsc.yml @@ -17,13 +17,13 @@ jobs: steps: - name: Checkout the repo - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 persist-credentials: false - name: Clone nodejs/TSC repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 path: .tmp @@ -31,7 +31,7 @@ jobs: repository: nodejs/TSC - name: Use Node.js ${{ env.NODE_VERSION }} - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version: ${{ env.NODE_VERSION }} diff --git a/.github/workflows/license-builder.yml b/.github/workflows/license-builder.yml index 6ccb3b2f743ed6..98ae67956a8501 100644 --- a/.github/workflows/license-builder.yml +++ b/.github/workflows/license-builder.yml @@ -11,7 +11,7 @@ jobs: if: github.repository == 'nodejs/node' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: persist-credentials: false - run: ./tools/license-builder.sh # Run the license builder tool diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index b8bc5ecb92efbd..7373aabaca0456 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -23,11 +23,11 @@ jobs: if: github.event.pull_request.draft == false runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: persist-credentials: false - name: Use Node.js ${{ env.NODE_VERSION }} - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version: ${{ env.NODE_VERSION }} - name: Environment Information @@ -38,65 +38,89 @@ jobs: if: github.event.pull_request.draft == false runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: persist-credentials: false - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: ${{ env.PYTHON_VERSION }} - name: Environment Information run: npx envinfo - name: Lint C/C++ files run: make lint-cpp - lint-md: - if: github.event.pull_request.draft == false + format-cpp: + if: ${{ github.event.pull_request.draft == false && github.base_ref == 'master' }} runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: + fetch-depth: 0 persist-credentials: false - name: Use Node.js ${{ env.NODE_VERSION }} - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version: ${{ env.NODE_VERSION }} + - name: Set up Python ${{ env.PYTHON_VERSION }} + uses: actions/setup-python@v3 + with: + python-version: ${{ env.PYTHON_VERSION }} - name: Environment Information run: npx envinfo - - name: Get release version numbers - if: ${{ github.event.pull_request && github.event.pull_request.base.ref == github.event.pull_request.base.repo.default_branch }} - id: get-released-versions - run: ./tools/lint-md/list-released-versions-from-changelogs.mjs - - name: Lint docs + - name: Format C/C++ files run: | - echo "::add-matcher::.github/workflows/remark-lint-problem-matcher.json" - NODE=$(command -v node) make lint-md - env: - NODE_RELEASED_VERSIONS: ${{ steps.get-released-versions.outputs.NODE_RELEASED_VERSIONS }} - - lint-js: + make format-cpp-build + # The `make format-cpp` error code is intentionally ignored here + # because it is irrelevant. We already check if the formatter produced + # a diff in the next line. + # Refs: https://github.com/nodejs/node/pull/42764 + CLANG_FORMAT_START="$(git merge-base HEAD refs/remotes/origin/$GITHUB_BASE_REF)" \ + make format-cpp || true + git --no-pager diff --exit-code && EXIT_CODE="$?" || EXIT_CODE="$?" + if [ "$EXIT_CODE" != "0" ] + then + echo + echo 'ERROR: Please run:' + echo + echo " CLANG_FORMAT_START="$\(git merge-base HEAD ${GITHUB_BASE_REF}\)" make format-cpp" + echo + echo 'to format the commits in your branch.' + exit "$EXIT_CODE" + fi + lint-js-and-md: if: github.event.pull_request.draft == false runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: persist-credentials: false - name: Use Node.js ${{ env.NODE_VERSION }} - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version: ${{ env.NODE_VERSION }} - name: Environment Information run: npx envinfo - name: Lint JavaScript files run: NODE=$(command -v node) make lint-js + - name: Get release version numbers + if: ${{ github.event.pull_request && github.event.pull_request.base.ref == github.event.pull_request.base.repo.default_branch }} + id: get-released-versions + run: ./tools/lint-md/list-released-versions-from-changelogs.mjs + - name: Lint markdown files + run: | + echo "::add-matcher::.github/workflows/remark-lint-problem-matcher.json" + NODE=$(command -v node) make lint-md + env: + NODE_RELEASED_VERSIONS: ${{ steps.get-released-versions.outputs.NODE_RELEASED_VERSIONS }} lint-py: if: github.event.pull_request.draft == false runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: persist-credentials: false - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: ${{ env.PYTHON_VERSION }} - name: Environment Information @@ -109,11 +133,11 @@ jobs: if: github.event.pull_request.draft == false runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: persist-credentials: false - name: Use Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: ${{ env.PYTHON_VERSION }} - name: Environment Information @@ -127,7 +151,7 @@ jobs: if: github.event.pull_request.draft == false runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: persist-credentials: false - run: shellcheck -V @@ -137,7 +161,7 @@ jobs: if: github.event.pull_request.draft == false runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: persist-credentials: false - uses: mszostok/codeowners-validator@v0.6.0 @@ -147,7 +171,7 @@ jobs: if: ${{ github.event.pull_request }} runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: fetch-depth: 2 persist-credentials: false diff --git a/.github/workflows/test-asan.yml b/.github/workflows/test-asan.yml index 5f2f1c93c3d521..29e545657002eb 100644 --- a/.github/workflows/test-asan.yml +++ b/.github/workflows/test-asan.yml @@ -43,11 +43,11 @@ jobs: LINK: clang++ CONFIG_FLAGS: --enable-asan steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: persist-credentials: false - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: ${{ env.PYTHON_VERSION }} - name: Environment Information diff --git a/.github/workflows/test-internet.yml b/.github/workflows/test-internet.yml index 9b504980301dbc..8325845963ecf9 100644 --- a/.github/workflows/test-internet.yml +++ b/.github/workflows/test-internet.yml @@ -29,11 +29,11 @@ jobs: test-internet: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: persist-credentials: false - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: ${{ env.PYTHON_VERSION }} - name: Environment Information diff --git a/.github/workflows/test-linux.yml b/.github/workflows/test-linux.yml index 22b5e9a35d3dd9..7d6f4303e9256e 100644 --- a/.github/workflows/test-linux.yml +++ b/.github/workflows/test-linux.yml @@ -32,11 +32,11 @@ jobs: if: github.event.pull_request.draft == false runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: persist-credentials: false - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: ${{ env.PYTHON_VERSION }} - name: Environment Information diff --git a/.github/workflows/test-macos.yml b/.github/workflows/test-macos.yml index 40885bd9e65c5a..0c76a44bae9695 100644 --- a/.github/workflows/test-macos.yml +++ b/.github/workflows/test-macos.yml @@ -38,11 +38,11 @@ jobs: if: github.event.pull_request.draft == false runs-on: macos-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: persist-credentials: false - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: ${{ env.PYTHON_VERSION }} - name: Environment Information diff --git a/.github/workflows/tools.yml b/.github/workflows/tools.yml index 7865a4166f81eb..50735145a57a08 100644 --- a/.github/workflows/tools.yml +++ b/.github/workflows/tools.yml @@ -1,4 +1,4 @@ -name: Tools update +name: Tools and deps update on: schedule: # Run once a week at 00:05 AM UTC on Sunday. @@ -7,7 +7,7 @@ on: workflow_dispatch: jobs: - tools-update: + tools-deps-update: if: github.repository == 'nodejs/node' runs-on: ubuntu-latest strategy: @@ -15,6 +15,8 @@ jobs: matrix: include: - id: eslint + subsystem: tools + label: tools run: | cd tools NEW_VERSION=$(npm view eslint dist-tags.latest) @@ -23,7 +25,15 @@ jobs: echo "NEW_VERSION=$NEW_VERSION" >> $GITHUB_ENV ./update-eslint.sh fi + - id: corepack + subsystem: deps + label: dependencies + run: | + make corepack-update + echo "NEW_VERSION=$(node deps/corepack/dist/corepack.js --version)" >> $GITHUB_ENV - id: lint-md-dependencies + subsystem: tools + label: tools run: | cd tools/lint-md npm ci @@ -40,6 +50,8 @@ jobs: make lint-md-rollup fi - id: doc + subsystem: tools + label: tools run: | cd tools/doc npm ci @@ -53,8 +65,18 @@ jobs: npm install --ignore-scripts $NEW_VERSION npm install --ignore-scripts fi + - id: undici + subsystem: deps + label: dependencies + run: | + NEW_VERSION=$(npm view undici dist-tags.latest) + CURRENT_VERSION=$(node -p "require('./deps/undici/src/package.json').version") + if [ "$NEW_VERSION" != "$CURRENT_VERSION" ]; then + echo "NEW_VERSION=$NEW_VERSION" >> $GITHUB_ENV + ./tools/update-undici.sh + fi steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: persist-credentials: false - run: ${{ matrix.run }} @@ -65,6 +87,6 @@ jobs: author: Node.js GitHub Bot body: This is an automated update of ${{ matrix.id }} to ${{ env.NEW_VERSION }}. branch: actions/tools-update-${{ matrix.id }} # Custom branch *just* for this Action. - commit-message: 'tools: update ${{ matrix.id }} to ${{ env.NEW_VERSION }}' - labels: tools - title: 'tools: update ${{ matrix.id }} to ${{ env.NEW_VERSION }}' + commit-message: '${{ matrix.subsystem }}: update ${{ matrix.id }} to ${{ env.NEW_VERSION }}' + labels: ${{ matrix.label }} + title: '${{ matrix.subsystem }}: update ${{ matrix.id }} to ${{ env.NEW_VERSION }}' diff --git a/.mailmap b/.mailmap index 9ae59edba936e5..120d69b7df2bbd 100644 --- a/.mailmap +++ b/.mailmap @@ -35,6 +35,7 @@ Anna Henningsen Anna Henningsen Anna Magdalena Kedzierska Antoine Amara +apeltop Aria Stewart Arlo Breault Arnaud Lefebvre @@ -49,6 +50,7 @@ Ashley Maceli Ashok Suthar Ashutosh Kumar Singh Atsuo Fukaya +Austin Kelleher Azard <330815461@qq.com> Ben Lugavere Ben Noordhuis @@ -81,6 +83,7 @@ Brian White Caleb Boyd Calvin Metcalf Calvin Metcalf +Camillo Bruni Caralyn Reisle Charles Charles Rudolph @@ -355,6 +358,7 @@ Mitar Milutinovic Mithun Sasidharan Mohammed Keyvanzadeh Mohammed Keyvanzadeh <62040526+VoltrexMaster@users.noreply.github.com> +MURAKAMI Masahiko Myles Borins Myles Borins Myles Borins @@ -370,6 +374,8 @@ Nils Kuhnhenn Nitzan Uziely Nitzan Uziely Noah Rose Ledesma +npm team +npm team Oliver Chang Oluwaseun Omoyajowo Onne Gorter @@ -537,6 +543,7 @@ Wyatt Preul Xavier J Ortiz xiaoyu <306766053@qq.com> Xu Meng +Xuguang Mei Yael Hermon Yang Guo Yash Ladha <18033231+yashLadha@users.noreply.github.com> diff --git a/AUTHORS b/AUTHORS index ad82e973277bb2..633793c76b9d68 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1183,7 +1183,7 @@ ikasumi_wt Yoshiya Hinosawa Syuhei Kobayashi YutamaKotaro -MURAKAMI Masahiko +MURAKAMI Masahiko Thomas Watson Daijiro Yamada Kelvin Jin @@ -2310,7 +2310,7 @@ Dzmitry_Prudnikau Ian McKellar Jennifer Bland Kyle Fuller -Camillo Bruni +Camillo Bruni Yongsheng Zhang Neeraj Laad Scott Van Gilder @@ -3288,7 +3288,7 @@ pengjie <37610029@qq.com> Philip julianjany <54538266+julianjany@users.noreply.github.com> bl-ue -npm-robot +npm team Shaun Keys Simone Busoli Derevianchenko Maksym <32910350+maks-white@users.noreply.github.com> @@ -3414,7 +3414,7 @@ Tony Gorez ofirbarak Bar Admoni ofir -Xuguang Mei +Xuguang Mei Elad Nava Balakrishna Avulapati Aaron Xie @@ -3428,5 +3428,31 @@ T•Ø•R•Ü•S Sean Quinlan <1011062+sbquinlan@users.noreply.github.com> Derek Wolpert <48101033+derekwolpert@users.noreply.github.com> wbt +Alexandru Comanescu +madflow +Austin Kelleher +apeltop +Livia Medeiros <74449973+LiviaMedeiros@users.noreply.github.com> +Nikolaos Papaspyrou +Matt Probert <1196252+mattpr@users.noreply.github.com> +Roch Devost +Kohei Ueno +bradh352 +Mikael Finstad +Damjan Cvetko +Randall Leeds +Khoo Hao Yit <40757009+KhooHaoYit@users.noreply.github.com> +Aroyan <43630681+aroyan@users.noreply.github.com> +theanarkh <2923878201@qq.com> +Vladimir Morozov +Anupama Codippily <47591753+AnupamaCodippily@users.noreply.github.com> +Greg Poole +Eliaz Bobadilla +Daeyeon Jeong +Daniel Roe +Niyas Sait +K.C.Ashish Kumar <703559+kcak11@users.noreply.github.com> +Niklas Mischkulnig <4586894+mischnic@users.noreply.github.com> +Liviu Ionescu # Generated by tools/update-authors.js diff --git a/BUILDING.md b/BUILDING.md index 424b2a7adc9984..31800bd66b0fc2 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -99,46 +99,37 @@ Node.js does not support a platform version if a vendor has expired support for it. In other words, Node.js does not support running on End-of-Life (EoL) platforms. This is true regardless of entries in the table below. -| Operating System | Architectures | Versions | Support Type | Notes | -| ---------------- | ---------------- | ------------------------------- | ----------------------------------------------- | ----------------------------------------- | -| GNU/Linux | x64 | kernel >= 3.10, glibc >= 2.17 | Tier 1 | e.g. Ubuntu 16.04[^1], Debian 9, EL 7[^2] | -| GNU/Linux | x64 | kernel >= 3.10, musl >= 1.1.19 | Experimental | e.g. Alpine 3.8 | -| GNU/Linux | x86 | kernel >= 3.10, glibc >= 2.17 | Experimental | Downgraded as of Node.js 10 | -| GNU/Linux | arm64 | kernel >= 4.5, glibc >= 2.17 | Tier 1 | e.g. Ubuntu 16.04, Debian 9, EL 7[^3] | -| GNU/Linux | armv7 | kernel >= 4.14, glibc >= 2.24 | Tier 1 | e.g. Ubuntu 18.04, Debian 9 | -| GNU/Linux | armv6 | kernel >= 4.14, glibc >= 2.24 | Experimental | Downgraded as of Node.js 12 | -| GNU/Linux | ppc64le >=power8 | kernel >= 3.10.0, glibc >= 2.17 | Tier 2 | e.g. Ubuntu 16.04[^1], EL 7[^2] | -| GNU/Linux | s390x | kernel >= 3.10.0, glibc >= 2.17 | Tier 2 | e.g. EL 7[^2] | -| Windows | x64, x86 (WoW64) | >= Windows 8.1/2012 R2 | Tier 1 | [^4],[^5] | -| Windows | x86 (native) | >= Windows 8.1/2012 R2 | Tier 1 (running) / Experimental (compiling)[^6] | | -| Windows | x64, x86 | Windows Server 2012 (not R2) | Experimental | | -| Windows | arm64 | >= Windows 10 | Tier 2 (compiling) / Experimental (running) | | -| macOS | x64 | >= 10.13 | Tier 1 | For notes about compilation see [^7] | -| macOS | arm64 | >= 11 | Tier 1 | | -| SmartOS | x64 | >= 18 | Tier 2 | | -| AIX | ppc64be >=power7 | >= 7.2 TL04 | Tier 2 | | -| FreeBSD | x64 | >= 12.2 | Experimental | | - -[^1]: GCC 8 is not provided on the base platform. Users will - need the - [Toolchain test builds PPA](https://launchpad.net/\~ubuntu-toolchain-r/+archive/ubuntu/test?field.series_filter=xenial) - or similar to source a newer compiler. - -[^2]: GCC 8 is not provided on the base platform. Users will - need the - [devtoolset-8](https://www.softwarecollections.org/en/scls/rhscl/devtoolset-8/) - or later to source a newer compiler. - -[^3]: Older kernel versions may work for ARM64. However the - Node.js test infrastructure only tests >= 4.5. - -[^4]: On Windows, running Node.js in Windows terminal emulators +| Operating System | Architectures | Versions | Support Type | Notes | +| ---------------- | ---------------- | --------------------------------- | ----------------------------------------------- | ------------------------------------ | +| GNU/Linux | x64 | kernel >= 4.18[^1], glibc >= 2.28 | Tier 1 | e.g. Ubuntu 20.04, Debian 10, RHEL 8 | +| GNU/Linux | x64 | kernel >= 3.10, musl >= 1.1.19 | Experimental | e.g. Alpine 3.8 | +| GNU/Linux | x86 | kernel >= 3.10, glibc >= 2.17 | Experimental | Downgraded as of Node.js 10 | +| GNU/Linux | arm64 | kernel >= 4.18[^1], glibc >= 2.28 | Tier 1 | e.g. Ubuntu 20.04, Debian 10, RHEL 8 | +| GNU/Linux | armv7 | kernel >= 4.18[^1], glibc >= 2.28 | Tier 1 | e.g. Ubuntu 20.04, Debian 10 | +| GNU/Linux | armv6 | kernel >= 4.14, glibc >= 2.24 | Experimental | Downgraded as of Node.js 12 | +| GNU/Linux | ppc64le >=power8 | kernel >= 4.18[^1], glibc >= 2.28 | Tier 2 | e.g. Ubuntu 20.04, RHEL 8 | +| GNU/Linux | s390x | kernel >= 4.18[^1], glibc >= 2.28 | Tier 2 | e.g. RHEL 8 | +| Windows | x64, x86 (WoW64) | >= Windows 10/Server 2016 | Tier 1 | [^2],[^3] | +| Windows | x86 (native) | >= Windows 10/Server 2016 | Tier 1 (running) / Experimental (compiling)[^4] | | +| Windows | x64, x86 | Windows 8.1/Server 2012 | Experimental | | +| Windows | arm64 | >= Windows 10 | Tier 2 (compiling) / Experimental (running) | | +| macOS | x64 | >= 10.15 | Tier 1 | For notes about compilation see [^5] | +| macOS | arm64 | >= 11 | Tier 1 | | +| SmartOS | x64 | >= 18 | Tier 2 | | +| AIX | ppc64be >=power8 | >= 7.2 TL04 | Tier 2 | | +| FreeBSD | x64 | >= 12.2 | Experimental | | + +[^1]: Older kernel versions may work. However official Node.js release + binaries are [built on RHEL 8 systems](#official-binary-platforms-and-toolchains) + with kernel 4.18. + +[^2]: On Windows, running Node.js in Windows terminal emulators like `mintty` requires the usage of [winpty](https://github.com/rprichard/winpty) for the tty channels to work (e.g. `winpty node.exe script.js`). In "Git bash" if you call the node shell alias (`node` without the `.exe` extension), `winpty` is used automatically. -[^5]: The Windows Subsystem for Linux (WSL) is not +[^3]: The Windows Subsystem for Linux (WSL) is not supported, but the GNU/Linux build process and binaries should work. The community will only address issues that reproduce on native GNU/Linux systems. Issues that only reproduce on WSL should be reported in the @@ -146,13 +137,12 @@ platforms. This is true regardless of entries in the table below. Windows binary (`node.exe`) in WSL will not work without workarounds such as stdio redirection. -[^6]: Running Node.js on x86 Windows should work and binaries +[^4]: Running Node.js on x86 Windows should work and binaries are provided. However, tests in our infrastructure only run on WoW64. Furthermore, compiling on x86 Windows is Experimental and may not be possible. -[^7]: Our macOS x64 Binaries are compiled with 10.13 as a target. - However there is no guarantee compiling on 10.13 will work as Xcode11 is +[^5]: Our macOS x64 Binaries are compiled with 10.15 as a target. Xcode11 is required to compile. ### Supported toolchains @@ -172,21 +162,19 @@ Binaries at are produced on: | Binary package | Platform and Toolchain | | ----------------------- | ------------------------------------------------------------------------------------------------------------- | | aix-ppc64 | AIX 7.2 TL04 on PPC64BE with GCC 8 | -| darwin-x64 | macOS 10.15, Xcode Command Line Tools 11 with -mmacosx-version-min=10.13 | -| darwin-arm64 (and .pkg) | macOS 11 (arm64), Xcode Command Line Tools 12 with -mmacosx-version-min=10.13 | -| linux-arm64 | CentOS 7 with devtoolset-8 / GCC 8[^8] | +| darwin-x64 | macOS 10.15, Xcode Command Line Tools 11 with -mmacosx-version-min=10.15 | +| darwin-arm64 (and .pkg) | macOS 11 (arm64), Xcode Command Line Tools 12 with -mmacosx-version-min=10.15 | +| linux-arm64 | RHEL 8 with GCC 8[^6] | | linux-armv7l | Cross-compiled on Ubuntu 18.04 x64 with [custom GCC toolchain](https://github.com/rvagg/rpi-newer-crosstools) | -| linux-ppc64le | CentOS 7 with devtoolset-8 / GCC 8[^8] | -| linux-s390x | RHEL 7 with devtoolset-8 / GCC 8[^8] | -| linux-x64 | CentOS 7 with devtoolset-8 / GCC 8[^8] | +| linux-ppc64le | RHEL 8 with GCC 8[^6] | +| linux-s390x | RHEL 8 with GCC 8[^6] | +| linux-x64 | RHEL 8 with GCC 8[^6] | | win-x64 and win-x86 | Windows 2012 R2 (x64) with Visual Studio 2019 | -[^8]: The Enterprise Linux devtoolset-8 allows us to compile binaries with GCC 8 - but linked to the glibc and libstdc++ versions of the host platforms - (CentOS 7 / RHEL 7). Therefore, binaries produced on these systems are - compatible with glibc >= 2.17 and libstdc++ >= 6.0.20 (`GLIBCXX_3.4.20`). - These are available on distributions natively supporting GCC 4.9, such as - Ubuntu 14.04 and Debian 8. +[^6]: Binaries produced on these systems are compatible with glibc >= 2.28 + and libstdc++ >= 6.0.25 (`GLIBCXX_3.4.25`). These are available on + distributions natively supporting GCC 8.1 or higher, such as Debian 10, + RHEL 8 and Ubuntu 20.04. #### OpenSSL asm support @@ -273,6 +261,10 @@ $ ./configure $ make -j4 ``` +We can speed up the builds by using [Ninja](https://ninja-build.org/). For more +information, see +[Building Node.js with Ninja](doc/contributing/building-node-with-ninja.md). + The `-j4` option will cause `make` to run 4 simultaneous compilation jobs which may reduce build time. For more information, see the [GNU Make Documentation](https://www.gnu.org/software/make/manual/html_node/Parallel.html). diff --git a/CHANGELOG.md b/CHANGELOG.md index 81364c0368ed08..1ea47a95309758 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ Select a Node.js version below to view the changelog history: +* [Node.js 18](doc/changelogs/CHANGELOG_V18.md) **Current** * [Node.js 17](doc/changelogs/CHANGELOG_V17.md) **Current** * [Node.js 16](doc/changelogs/CHANGELOG_V16.md) **Long Term Support** * [Node.js 15](doc/changelogs/CHANGELOG_V15.md) End-of-Life @@ -26,6 +27,7 @@ release. + @@ -33,7 +35,14 @@ release. + " if slot == highlight_address: @@ -2974,12 +3043,12 @@ def output_words(self, f, start_address, end_address, f.write("") self.td_from_address(f, slot) f.write(address_fmt % self.format_address(slot)) - self.td_from_address(f, maybe_address) + self.td_from_address(f, maybe_uncompressed_address) f.write(": %s " % straddress) f.write("") f.write("" % (heap_object or '')) f.write("") @@ -3104,7 +3173,7 @@ def annotate_disasm_addresses(self, line): object_info = self.padawan.SenseObject(maybe_address) if not object_info: continue - extra.append(cgi.escape(str(object_info))) + extra.append(html.escape(str(object_info))) if len(extra) == 0: return line return ("%s ;; %s" % @@ -3134,9 +3203,9 @@ def format_disasm_line( # Some disassemblers insert spaces between each byte, # while some do not. if code[2] == " ": - op_offset = 3 * num_bytes - 1 + op_offset = 3 * num_bytes - 1 else: - op_offset = 2 * num_bytes + op_offset = 2 * num_bytes # Compute the actual call target which the disassembler is too stupid # to figure out (it adds the call offset to the disassembly offset rather @@ -3168,7 +3237,7 @@ def output_comment_box(self, f, prefix, address): comment = self.comments.get_comment(address) value = "" if comment: - value = " value=\"%s\"" % cgi.escape(comment) + value = " value=\"%s\"" % html.escape(comment) f.write("" % (prefix, @@ -3230,7 +3299,12 @@ def output_search_res(self, f, straddress): straddress) else: # Print as words - self.output_words(f, address - 8, address + 32, address, "Dump") + self.output_words(f, address - 8, address + 32, address, "Dump", + self.heap.MachinePointerSize()) + + if self.heap.IsPointerCompressed(): + self.output_words(f, address - 8, address + 32, address, + "Tagged Dump", self.heap.TaggedPointerSize()) # Print as ASCII f.write("
") @@ -3327,10 +3401,10 @@ def output_disasm_pc(self, f): DUMP_FILE_RE = re.compile(r"[-_0-9a-zA-Z][-\._0-9a-zA-Z]*\.dmp$") -class InspectionWebServer(BaseHTTPServer.HTTPServer): +class InspectionWebServer(http_server.HTTPServer): + def __init__(self, port_number, switches, minidump_name): - BaseHTTPServer.HTTPServer.__init__( - self, ('localhost', port_number), InspectionWebHandler) + super().__init__(('localhost', port_number), InspectionWebHandler) splitpath = os.path.split(minidump_name) self.dumppath = splitpath[0] self.dumpfilename = splitpath[1] @@ -3348,7 +3422,7 @@ def output_dump_desc_field(self, f, name): desc = "" f.write("\n" % - (cgi.escape(name), desc)) + (html.escape(name), desc)) def set_dump_desc(self, name, description): if not DUMP_FILE_RE.match(name): @@ -3399,8 +3473,8 @@ def output_dumps(self, f): fnames = dumps_by_time[mtime] for fname in fnames: f.write("\n") - f.write("\n" % ( - (urllib.urlencode({ 'dump' : fname }), fname))) + f.write("\n" % + ((urllib.parse.urlencode({'dump': fname}), fname))) f.write("") @@ -3534,10 +3608,10 @@ def do_dd(self, args): self.dd_start = self.ParseAddressExpr(args[0]) self.dd_num = int(args[1], 16) if len(args) > 1 else 0x10 else: - self.dd_start += self.dd_num * self.reader.PointerSize() + self.dd_start += self.dd_num * self.reader.MachinePointerSize() if not self.reader.IsAlignedAddress(self.dd_start): print("Warning: Dumping un-aligned memory, is this what you had in mind?") - end = self.dd_start + self.reader.PointerSize() * self.dd_num + end = self.dd_start + self.reader.MachinePointerSize() * self.dd_num self.padawan.InterpretMemory(self.dd_start, end) def do_do(self, address): @@ -3823,12 +3897,19 @@ def PrintModuleDetails(reader, module): def AnalyzeMinidump(options, minidump_name): reader = MinidumpReader(options, minidump_name) + # Use a separate function to prevent leaking the minidump buffer through + # ctypes in local variables. + _AnalyzeMinidump(options, reader) + reader.Dispose() + + +def _AnalyzeMinidump(options, reader): heap = None stack_top = reader.ExceptionSP() stack_bottom = reader.StackBottom() stack_map = {reader.ExceptionIP(): -1} - for slot in range(stack_top, stack_bottom, reader.PointerSize()): + for slot in range(stack_top, stack_bottom, reader.MachinePointerSize()): maybe_address = reader.ReadUIntPtr(slot) if not maybe_address in stack_map: stack_map[maybe_address] = slot @@ -3920,7 +4001,6 @@ def AnalyzeMinidump(options, minidump_name): print("Annotated stack (from exception.esp to bottom):") stack_start = padawan.PrintStackTraceMessage() padawan.InterpretMemory(stack_start, stack_bottom) - reader.Dispose() if __name__ == "__main__": diff --git a/deps/v8/tools/heap-layout/heap-layout-viewer-template.html b/deps/v8/tools/heap-layout/heap-layout-viewer-template.html new file mode 100644 index 00000000000000..c50f5cc3d38f45 --- /dev/null +++ b/deps/v8/tools/heap-layout/heap-layout-viewer-template.html @@ -0,0 +1,14 @@ + + + + \ No newline at end of file diff --git a/deps/v8/tools/heap-layout/heap-layout-viewer.mjs b/deps/v8/tools/heap-layout/heap-layout-viewer.mjs new file mode 100644 index 00000000000000..ada4a02311ce5d --- /dev/null +++ b/deps/v8/tools/heap-layout/heap-layout-viewer.mjs @@ -0,0 +1,225 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import {GB, MB} from '../js/helper.mjs'; +import {DOM} from '../js/web-api-helper.mjs'; + +import {getColorFromSpaceName, kSpaceNames} from './space-categories.mjs'; + +DOM.defineCustomElement('heap-layout-viewer', + (templateText) => + class HeapLayoutViewer extends HTMLElement { + constructor() { + super(); + const shadowRoot = this.attachShadow({mode: 'open'}); + shadowRoot.innerHTML = templateText; + this.chart = echarts.init(this.$('#chart'), null, { + renderer: 'canvas', + }); + window.addEventListener('resize', () => { + this.chart.resize(); + }); + this.currentIndex = 0; + } + + $(id) { + return this.shadowRoot.querySelector(id); + } + + set data(value) { + this._data = value; + this.stateChanged(); + } + + get data() { + return this._data; + } + + hide() { + this.$('#container').style.display = 'none'; + } + + show() { + this.$('#container').style.display = 'block'; + } + + stateChanged() { + this.drawChart(0); + } + + getChartTitle(index) { + return this.data[index].header; + } + + getSeriesData(pageinfos) { + let ret = []; + for (let pageinfo of pageinfos) { + ret.push({value: pageinfo}); + } + return ret; + } + + getChartSeries(index) { + const snapshot = this.data[index]; + let series = []; + for (const [space_name, pageinfos] of Object.entries(snapshot.data)) { + let space_series = { + name: space_name, + type: 'custom', + renderItem(params, api) { + const addressBegin = api.value(1); + const addressEnd = api.value(2); + const allocated = api.value(3); + const start = api.coord([addressBegin, 0]); + const end = api.coord([addressEnd, 0]); + + const allocatedRate = allocated / (addressEnd - addressBegin); + const unAllocatedRate = 1 - allocatedRate; + + const standardH = api.size([0, 1])[1]; + const standardY = start[1] - standardH / 2; + + const allocatedY = standardY + standardH * unAllocatedRate; + const allocatedH = standardH * allocatedRate; + + const unAllocatedY = standardY; + const unAllocatedH = standardH - allocatedH; + + const allocatedShape = echarts.graphic.clipRectByRect( + { + x: start[0], + y: allocatedY, + width: end[0] - start[0], + height: allocatedH, + }, + { + x: params.coordSys.x, + y: params.coordSys.y, + width: params.coordSys.width, + height: params.coordSys.height, + }); + + const unAllocatedShape = echarts.graphic.clipRectByRect( + { + x: start[0], + y: unAllocatedY, + width: end[0] - start[0], + height: unAllocatedH, + }, + { + x: params.coordSys.x, + y: params.coordSys.y, + width: params.coordSys.width, + height: params.coordSys.height, + }); + + const ret = { + type: 'group', + children: [ + { + type: 'rect', + shape: allocatedShape, + style: api.style(), + }, + { + type: 'rect', + shape: unAllocatedShape, + style: { + fill: '#000000', + }, + }, + ], + }; + return ret; + }, + data: this.getSeriesData(pageinfos), + encode: { + x: [1, 2], + }, + itemStyle: { + color: getColorFromSpaceName(space_name), + }, + }; + series.push(space_series); + } + return series; + } + + drawChart(index) { + if (index >= this.data.length || index < 0) { + console.error('Invalid index:', index); + return; + } + const option = { + tooltip: { + formatter(params) { + const ret = params.marker + params.value[0] + '
' + + 'address:' + (params.value[1] / MB).toFixed(3) + 'MB' + + '
' + + 'size:' + ((params.value[2] - params.value[1]) / MB).toFixed(3) + + 'MB' + + '
' + + 'allocated:' + (params.value[3] / MB).toFixed(3) + 'MB' + + '
' + + 'wasted:' + params.value[4] + 'B'; + return ret; + }, + }, + grid: { + bottom: 120, + top: 120, + }, + dataZoom: [ + { + type: 'slider', + filterMode: 'weakFilter', + showDataShadow: true, + labelFormatter: '', + }, + { + type: 'inside', + filterMode: 'weakFilter', + }, + ], + legend: { + show: true, + data: kSpaceNames, + top: '6%', + type: 'scroll', + }, + title: { + text: this.getChartTitle(index), + left: 'center', + }, + xAxis: { + name: 'Address offset in heap(MB)', + nameLocation: 'center', + nameTextStyle: { + fontSize: 25, + padding: [30, 0, 50, 0], + }, + type: 'value', + min: 0, + max: 4 * GB, + axisLabel: { + rotate: 0, + formatter(value, index) { + value = value / MB; + value = value.toFixed(3); + return value; + }, + }, + }, + yAxis: { + data: ['Page'], + }, + series: this.getChartSeries(index), + }; + + this.show(); + this.chart.resize(); + this.chart.setOption(option); + this.currentIndex = index; + } +}); diff --git a/deps/v8/tools/heap-layout/heap-size-trend-viewer-template.html b/deps/v8/tools/heap-layout/heap-size-trend-viewer-template.html new file mode 100644 index 00000000000000..cbf2bc711dca04 --- /dev/null +++ b/deps/v8/tools/heap-layout/heap-size-trend-viewer-template.html @@ -0,0 +1,14 @@ + + + + \ No newline at end of file diff --git a/deps/v8/tools/heap-layout/heap-size-trend-viewer.mjs b/deps/v8/tools/heap-layout/heap-size-trend-viewer.mjs new file mode 100644 index 00000000000000..d7b8737d7f33e3 --- /dev/null +++ b/deps/v8/tools/heap-layout/heap-size-trend-viewer.mjs @@ -0,0 +1,266 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import {MB} from '../js/helper.mjs'; +import {DOM} from '../js/web-api-helper.mjs'; + +import {getColorFromSpaceName, kSpaceNames} from './space-categories.mjs'; + +class TrendLineHelper { + static re_gc_count = /(?<=(Before|After) GC:)\d+(?=,)/; + static re_allocated = /allocated/; + static re_space_name = /^[a-z_]+_space/; + + static snapshotHeaderToXLabel(header) { + const gc_count = this.re_gc_count.exec(header)[0]; + const alpha = header[0]; + return alpha + gc_count; + } + + static getLineSymbolFromTrendLineName(trend_line_name) { + const is_allocated_line = this.re_allocated.test(trend_line_name); + if (is_allocated_line) { + return 'emptyTriangle'; + } + return 'emptyCircle'; + } + + static getSizeTrendLineName(space_name) { + return space_name + ' size'; + } + + static getAllocatedTrendSizeName(space_name) { + return space_name + ' allocated'; + } + + static getSpaceNameFromTrendLineName(trend_line_name) { + const space_name = this.re_space_name.exec(trend_line_name)[0]; + return space_name; + } +} + +DOM.defineCustomElement('heap-size-trend-viewer', + (templateText) => + class HeapSizeTrendViewer extends HTMLElement { + constructor() { + super(); + const shadowRoot = this.attachShadow({mode: 'open'}); + shadowRoot.innerHTML = templateText; + this.chart = echarts.init(this.$('#chart'), null, { + renderer: 'canvas', + }); + this.chart.getZr().on('click', 'series.line', (params) => { + const pointInPixel = [params.offsetX, params.offsetY]; + const pointInGrid = + this.chart.convertFromPixel({seriesIndex: 0}, pointInPixel); + const xIndex = pointInGrid[0]; + this.dispatchEvent(new CustomEvent('change', { + bubbles: true, + composed: true, + detail: xIndex, + })); + this.setXMarkLine(xIndex); + }); + this.chartXAxisData = null; + this.chartSeriesData = null; + this.currentIndex = 0; + window.addEventListener('resize', () => { + this.chart.resize(); + }); + } + + $(id) { + return this.shadowRoot.querySelector(id); + } + + set data(value) { + this._data = value; + this.stateChanged(); + } + + get data() { + return this._data; + } + + hide() { + this.$('#container').style.display = 'none'; + } + + show() { + this.$('#container').style.display = 'block'; + } + + stateChanged() { + this.initTrendLineNames(); + this.initXAxisDataAndSeries(); + this.drawChart(); + } + + initTrendLineNames() { + this.trend_line_names = []; + for (const space_name of kSpaceNames) { + this.trend_line_names.push( + TrendLineHelper.getSizeTrendLineName(space_name)); + this.trend_line_names.push( + TrendLineHelper.getAllocatedTrendSizeName(space_name)); + } + } + + // X axis represent the moment before or after nth GC : [B1,A1,...Bn,An]. + initXAxisDataAndSeries() { + this.chartXAxisData = []; + this.chartSeriesData = []; + let trend_line_name_data_dict = {}; + + for (const trend_line_name of this.trend_line_names) { + trend_line_name_data_dict[trend_line_name] = []; + } + + // Init x axis data and trend line series. + for (const snapshot of this.data) { + this.chartXAxisData.push( + TrendLineHelper.snapshotHeaderToXLabel(snapshot.header)); + for (const [space_name, pageinfos] of Object.entries(snapshot.data)) { + const size_trend_line_name = + TrendLineHelper.getSizeTrendLineName(space_name); + const allocated_trend_line_name = + TrendLineHelper.getAllocatedTrendSizeName(space_name); + let size_sum = 0; + let allocated_sum = 0; + for (const pageinfo of pageinfos) { + size_sum += pageinfo[2] - pageinfo[1]; + allocated_sum += pageinfo[3]; + } + trend_line_name_data_dict[size_trend_line_name].push(size_sum); + trend_line_name_data_dict[allocated_trend_line_name].push( + allocated_sum); + } + } + + // Init mark line series as the first series + const markline_series = { + name: 'mark-line', + type: 'line', + + markLine: { + silent: true, + symbol: 'none', + label: { + show: false, + }, + lineStyle: { + color: '#333', + }, + data: [ + { + xAxis: 0, + }, + ], + }, + }; + this.chartSeriesData.push(markline_series); + + for (const [trend_line_name, trend_line_data] of Object.entries( + trend_line_name_data_dict)) { + const color = getColorFromSpaceName( + TrendLineHelper.getSpaceNameFromTrendLineName(trend_line_name)); + const trend_line_series = { + name: trend_line_name, + type: 'line', + data: trend_line_data, + lineStyle: { + color: color, + }, + itemStyle: { + color: color, + }, + symbol: TrendLineHelper.getLineSymbolFromTrendLineName(trend_line_name), + symbolSize: 8, + }; + this.chartSeriesData.push(trend_line_series); + } + } + + setXMarkLine(index) { + if (index < 0 || index >= this.data.length) { + console.error('Invalid index:', index); + return; + } + // Set the mark-line series + this.chartSeriesData[0].markLine.data[0].xAxis = index; + this.chart.setOption({ + series: this.chartSeriesData, + }); + this.currentIndex = index; + } + + drawChart() { + const option = { + dataZoom: [ + { + type: 'inside', + filterMode: 'weakFilter', + }, + { + type: 'slider', + filterMode: 'weakFilter', + labelFormatter: '', + }, + ], + title: { + text: 'Size Trend', + left: 'center', + }, + tooltip: { + trigger: 'axis', + position(point, params, dom, rect, size) { + let ret_x = point[0] + 10; + if (point[0] > size.viewSize[0] * 0.7) { + ret_x = point[0] - dom.clientWidth - 10; + } + return [ret_x, '85%']; + }, + formatter(params) { + const colorSpan = (color) => + ''; + let result = '

' + params[0].axisValue + '

'; + params.forEach((item) => { + const xx = '

' + colorSpan(item.color) + ' ' + + item.seriesName + ': ' + (item.data / MB).toFixed(2) + 'MB' + + '

'; + result += xx; + }); + + return result; + }, + }, + legend: { + data: this.trend_line_names, + top: '6%', + type: 'scroll', + }, + + xAxis: { + minInterval: 1, + type: 'category', + boundaryGap: false, + data: this.chartXAxisData, + }, + yAxis: { + type: 'value', + axisLabel: { + formatter(value, index) { + return (value / MB).toFixed(3) + 'MB'; + }, + }, + }, + + series: this.chartSeriesData, + }; + this.show(); + this.chart.resize(); + this.chart.setOption(option); + } +}); diff --git a/deps/v8/tools/heap-layout/index.css b/deps/v8/tools/heap-layout/index.css new file mode 100644 index 00000000000000..53fcf97defca69 --- /dev/null +++ b/deps/v8/tools/heap-layout/index.css @@ -0,0 +1,24 @@ +:root { + --surface-color: #ffffff; + --primary-color: #bb86fc; + --on-primary-color: #000000; + --error-color: #cf6679; + --file-reader-background-color: #ffffff80; + --file-reader-border-color: #000000; +} + +body { + font-family: "Roboto", sans-serif; + margin-left: 5%; + margin-right: 5%; +} + +.button-container { + text-align: center; + display: none; +} + +button { + height: 50px; + width: 100px; +} diff --git a/deps/v8/tools/heap-layout/index.html b/deps/v8/tools/heap-layout/index.html new file mode 100644 index 00000000000000..0f33a730499581 --- /dev/null +++ b/deps/v8/tools/heap-layout/index.html @@ -0,0 +1,72 @@ + + + + + + + + V8 Heap Layout + + + + + + + + + + + + + +

V8 Heap Layout

+ + + +
+ + +
+ +

Heap layout is a HTML-based tool for visualizing V8-internal heap layout.

+

Visualize heap layout that have been gathered using

+
    +
  • --trace-gc-heap-layout on V8
  • + +
+ + + + \ No newline at end of file diff --git a/deps/v8/tools/heap-layout/space-categories.mjs b/deps/v8/tools/heap-layout/space-categories.mjs new file mode 100644 index 00000000000000..95b52ba9cec65d --- /dev/null +++ b/deps/v8/tools/heap-layout/space-categories.mjs @@ -0,0 +1,32 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +export const kSpaceNames = [ + 'to_space', + 'from_space', + 'old_space', + 'map_space', + 'code_space', + 'large_object_space', + 'new_large_object_space', + 'code_large_object_space', + 'ro_space', +]; + +const kSpaceColors = [ + '#5b8ff9', + '#5ad8a6', + '#5d7092', + '#f6bd16', + '#e8684a', + '#6dc8ec', + '#9270ca', + '#ff9d4d', + '#269a99', +]; + +export function getColorFromSpaceName(space_name) { + const index = kSpaceNames.indexOf(space_name); + return kSpaceColors[index]; +} diff --git a/deps/v8/tools/heap-layout/trace-file-reader.mjs b/deps/v8/tools/heap-layout/trace-file-reader.mjs new file mode 100644 index 00000000000000..880acf9fad22ae --- /dev/null +++ b/deps/v8/tools/heap-layout/trace-file-reader.mjs @@ -0,0 +1,110 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import {calcOffsetInVMCage} from '../js/helper.mjs'; +import {DOM, FileReader,} from '../js/web-api-helper.mjs'; + +import {kSpaceNames} from './space-categories.mjs'; + +class TraceLogParseHelper { + static re_gc_header = /(Before|After) GC:\d/; + static re_page_info = + /\{owner:.+,address:.+,size:.+,allocated_bytes:.+,wasted_memory:.+\}/; + static re_owner = /(?<=owner:)[a-z_]+_space/; + static re_address = /(?<=address:)0x[a-f0-9]+(?=,)/; + static re_size = /(?<=size:)\d+(?=,)/; + static re_allocated_bytes = /(?<=allocated_bytes:)\d+(?=,)/; + static re_wasted_memory = /(?<=wasted_memory:)\d+(?=})/; + + static matchGCHeader(content) { + return this.re_gc_header.test(content); + } + + static matchPageInfo(content) { + return this.re_page_info.test(content); + } + + static parsePageInfo(content) { + const owner = this.re_owner.exec(content)[0]; + const address = + calcOffsetInVMCage(BigInt(this.re_address.exec(content)[0], 16)); + const size = parseInt(this.re_size.exec(content)[0]); + const allocated_bytes = parseInt(this.re_allocated_bytes.exec(content)[0]); + const wasted_memory = parseInt(this.re_wasted_memory.exec(content)[0]); + const info = [ + owner, + address, + address + size, + allocated_bytes, + wasted_memory, + ]; + return info; + } + + // Create a empty snapshot. + static createSnapShotData() { + let snapshot = {header: null, data: {}}; + for (let space_name of kSpaceNames) { + snapshot.data[space_name] = []; + } + return snapshot; + } + + static createModelFromV8TraceFile(contents) { + let snapshots = []; + let snapshot = this.createSnapShotData(); + + // Fill data info a snapshot, then push it into snapshots. + for (let content of contents) { + if (this.matchGCHeader(content)) { + if (snapshot.header != null) { + snapshots.push(snapshot); + } + snapshot = this.createSnapShotData(); + snapshot.header = content; + continue; + } + + if (this.matchPageInfo(content)) { + let pageinfo = this.parsePageInfo(content); + try { + snapshot.data[pageinfo[0]].push(pageinfo); + } catch (e) { + console.error(e); + } + } + } + // EOL, push the last. + if (snapshot.header != null) { + snapshots.push(snapshot); + } + return snapshots; + } +} + +DOM.defineCustomElement('../js/log-file-reader', 'trace-file-reader', + (templateText) => + class TraceFileReader extends FileReader { + constructor() { + super(templateText); + this.fullDataFromFile = ''; + this.addEventListener('fileuploadchunk', (e) => this.handleLoadChunk(e)); + + this.addEventListener('fileuploadend', (e) => this.handleLoadEnd(e)); + } + + handleLoadChunk(event) { + this.fullDataFromFile += event.detail; + } + + handleLoadEnd(event) { + let contents = this.fullDataFromFile.split('\n'); + let snapshots = TraceLogParseHelper.createModelFromV8TraceFile(contents); + this.dispatchEvent(new CustomEvent('change', { + bubbles: true, + composed: true, + detail: snapshots, + })); + } +}); diff --git a/deps/v8/tools/heap-stats/categories.js b/deps/v8/tools/heap-stats/categories.js index 2bd08fad02dfc8..e4e570c4b40bd0 100644 --- a/deps/v8/tools/heap-stats/categories.js +++ b/deps/v8/tools/heap-stats/categories.js @@ -117,6 +117,7 @@ export const CATEGORIES = new Map([ 'BOILERPLATE_PROPERTY_DICTIONARY_TYPE', 'BYTE_ARRAY_TYPE', 'CALL_HANDLER_INFO_TYPE', + 'CALL_SITE_INFO_TYPE', 'CELL_TYPE', 'CODE_STUBS_TABLE_TYPE', 'CONTEXT_EXTENSION_TYPE', @@ -148,7 +149,6 @@ export const CATEGORIES = new Map([ 'SCRIPT_SHARED_FUNCTION_INFOS_TYPE', 'SERIALIZED_OBJECTS_TYPE', 'SINGLE_CHARACTER_STRING_CACHE_TYPE', - 'STACK_FRAME_INFO_TYPE', 'STRING_SPLIT_CACHE_TYPE', 'STRING_TABLE_TYPE', 'TRANSITION_ARRAY_TYPE', diff --git a/deps/v8/tools/heap-stats/index.html b/deps/v8/tools/heap-stats/index.html index efb74af011b805..9f053a8730f51f 100644 --- a/deps/v8/tools/heap-stats/index.html +++ b/deps/v8/tools/heap-stats/index.html @@ -80,23 +80,32 @@

V8 Heap Statistics

Visualize object statistics that have been gathered using

    -
  • --trace-gc-object-stats on V8
  • +
  • Use --trace-gc-object-stats for V8 and load the contents of stdout
  • Chrome's tracing infrastructure collecting data for the category - v8.gc_stats. + disabled-by-default-v8.gc_stats and directly load the + results.html or trace.json.gzip file.
-

- Note that you only get a data point on major GCs. You can enforce this by - using the --gc-global flag. -

-

- Note that the visualizer needs to run on a web server due to HTML imports - requiring CORS. -

+ + Additional information: +
    +
  • + You only get a data point on major GCs. You can enforce this by + using the --gc-global V8 flag. +
  • +
  • + For more frequent data points you can also the + --gc-interval=$AFTER_N_ALLOCATIONS V8. +
  • +
  • + The visualizer needs to run on a web server due to HTML imports + requiring CORS. +
  • +
      diff --git a/deps/v8/tools/heap-stats/trace-file-reader.js b/deps/v8/tools/heap-stats/trace-file-reader.js index e297723e6fb5a9..ef83b30db11a67 100644 --- a/deps/v8/tools/heap-stats/trace-file-reader.js +++ b/deps/v8/tools/heap-stats/trace-file-reader.js @@ -78,6 +78,27 @@ defineCustomElement('trace-file-reader', (templateText) => }; // Delay the loading a bit to allow for CSS animations to happen. setTimeout(() => reader.readAsArrayBuffer(file), 0); + } else if (file.type == 'text/html') { + // try extracting the data from a results.html file + reader.onload = (e) => { + try { + let html = document.createElement('html'); + html.innerHTML = e.target.result; + for (let dataScript of html.querySelectorAll('#viewer-data')) { + const base64 = dataScript.innerText.slice(1,-1); + const binary = globalThis.atob(base64); + const textResult = pako.inflate(binary, {to: 'string'}); + this.processRawText(file, textResult); + } + this.section.className = 'success'; + this.$('#fileReader').classList.add('done'); + } catch (err) { + console.error(err); + this.section.className = 'failure'; + } + }; + // Delay the loading a bit to allow for CSS animations to happen. + setTimeout(() => reader.readAsText(file), 0); } else { reader.onload = (e) => { try { diff --git a/deps/v8/tools/index.html b/deps/v8/tools/index.html index 53b22f170d549e..dfb0be7b34e40c 100644 --- a/deps/v8/tools/index.html +++ b/deps/v8/tools/index.html @@ -80,6 +80,10 @@

      Welcome to the V8 Tools Landing Page

      Heap Stats
      Visualize heap memory usage.
      +
      +
      Heap Layout
      +
      Visualize heap memory layout.
      +
      Parse Processor
      Analyse parse, compile and first-execution.
      @@ -88,10 +92,6 @@

      Welcome to the V8 Tools Landing Page

      Profview
      Fancy sampling profile viewer.
      -
      -
      Tick Processor
      -
      Simple sampling profile viewer.
      -
      Turbolizer
      Visualise the sea of nodes graph generated by TurboFan.
      diff --git a/deps/v8/tools/js/helper.mjs b/deps/v8/tools/js/helper.mjs new file mode 100644 index 00000000000000..1da31b7e55eb9b --- /dev/null +++ b/deps/v8/tools/js/helper.mjs @@ -0,0 +1,68 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +export const KB = 1024; +export const MB = KB * KB; +export const GB = MB * KB; +export const kMillis2Seconds = 1 / 1000; +export const kMicro2Milli = 1 / 1000; + +export function formatBytes(bytes, digits = 2) { + const units = ['B', 'KiB', 'MiB', 'GiB']; + const divisor = 1024; + let index = 0; + while (index < units.length && bytes >= divisor) { + index++; + bytes /= divisor; + } + return bytes.toFixed(digits) + units[index]; +} + +export function formatMicroSeconds(micro) { + return (micro * kMicro2Milli).toFixed(1) + 'ms'; +} + +export function formatDurationMicros(micros, secondsDigits = 3) { + return formatDurationMillis(micros * kMicro2Milli, secondsDigits); +} + +export function formatDurationMillis(millis, secondsDigits = 3) { + if (millis < 1000) { + if (millis < 1) { + return (millis / kMicro2Milli).toFixed(1) + 'ns'; + } + return millis.toFixed(2) + 'ms'; + } + let seconds = millis / 1000; + const hours = Math.floor(seconds / 3600); + const minutes = Math.floor((seconds % 3600) / 60); + seconds = seconds % 60; + let buffer = ''; + if (hours > 0) buffer += hours + 'h '; + if (hours > 0 || minutes > 0) buffer += minutes + 'm '; + buffer += seconds.toFixed(secondsDigits) + 's'; + return buffer; +} + +// Get the offset in the 4GB virtual memory cage. +export function calcOffsetInVMCage(address) { + let mask = (1n << 32n) - 1n; + let ret = Number(address & mask); + return ret; +} + +export function delay(time) { + return new Promise(resolver => setTimeout(resolver, time)); +} + +export function defer() { + let resolve_func, reject_func; + const p = new Promise((resolve, reject) => { + resolve_func = resolve; + reject_func = resolve; + }); + p.resolve = resolve_func; + p.reject = reject_func; + return p; +} diff --git a/deps/v8/tools/js/log-file-reader-template.html b/deps/v8/tools/js/log-file-reader-template.html new file mode 100644 index 00000000000000..416c7e33d37d1d --- /dev/null +++ b/deps/v8/tools/js/log-file-reader-template.html @@ -0,0 +1,117 @@ + + + + + + +
      +
      + + Drag and drop a v8.log file into this area, or click to choose from disk. + + +
      +
      +
      +
      +
      +
      +
      diff --git a/deps/v8/tools/js/web-api-helper.mjs b/deps/v8/tools/js/web-api-helper.mjs new file mode 100644 index 00000000000000..8c6ecc1ceb52a6 --- /dev/null +++ b/deps/v8/tools/js/web-api-helper.mjs @@ -0,0 +1,295 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import {delay, formatBytes} from './helper.mjs'; + +export class V8CustomElement extends HTMLElement { + _updateTimeoutId; + _updateCallback = this.forceUpdate.bind(this); + + constructor(templateText) { + super(); + const shadowRoot = this.attachShadow({mode: 'open'}); + shadowRoot.innerHTML = templateText; + } + + $(id) { + return this.shadowRoot.querySelector(id); + } + + querySelectorAll(query) { + return this.shadowRoot.querySelectorAll(query); + } + + requestUpdate(useAnimation = false) { + if (useAnimation) { + window.cancelAnimationFrame(this._updateTimeoutId); + this._updateTimeoutId = + window.requestAnimationFrame(this._updateCallback); + } else { + // Use timeout tasks to asynchronously update the UI without blocking. + clearTimeout(this._updateTimeoutId); + const kDelayMs = 5; + this._updateTimeoutId = setTimeout(this._updateCallback, kDelayMs); + } + } + + forceUpdate() { + this._updateTimeoutId = undefined; + this._update(); + } + + _update() { + throw Error('Subclass responsibility'); + } + + get isFocused() { + return document.activeElement === this; + } +} + +export class FileReader extends V8CustomElement { + constructor(templateText) { + super(templateText); + this.addEventListener('click', this.handleClick.bind(this)); + this.addEventListener('dragover', this.handleDragOver.bind(this)); + this.addEventListener('drop', this.handleChange.bind(this)); + this.$('#file').addEventListener('change', this.handleChange.bind(this)); + this.fileReader = this.$('#fileReader'); + this.fileReader.addEventListener('keydown', this.handleKeyEvent.bind(this)); + this.progressNode = this.$('#progress'); + this.progressTextNode = this.$('#progressText'); + } + + set error(message) { + this._updateLabel(message); + this.root.className = 'fail'; + } + + _updateLabel(text) { + this.$('#label').innerText = text; + } + + handleKeyEvent(event) { + if (event.key == 'Enter') this.handleClick(event); + } + + handleClick(event) { + this.$('#file').click(); + } + + handleChange(event) { + // Used for drop and file change. + event.preventDefault(); + const host = event.dataTransfer ? event.dataTransfer : event.target; + this.readFile(host.files[0]); + } + + handleDragOver(event) { + event.preventDefault(); + } + + connectedCallback() { + this.fileReader.focus(); + } + + get root() { + return this.$('#root'); + } + + setProgress(progress, processedBytes = 0) { + this.progress = Math.max(0, Math.min(progress, 1)); + this.processedBytes = processedBytes; + } + + updateProgressBar() { + // Create a circular progress bar, starting at 12 o'clock. + this.progressNode.style.backgroundImage = `conic-gradient( + var(--primary-color) 0%, + var(--primary-color) ${this.progress * 100}%, + var(--surface-color) ${this.progress * 100}%)`; + this.progressTextNode.innerText = + this.processedBytes ? formatBytes(this.processedBytes, 1) : ''; + if (this.root.className == 'loading') { + window.requestAnimationFrame(() => this.updateProgressBar()); + } + } + + readFile(file) { + this.dispatchEvent(new CustomEvent('fileuploadstart', { + bubbles: true, + composed: true, + detail: { + progressCallback: this.setProgress.bind(this), + totalSize: file.size, + } + })); + if (!file) { + this.error = 'Failed to load file.'; + return; + } + this.fileReader.blur(); + this.setProgress(0); + this.root.className = 'loading'; + // Delay the loading a bit to allow for CSS animations to happen. + window.requestAnimationFrame(() => this.asyncReadFile(file)); + } + + async asyncReadFile(file) { + this.updateProgressBar(); + const decoder = globalThis.TextDecoderStream; + if (decoder) { + await this._streamFile(file, decoder); + } else { + await this._readFullFile(file); + } + this._updateLabel(`Finished loading '${file.name}'.`); + this.dispatchEvent( + new CustomEvent('fileuploadend', {bubbles: true, composed: true})); + this.root.className = 'done'; + } + + async _readFullFile(file) { + const text = await file.text(); + this._handleFileChunk(text); + } + + async _streamFile(file, decoder) { + const stream = file.stream().pipeThrough(new decoder()); + const reader = stream.getReader(); + let chunk, readerDone; + do { + const readResult = await reader.read(); + chunk = readResult.value; + readerDone = readResult.done; + if (!chunk) break; + this._handleFileChunk(chunk); + // Artificial delay to allow for layout updates. + await delay(5); + } while (!readerDone); + } + + _handleFileChunk(chunk) { + this.dispatchEvent(new CustomEvent('fileuploadchunk', { + bubbles: true, + composed: true, + detail: chunk, + })); + } +} + +export class DOM { + static element(type, options) { + const node = document.createElement(type); + if (options === undefined) return node; + if (typeof options === 'string') { + // Old behaviour: options = class string + node.className = options; + } else if (Array.isArray(options)) { + // Old behaviour: options = class array + DOM.addClasses(node, options); + } else { + // New behaviour: options = attribute dict + for (const [key, value] of Object.entries(options)) { + if (key == 'className') { + node.className = value; + } else if (key == 'classList') { + DOM.addClasses(node, value); + } else if (key == 'textContent') { + node.textContent = value; + } else if (key == 'children') { + for (const child of value) { + node.appendChild(child); + } + } else { + node.setAttribute(key, value); + } + } + } + return node; + } + + static addClasses(node, classes) { + const classList = node.classList; + if (typeof classes === 'string') { + classList.add(classes); + } else { + for (let i = 0; i < classes.length; i++) { + classList.add(classes[i]); + } + } + return node; + } + + static text(string) { + return document.createTextNode(string); + } + + static button(label, clickHandler) { + const button = DOM.element('button'); + button.innerText = label; + if (typeof clickHandler != 'function') { + throw new Error( + `DOM.button: Expected function but got clickHandler=${clickHandler}`); + } + button.onclick = clickHandler; + return button; + } + + static div(options) { + return this.element('div', options); + } + + static span(options) { + return this.element('span', options); + } + + static table(options) { + return this.element('table', options); + } + + static tbody(options) { + return this.element('tbody', options); + } + + static td(textOrNode, className) { + const node = this.element('td'); + if (typeof textOrNode === 'object') { + node.appendChild(textOrNode); + } else if (textOrNode) { + node.innerText = textOrNode; + } + if (className) node.className = className; + return node; + } + + static tr(classes) { + return this.element('tr', classes); + } + + static removeAllChildren(node) { + let range = document.createRange(); + range.selectNodeContents(node); + range.deleteContents(); + } + + static defineCustomElement( + path, nameOrGenerator, maybeGenerator = undefined) { + let generator = nameOrGenerator; + let name = nameOrGenerator; + if (typeof nameOrGenerator == 'function') { + console.assert(maybeGenerator === undefined); + name = path.substring(path.lastIndexOf('/') + 1, path.length); + } else { + console.assert(typeof nameOrGenerator == 'string'); + generator = maybeGenerator; + } + path = path + '-template.html'; + fetch(path) + .then(stream => stream.text()) + .then( + templateText => + customElements.define(name, generator(templateText))); + } +} diff --git a/deps/v8/tools/logreader.mjs b/deps/v8/tools/logreader.mjs index ecd7b573a29da9..d6815733d1f439 100644 --- a/deps/v8/tools/logreader.mjs +++ b/deps/v8/tools/logreader.mjs @@ -32,64 +32,65 @@ // Parses dummy variable for readability; -export const parseString = 'parse-string'; +export function parseString(field) { return field }; export const parseVarArgs = 'parse-var-args'; /** * Base class for processing log files. * - * @param {Array.} dispatchTable A table used for parsing and processing - * log records. * @param {boolean} timedRange Ignore ticks outside timed range. * @param {boolean} pairwiseTimedRange Ignore ticks outside pairs of timer * markers. * @constructor */ export class LogReader { - constructor (dispatchTable, timedRange, pairwiseTimedRange) { - /** - * @type {Array.} - */ - this.dispatchTable_ = dispatchTable; - - /** - * @type {boolean} - */ + constructor(timedRange=false, pairwiseTimedRange=false) { + this.dispatchTable_ = new Map(); this.timedRange_ = timedRange; - - /** - * @type {boolean} - */ this.pairwiseTimedRange_ = pairwiseTimedRange; - if (pairwiseTimedRange) { - this.timedRange_ = true; - } - - /** - * Current line. - * @type {number} - */ + if (pairwiseTimedRange) this.timedRange_ = true; this.lineNum_ = 0; - - /** - * CSV lines parser. - * @type {CsvParser} - */ this.csvParser_ = new CsvParser(); - - /** - * Keeps track of whether we've seen a "current-time" tick yet. - * @type {boolean} - */ + // Variables for tracking of 'current-time' log entries: this.hasSeenTimerMarker_ = false; - - /** - * List of log lines seen since last "current-time" tick. - * @type {Array.} - */ this.logLinesSinceLastTimerMarker_ = []; } +/** + * @param {Object} table A table used for parsing and processing + * log records. + * exampleDispatchTable = { + * "log-entry-XXX": { + * parser: [parseString, parseInt, ..., parseVarArgs], + * processor: this.processXXX.bind(this) + * }, + * ... + * } + */ + setDispatchTable(table) { + if (Object.getPrototypeOf(table) !== null) { + throw new Error("Dispatch expected table.__proto__=null for speedup"); + } + for (let name in table) { + const parser = table[name]; + if (parser === undefined) continue; + if (!parser.isAsync) parser.isAsync = false; + if (!Array.isArray(parser.parsers)) { + throw new Error(`Invalid parsers: dispatchTable['${ + name}'].parsers should be an Array.`); + } + let type = typeof parser.processor; + if (type !== 'function') { + throw new Error(`Invalid processor: typeof dispatchTable['${ + name}'].processor is '${type}' instead of 'function'`); + } + if (!parser.processor.name.startsWith('bound ')) { + parser.processor = parser.processor.bind(this); + } + this.dispatchTable_.set(name, parser); + } + } + /** * A thin wrapper around shell's 'read' function showing a file name on error. @@ -118,7 +119,18 @@ export class LogReader { * @param {string} chunk A portion of log. */ async processLogChunk(chunk) { - await this.processLog_(chunk.split('\n')); + let end = chunk.length; + let current = 0; + // Kept for debugging in case of parsing errors. + let lineNumber = 0; + while (current < end) { + const next = chunk.indexOf("\n", current); + if (next === -1) break; + lineNumber++; + const line = chunk.substring(current, next); + current = next + 1; + await this.processLogLine(line); + } } /** @@ -162,33 +174,24 @@ export class LogReader { processStack(pc, func, stack) { const fullStack = func ? [pc, func] : [pc]; let prevFrame = pc; - for (let i = 0, n = stack.length; i < n; ++i) { + const length = stack.length; + for (let i = 0, n = length; i < n; ++i) { const frame = stack[i]; - const firstChar = frame.charAt(0); - if (firstChar == '+' || firstChar == '-') { + const firstChar = frame[0]; + if (firstChar === '+' || firstChar === '-') { // An offset from the previous frame. prevFrame += parseInt(frame, 16); fullStack.push(prevFrame); // Filter out possible 'overflow' string. - } else if (firstChar != 'o') { + } else if (firstChar !== 'o') { fullStack.push(parseInt(frame, 16)); } else { - console.error(`dropping: ${frame}`); + console.error(`Dropping unknown tick frame: ${frame}`); } } return fullStack; } - /** - * Returns whether a particular dispatch must be skipped. - * - * @param {!Object} dispatch Dispatch record. - * @return {boolean} True if dispatch must be skipped. - */ - skipDispatch(dispatch) { - return false; - } - /** * Does a dispatch of a log record. * @@ -198,31 +201,23 @@ export class LogReader { async dispatchLogRow_(fields) { // Obtain the dispatch. const command = fields[0]; - const dispatch = this.dispatchTable_[command]; + const dispatch = this.dispatchTable_.get(command); if (dispatch === undefined) return; - if (dispatch === null || this.skipDispatch(dispatch)) { - return; - } - + const parsers = dispatch.parsers; + const length = parsers.length; // Parse fields. - const parsedFields = []; - for (let i = 0; i < dispatch.parsers.length; ++i) { - const parser = dispatch.parsers[i]; - if (parser === parseString) { - parsedFields.push(fields[1 + i]); - } else if (typeof parser == 'function') { - parsedFields.push(parser(fields[1 + i])); - } else if (parser === parseVarArgs) { - // var-args - parsedFields.push(fields.slice(1 + i)); + const parsedFields = new Array(length); + for (let i = 0; i < length; ++i) { + const parser = parsers[i]; + if (parser === parseVarArgs) { + parsedFields[i] = fields.slice(1 + i); break; } else { - throw new Error(`Invalid log field parser: ${parser}`); + parsedFields[i] = parser(fields[1 + i]); } } - // Run the processor. - await dispatch.processor.apply(this, parsedFields); + await dispatch.processor(...parsedFields); } /** diff --git a/deps/v8/tools/mb/PRESUBMIT.py b/deps/v8/tools/mb/PRESUBMIT.py index 6f5307c63eb3c0..150de90e886304 100644 --- a/deps/v8/tools/mb/PRESUBMIT.py +++ b/deps/v8/tools/mb/PRESUBMIT.py @@ -3,6 +3,10 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +# This line is 'magic' in that git-cl looks for it to decide whether to +# use Python3 instead of Python2 when running the code in this file. +USE_PYTHON3 = True + def _CommonChecks(input_api, output_api): results = [] @@ -12,8 +16,9 @@ def _CommonChecks(input_api, output_api): results.extend(input_api.RunTests(pylint_checks)) # Run the MB unittests. - results.extend(input_api.canned_checks.RunUnitTestsInDirectory( - input_api, output_api, '.', [ r'^.+_unittest\.py$'])) + results.extend( + input_api.canned_checks.RunUnitTestsInDirectory(input_api, output_api, + '.', [r'^.+_test\.py$'])) # Validate the format of the mb_config.pyl file. cmd = [input_api.python_executable, 'mb.py', 'validate'] @@ -23,12 +28,10 @@ def _CommonChecks(input_api, output_api): cmd=cmd, kwargs=kwargs, message=output_api.PresubmitError)])) + is_mb_config = (lambda filepath: 'mb_config.pyl' in filepath.LocalPath()) results.extend( input_api.canned_checks.CheckLongLines( - input_api, - output_api, - maxlen=80, - source_file_filter=lambda x: 'mb_config.pyl' in x.LocalPath())) + input_api, output_api, maxlen=80, source_file_filter=is_mb_config)) return results diff --git a/deps/v8/tools/mb/mb.py b/deps/v8/tools/mb/mb.py index 408e2b566a0575..1ba74b747b853d 100755 --- a/deps/v8/tools/mb/mb.py +++ b/deps/v8/tools/mb/mb.py @@ -1150,6 +1150,8 @@ def Call(self, cmd, env=None, buffer_output=True): stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) out, err = p.communicate() + out = out.decode('utf-8') + err = err.decode('utf-8') else: p = subprocess.Popen(cmd, shell=False, cwd=self.chromium_src_dir, env=env) diff --git a/deps/v8/tools/mb/mb_test.py b/deps/v8/tools/mb/mb_test.py new file mode 100755 index 00000000000000..fb59c4aa29aad6 --- /dev/null +++ b/deps/v8/tools/mb/mb_test.py @@ -0,0 +1,677 @@ +#!/usr/bin/python +# Copyright 2016 the V8 project authors. All rights reserved. +# Copyright 2015 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +"""Tests for mb.py.""" + +import json +import StringIO +import os +import sys +import unittest + +import mb + + +class FakeMBW(mb.MetaBuildWrapper): + + def __init__(self, win32=False): + super(FakeMBW, self).__init__() + + # Override vars for test portability. + if win32: + self.chromium_src_dir = 'c:\\fake_src' + self.default_config = 'c:\\fake_src\\tools\\mb\\mb_config.pyl' + self.default_isolate_map = ('c:\\fake_src\\testing\\buildbot\\' + 'gn_isolate_map.pyl') + self.platform = 'win32' + self.executable = 'c:\\python\\python.exe' + self.sep = '\\' + else: + self.chromium_src_dir = '/fake_src' + self.default_config = '/fake_src/tools/mb/mb_config.pyl' + self.default_isolate_map = '/fake_src/testing/buildbot/gn_isolate_map.pyl' + self.executable = '/usr/bin/python' + self.platform = 'linux2' + self.sep = '/' + + self.files = {} + self.calls = [] + self.cmds = [] + self.cross_compile = None + self.out = '' + self.err = '' + self.rmdirs = [] + + def ExpandUser(self, path): + return '$HOME/%s' % path + + def Exists(self, path): + return self.files.get(path) is not None + + def MaybeMakeDirectory(self, path): + self.files[path] = True + + def PathJoin(self, *comps): + return self.sep.join(comps) + + def ReadFile(self, path): + return self.files[path] + + def WriteFile(self, path, contents, force_verbose=False): + if self.args.dryrun or self.args.verbose or force_verbose: + self.Print('\nWriting """\\\n%s""" to %s.\n' % (contents, path)) + self.files[path] = contents + + def Call(self, cmd, env=None, buffer_output=True): + self.calls.append(cmd) + if self.cmds: + return self.cmds.pop(0) + return 0, '', '' + + def Print(self, *args, **kwargs): + sep = kwargs.get('sep', ' ') + end = kwargs.get('end', '\n') + f = kwargs.get('file', sys.stdout) + if f == sys.stderr: + self.err += sep.join(args) + end + else: + self.out += sep.join(args) + end + + def TempFile(self, mode='w'): + return FakeFile(self.files) + + def RemoveFile(self, path): + del self.files[path] + + def RemoveDirectory(self, path): + self.rmdirs.append(path) + files_to_delete = [f for f in self.files if f.startswith(path)] + for f in files_to_delete: + self.files[f] = None + + +class FakeFile(object): + + def __init__(self, files): + self.name = '/tmp/file' + self.buf = '' + self.files = files + + def write(self, contents): + self.buf += contents + + def close(self): + self.files[self.name] = self.buf + + +TEST_CONFIG = """\ +{ + 'builder_groups': { + 'chromium': {}, + 'fake_builder_group': { + 'fake_builder': 'rel_bot', + 'fake_debug_builder': 'debug_goma', + 'fake_args_bot': '//build/args/bots/fake_builder_group/fake_args_bot.gn', + 'fake_multi_phase': { 'phase_1': 'phase_1', 'phase_2': 'phase_2'}, + 'fake_args_file': 'args_file_goma', + 'fake_args_file_twice': 'args_file_twice', + }, + }, + 'configs': { + 'args_file_goma': ['args_file', 'goma'], + 'args_file_twice': ['args_file', 'args_file'], + 'rel_bot': ['rel', 'goma', 'fake_feature1'], + 'debug_goma': ['debug', 'goma'], + 'phase_1': ['phase_1'], + 'phase_2': ['phase_2'], + }, + 'mixins': { + 'fake_feature1': { + 'gn_args': 'enable_doom_melon=true', + }, + 'goma': { + 'gn_args': 'use_goma=true', + }, + 'args_file': { + 'args_file': '//build/args/fake.gn', + }, + 'phase_1': { + 'gn_args': 'phase=1', + }, + 'phase_2': { + 'gn_args': 'phase=2', + }, + 'rel': { + 'gn_args': 'is_debug=false', + }, + 'debug': { + 'gn_args': 'is_debug=true', + }, + }, +} +""" + +TRYSERVER_CONFIG = """\ +{ + 'builder_groups': { + 'not_a_tryserver': { + 'fake_builder': 'fake_config', + }, + 'tryserver.chromium.linux': { + 'try_builder': 'fake_config', + }, + 'tryserver.chromium.mac': { + 'try_builder2': 'fake_config', + }, + }, + 'luci_tryservers': { + 'luci_tryserver1': ['luci_builder1'], + 'luci_tryserver2': ['luci_builder2'], + }, + 'configs': {}, + 'mixins': {}, +} +""" + + +class UnitTest(unittest.TestCase): + + def fake_mbw(self, files=None, win32=False): + mbw = FakeMBW(win32=win32) + mbw.files.setdefault(mbw.default_config, TEST_CONFIG) + mbw.files.setdefault( + mbw.ToAbsPath('//testing/buildbot/gn_isolate_map.pyl'), '''{ + "foo_unittests": { + "label": "//foo:foo_unittests", + "type": "console_test_launcher", + "args": [], + }, + }''') + mbw.files.setdefault( + mbw.ToAbsPath('//build/args/bots/fake_builder_group/fake_args_bot.gn'), + 'is_debug = false\n') + if files: + for path, contents in files.items(): + mbw.files[path] = contents + return mbw + + def check(self, args, mbw=None, files=None, out=None, err=None, ret=None): + if not mbw: + mbw = self.fake_mbw(files) + + actual_ret = mbw.Main(args) + + self.assertEqual(actual_ret, ret) + if out is not None: + self.assertEqual(mbw.out, out) + if err is not None: + self.assertEqual(mbw.err, err) + return mbw + + def test_analyze(self): + files = { + '/tmp/in.json': + '''{\ + "files": ["foo/foo_unittest.cc"], + "test_targets": ["foo_unittests"], + "additional_compile_targets": ["all"] + }''', + '/tmp/out.json.gn': + '''{\ + "status": "Found dependency", + "compile_targets": ["//foo:foo_unittests"], + "test_targets": ["//foo:foo_unittests"] + }''' + } + + mbw = self.fake_mbw(files) + mbw.Call = lambda cmd, env=None, buffer_output=True: (0, '', '') + + self.check([ + 'analyze', '-c', 'debug_goma', '//out/Default', '/tmp/in.json', + '/tmp/out.json' + ], + mbw=mbw, + ret=0) + out = json.loads(mbw.files['/tmp/out.json']) + self.assertEqual( + out, { + 'status': 'Found dependency', + 'compile_targets': ['foo:foo_unittests'], + 'test_targets': ['foo_unittests'] + }) + + def test_analyze_optimizes_compile_for_all(self): + files = { + '/tmp/in.json': + '''{\ + "files": ["foo/foo_unittest.cc"], + "test_targets": ["foo_unittests"], + "additional_compile_targets": ["all"] + }''', + '/tmp/out.json.gn': + '''{\ + "status": "Found dependency", + "compile_targets": ["//foo:foo_unittests", "all"], + "test_targets": ["//foo:foo_unittests"] + }''' + } + + mbw = self.fake_mbw(files) + mbw.Call = lambda cmd, env=None, buffer_output=True: (0, '', '') + + self.check([ + 'analyze', '-c', 'debug_goma', '//out/Default', '/tmp/in.json', + '/tmp/out.json' + ], + mbw=mbw, + ret=0) + out = json.loads(mbw.files['/tmp/out.json']) + + # check that 'foo_unittests' is not in the compile_targets + self.assertEqual(['all'], out['compile_targets']) + + def test_analyze_handles_other_toolchains(self): + files = { + '/tmp/in.json': + '''{\ + "files": ["foo/foo_unittest.cc"], + "test_targets": ["foo_unittests"], + "additional_compile_targets": ["all"] + }''', + '/tmp/out.json.gn': + '''{\ + "status": "Found dependency", + "compile_targets": ["//foo:foo_unittests", + "//foo:foo_unittests(bar)"], + "test_targets": ["//foo:foo_unittests"] + }''' + } + + mbw = self.fake_mbw(files) + mbw.Call = lambda cmd, env=None, buffer_output=True: (0, '', '') + + self.check([ + 'analyze', '-c', 'debug_goma', '//out/Default', '/tmp/in.json', + '/tmp/out.json' + ], + mbw=mbw, + ret=0) + out = json.loads(mbw.files['/tmp/out.json']) + + # crbug.com/736215: If GN returns a label containing a toolchain, + # MB (and Ninja) don't know how to handle it; to work around this, + # we give up and just build everything we were asked to build. The + # output compile_targets should include all of the input test_targets and + # additional_compile_targets. + self.assertEqual(['all', 'foo_unittests'], out['compile_targets']) + + def test_analyze_handles_way_too_many_results(self): + too_many_files = ', '.join(['"//foo:foo%d"' % i for i in range(4 * 1024)]) + files = { + '/tmp/in.json': + '''{\ + "files": ["foo/foo_unittest.cc"], + "test_targets": ["foo_unittests"], + "additional_compile_targets": ["all"] + }''', + '/tmp/out.json.gn': + '''{\ + "status": "Found dependency", + "compile_targets": [''' + too_many_files + '''], + "test_targets": ["//foo:foo_unittests"] + }''' + } + + mbw = self.fake_mbw(files) + mbw.Call = lambda cmd, env=None, buffer_output=True: (0, '', '') + + self.check([ + 'analyze', '-c', 'debug_goma', '//out/Default', '/tmp/in.json', + '/tmp/out.json' + ], + mbw=mbw, + ret=0) + out = json.loads(mbw.files['/tmp/out.json']) + + # If GN returns so many compile targets that we might have command-line + # issues, we should give up and just build everything we were asked to + # build. The output compile_targets should include all of the input + # test_targets and additional_compile_targets. + self.assertEqual(['all', 'foo_unittests'], out['compile_targets']) + + def test_gen(self): + mbw = self.fake_mbw() + self.check(['gen', '-c', 'debug_goma', '//out/Default', '-g', '/goma'], + mbw=mbw, + ret=0) + self.assertMultiLineEqual(mbw.files['/fake_src/out/Default/args.gn'], + ('goma_dir = "/goma"\n' + 'is_debug = true\n' + 'use_goma = true\n')) + + # Make sure we log both what is written to args.gn and the command line. + self.assertIn('Writing """', mbw.out) + self.assertIn('/fake_src/buildtools/linux64/gn gen //out/Default --check', + mbw.out) + + mbw = self.fake_mbw(win32=True) + self.check(['gen', '-c', 'debug_goma', '-g', 'c:\\goma', '//out/Debug'], + mbw=mbw, + ret=0) + self.assertMultiLineEqual(mbw.files['c:\\fake_src\\out\\Debug\\args.gn'], + ('goma_dir = "c:\\\\goma"\n' + 'is_debug = true\n' + 'use_goma = true\n')) + self.assertIn( + 'c:\\fake_src\\buildtools\\win\\gn.exe gen //out/Debug ' + '--check\n', mbw.out) + + mbw = self.fake_mbw() + self.check([ + 'gen', '-m', 'fake_builder_group', '-b', 'fake_args_bot', '//out/Debug' + ], + mbw=mbw, + ret=0) + # TODO(almuthanna): disable test temporarily to + # solve this issue https://crbug.com/v8/11102 + # self.assertEqual( + # mbw.files['/fake_src/out/Debug/args.gn'], + # 'import("//build/args/bots/fake_builder_group/fake_args_bot.gn")\n') + + def test_gen_args_file_mixins(self): + mbw = self.fake_mbw() + self.check([ + 'gen', '-m', 'fake_builder_group', '-b', 'fake_args_file', '//out/Debug' + ], + mbw=mbw, + ret=0) + + self.assertEqual(mbw.files['/fake_src/out/Debug/args.gn'], + ('import("//build/args/fake.gn")\n' + 'use_goma = true\n')) + + mbw = self.fake_mbw() + self.check([ + 'gen', '-m', 'fake_builder_group', '-b', 'fake_args_file_twice', + '//out/Debug' + ], + mbw=mbw, + ret=1) + + def test_gen_fails(self): + mbw = self.fake_mbw() + mbw.Call = lambda cmd, env=None, buffer_output=True: (1, '', '') + self.check(['gen', '-c', 'debug_goma', '//out/Default'], mbw=mbw, ret=1) + + def test_gen_swarming(self): + files = { + '/tmp/swarming_targets': + 'base_unittests\n', + '/fake_src/testing/buildbot/gn_isolate_map.pyl': + ("{'base_unittests': {" + " 'label': '//base:base_unittests'," + " 'type': 'raw'," + " 'args': []," + "}}\n"), + '/fake_src/out/Default/base_unittests.runtime_deps': + ("base_unittests\n"), + } + mbw = self.fake_mbw(files) + self.check([ + 'gen', '-c', 'debug_goma', '--swarming-targets-file', + '/tmp/swarming_targets', '//out/Default' + ], + mbw=mbw, + ret=0) + self.assertIn('/fake_src/out/Default/base_unittests.isolate', mbw.files) + self.assertIn('/fake_src/out/Default/base_unittests.isolated.gen.json', + mbw.files) + + def test_gen_swarming_script(self): + files = { + '/tmp/swarming_targets': + 'cc_perftests\n', + '/fake_src/testing/buildbot/gn_isolate_map.pyl': + ("{'cc_perftests': {" + " 'label': '//cc:cc_perftests'," + " 'type': 'script'," + " 'script': '/fake_src/out/Default/test_script.py'," + " 'args': []," + "}}\n"), + 'c:\\fake_src\out\Default\cc_perftests.exe.runtime_deps': + ("cc_perftests\n"), + } + mbw = self.fake_mbw(files=files, win32=True) + self.check([ + 'gen', '-c', 'debug_goma', '--swarming-targets-file', + '/tmp/swarming_targets', '--isolate-map-file', + '/fake_src/testing/buildbot/gn_isolate_map.pyl', '//out/Default' + ], + mbw=mbw, + ret=0) + self.assertIn('c:\\fake_src\\out\\Default\\cc_perftests.isolate', mbw.files) + self.assertIn('c:\\fake_src\\out\\Default\\cc_perftests.isolated.gen.json', + mbw.files) + + def test_multiple_isolate_maps(self): + files = { + '/tmp/swarming_targets': + 'cc_perftests\n', + '/fake_src/testing/buildbot/gn_isolate_map.pyl': + ("{'cc_perftests': {" + " 'label': '//cc:cc_perftests'," + " 'type': 'raw'," + " 'args': []," + "}}\n"), + '/fake_src/testing/buildbot/gn_isolate_map2.pyl': + ("{'cc_perftests2': {" + " 'label': '//cc:cc_perftests'," + " 'type': 'raw'," + " 'args': []," + "}}\n"), + 'c:\\fake_src\out\Default\cc_perftests.exe.runtime_deps': + ("cc_perftests\n"), + } + mbw = self.fake_mbw(files=files, win32=True) + self.check([ + 'gen', '-c', 'debug_goma', '--swarming-targets-file', + '/tmp/swarming_targets', '--isolate-map-file', + '/fake_src/testing/buildbot/gn_isolate_map.pyl', '--isolate-map-file', + '/fake_src/testing/buildbot/gn_isolate_map2.pyl', '//out/Default' + ], + mbw=mbw, + ret=0) + self.assertIn('c:\\fake_src\\out\\Default\\cc_perftests.isolate', mbw.files) + self.assertIn('c:\\fake_src\\out\\Default\\cc_perftests.isolated.gen.json', + mbw.files) + + def test_duplicate_isolate_maps(self): + files = { + '/tmp/swarming_targets': + 'cc_perftests\n', + '/fake_src/testing/buildbot/gn_isolate_map.pyl': + ("{'cc_perftests': {" + " 'label': '//cc:cc_perftests'," + " 'type': 'raw'," + " 'args': []," + "}}\n"), + '/fake_src/testing/buildbot/gn_isolate_map2.pyl': + ("{'cc_perftests': {" + " 'label': '//cc:cc_perftests'," + " 'type': 'raw'," + " 'args': []," + "}}\n"), + 'c:\\fake_src\out\Default\cc_perftests.exe.runtime_deps': + ("cc_perftests\n"), + } + mbw = self.fake_mbw(files=files, win32=True) + # Check that passing duplicate targets into mb fails. + self.check([ + 'gen', '-c', 'debug_goma', '--swarming-targets-file', + '/tmp/swarming_targets', '--isolate-map-file', + '/fake_src/testing/buildbot/gn_isolate_map.pyl', '--isolate-map-file', + '/fake_src/testing/buildbot/gn_isolate_map2.pyl', '//out/Default' + ], + mbw=mbw, + ret=1) + + def test_isolate(self): + files = { + '/fake_src/out/Default/toolchain.ninja': + "", + '/fake_src/testing/buildbot/gn_isolate_map.pyl': + ("{'base_unittests': {" + " 'label': '//base:base_unittests'," + " 'type': 'raw'," + " 'args': []," + "}}\n"), + '/fake_src/out/Default/base_unittests.runtime_deps': + ("base_unittests\n"), + } + self.check( + ['isolate', '-c', 'debug_goma', '//out/Default', 'base_unittests'], + files=files, + ret=0) + + # test running isolate on an existing build_dir + files['/fake_src/out/Default/args.gn'] = 'is_debug = True\n' + self.check(['isolate', '//out/Default', 'base_unittests'], + files=files, + ret=0) + + self.check(['isolate', '//out/Default', 'base_unittests'], + files=files, + ret=0) + + def test_run(self): + files = { + '/fake_src/testing/buildbot/gn_isolate_map.pyl': + ("{'base_unittests': {" + " 'label': '//base:base_unittests'," + " 'type': 'raw'," + " 'args': []," + "}}\n"), + '/fake_src/out/Default/base_unittests.runtime_deps': + ("base_unittests\n"), + } + self.check(['run', '-c', 'debug_goma', '//out/Default', 'base_unittests'], + files=files, + ret=0) + + def test_lookup(self): + self.check(['lookup', '-c', 'debug_goma'], + ret=0, + out=('\n' + 'Writing """\\\n' + 'is_debug = true\n' + 'use_goma = true\n' + '""" to _path_/args.gn.\n\n' + '/fake_src/buildtools/linux64/gn gen _path_\n')) + + def test_quiet_lookup(self): + self.check(['lookup', '-c', 'debug_goma', '--quiet'], + ret=0, + out=('is_debug = true\n' + 'use_goma = true\n')) + + def test_lookup_goma_dir_expansion(self): + self.check(['lookup', '-c', 'rel_bot', '-g', '/foo'], + ret=0, + out=('\n' + 'Writing """\\\n' + 'enable_doom_melon = true\n' + 'goma_dir = "/foo"\n' + 'is_debug = false\n' + 'use_goma = true\n' + '""" to _path_/args.gn.\n\n' + '/fake_src/buildtools/linux64/gn gen _path_\n')) + + def test_help(self): + orig_stdout = sys.stdout + try: + sys.stdout = StringIO.StringIO() + self.assertRaises(SystemExit, self.check, ['-h']) + self.assertRaises(SystemExit, self.check, ['help']) + self.assertRaises(SystemExit, self.check, ['help', 'gen']) + finally: + sys.stdout = orig_stdout + + def test_multiple_phases(self): + # Check that not passing a --phase to a multi-phase builder fails. + mbw = self.check( + ['lookup', '-m', 'fake_builder_group', '-b', 'fake_multi_phase'], ret=1) + self.assertIn('Must specify a build --phase', mbw.out) + + # Check that passing a --phase to a single-phase builder fails. + mbw = self.check([ + 'lookup', '-m', 'fake_builder_group', '-b', 'fake_builder', '--phase', + 'phase_1' + ], + ret=1) + self.assertIn('Must not specify a build --phase', mbw.out) + + # Check that passing a wrong phase key to a multi-phase builder fails. + mbw = self.check([ + 'lookup', '-m', 'fake_builder_group', '-b', 'fake_multi_phase', + '--phase', 'wrong_phase' + ], + ret=1) + self.assertIn('Phase wrong_phase doesn\'t exist', mbw.out) + + # Check that passing a correct phase key to a multi-phase builder passes. + mbw = self.check([ + 'lookup', '-m', 'fake_builder_group', '-b', 'fake_multi_phase', + '--phase', 'phase_1' + ], + ret=0) + self.assertIn('phase = 1', mbw.out) + + mbw = self.check([ + 'lookup', '-m', 'fake_builder_group', '-b', 'fake_multi_phase', + '--phase', 'phase_2' + ], + ret=0) + self.assertIn('phase = 2', mbw.out) + + def test_recursive_lookup(self): + files = { + '/fake_src/build/args/fake.gn': ('enable_doom_melon = true\n' + 'enable_antidoom_banana = true\n') + } + self.check([ + 'lookup', '-m', 'fake_builder_group', '-b', 'fake_args_file', + '--recursive' + ], + files=files, + ret=0, + out=('enable_antidoom_banana = true\n' + 'enable_doom_melon = true\n' + 'use_goma = true\n')) + + def test_validate(self): + mbw = self.fake_mbw() + self.check(['validate'], mbw=mbw, ret=0) + + def test_buildbucket(self): + mbw = self.fake_mbw() + mbw.files[mbw.default_config] = TRYSERVER_CONFIG + self.check(['gerrit-buildbucket-config'], + mbw=mbw, + ret=0, + out=('# This file was generated using ' + '"tools/mb/mb.py gerrit-buildbucket-config".\n' + '[bucket "luci.luci_tryserver1"]\n' + '\tbuilder = luci_builder1\n' + '[bucket "luci.luci_tryserver2"]\n' + '\tbuilder = luci_builder2\n' + '[bucket "builder_group.tryserver.chromium.linux"]\n' + '\tbuilder = try_builder\n' + '[bucket "builder_group.tryserver.chromium.mac"]\n' + '\tbuilder = try_builder2\n')) + + +if __name__ == '__main__': + unittest.main() diff --git a/deps/v8/tools/mb/mb_unittest.py b/deps/v8/tools/mb/mb_unittest.py deleted file mode 100755 index 86d9cd403b603e..00000000000000 --- a/deps/v8/tools/mb/mb_unittest.py +++ /dev/null @@ -1,626 +0,0 @@ -#!/usr/bin/python -# Copyright 2016 the V8 project authors. All rights reserved. -# Copyright 2015 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Tests for mb.py.""" - -import json -import StringIO -import os -import sys -import unittest - -import mb - - -class FakeMBW(mb.MetaBuildWrapper): - def __init__(self, win32=False): - super(FakeMBW, self).__init__() - - # Override vars for test portability. - if win32: - self.chromium_src_dir = 'c:\\fake_src' - self.default_config = 'c:\\fake_src\\tools\\mb\\mb_config.pyl' - self.default_isolate_map = ('c:\\fake_src\\testing\\buildbot\\' - 'gn_isolate_map.pyl') - self.platform = 'win32' - self.executable = 'c:\\python\\python.exe' - self.sep = '\\' - else: - self.chromium_src_dir = '/fake_src' - self.default_config = '/fake_src/tools/mb/mb_config.pyl' - self.default_isolate_map = '/fake_src/testing/buildbot/gn_isolate_map.pyl' - self.executable = '/usr/bin/python' - self.platform = 'linux2' - self.sep = '/' - - self.files = {} - self.calls = [] - self.cmds = [] - self.cross_compile = None - self.out = '' - self.err = '' - self.rmdirs = [] - - def ExpandUser(self, path): - return '$HOME/%s' % path - - def Exists(self, path): - return self.files.get(path) is not None - - def MaybeMakeDirectory(self, path): - self.files[path] = True - - def PathJoin(self, *comps): - return self.sep.join(comps) - - def ReadFile(self, path): - return self.files[path] - - def WriteFile(self, path, contents, force_verbose=False): - if self.args.dryrun or self.args.verbose or force_verbose: - self.Print('\nWriting """\\\n%s""" to %s.\n' % (contents, path)) - self.files[path] = contents - - def Call(self, cmd, env=None, buffer_output=True): - self.calls.append(cmd) - if self.cmds: - return self.cmds.pop(0) - return 0, '', '' - - def Print(self, *args, **kwargs): - sep = kwargs.get('sep', ' ') - end = kwargs.get('end', '\n') - f = kwargs.get('file', sys.stdout) - if f == sys.stderr: - self.err += sep.join(args) + end - else: - self.out += sep.join(args) + end - - def TempFile(self, mode='w'): - return FakeFile(self.files) - - def RemoveFile(self, path): - del self.files[path] - - def RemoveDirectory(self, path): - self.rmdirs.append(path) - files_to_delete = [f for f in self.files if f.startswith(path)] - for f in files_to_delete: - self.files[f] = None - - -class FakeFile(object): - def __init__(self, files): - self.name = '/tmp/file' - self.buf = '' - self.files = files - - def write(self, contents): - self.buf += contents - - def close(self): - self.files[self.name] = self.buf - - -TEST_CONFIG = """\ -{ - 'builder_groups': { - 'chromium': {}, - 'fake_builder_group': { - 'fake_builder': 'rel_bot', - 'fake_debug_builder': 'debug_goma', - 'fake_args_bot': '//build/args/bots/fake_builder_group/fake_args_bot.gn', - 'fake_multi_phase': { 'phase_1': 'phase_1', 'phase_2': 'phase_2'}, - 'fake_args_file': 'args_file_goma', - 'fake_args_file_twice': 'args_file_twice', - }, - }, - 'configs': { - 'args_file_goma': ['args_file', 'goma'], - 'args_file_twice': ['args_file', 'args_file'], - 'rel_bot': ['rel', 'goma', 'fake_feature1'], - 'debug_goma': ['debug', 'goma'], - 'phase_1': ['phase_1'], - 'phase_2': ['phase_2'], - }, - 'mixins': { - 'fake_feature1': { - 'gn_args': 'enable_doom_melon=true', - }, - 'goma': { - 'gn_args': 'use_goma=true', - }, - 'args_file': { - 'args_file': '//build/args/fake.gn', - }, - 'phase_1': { - 'gn_args': 'phase=1', - }, - 'phase_2': { - 'gn_args': 'phase=2', - }, - 'rel': { - 'gn_args': 'is_debug=false', - }, - 'debug': { - 'gn_args': 'is_debug=true', - }, - }, -} -""" - - -TRYSERVER_CONFIG = """\ -{ - 'builder_groups': { - 'not_a_tryserver': { - 'fake_builder': 'fake_config', - }, - 'tryserver.chromium.linux': { - 'try_builder': 'fake_config', - }, - 'tryserver.chromium.mac': { - 'try_builder2': 'fake_config', - }, - }, - 'luci_tryservers': { - 'luci_tryserver1': ['luci_builder1'], - 'luci_tryserver2': ['luci_builder2'], - }, - 'configs': {}, - 'mixins': {}, -} -""" - - -class UnitTest(unittest.TestCase): - def fake_mbw(self, files=None, win32=False): - mbw = FakeMBW(win32=win32) - mbw.files.setdefault(mbw.default_config, TEST_CONFIG) - mbw.files.setdefault( - mbw.ToAbsPath('//testing/buildbot/gn_isolate_map.pyl'), - '''{ - "foo_unittests": { - "label": "//foo:foo_unittests", - "type": "console_test_launcher", - "args": [], - }, - }''') - mbw.files.setdefault( - mbw.ToAbsPath('//build/args/bots/fake_builder_group/fake_args_bot.gn'), - 'is_debug = false\n') - if files: - for path, contents in files.items(): - mbw.files[path] = contents - return mbw - - def check(self, args, mbw=None, files=None, out=None, err=None, ret=None): - if not mbw: - mbw = self.fake_mbw(files) - - actual_ret = mbw.Main(args) - - self.assertEqual(actual_ret, ret) - if out is not None: - self.assertEqual(mbw.out, out) - if err is not None: - self.assertEqual(mbw.err, err) - return mbw - - def test_analyze(self): - files = {'/tmp/in.json': '''{\ - "files": ["foo/foo_unittest.cc"], - "test_targets": ["foo_unittests"], - "additional_compile_targets": ["all"] - }''', - '/tmp/out.json.gn': '''{\ - "status": "Found dependency", - "compile_targets": ["//foo:foo_unittests"], - "test_targets": ["//foo:foo_unittests"] - }'''} - - mbw = self.fake_mbw(files) - mbw.Call = lambda cmd, env=None, buffer_output=True: (0, '', '') - - self.check(['analyze', '-c', 'debug_goma', '//out/Default', - '/tmp/in.json', '/tmp/out.json'], mbw=mbw, ret=0) - out = json.loads(mbw.files['/tmp/out.json']) - self.assertEqual(out, { - 'status': 'Found dependency', - 'compile_targets': ['foo:foo_unittests'], - 'test_targets': ['foo_unittests'] - }) - - def test_analyze_optimizes_compile_for_all(self): - files = {'/tmp/in.json': '''{\ - "files": ["foo/foo_unittest.cc"], - "test_targets": ["foo_unittests"], - "additional_compile_targets": ["all"] - }''', - '/tmp/out.json.gn': '''{\ - "status": "Found dependency", - "compile_targets": ["//foo:foo_unittests", "all"], - "test_targets": ["//foo:foo_unittests"] - }'''} - - mbw = self.fake_mbw(files) - mbw.Call = lambda cmd, env=None, buffer_output=True: (0, '', '') - - self.check(['analyze', '-c', 'debug_goma', '//out/Default', - '/tmp/in.json', '/tmp/out.json'], mbw=mbw, ret=0) - out = json.loads(mbw.files['/tmp/out.json']) - - # check that 'foo_unittests' is not in the compile_targets - self.assertEqual(['all'], out['compile_targets']) - - def test_analyze_handles_other_toolchains(self): - files = {'/tmp/in.json': '''{\ - "files": ["foo/foo_unittest.cc"], - "test_targets": ["foo_unittests"], - "additional_compile_targets": ["all"] - }''', - '/tmp/out.json.gn': '''{\ - "status": "Found dependency", - "compile_targets": ["//foo:foo_unittests", - "//foo:foo_unittests(bar)"], - "test_targets": ["//foo:foo_unittests"] - }'''} - - mbw = self.fake_mbw(files) - mbw.Call = lambda cmd, env=None, buffer_output=True: (0, '', '') - - self.check(['analyze', '-c', 'debug_goma', '//out/Default', - '/tmp/in.json', '/tmp/out.json'], mbw=mbw, ret=0) - out = json.loads(mbw.files['/tmp/out.json']) - - # crbug.com/736215: If GN returns a label containing a toolchain, - # MB (and Ninja) don't know how to handle it; to work around this, - # we give up and just build everything we were asked to build. The - # output compile_targets should include all of the input test_targets and - # additional_compile_targets. - self.assertEqual(['all', 'foo_unittests'], out['compile_targets']) - - def test_analyze_handles_way_too_many_results(self): - too_many_files = ', '.join(['"//foo:foo%d"' % i for i in range(4 * 1024)]) - files = {'/tmp/in.json': '''{\ - "files": ["foo/foo_unittest.cc"], - "test_targets": ["foo_unittests"], - "additional_compile_targets": ["all"] - }''', - '/tmp/out.json.gn': '''{\ - "status": "Found dependency", - "compile_targets": [''' + too_many_files + '''], - "test_targets": ["//foo:foo_unittests"] - }'''} - - mbw = self.fake_mbw(files) - mbw.Call = lambda cmd, env=None, buffer_output=True: (0, '', '') - - self.check(['analyze', '-c', 'debug_goma', '//out/Default', - '/tmp/in.json', '/tmp/out.json'], mbw=mbw, ret=0) - out = json.loads(mbw.files['/tmp/out.json']) - - # If GN returns so many compile targets that we might have command-line - # issues, we should give up and just build everything we were asked to - # build. The output compile_targets should include all of the input - # test_targets and additional_compile_targets. - self.assertEqual(['all', 'foo_unittests'], out['compile_targets']) - - def test_gen(self): - mbw = self.fake_mbw() - self.check(['gen', '-c', 'debug_goma', '//out/Default', '-g', '/goma'], - mbw=mbw, ret=0) - self.assertMultiLineEqual(mbw.files['/fake_src/out/Default/args.gn'], - ('goma_dir = "/goma"\n' - 'is_debug = true\n' - 'use_goma = true\n')) - - # Make sure we log both what is written to args.gn and the command line. - self.assertIn('Writing """', mbw.out) - self.assertIn('/fake_src/buildtools/linux64/gn gen //out/Default --check', - mbw.out) - - mbw = self.fake_mbw(win32=True) - self.check(['gen', '-c', 'debug_goma', '-g', 'c:\\goma', '//out/Debug'], - mbw=mbw, ret=0) - self.assertMultiLineEqual(mbw.files['c:\\fake_src\\out\\Debug\\args.gn'], - ('goma_dir = "c:\\\\goma"\n' - 'is_debug = true\n' - 'use_goma = true\n')) - self.assertIn('c:\\fake_src\\buildtools\\win\\gn.exe gen //out/Debug ' - '--check\n', mbw.out) - - mbw = self.fake_mbw() - self.check(['gen', '-m', 'fake_builder_group', '-b', 'fake_args_bot', - '//out/Debug'], - mbw=mbw, ret=0) - # TODO(almuthanna): disable test temporarily to - # solve this issue https://crbug.com/v8/11102 - # self.assertEqual( - # mbw.files['/fake_src/out/Debug/args.gn'], - # 'import("//build/args/bots/fake_builder_group/fake_args_bot.gn")\n') - - def test_gen_args_file_mixins(self): - mbw = self.fake_mbw() - self.check(['gen', '-m', 'fake_builder_group', '-b', 'fake_args_file', - '//out/Debug'], mbw=mbw, ret=0) - - self.assertEqual( - mbw.files['/fake_src/out/Debug/args.gn'], - ('import("//build/args/fake.gn")\n' - 'use_goma = true\n')) - - mbw = self.fake_mbw() - self.check(['gen', '-m', 'fake_builder_group', '-b', 'fake_args_file_twice', - '//out/Debug'], mbw=mbw, ret=1) - - def test_gen_fails(self): - mbw = self.fake_mbw() - mbw.Call = lambda cmd, env=None, buffer_output=True: (1, '', '') - self.check(['gen', '-c', 'debug_goma', '//out/Default'], mbw=mbw, ret=1) - - def test_gen_swarming(self): - files = { - '/tmp/swarming_targets': 'base_unittests\n', - '/fake_src/testing/buildbot/gn_isolate_map.pyl': ( - "{'base_unittests': {" - " 'label': '//base:base_unittests'," - " 'type': 'raw'," - " 'args': []," - "}}\n" - ), - '/fake_src/out/Default/base_unittests.runtime_deps': ( - "base_unittests\n" - ), - } - mbw = self.fake_mbw(files) - self.check(['gen', - '-c', 'debug_goma', - '--swarming-targets-file', '/tmp/swarming_targets', - '//out/Default'], mbw=mbw, ret=0) - self.assertIn('/fake_src/out/Default/base_unittests.isolate', - mbw.files) - self.assertIn('/fake_src/out/Default/base_unittests.isolated.gen.json', - mbw.files) - - def test_gen_swarming_script(self): - files = { - '/tmp/swarming_targets': 'cc_perftests\n', - '/fake_src/testing/buildbot/gn_isolate_map.pyl': ( - "{'cc_perftests': {" - " 'label': '//cc:cc_perftests'," - " 'type': 'script'," - " 'script': '/fake_src/out/Default/test_script.py'," - " 'args': []," - "}}\n" - ), - 'c:\\fake_src\out\Default\cc_perftests.exe.runtime_deps': ( - "cc_perftests\n" - ), - } - mbw = self.fake_mbw(files=files, win32=True) - self.check(['gen', - '-c', 'debug_goma', - '--swarming-targets-file', '/tmp/swarming_targets', - '--isolate-map-file', - '/fake_src/testing/buildbot/gn_isolate_map.pyl', - '//out/Default'], mbw=mbw, ret=0) - self.assertIn('c:\\fake_src\\out\\Default\\cc_perftests.isolate', - mbw.files) - self.assertIn('c:\\fake_src\\out\\Default\\cc_perftests.isolated.gen.json', - mbw.files) - - - def test_multiple_isolate_maps(self): - files = { - '/tmp/swarming_targets': 'cc_perftests\n', - '/fake_src/testing/buildbot/gn_isolate_map.pyl': ( - "{'cc_perftests': {" - " 'label': '//cc:cc_perftests'," - " 'type': 'raw'," - " 'args': []," - "}}\n" - ), - '/fake_src/testing/buildbot/gn_isolate_map2.pyl': ( - "{'cc_perftests2': {" - " 'label': '//cc:cc_perftests'," - " 'type': 'raw'," - " 'args': []," - "}}\n" - ), - 'c:\\fake_src\out\Default\cc_perftests.exe.runtime_deps': ( - "cc_perftests\n" - ), - } - mbw = self.fake_mbw(files=files, win32=True) - self.check(['gen', - '-c', 'debug_goma', - '--swarming-targets-file', '/tmp/swarming_targets', - '--isolate-map-file', - '/fake_src/testing/buildbot/gn_isolate_map.pyl', - '--isolate-map-file', - '/fake_src/testing/buildbot/gn_isolate_map2.pyl', - '//out/Default'], mbw=mbw, ret=0) - self.assertIn('c:\\fake_src\\out\\Default\\cc_perftests.isolate', - mbw.files) - self.assertIn('c:\\fake_src\\out\\Default\\cc_perftests.isolated.gen.json', - mbw.files) - - - def test_duplicate_isolate_maps(self): - files = { - '/tmp/swarming_targets': 'cc_perftests\n', - '/fake_src/testing/buildbot/gn_isolate_map.pyl': ( - "{'cc_perftests': {" - " 'label': '//cc:cc_perftests'," - " 'type': 'raw'," - " 'args': []," - "}}\n" - ), - '/fake_src/testing/buildbot/gn_isolate_map2.pyl': ( - "{'cc_perftests': {" - " 'label': '//cc:cc_perftests'," - " 'type': 'raw'," - " 'args': []," - "}}\n" - ), - 'c:\\fake_src\out\Default\cc_perftests.exe.runtime_deps': ( - "cc_perftests\n" - ), - } - mbw = self.fake_mbw(files=files, win32=True) - # Check that passing duplicate targets into mb fails. - self.check(['gen', - '-c', 'debug_goma', - '--swarming-targets-file', '/tmp/swarming_targets', - '--isolate-map-file', - '/fake_src/testing/buildbot/gn_isolate_map.pyl', - '--isolate-map-file', - '/fake_src/testing/buildbot/gn_isolate_map2.pyl', - '//out/Default'], mbw=mbw, ret=1) - - def test_isolate(self): - files = { - '/fake_src/out/Default/toolchain.ninja': "", - '/fake_src/testing/buildbot/gn_isolate_map.pyl': ( - "{'base_unittests': {" - " 'label': '//base:base_unittests'," - " 'type': 'raw'," - " 'args': []," - "}}\n" - ), - '/fake_src/out/Default/base_unittests.runtime_deps': ( - "base_unittests\n" - ), - } - self.check(['isolate', '-c', 'debug_goma', '//out/Default', - 'base_unittests'], files=files, ret=0) - - # test running isolate on an existing build_dir - files['/fake_src/out/Default/args.gn'] = 'is_debug = True\n' - self.check(['isolate', '//out/Default', 'base_unittests'], - files=files, ret=0) - - self.check(['isolate', '//out/Default', 'base_unittests'], - files=files, ret=0) - - def test_run(self): - files = { - '/fake_src/testing/buildbot/gn_isolate_map.pyl': ( - "{'base_unittests': {" - " 'label': '//base:base_unittests'," - " 'type': 'raw'," - " 'args': []," - "}}\n" - ), - '/fake_src/out/Default/base_unittests.runtime_deps': ( - "base_unittests\n" - ), - } - self.check(['run', '-c', 'debug_goma', '//out/Default', - 'base_unittests'], files=files, ret=0) - - def test_lookup(self): - self.check(['lookup', '-c', 'debug_goma'], ret=0, - out=('\n' - 'Writing """\\\n' - 'is_debug = true\n' - 'use_goma = true\n' - '""" to _path_/args.gn.\n\n' - '/fake_src/buildtools/linux64/gn gen _path_\n')) - - def test_quiet_lookup(self): - self.check(['lookup', '-c', 'debug_goma', '--quiet'], ret=0, - out=('is_debug = true\n' - 'use_goma = true\n')) - - def test_lookup_goma_dir_expansion(self): - self.check(['lookup', '-c', 'rel_bot', '-g', '/foo'], ret=0, - out=('\n' - 'Writing """\\\n' - 'enable_doom_melon = true\n' - 'goma_dir = "/foo"\n' - 'is_debug = false\n' - 'use_goma = true\n' - '""" to _path_/args.gn.\n\n' - '/fake_src/buildtools/linux64/gn gen _path_\n')) - - def test_help(self): - orig_stdout = sys.stdout - try: - sys.stdout = StringIO.StringIO() - self.assertRaises(SystemExit, self.check, ['-h']) - self.assertRaises(SystemExit, self.check, ['help']) - self.assertRaises(SystemExit, self.check, ['help', 'gen']) - finally: - sys.stdout = orig_stdout - - def test_multiple_phases(self): - # Check that not passing a --phase to a multi-phase builder fails. - mbw = self.check(['lookup', '-m', 'fake_builder_group', - '-b', 'fake_multi_phase'], - ret=1) - self.assertIn('Must specify a build --phase', mbw.out) - - # Check that passing a --phase to a single-phase builder fails. - mbw = self.check(['lookup', '-m', 'fake_builder_group', - '-b', 'fake_builder', - '--phase', 'phase_1'], ret=1) - self.assertIn('Must not specify a build --phase', mbw.out) - - # Check that passing a wrong phase key to a multi-phase builder fails. - mbw = self.check(['lookup', '-m', 'fake_builder_group', - '-b', 'fake_multi_phase', - '--phase', 'wrong_phase'], ret=1) - self.assertIn('Phase wrong_phase doesn\'t exist', mbw.out) - - # Check that passing a correct phase key to a multi-phase builder passes. - mbw = self.check(['lookup', '-m', 'fake_builder_group', - '-b', 'fake_multi_phase', - '--phase', 'phase_1'], ret=0) - self.assertIn('phase = 1', mbw.out) - - mbw = self.check(['lookup', '-m', 'fake_builder_group', - '-b', 'fake_multi_phase', - '--phase', 'phase_2'], ret=0) - self.assertIn('phase = 2', mbw.out) - - def test_recursive_lookup(self): - files = { - '/fake_src/build/args/fake.gn': ( - 'enable_doom_melon = true\n' - 'enable_antidoom_banana = true\n' - ) - } - self.check(['lookup', '-m', 'fake_builder_group', '-b', 'fake_args_file', - '--recursive'], files=files, ret=0, - out=('enable_antidoom_banana = true\n' - 'enable_doom_melon = true\n' - 'use_goma = true\n')) - - def test_validate(self): - mbw = self.fake_mbw() - self.check(['validate'], mbw=mbw, ret=0) - - def test_buildbucket(self): - mbw = self.fake_mbw() - mbw.files[mbw.default_config] = TRYSERVER_CONFIG - self.check(['gerrit-buildbucket-config'], mbw=mbw, - ret=0, - out=('# This file was generated using ' - '"tools/mb/mb.py gerrit-buildbucket-config".\n' - '[bucket "luci.luci_tryserver1"]\n' - '\tbuilder = luci_builder1\n' - '[bucket "luci.luci_tryserver2"]\n' - '\tbuilder = luci_builder2\n' - '[bucket "builder_group.tryserver.chromium.linux"]\n' - '\tbuilder = try_builder\n' - '[bucket "builder_group.tryserver.chromium.mac"]\n' - '\tbuilder = try_builder2\n')) - - -if __name__ == '__main__': - unittest.main() diff --git a/deps/v8/tools/parse-processor.mjs b/deps/v8/tools/parse-processor.mjs index e604908db89b87..fc5868b008e7be 100644 --- a/deps/v8/tools/parse-processor.mjs +++ b/deps/v8/tools/parse-processor.mjs @@ -746,7 +746,7 @@ function startOf(timestamp, time) { export class ParseProcessor extends LogReader { constructor() { super(); - this.dispatchTable_ = { + this.setDispatchTable({ // Avoid accidental leaking of __proto__ properties and force this object // to be in dictionary-mode. __proto__: null, @@ -780,7 +780,7 @@ export class ParseProcessor extends LogReader { parsers: [parseInt, parseString, parseString], processor: this.processScriptSource }, - }; + }); this.functionEventDispatchTable_ = { // Avoid accidental leaking of __proto__ properties and force this object // to be in dictionary-mode. @@ -820,20 +820,7 @@ export class ParseProcessor extends LogReader { } processString(string) { - let end = string.length; - let current = 0; - let next = 0; - let line; - let i = 0; - let entry; - while (current < end) { - next = string.indexOf("\n", current); - if (next === -1) break; - i++; - line = string.substring(current, next); - current = next + 1; - this.processLogLine(line); - } + this.processLogChunk(string); this.postProcess(); } diff --git a/deps/v8/tools/predictable_wrapper.py b/deps/v8/tools/predictable_wrapper.py index ad5adf7d297f79..ea80653c86b813 100644 --- a/deps/v8/tools/predictable_wrapper.py +++ b/deps/v8/tools/predictable_wrapper.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright 2017 the V8 project authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -31,10 +31,16 @@ # Predictable mode works only when run on the host os. command.setup(utils.GuessOS(), None) +def maybe_decode(message): + if not isinstance(message, str): + return message.decode() + return message + + def main(args): def allocation_str(stdout): for line in reversed((stdout or '').splitlines()): - if line.startswith('### Allocations = '): + if maybe_decode(line).startswith('### Allocations = '): return line return None diff --git a/deps/v8/tools/process-wasm-compilation-times.py b/deps/v8/tools/process-wasm-compilation-times.py index 5002fc00e23d1e..37c5998657aa9c 100755 --- a/deps/v8/tools/process-wasm-compilation-times.py +++ b/deps/v8/tools/process-wasm-compilation-times.py @@ -70,13 +70,13 @@ def AddTFLine(self, words): self.has_tf = True # 0 1 2 3 4 5 6 7 8 9 10 11 # Compiled function #6 using TurboFan, took 0 ms and 14440 / 44656 - # 12 13 14 15 16 17 - # max/total bytes, codesize 24 name wasm-function#6 + # 12 13 14 15 16 17 18 19 + # max/total bytes; bodysize 12 codesize 24 name wasm-function#6 self.time_tf = int(words[6]) self.mem_tf_max = int(words[9]) self.mem_tf_total = int(words[11]) - self.size_tf = int(words[15]) - self.name = words[17] + self.size_tf = int(words[17]) + self.name = words[19] def AddLiftoffLine(self, words): assert self.index == words[2], "wrong function" @@ -109,7 +109,8 @@ def __str__(self): with open(sys.argv[1], "r") as f: for line in f.readlines(): words = line.strip().split(" ") - if words[0] != "Compiled": continue + if words[0] != "Compiled" or words[1] != "function": + continue name = words[2] RegisterName(name) if name in funcs_dict: diff --git a/deps/v8/tools/profile.mjs b/deps/v8/tools/profile.mjs index 5f0b1667ec0ae4..ba2a523fb51d59 100644 --- a/deps/v8/tools/profile.mjs +++ b/deps/v8/tools/profile.mjs @@ -234,6 +234,35 @@ export class Script { } +const kOffsetPairRegex = /C([0-9]+)O([0-9]+)/g; +class SourcePositionTable { + constructor(encodedTable) { + this._offsets = []; + while (true) { + const regexResult = kOffsetPairRegex.exec(encodedTable); + if (!regexResult) break; + const codeOffset = parseInt(regexResult[1]); + const scriptOffset = parseInt(regexResult[2]); + if (isNaN(codeOffset) || isNaN(scriptOffset)) continue; + this._offsets.push({code: codeOffset, script: scriptOffset}); + } + } + + getScriptOffset(codeOffset) { + if (codeOffset < 0) { + throw new Exception(`Invalid codeOffset=${codeOffset}, should be >= 0`); + } + for (let i = this.offsetTable.length - 1; i >= 0; i--) { + const offset = this._offsets[i]; + if (offset.code <= codeOffset) { + return offset.script; + } + } + return this._offsets[0].script; + } +} + + class SourceInfo { script; start; @@ -243,13 +272,16 @@ class SourceInfo { fns; disassemble; - setSourcePositionInfo(script, startPos, endPos, sourcePositionTable, inliningPositions, inlinedFunctions) { + setSourcePositionInfo( + script, startPos, endPos, sourcePositionTableData, inliningPositions, + inlinedFunctions) { this.script = script; this.start = startPos; this.end = endPos; - this.positions = sourcePositionTable; + this.positions = sourcePositionTableData; this.inlined = inliningPositions; this.fns = inlinedFunctions; + this.sourcePositionTable = new SourcePositionTable(sourcePositionTableData); } setDisassemble(code) { @@ -261,6 +293,10 @@ class SourceInfo { } } +const kProfileOperationMove = 0; +const kProfileOperationDelete = 1; +const kProfileOperationTick = 2; + /** * Creates a profile object for processing profiling-related events * and calculating function execution times. @@ -271,9 +307,10 @@ export class Profile { codeMap_ = new CodeMap(); topDownTree_ = new CallTree(); bottomUpTree_ = new CallTree(); - c_entries_ = {}; + c_entries_ = {__proto__:null}; scripts_ = []; urlToScript_ = new Map(); + warnings = new Set(); serializeVMSymbols() { let result = this.codeMap_.getAllStaticEntriesWithAddresses(); @@ -300,9 +337,9 @@ export class Profile { * @enum {number} */ static Operation = { - MOVE: 0, - DELETE: 1, - TICK: 2 + MOVE: kProfileOperationMove, + DELETE: kProfileOperationDelete, + TICK: kProfileOperationTick } /** @@ -314,7 +351,6 @@ export class Profile { COMPILED: 0, IGNITION: 1, BASELINE: 2, - TURBOPROP: 4, TURBOFAN: 5, } @@ -346,8 +382,6 @@ export class Profile { return this.CodeState.IGNITION; case '^': return this.CodeState.BASELINE; - case '+': - return this.CodeState.TURBOPROP; case '*': return this.CodeState.TURBOFAN; } @@ -361,8 +395,6 @@ export class Profile { return "Unopt"; } else if (state === this.CodeState.BASELINE) { return "Baseline"; - } else if (state === this.CodeState.TURBOPROP) { - return "Turboprop"; } else if (state === this.CodeState.TURBOFAN) { return "Opt"; } @@ -459,7 +491,7 @@ export class Profile { // As code and functions are in the same address space, // it is safe to put them in a single code map. let func = this.codeMap_.findDynamicEntryByStartAddress(funcAddr); - if (!func) { + if (func === null) { func = new FunctionEntry(name); this.codeMap_.addCode(funcAddr, func); } else if (func.name !== name) { @@ -467,7 +499,7 @@ export class Profile { func.name = name; } let entry = this.codeMap_.findDynamicEntryByStartAddress(start); - if (entry) { + if (entry !== null) { if (entry.size === size && entry.func === func) { // Entry state has changed. entry.state = state; @@ -476,7 +508,7 @@ export class Profile { entry = null; } } - if (!entry) { + if (entry === null) { entry = new DynamicFuncCodeEntry(size, type, func, state); this.codeMap_.addCode(start, entry); } @@ -493,7 +525,7 @@ export class Profile { try { this.codeMap_.moveCode(from, to); } catch (e) { - this.handleUnknownCode(Profile.Operation.MOVE, from); + this.handleUnknownCode(kProfileOperationMove, from); } } @@ -510,7 +542,7 @@ export class Profile { try { this.codeMap_.deleteCode(start); } catch (e) { - this.handleUnknownCode(Profile.Operation.DELETE, start); + this.handleUnknownCode(kProfileOperationDelete, start); } } @@ -521,16 +553,16 @@ export class Profile { inliningPositions, inlinedFunctions) { const script = this.getOrCreateScript(scriptId); const entry = this.codeMap_.findDynamicEntryByStartAddress(start); - if (!entry) return; + if (entry === null) return; // Resolve the inlined functions list. if (inlinedFunctions.length > 0) { inlinedFunctions = inlinedFunctions.substring(1).split("S"); for (let i = 0; i < inlinedFunctions.length; i++) { const funcAddr = parseInt(inlinedFunctions[i]); const func = this.codeMap_.findDynamicEntryByStartAddress(funcAddr); - if (!func || func.funcId === undefined) { + if (func === null || func.funcId === undefined) { // TODO: fix - console.warn(`Could not find function ${inlinedFunctions[i]}`); + this.warnings.add(`Could not find function ${inlinedFunctions[i]}`); inlinedFunctions[i] = null; } else { inlinedFunctions[i] = func.funcId; @@ -547,7 +579,9 @@ export class Profile { addDisassemble(start, kind, disassemble) { const entry = this.codeMap_.findDynamicEntryByStartAddress(start); - if (entry) this.getOrCreateSourceInfo(entry).setDisassemble(disassemble); + if (entry !== null) { + this.getOrCreateSourceInfo(entry).setDisassemble(disassemble); + } return entry; } @@ -563,7 +597,7 @@ export class Profile { getOrCreateScript(id) { let script = this.scripts_[id]; - if (!script) { + if (script === undefined) { script = new Script(id); this.scripts_[id] = script; } @@ -623,7 +657,7 @@ export class Profile { for (let i = 0; i < stack.length; ++i) { const pc = stack[i]; const entry = this.codeMap_.findEntry(pc); - if (entry) { + if (entry !== null) { entryStack.push(entry); const name = entry.getName(); if (i === 0 && (entry.type === 'CPP' || entry.type === 'SHARED_LIB')) { @@ -636,12 +670,13 @@ export class Profile { nameStack.push(name); } } else { - this.handleUnknownCode(Profile.Operation.TICK, pc, i); + this.handleUnknownCode(kProfileOperationTick, pc, i); if (i === 0) nameStack.push("UNKNOWN"); entryStack.push(pc); } if (look_for_first_c_function && i > 0 && - (!entry || entry.type !== 'CPP') && last_seen_c_function !== '') { + (entry === null || entry.type !== 'CPP') + && last_seen_c_function !== '') { if (this.c_entries_[last_seen_c_function] === undefined) { this.c_entries_[last_seen_c_function] = 0; } @@ -716,7 +751,7 @@ export class Profile { getFlatProfile(opt_label) { const counters = new CallTree(); const rootLabel = opt_label || CallTree.ROOT_NODE_LABEL; - const precs = {}; + const precs = {__proto__:null}; precs[rootLabel] = 0; const root = counters.findOrAddChild(rootLabel); @@ -968,9 +1003,7 @@ class CallTree { * @param {Array} path Call path. */ addPath(path) { - if (path.length == 0) { - return; - } + if (path.length == 0) return; let curr = this.root_; for (let i = 0; i < path.length; ++i) { curr = curr.findOrAddChild(path[i]); @@ -1084,21 +1117,14 @@ class CallTree { * @param {CallTreeNode} opt_parent Node parent. */ class CallTreeNode { - /** - * Node self weight (how many times this node was the last node in - * a call path). - * @type {number} - */ - selfWeight = 0; - - /** - * Node total weight (includes weights of all children). - * @type {number} - */ - totalWeight = 0; - children = {}; constructor(label, opt_parent) { + // Node self weight (how many times this node was the last node in + // a call path). + this.selfWeight = 0; + // Node total weight (includes weights of all children). + this.totalWeight = 0; + this. children = { __proto__:null }; this.label = label; this.parent = opt_parent; } @@ -1141,7 +1167,8 @@ class CallTreeNode { * @param {string} label Child node label. */ findChild(label) { - return this.children[label] || null; + const found = this.children[label]; + return found === undefined ? null : found; } /** @@ -1151,7 +1178,9 @@ class CallTreeNode { * @param {string} label Child node label. */ findOrAddChild(label) { - return this.findChild(label) || this.addChild(label); + const found = this.findChild(label) + if (found === null) return this.addChild(label); + return found; } /** @@ -1171,7 +1200,7 @@ class CallTreeNode { * @param {function(CallTreeNode)} f Visitor function. */ walkUpToRoot(f) { - for (let curr = this; curr != null; curr = curr.parent) { + for (let curr = this; curr !== null; curr = curr.parent) { f(curr); } } diff --git a/deps/v8/tools/release/PRESUBMIT.py b/deps/v8/tools/release/PRESUBMIT.py index a982b2e153aec1..ed4557d98ecaea 100644 --- a/deps/v8/tools/release/PRESUBMIT.py +++ b/deps/v8/tools/release/PRESUBMIT.py @@ -2,9 +2,15 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +# This line is 'magic' in that git-cl looks for it to decide whether to +# use Python3 instead of Python2 when running the code in this file. +USE_PYTHON3 = True + + def _CommonChecks(input_api, output_api): tests = input_api.canned_checks.GetUnitTestsInDirectory( - input_api, output_api, '.', files_to_check=['test_scripts.py$']) + input_api, output_api, '.', files_to_check=['test_scripts.py$'], + run_on_python2=False) return input_api.RunTests(tests) def CheckChangeOnUpload(input_api, output_api): diff --git a/deps/v8/tools/release/auto_push.py b/deps/v8/tools/release/auto_push.py index 4cb968787f8048..eb52e2a316708a 100755 --- a/deps/v8/tools/release/auto_push.py +++ b/deps/v8/tools/release/auto_push.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright 2013 the V8 project authors. All rights reserved. # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are @@ -26,9 +26,6 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# for py2/py3 compatibility -from __future__ import print_function - import argparse import json import os diff --git a/deps/v8/tools/release/auto_roll.py b/deps/v8/tools/release/auto_roll.py index 76247b1fb3ee29..5d44505c2ab11e 100755 --- a/deps/v8/tools/release/auto_roll.py +++ b/deps/v8/tools/release/auto_roll.py @@ -3,9 +3,6 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -# for py2/py3 compatibility -from __future__ import print_function - import argparse import os import sys @@ -80,7 +77,7 @@ def RunStep(self): version = self.GetVersionTag(revision) assert version, "Internal error. All recent releases should have a tag" - if SortingKey(self["last_version"]) < SortingKey(version): + if LooseVersion(self["last_version"]) < LooseVersion(version): self["roll"] = revision break else: diff --git a/deps/v8/tools/release/auto_tag.py b/deps/v8/tools/release/auto_tag.py deleted file mode 100755 index 7e77c313d83d17..00000000000000 --- a/deps/v8/tools/release/auto_tag.py +++ /dev/null @@ -1,204 +0,0 @@ -#!/usr/bin/env python -# Copyright 2014 the V8 project authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -# for py2/py3 compatibility -from __future__ import print_function - -import argparse -import sys - -from common_includes import * - - -class Preparation(Step): - MESSAGE = "Preparation." - - def RunStep(self): - # TODO(machenbach): Remove after the git switch. - if self.Config("PERSISTFILE_BASENAME") == "/tmp/v8-auto-tag-tempfile": - print("This script is disabled until after the v8 git migration.") - return True - - self.CommonPrepare() - self.PrepareBranch() - self.GitCheckout("main") - self.vc.Pull() - - -class GetTags(Step): - MESSAGE = "Get all V8 tags." - - def RunStep(self): - self.GitCreateBranch(self._config["BRANCHNAME"]) - self["tags"] = self.vc.GetTags() - - -class GetOldestUntaggedVersion(Step): - MESSAGE = "Check if there's a version on bleeding edge without a tag." - - def RunStep(self): - tags = set(self["tags"]) - self["candidate"] = None - self["candidate_version"] = None - self["next"] = None - self["next_version"] = None - - # Iterate backwards through all automatic version updates. - for git_hash in self.GitLog( - format="%H", grep="\\[Auto\\-roll\\] Bump up version to").splitlines(): - - # Get the version. - if not self.GitCheckoutFileSafe(VERSION_FILE, git_hash): - continue - - self.ReadAndPersistVersion() - version = self.ArrayToVersion("") - - # Strip off trailing patch level (tags don't include tag level 0). - if version.endswith(".0"): - version = version[:-2] - - # Clean up checked-out version file. - self.GitCheckoutFileSafe(VERSION_FILE, "HEAD") - - if version in tags: - if self["candidate"]: - # Revision "git_hash" is tagged already and "candidate" was the next - # newer revision without a tag. - break - else: - print("Stop as %s is the latest version and it has been tagged." % - version) - self.CommonCleanup() - return True - else: - # This is the second oldest version without a tag. - self["next"] = self["candidate"] - self["next_version"] = self["candidate_version"] - - # This is the oldest version without a tag. - self["candidate"] = git_hash - self["candidate_version"] = version - - if not self["candidate"] or not self["candidate_version"]: - print("Nothing found to tag.") - self.CommonCleanup() - return True - - print("Candidate for tagging is %s with version %s" % - (self["candidate"], self["candidate_version"])) - - -class GetLKGRs(Step): - MESSAGE = "Get the last lkgrs." - - def RunStep(self): - revision_url = "https://v8-status.appspot.com/revisions?format=json" - status_json = self.ReadURL(revision_url, wait_plan=[5, 20]) - self["lkgrs"] = [entry["revision"] - for entry in json.loads(status_json) if entry["status"]] - - -class CalculateTagRevision(Step): - MESSAGE = "Calculate the revision to tag." - - def LastLKGR(self, min_rev, max_rev): - """Finds the newest lkgr between min_rev (inclusive) and max_rev - (exclusive). - """ - for lkgr in self["lkgrs"]: - # LKGRs are reverse sorted. - if int(min_rev) <= int(lkgr) and int(lkgr) < int(max_rev): - return lkgr - return None - - def RunStep(self): - # Get the lkgr after the tag candidate and before the next tag candidate. - candidate_svn = self.vc.GitSvn(self["candidate"]) - if self["next"]: - next_svn = self.vc.GitSvn(self["next"]) - else: - # Don't include the version change commit itself if there is no upper - # limit yet. - candidate_svn = str(int(candidate_svn) + 1) - next_svn = sys.maxsize - lkgr_svn = self.LastLKGR(candidate_svn, next_svn) - - if not lkgr_svn: - print("There is no lkgr since the candidate version yet.") - self.CommonCleanup() - return True - - # Let's check if the lkgr is at least three hours old. - self["lkgr"] = self.vc.SvnGit(lkgr_svn) - if not self["lkgr"]: - print("Couldn't find git hash for lkgr %s" % lkgr_svn) - self.CommonCleanup() - return True - - lkgr_utc_time = int(self.GitLog(n=1, format="%at", git_hash=self["lkgr"])) - current_utc_time = self._side_effect_handler.GetUTCStamp() - - if current_utc_time < lkgr_utc_time + 10800: - print("Candidate lkgr %s is too recent for tagging." % lkgr_svn) - self.CommonCleanup() - return True - - print("Tagging revision %s with %s" % (lkgr_svn, self["candidate_version"])) - - -class MakeTag(Step): - MESSAGE = "Tag the version." - - def RunStep(self): - if not self._options.dry_run: - self.GitReset(self["lkgr"]) - # FIXME(machenbach): Make this work with the git repo. - self.vc.Tag(self["candidate_version"], - "svn/bleeding_edge", - "This won't work!") - - -class CleanUp(Step): - MESSAGE = "Clean up." - - def RunStep(self): - self.CommonCleanup() - - -class AutoTag(ScriptsBase): - def _PrepareOptions(self, parser): - parser.add_argument("--dry_run", help="Don't tag the new version.", - default=False, action="store_true") - - def _ProcessOptions(self, options): # pragma: no cover - if not options.dry_run and not options.author: - print("Specify your chromium.org email with -a") - return False - options.wait_for_lgtm = False - options.force_readline_defaults = True - options.force_upload = True - return True - - def _Config(self): - return { - "BRANCHNAME": "auto-tag-v8", - "PERSISTFILE_BASENAME": "/tmp/v8-auto-tag-tempfile", - } - - def _Steps(self): - return [ - Preparation, - GetTags, - GetOldestUntaggedVersion, - GetLKGRs, - CalculateTagRevision, - MakeTag, - CleanUp, - ] - - -if __name__ == "__main__": # pragma: no cover - sys.exit(AutoTag().Run()) diff --git a/deps/v8/tools/release/check_clusterfuzz.py b/deps/v8/tools/release/check_clusterfuzz.py deleted file mode 100755 index b1b7e084df0c52..00000000000000 --- a/deps/v8/tools/release/check_clusterfuzz.py +++ /dev/null @@ -1,231 +0,0 @@ -#!/usr/bin/env python -# Copyright 2014 the V8 project authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -""" -Script to check for new clusterfuzz issues since the last rolled v8 revision. - -Returns a json list with test case IDs if any. - -Security considerations: The security key and request data must never be -written to public logs. Public automated callers of this script should -suppress stdout and stderr and only process contents of the results_file. -""" - -# for py2/py3 compatibility -from __future__ import print_function - -import argparse -import httplib -import json -import os -import re -import sys -import urllib -import urllib2 - - -# Constants to git repos. -BASE_URL = "https://chromium.googlesource.com" -DEPS_LOG = BASE_URL + "/chromium/src/+log/main/DEPS?format=JSON" - -# Constants for retrieving v8 rolls. -CRREV = "https://cr-rev.appspot.com/_ah/api/crrev/v1/commit/%s" -V8_COMMIT_RE = re.compile( - r"^Update V8 to version \d+\.\d+\.\d+ \(based on ([a-fA-F0-9]+)\)\..*") - -# Constants for the clusterfuzz backend. -HOSTNAME = "backend-dot-cluster-fuzz.appspot.com" - -# Crash patterns. -V8_INTERNAL_RE = re.compile(r"^v8::internal.*") -ANY_RE = re.compile(r".*") - -# List of all api requests. -BUG_SPECS = [ - { - "args": { - "job_type": "linux_asan_chrome_v8", - "reproducible": "True", - "open": "True", - "bug_information": "", - }, - "crash_state": V8_INTERNAL_RE, - }, - { - "args": { - "job_type": "linux_asan_d8", - "reproducible": "True", - "open": "True", - "bug_information": "", - }, - "crash_state": ANY_RE, - }, - { - "args": { - "job_type": "linux_asan_d8_dbg", - "reproducible": "True", - "open": "True", - "bug_information": "", - }, - "crash_state": ANY_RE, - }, - { - "args": { - "job_type": "linux_asan_d8_ignition_dbg", - "reproducible": "True", - "open": "True", - "bug_information": "", - }, - "crash_state": ANY_RE, - }, - { - "args": { - "job_type": "linux_asan_d8_v8_arm_dbg", - "reproducible": "True", - "open": "True", - "bug_information": "", - }, - "crash_state": ANY_RE, - }, - { - "args": { - "job_type": "linux_asan_d8_ignition_v8_arm_dbg", - "reproducible": "True", - "open": "True", - "bug_information": "", - }, - "crash_state": ANY_RE, - }, - { - "args": { - "job_type": "linux_asan_d8_v8_arm64_dbg", - "reproducible": "True", - "open": "True", - "bug_information": "", - }, - "crash_state": ANY_RE, - }, - { - "args": { - "job_type": "linux_asan_d8_v8_mipsel_dbg", - "reproducible": "True", - "open": "True", - "bug_information": "", - }, - "crash_state": ANY_RE, - }, -] - - -def GetRequest(url): - url_fh = urllib2.urlopen(url, None, 60) - try: - return url_fh.read() - finally: - url_fh.close() - - -def GetLatestV8InChromium(): - """Returns the commit position number of the latest v8 roll in chromium.""" - - # Check currently rolled v8 revision. - result = GetRequest(DEPS_LOG) - if not result: - return None - - # Strip security header and load json. - commits = json.loads(result[5:]) - - git_revision = None - for commit in commits["log"]: - # Get latest commit that matches the v8 roll pattern. Ignore cherry-picks. - match = re.match(V8_COMMIT_RE, commit["message"]) - if match: - git_revision = match.group(1) - break - else: - return None - - # Get commit position number for v8 revision. - result = GetRequest(CRREV % git_revision) - if not result: - return None - - commit = json.loads(result) - assert commit["repo"] == "v8/v8" - return commit["number"] - - -def APIRequest(key, **params): - """Send a request to the clusterfuzz api. - - Returns a json dict of the response. - """ - - params["api_key"] = key - params = urllib.urlencode(params) - - headers = {"Content-type": "application/x-www-form-urlencoded"} - - try: - conn = httplib.HTTPSConnection(HOSTNAME) - conn.request("POST", "/_api/", params, headers) - - response = conn.getresponse() - - # Never leak "data" into public logs. - data = response.read() - except: - raise Exception("ERROR: Connection problem.") - - try: - return json.loads(data) - except: - raise Exception("ERROR: Could not read response. Is your key valid?") - - return None - - -def Main(): - parser = argparse.ArgumentParser() - parser.add_argument("-k", "--key-file", required=True, - help="A file with the clusterfuzz api key.") - parser.add_argument("-r", "--results-file", - help="A file to write the results to.") - options = parser.parse_args() - - # Get api key. The key's content must never be logged. - assert options.key_file - with open(options.key_file) as f: - key = f.read().strip() - assert key - - revision_number = GetLatestV8InChromium() - - results = [] - for spec in BUG_SPECS: - args = dict(spec["args"]) - # Use incremented revision as we're interested in all revision greater than - # what's currently rolled into chromium. - if revision_number: - args["revision_greater_or_equal"] = str(int(revision_number) + 1) - - # Never print issue details in public logs. - issues = APIRequest(key, **args) - assert issues is not None - for issue in issues: - if (re.match(spec["crash_state"], issue["crash_state"]) and - not issue.get('has_bug_flag')): - results.append(issue["id"]) - - if options.results_file: - with open(options.results_file, "w") as f: - f.write(json.dumps(results)) - else: - print(results) - - -if __name__ == "__main__": - sys.exit(Main()) diff --git a/deps/v8/tools/release/common_includes.py b/deps/v8/tools/release/common_includes.py index b61a3e2e27fae8..afbcc9f8377365 100644 --- a/deps/v8/tools/release/common_includes.py +++ b/deps/v8/tools/release/common_includes.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright 2013 the V8 project authors. All rights reserved. # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are @@ -26,12 +26,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# for py2/py3 compatibility -from __future__ import print_function - import argparse import datetime -import httplib +from distutils.version import LooseVersion import glob import imp import json @@ -43,11 +40,14 @@ import textwrap import time import urllib -import urllib2 from git_recipes import GitRecipesMixin from git_recipes import GitFailedException +import http.client as httplib +import urllib.request as urllib2 + + DAY_IN_SECONDS = 24 * 60 * 60 PUSH_MSG_GIT_RE = re.compile(r".* \(based on (?P[a-fA-F0-9]+)\)$") PUSH_MSG_NEW_RE = re.compile(r"^Version \d+\.\d+\.\d+$") @@ -92,16 +92,6 @@ def MSub(rexp, replacement, text): return re.sub(rexp, replacement, text, flags=re.MULTILINE) -def SortingKey(version): - """Key for sorting version number strings: '3.11' > '3.2.1.1'""" - version_keys = map(int, version.split(".")) - # Fill up to full version numbers to normalize comparison. - while len(version_keys) < 4: # pragma: no cover - version_keys.append(0) - # Fill digits. - return ".".join(map("{0:04d}".format, version_keys)) - - # Some commands don't like the pipe, e.g. calling vi from within the script or # from subscripts like git cl upload. def Command(cmd, args="", prefix="", pipe=True, cwd=None): @@ -113,7 +103,7 @@ def Command(cmd, args="", prefix="", pipe=True, cwd=None): sys.stdout.flush() try: if pipe: - return subprocess.check_output(cmd_line, shell=True, cwd=cwd) + return subprocess.check_output(cmd_line, shell=True, cwd=cwd).decode('utf-8') else: return subprocess.check_call(cmd_line, shell=True, cwd=cwd) except subprocess.CalledProcessError: @@ -256,7 +246,7 @@ def GetBranches(self): lambda s: re.match(r"^branch\-heads/\d+\.\d+$", s), self.step.GitRemotes()) # Remove 'branch-heads/' prefix. - return map(lambda s: s[13:], branches) + return [b[13:] for b in branches] def MainBranch(self): return "main" @@ -557,7 +547,7 @@ def GetRecentReleases(self, max_age): int(time_now - max_age)).strip() # Filter out revisions who's tag is off by one or more commits. - return filter(lambda r: self.GetVersionTag(r), revisions.splitlines()) + return list(filter(self.GetVersionTag, revisions.splitlines())) def GetLatestVersion(self): # Use cached version if available. @@ -571,7 +561,7 @@ def GetLatestVersion(self): only_version_tags = NormalizeVersionTags(all_tags) version = sorted(only_version_tags, - key=SortingKey, reverse=True)[0] + key=LooseVersion, reverse=True)[0] self["latest_version"] = version return version diff --git a/deps/v8/tools/release/create_release.py b/deps/v8/tools/release/create_release.py index d1a066f00b6171..37e9e8673c6d7d 100755 --- a/deps/v8/tools/release/create_release.py +++ b/deps/v8/tools/release/create_release.py @@ -1,19 +1,18 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright 2015 the V8 project authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -# for py2/py3 compatibility -from __future__ import print_function - import argparse import os import sys import tempfile -import urllib2 from common_includes import * +import urllib.request as urllib2 + + class Preparation(Step): MESSAGE = "Preparation." @@ -48,7 +47,7 @@ def RunStep(self): # Use the highest version from main or from tags to determine the new # version. authoritative_version = sorted( - [main_version, latest_version], key=SortingKey)[1] + [main_version, latest_version], key=LooseVersion)[1] self.StoreVersion(authoritative_version, "authoritative_") # Variables prefixed with 'new_' contain the new version numbers for the diff --git a/deps/v8/tools/release/git_recipes.py b/deps/v8/tools/release/git_recipes.py index a90266aa7146df..865e13bf31a120 100644 --- a/deps/v8/tools/release/git_recipes.py +++ b/deps/v8/tools/release/git_recipes.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright 2014 the V8 project authors. All rights reserved. # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are diff --git a/deps/v8/tools/release/list_deprecated.py b/deps/v8/tools/release/list_deprecated.py index e25a5d713be43a..3549ecd427e785 100755 --- a/deps/v8/tools/release/list_deprecated.py +++ b/deps/v8/tools/release/list_deprecated.py @@ -30,13 +30,13 @@ def __init__(self, path): self.blame_list = self.get_blame_list() @classmethod - def get_api_header_files(clazz, options): + def get_api_header_files(cls, options): files = subprocess.check_output( ['git', 'ls-tree', '--name-only', '-r', 'HEAD', options.include_dir], encoding='UTF-8') - files = filter(lambda l: l.endswith('.h'), files.splitlines()) + files = map(Path, filter(lambda l: l.endswith('.h'), files.splitlines())) with Pool(processes=24) as pool: - return pool.map(HeaderFile, files) + return pool.map(cls, files) def extract_version(self, hash): if hash in VERSION_CACHE: @@ -129,14 +129,22 @@ def filter_and_print(self, macro, options): content = line[start:pos].strip().replace('""', '') deprecated.append((index + 1, commit_datetime, commit_hash, content)) index = index + 1 - if len(deprecated) == 0: return for linenumber, commit_datetime, commit_hash, content in deprecated: - commit_date = commit_datetime.date() - file_position = (f"{self.path}:{linenumber}").ljust(40) - v8_version = self.extract_version(commit_hash) - print(f"{file_position} v{v8_version} {commit_date} {commit_hash[:8]}" - f" {content}") - return len(deprecated) + self.print_details(linenumber, commit_datetime, commit_hash, content) + + def print_details(self, linenumber, commit_datetime, commit_hash, content): + commit_date = commit_datetime.date() + file_position = (f"{self.path}:{linenumber}").ljust(40) + v8_version = f"v{self.extract_version(commit_hash)}".rjust(5) + print(f"{file_position} {v8_version} {commit_date} {commit_hash[:8]}" + f" {content}") + + def print_v8_version(self, options): + commit_hash, commit_datetime = subprocess.check_output( + ['git', 'log', '-1', '--format=%H%n%ct', self.path], + encoding='UTF-8').splitlines() + commit_datetime = datetime.fromtimestamp(int(commit_datetime)) + self.print_details(11, commit_datetime, commit_hash, content="") def parse_options(args): @@ -163,10 +171,17 @@ def parse_options(args): def main(args): options = parse_options(args) + + print("# CURRENT V8 VERSION:") + version = HeaderFile(Path(options.include_dir) / 'v8-version.h') + version.print_v8_version(options) + header_files = HeaderFile.get_api_header_files(options) + print("\n") print("# V8_DEPRECATE_SOON:") for header in header_files: header.filter_and_print("V8_DEPRECATE_SOON", options) + print("\n") print("# V8_DEPRECATED:") for header in header_files: diff --git a/deps/v8/tools/release/merge_to_branch.py b/deps/v8/tools/release/merge_to_branch.py index 08a36125f8d385..ca5b2ce67461f2 100755 --- a/deps/v8/tools/release/merge_to_branch.py +++ b/deps/v8/tools/release/merge_to_branch.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright 2014 the V8 project authors. All rights reserved. # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are @@ -26,9 +26,6 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# for py2/py3 compatibility -from __future__ import print_function - import argparse from collections import OrderedDict import sys diff --git a/deps/v8/tools/release/roll_merge.py b/deps/v8/tools/release/roll_merge.py index d25f95e3971afe..d1abe56eddf457 100755 --- a/deps/v8/tools/release/roll_merge.py +++ b/deps/v8/tools/release/roll_merge.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright 2014 the V8 project authors. All rights reserved. # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are @@ -26,9 +26,6 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# for py2/py3 compatibility -from __future__ import print_function - import argparse from collections import OrderedDict import sys diff --git a/deps/v8/tools/release/script_test.py b/deps/v8/tools/release/script_test.py index 0f345b7fa838c6..a0899911c6d3b8 100755 --- a/deps/v8/tools/release/script_test.py +++ b/deps/v8/tools/release/script_test.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright 2014 the V8 project authors. All rights reserved. # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are @@ -29,9 +29,6 @@ # Wraps test execution with a coverage analysis. To get the best speed, the # native python coverage version >= 3.7.1 should be installed. -# for py2/py3 compatibility -from __future__ import print_function - import coverage import os import unittest diff --git a/deps/v8/tools/release/search_related_commits.py b/deps/v8/tools/release/search_related_commits.py deleted file mode 100755 index 48e6ae25922dec..00000000000000 --- a/deps/v8/tools/release/search_related_commits.py +++ /dev/null @@ -1,221 +0,0 @@ -#!/usr/bin/env python -# Copyright 2015 the V8 project authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -# for py2/py3 compatibility -from __future__ import print_function - -import argparse -import operator -import os -import re -from sets import Set -from subprocess import Popen, PIPE -import sys - -def search_all_related_commits( - git_working_dir, start_hash, until, separator, verbose=False): - - all_commits_raw = _find_commits_inbetween( - start_hash, until, git_working_dir, verbose) - if verbose: - print("All commits between and : " + all_commits_raw) - - # Adding start hash too - all_commits = [start_hash] - all_commits.extend(all_commits_raw.splitlines()) - all_related_commits = {} - already_treated_commits = Set([]) - for commit in all_commits: - if commit in already_treated_commits: - continue - - related_commits = _search_related_commits( - git_working_dir, commit, until, separator, verbose) - if len(related_commits) > 0: - all_related_commits[commit] = related_commits - already_treated_commits.update(related_commits) - - already_treated_commits.update(commit) - - return all_related_commits - -def _search_related_commits( - git_working_dir, start_hash, until, separator, verbose=False): - - if separator: - commits_between = _find_commits_inbetween( - start_hash, separator, git_working_dir, verbose) - if commits_between == "": - return [] - - # Extract commit position - original_message = git_execute( - git_working_dir, - ["show", "-s", "--format=%B", start_hash], - verbose) - title = original_message.splitlines()[0] - - matches = re.search("(\{#)([0-9]*)(\})", original_message) - - if not matches: - return [] - - commit_position = matches.group(2) - if verbose: - print("1.) Commit position to look for: " + commit_position) - - search_range = start_hash + ".." + until - - def git_args(grep_pattern): - return [ - "log", - "--reverse", - "--grep=" + grep_pattern, - "--format=%H", - search_range, - ] - - found_by_hash = git_execute( - git_working_dir, git_args(start_hash), verbose).strip() - - if verbose: - print("2.) Found by hash: " + found_by_hash) - - found_by_commit_pos = git_execute( - git_working_dir, git_args(commit_position), verbose).strip() - - if verbose: - print("3.) Found by commit position: " + found_by_commit_pos) - - # Replace brackets or else they are wrongly interpreted by --grep - title = title.replace("[", "\\[") - title = title.replace("]", "\\]") - - found_by_title = git_execute( - git_working_dir, git_args(title), verbose).strip() - - if verbose: - print("4.) Found by title: " + found_by_title) - - hits = ( - _convert_to_array(found_by_hash) + - _convert_to_array(found_by_commit_pos) + - _convert_to_array(found_by_title)) - hits = _remove_duplicates(hits) - - if separator: - for current_hit in hits: - commits_between = _find_commits_inbetween( - separator, current_hit, git_working_dir, verbose) - if commits_between != "": - return hits - return [] - - return hits - -def _find_commits_inbetween(start_hash, end_hash, git_working_dir, verbose): - commits_between = git_execute( - git_working_dir, - ["rev-list", "--reverse", start_hash + ".." + end_hash], - verbose) - return commits_between.strip() - -def _convert_to_array(string_of_hashes): - return string_of_hashes.splitlines() - -def _remove_duplicates(array): - no_duplicates = [] - for current in array: - if not current in no_duplicates: - no_duplicates.append(current) - return no_duplicates - -def git_execute(working_dir, args, verbose=False): - command = ["git", "-C", working_dir] + args - if verbose: - print("Git working dir: " + working_dir) - print("Executing git command:" + str(command)) - p = Popen(args=command, stdin=PIPE, - stdout=PIPE, stderr=PIPE) - output, err = p.communicate() - rc = p.returncode - if rc != 0: - raise Exception(err) - if verbose: - print("Git return value: " + output) - return output - -def _pretty_print_entry(hash, git_dir, pre_text, verbose): - text_to_print = git_execute( - git_dir, - ["show", - "--quiet", - "--date=iso", - hash, - "--format=%ad # %H # %s"], - verbose) - return pre_text + text_to_print.strip() - -def main(options): - all_related_commits = search_all_related_commits( - options.git_dir, - options.of[0], - options.until[0], - options.separator, - options.verbose) - - sort_key = lambda x: ( - git_execute( - options.git_dir, - ["show", "--quiet", "--date=iso", x, "--format=%ad"], - options.verbose)).strip() - - high_level_commits = sorted(all_related_commits.keys(), key=sort_key) - - for current_key in high_level_commits: - if options.prettyprint: - yield _pretty_print_entry( - current_key, - options.git_dir, - "+", - options.verbose) - else: - yield "+" + current_key - - found_commits = all_related_commits[current_key] - for current_commit in found_commits: - if options.prettyprint: - yield _pretty_print_entry( - current_commit, - options.git_dir, - "| ", - options.verbose) - else: - yield "| " + current_commit - -if __name__ == "__main__": # pragma: no cover - parser = argparse.ArgumentParser( - "This tool analyzes the commit range between and . " - "It finds commits which belong together e.g. Implement/Revert pairs and " - "Implement/Port/Revert triples. All supplied hashes need to be " - "from the same branch e.g. main.") - parser.add_argument("-g", "--git-dir", required=False, default=".", - help="The path to your git working directory.") - parser.add_argument("--verbose", action="store_true", - help="Enables a very verbose output") - parser.add_argument("of", nargs=1, - help="Hash of the commit to be searched.") - parser.add_argument("until", nargs=1, - help="Commit when searching should stop") - parser.add_argument("--separator", required=False, - help="The script will only list related commits " - "which are separated by hash <--separator>.") - parser.add_argument("--prettyprint", action="store_true", - help="Pretty prints the output") - - args = sys.argv[1:] - options = parser.parse_args(args) - for current_line in main(options): - print(current_line) diff --git a/deps/v8/tools/release/test_scripts.py b/deps/v8/tools/release/test_scripts.py index e8757cf27719e0..1777984a5896cc 100755 --- a/deps/v8/tools/release/test_scripts.py +++ b/deps/v8/tools/release/test_scripts.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright 2013 the V8 project authors. All rights reserved. # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are @@ -26,9 +26,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# for py2/py3 compatibility -from __future__ import print_function - +import json import os import shutil import tempfile @@ -44,7 +42,6 @@ from create_release import * import merge_to_branch from merge_to_branch import MergeToBranch -from auto_tag import AutoTag import roll_merge from roll_merge import RollMerge @@ -93,6 +90,10 @@ def testNormalizeVersionTags(self): ] self.assertEquals(expected, NormalizeVersionTags(input)) + def testCommand(self): + """Ensure json can decode the output of commands.""" + json.dumps(Command('ls', pipe=True)) + def Cmd(*args, **kwargs): """Convenience function returning a shell command test expectation.""" diff --git a/deps/v8/tools/release/test_search_related_commits.py b/deps/v8/tools/release/test_search_related_commits.py deleted file mode 100755 index 6943915fd63a99..00000000000000 --- a/deps/v8/tools/release/test_search_related_commits.py +++ /dev/null @@ -1,274 +0,0 @@ -#!/usr/bin/env python -# Copyright 2015 the V8 project authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -from collections import namedtuple -from os import path -import search_related_commits -import shutil -from subprocess import Popen, PIPE, check_call -import unittest - - -TEST_CONFIG = { - "GIT_REPO": "/tmp/test-v8-search-related-commits", -} - -class TestSearchRelatedCommits(unittest.TestCase): - - base_dir = TEST_CONFIG["GIT_REPO"] - - def _execute_git(self, git_args): - - fullCommand = ["git", "-C", self.base_dir] + git_args - p = Popen(args=fullCommand, stdin=PIPE, - stdout=PIPE, stderr=PIPE) - output, err = p.communicate() - rc = p.returncode - if rc != 0: - raise Exception(err) - return output - - def setUp(self): - if path.exists(self.base_dir): - shutil.rmtree(self.base_dir) - - check_call(["git", "init", self.base_dir]) - - # Initial commit - message = """[turbofan] Sanitize language mode for javascript operators. - - R=mstarzinger@chromium.org - - Review URL: https://codereview.chromium.org/1084243005 - - Cr-Commit-Position: refs/heads/main@{#28059}""" - self._make_empty_commit(message) - - message = """[crankshaft] Do some stuff - - R=hablich@chromium.org - - Review URL: https://codereview.chromium.org/1084243007 - - Cr-Commit-Position: refs/heads/main@{#28030}""" - - self._make_empty_commit(message) - - def tearDown(self): - if path.exists(self.base_dir): - shutil.rmtree(self.base_dir) - - def _assert_correct_standard_result( - self, result, all_commits, hash_of_first_commit): - self.assertEqual(len(result), 1, "Main commit not found") - self.assertTrue( - result.get(hash_of_first_commit), - "Main commit is wrong") - - self.assertEqual( - len(result[hash_of_first_commit]), - 1, - "Child commit not found") - self.assertEqual( - all_commits[2], - result[hash_of_first_commit][0], - "Child commit wrong") - - def _get_commits(self): - commits = self._execute_git( - ["log", "--format=%H", "--reverse"]).splitlines() - return commits - - def _make_empty_commit(self, message): - self._execute_git(["commit", "--allow-empty", "-m", message]) - - def testSearchByCommitPosition(self): - message = """Revert of some stuff. - > Cr-Commit-Position: refs/heads/main@{#28059} - R=mstarzinger@chromium.org - - Review URL: https://codereview.chromium.org/1084243005 - - Cr-Commit-Position: refs/heads/main@{#28088}""" - - self._make_empty_commit(message) - - commits = self._get_commits() - hash_of_first_commit = commits[0] - - result = search_related_commits.search_all_related_commits( - self.base_dir, hash_of_first_commit, "HEAD", None) - - self._assert_correct_standard_result(result, commits, hash_of_first_commit) - - def testSearchByTitle(self): - message = """Revert of some stuff. - > [turbofan] Sanitize language mode for javascript operators. - > Cr-Commit-Position: refs/heads/main@{#289} - R=mstarzinger@chromium.org - - Review URL: https://codereview.chromium.org/1084243005 - - Cr-Commit-Position: refs/heads/main@{#28088}""" - - self._make_empty_commit(message) - - commits = self._get_commits() - hash_of_first_commit = commits[0] - - result = search_related_commits.search_all_related_commits( - self.base_dir, hash_of_first_commit, "HEAD", None) - - self._assert_correct_standard_result(result, commits, hash_of_first_commit) - - def testSearchByHash(self): - commits = self._get_commits() - hash_of_first_commit = commits[0] - - message = """Revert of some stuff. - > [turbofan] Sanitize language mode for javascript operators. - > Reverting """ + hash_of_first_commit + """ - > R=mstarzinger@chromium.org - - Review URL: https://codereview.chromium.org/1084243005 - - Cr-Commit-Position: refs/heads/main@{#28088}""" - - self._make_empty_commit(message) - - #Fetch again for an update - commits = self._get_commits() - hash_of_first_commit = commits[0] - - result = search_related_commits.search_all_related_commits( - self.base_dir, - hash_of_first_commit, - "HEAD", - None) - - self._assert_correct_standard_result(result, commits, hash_of_first_commit) - - def testConsiderSeparator(self): - commits = self._get_commits() - hash_of_first_commit = commits[0] - - # Related commits happen before separator so it is not a hit - message = """Revert of some stuff: Not a hit - > [turbofan] Sanitize language mode for javascript operators. - > Reverting """ + hash_of_first_commit + """ - > R=mstarzinger@chromium.org - - Review URL: https://codereview.chromium.org/1084243005 - - Cr-Commit-Position: refs/heads/main@{#28088}""" - self._make_empty_commit(message) - - # Related commits happen before and after separator so it is a hit - commit_pos_of_main = "27088" - message = """Implement awesome feature: Main commit - - Review URL: https://codereview.chromium.org/1084243235 - - Cr-Commit-Position: refs/heads/main@{#""" + commit_pos_of_main + "}" - self._make_empty_commit(message) - - # Separator commit - message = """Commit which is the origin of the branch - - Review URL: https://codereview.chromium.org/1084243456 - - Cr-Commit-Position: refs/heads/main@{#28173}""" - self._make_empty_commit(message) - - # Filler commit - message = "Some unrelated commit: Not a hit" - self._make_empty_commit(message) - - # Related commit after separator: a hit - message = "Patch r" + commit_pos_of_main +""" done - - Review URL: https://codereview.chromium.org/1084243235 - - Cr-Commit-Position: refs/heads/main@{#29567}""" - self._make_empty_commit(message) - - #Fetch again for an update - commits = self._get_commits() - hash_of_first_commit = commits[0] - hash_of_hit = commits[3] - hash_of_separator = commits[4] - hash_of_child_hit = commits[6] - - result = search_related_commits.search_all_related_commits( - self.base_dir, - hash_of_first_commit, - "HEAD", - hash_of_separator) - - self.assertTrue(result.get(hash_of_hit), "Hit not found") - self.assertEqual(len(result), 1, "More than one hit found") - self.assertEqual( - len(result.get(hash_of_hit)), - 1, - "More than one child hit found") - self.assertEqual( - result.get(hash_of_hit)[0], - hash_of_child_hit, - "Wrong commit found") - - def testPrettyPrint(self): - message = """Revert of some stuff. - > [turbofan] Sanitize language mode for javascript operators. - > Cr-Commit-Position: refs/heads/main@{#289} - R=mstarzinger@chromium.org - - Review URL: https://codereview.chromium.org/1084243005 - - Cr-Commit-Position: refs/heads/main@{#28088}""" - - self._make_empty_commit(message) - - commits = self._get_commits() - hash_of_first_commit = commits[0] - OptionsStruct = namedtuple( - "OptionsStruct", - "git_dir of until all prettyprint separator verbose") - options = OptionsStruct( - git_dir= self.base_dir, - of= [hash_of_first_commit], - until= [commits[2]], - all= True, - prettyprint= True, - separator = None, - verbose=False) - output = [] - for current_line in search_related_commits.main(options): - output.append(current_line) - - self.assertIs(len(output), 2, "Not exactly two entries written") - self.assertTrue(output[0].startswith("+"), "Main entry not marked with +") - self.assertTrue(output[1].startswith("| "), "Child entry not marked with |") - - def testNothingFound(self): - commits = self._get_commits() - - self._execute_git(["commit", "--allow-empty", "-m", "A"]) - self._execute_git(["commit", "--allow-empty", "-m", "B"]) - self._execute_git(["commit", "--allow-empty", "-m", "C"]) - self._execute_git(["commit", "--allow-empty", "-m", "D"]) - - hash_of_first_commit = commits[0] - result = search_related_commits.search_all_related_commits( - self.base_dir, - hash_of_first_commit, - "HEAD", - None) - - self.assertEqual(len(result), 0, "Results found where none should be.") - - -if __name__ == "__main__": - #import sys;sys.argv = ['', 'Test.testName'] - unittest.main() diff --git a/deps/v8/tools/run.py b/deps/v8/tools/run.py index 59b3c15e682b58..99ccea1f6c3edc 100755 --- a/deps/v8/tools/run.py +++ b/deps/v8/tools/run.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright 2014 the V8 project authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -6,8 +6,6 @@ """This program wraps an arbitrary command since gn currently can only execute scripts.""" -from __future__ import print_function - import subprocess import sys diff --git a/deps/v8/tools/run_perf.py b/deps/v8/tools/run_perf.py index 1e22b298a8025d..5b2862fdd96428 100644 --- a/deps/v8/tools/run_perf.py +++ b/deps/v8/tools/run_perf.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python3 # Copyright 2014 the V8 project authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -102,11 +103,9 @@ The test flags are passed to the js test file after '--'. """ -# for py2/py3 compatibility -from __future__ import print_function -from functools import reduce - from collections import OrderedDict +from math import sqrt +from statistics import mean, stdev import copy import json import logging @@ -119,18 +118,11 @@ import time import traceback -import numpy - from testrunner.local import android from testrunner.local import command from testrunner.local import utils from testrunner.objects.output import Output, NULL_OUTPUT -# for py2/py3 compatibility -try: - basestring # Python 2 -except NameError: # Python 3 - basestring = str SUPPORTED_ARCHS = ['arm', 'ia32', @@ -265,11 +257,12 @@ def HasEnoughRuns(self, graph_config, confidence_level): return False logging.debug(' Results: %d entries', len(results)) - mean = numpy.mean(results) - mean_stderr = numpy.std(results) / numpy.sqrt(len(results)) - logging.debug(' Mean: %.2f, mean_stderr: %.2f', mean, mean_stderr) - logging.info('>>> Confidence level is %.2f', mean / (1000.0 * mean_stderr)) - return confidence_level * mean_stderr < mean / 1000.0 + avg = mean(results) + avg_stderr = stdev(results) / sqrt(len(results)) + logging.debug(' Mean: %.2f, mean_stderr: %.2f', avg, avg_stderr) + logging.info('>>> Confidence level is %.2f', + avg / max(1000.0 * avg_stderr, .1)) + return confidence_level * avg_stderr < avg / 1000.0 def __str__(self): # pragma: no cover return json.dumps(self.ToDict(), indent=2, separators=(',', ': ')) @@ -289,7 +282,8 @@ def RunResultsProcessor(results_processor, output, count): stderr=subprocess.PIPE, ) new_output = copy.copy(output) - new_output.stdout, _ = p.communicate(input=output.stdout) + new_output.stdout = p.communicate( + input=output.stdout.encode('utf-8'))[0].decode('utf-8') logging.info('>>> Processed stdout (#%d):\n%s', count, output.stdout) return new_output @@ -340,7 +334,7 @@ def __init__(self, suite, parent, arch): assert isinstance(suite.get('path', []), list) assert isinstance(suite.get('owners', []), list) - assert isinstance(suite['name'], basestring) + assert isinstance(suite['name'], str) assert isinstance(suite.get('flags', []), list) assert isinstance(suite.get('test_flags', []), list) assert isinstance(suite.get('resources', []), list) diff --git a/deps/v8/tools/sanitizers/tsan_suppressions.txt b/deps/v8/tools/sanitizers/tsan_suppressions.txt index 270340e4843d60..f9e3942039d824 100644 --- a/deps/v8/tools/sanitizers/tsan_suppressions.txt +++ b/deps/v8/tools/sanitizers/tsan_suppressions.txt @@ -4,3 +4,7 @@ # Incorrectly detected lock cycles in test-lockers # https://code.google.com/p/thread-sanitizer/issues/detail?id=81 deadlock:LockAndUnlockDifferentIsolatesThread::Run + +# A global safepoint might lock client isolate mutexes in any order, which +# would be reported as potential deadlocks. +deadlock:GlobalSafepoint::EnterGlobalSafepointScope diff --git a/deps/v8/tools/splaytree.mjs b/deps/v8/tools/splaytree.mjs index d942d1f463bcd9..ac25cf0668d69a 100644 --- a/deps/v8/tools/splaytree.mjs +++ b/deps/v8/tools/splaytree.mjs @@ -49,7 +49,7 @@ export class SplayTree { * @return {boolean} Whether the tree is empty. */ isEmpty() { - return !this.root_; + return this.root_ === null; } /** @@ -100,7 +100,7 @@ export class SplayTree { throw Error(`Key not found: ${key}`); } const removed = this.root_; - if (!this.root_.left) { + if (this.root_.left === null) { this.root_ = this.root_.right; } else { const { right } = this.root_; @@ -133,7 +133,7 @@ export class SplayTree { findMin() { if (this.isEmpty()) return null; let current = this.root_; - while (current.left) { + while (current.left !== null) { current = current.left; } return current; @@ -145,7 +145,7 @@ export class SplayTree { findMax(opt_startNode) { if (this.isEmpty()) return null; let current = opt_startNode || this.root_; - while (current.right) { + while (current.right !== null) { current = current.right; } return current; @@ -164,7 +164,7 @@ export class SplayTree { // the left subtree. if (this.root_.key <= key) { return this.root_; - } else if (this.root_.left) { + } else if (this.root_.left !== null) { return this.findMax(this.root_.left); } else { return null; @@ -186,7 +186,7 @@ export class SplayTree { */ exportValues() { const result = []; - this.traverse_(function(node) { result.push(node.value); }); + this.traverse_(function(node) { result.push(node.value) }); return result; } @@ -212,36 +212,28 @@ export class SplayTree { let current = this.root_; while (true) { if (key < current.key) { - if (!current.left) { - break; - } + if (current.left === null) break; if (key < current.left.key) { // Rotate right. const tmp = current.left; current.left = tmp.right; tmp.right = current; current = tmp; - if (!current.left) { - break; - } + if (current.left === null) break; } // Link right. right.left = current; right = current; current = current.left; } else if (key > current.key) { - if (!current.right) { - break; - } + if (current.right === null) break; if (key > current.right.key) { // Rotate left. const tmp = current.right; current.right = tmp.left; tmp.left = current; current = tmp; - if (!current.right) { - break; - } + if (current.right === null) break; } // Link left. left.right = current; @@ -269,9 +261,7 @@ export class SplayTree { const nodesToVisit = [this.root_]; while (nodesToVisit.length > 0) { const node = nodesToVisit.shift(); - if (node == null) { - continue; - } + if (node === null) continue; f(node); nodesToVisit.push(node.left); nodesToVisit.push(node.right); @@ -298,4 +288,4 @@ class SplayTreeNode { */ this.right = null; } -}; \ No newline at end of file +}; diff --git a/deps/v8/tools/system-analyzer/app-model.mjs b/deps/v8/tools/system-analyzer/app-model.mjs index 4e339cb0d5940e..5bc15fe6a62de5 100644 --- a/deps/v8/tools/system-analyzer/app-model.mjs +++ b/deps/v8/tools/system-analyzer/app-model.mjs @@ -17,7 +17,6 @@ class State { _mapTimeline; _deoptTimeline; _codeTimeline; - _apiTimeline; _tickTimeline; _timerTimeline; _minStartTime = Number.POSITIVE_INFINITY; @@ -42,13 +41,12 @@ class State { } setTimelines( - mapTimeline, icTimeline, deoptTimeline, codeTimeline, apiTimeline, - tickTimeline, timerTimeline) { + mapTimeline, icTimeline, deoptTimeline, codeTimeline, tickTimeline, + timerTimeline) { this._mapTimeline = mapTimeline; this._icTimeline = icTimeline; this._deoptTimeline = deoptTimeline; this._codeTimeline = codeTimeline; - this._apiTimeline = apiTimeline; this._tickTimeline = tickTimeline; this._timerTimeline = timerTimeline; for (let timeline of arguments) { @@ -78,10 +76,6 @@ class State { return this._codeTimeline; } - get apiTimeline() { - return this._apiTimeline; - } - get tickTimeline() { return this._tickTimeline; } @@ -93,8 +87,7 @@ class State { get timelines() { return [ this._mapTimeline, this._icTimeline, this._deoptTimeline, - this._codeTimeline, this._apiTimeline, this._tickTimeline, - this._timerTimeline + this._codeTimeline, this._tickTimeline, this._timerTimeline ]; } diff --git a/deps/v8/tools/system-analyzer/helper.mjs b/deps/v8/tools/system-analyzer/helper.mjs index ba6d0614f2242a..717faca5d50e26 100644 --- a/deps/v8/tools/system-analyzer/helper.mjs +++ b/deps/v8/tools/system-analyzer/helper.mjs @@ -2,63 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -export const KB = 1024; -export const MB = KB * KB; -export const GB = MB * KB; -export const kMicro2Milli = 1 / 1000; - -export function formatBytes(bytes) { - const units = ['B', 'KiB', 'MiB', 'GiB']; - const divisor = 1024; - let index = 0; - while (index < units.length && bytes >= divisor) { - index++; - bytes /= divisor; - } - return bytes.toFixed(2) + units[index]; -} - -export function formatMicroSeconds(micro) { - return (micro * kMicro2Milli).toFixed(1) + 'ms'; -} - -export function formatDurationMicros(micros, secondsDigits = 3) { - return formatDurationMillis(micros * kMicro2Milli, secondsDigits); -} - -export function formatDurationMillis(millis, secondsDigits = 3) { - if (millis < 1000) { - if (millis < 1) { - return (millis / kMicro2Milli).toFixed(1) + 'ns'; - } - return millis.toFixed(2) + 'ms'; - } - let seconds = millis / 1000; - const hours = Math.floor(seconds / 3600); - const minutes = Math.floor((seconds % 3600) / 60); - seconds = seconds % 60; - let buffer = '' - if (hours > 0) buffer += hours + 'h '; - if (hours > 0 || minutes > 0) buffer += minutes + 'm '; - buffer += seconds.toFixed(secondsDigits) + 's' - return buffer; -} - -export function delay(time) { - return new Promise(resolver => setTimeout(resolver, time)); -} - -export function defer() { - let resolve_func, reject_func; - const p = new Promise((resolve, reject) => { - resolve_func = resolve; - reject_func = resolve; - }); - p.resolve = resolve_func; - p.reject = reject_func; - return p; -} - export class Group { constructor(key, id, parentTotal, entries) { this.key = key; @@ -105,3 +48,35 @@ export function groupBy(array, keyFunction, collect = false) { // Sort by length return groups.sort((a, b) => b.length - a.length); } + +export function arrayEquals(left, right) { + if (left == right) return true; + if (left.length != right.length) return false; + for (let i = 0; i < left.length; i++) { + if (left[i] != right[i]) return false; + } + return true; +} + +export function entriesEquals(left, right) { + if (left == right) return true; + if (left == undefined) return right == undefined; + const leftEntries = Object.entries(left); + const rightEntries = Object.entries(right); + if (leftEntries.length !== rightEntries.length) return false; + for (let i = 0; i < leftEntries.length; i++) { + const l = leftEntries[i]; + const r = rightEntries[i]; + if (l[0] != r[0]) return false; + if (l[1] != r[1]) return false; + } + return true; +} + +export function keysEquals(left, right) { + if (left == right) return true; + if (left == undefined) return right == undefined; + return arrayEquals(Object.keys(left), Object.keys(right)); +} + +export * from '../js/helper.mjs' diff --git a/deps/v8/tools/system-analyzer/index.css b/deps/v8/tools/system-analyzer/index.css index 4525f0d9b40f12..4d2cda9035cd07 100644 --- a/deps/v8/tools/system-analyzer/index.css +++ b/deps/v8/tools/system-analyzer/index.css @@ -1,4 +1,5 @@ :root { + --code-font: Consolas, Monaco, Menlo, monospace; --background-color: #000000; --surface-color-rgb: 18, 18, 18; --surface-color: rgb(var(--surface-color-rgb)); @@ -13,6 +14,7 @@ --map-background-color: #5e5454; --timeline-background-color: #1f1f1f; --file-reader-background-color: #ffffff80; + --file-reader-border-color: #ffffff; --red: #dc6eae; --green: #aedc6e; --yellow: #eeff41; @@ -68,6 +70,10 @@ kbd { white-space: nowrap; } +kbd, code, pre { + font-family: var(--code-font); +} + a { color: var(--primary-color); text-decoration: none; @@ -173,9 +179,9 @@ button:hover { .colorbox { display: inline-block; - width: 10px; - height: 10px; - border: 1px var(--background-color) solid; + width: 8px; + height: 8px; + border: 2px var(--background-color) solid; border-radius: 50%; } @@ -227,6 +233,7 @@ button:hover, .mark:hover, .clickable:active, .mark:active { + border-radius: 3px; background-color: var(--primary-color); color: var(--on-primary-color); cursor: pointer; @@ -243,17 +250,16 @@ button:hover, padding: 0 10px 0 10px; } .legend dt { - font-family: monospace; + font-family: var(--code-font); } .legend h3 { margin-top: 10px; } - .panelCloserLabel { float: left; cursor: zoom-out; - margin: 0 10px 0 0; + margin: 0 5px 0 0; transition: transform 0.2s ease-out; user-select: none; } @@ -274,4 +280,4 @@ button:hover, } .panelCloserInput:checked ~ * { display: none; -} \ No newline at end of file +} diff --git a/deps/v8/tools/system-analyzer/index.html b/deps/v8/tools/system-analyzer/index.html index e85a59d6e69cce..f8b99f3ddac6f8 100644 --- a/deps/v8/tools/system-analyzer/index.html +++ b/deps/v8/tools/system-analyzer/index.html @@ -11,7 +11,7 @@ - + - - - - -

      - Chrome V8 profiling log processor -

      -

      -Process V8's profiling information log (sampling profiler tick information) -in your browser. Particularly useful if you don't have the V8 shell (d8) -at hand on your system. You still have to run Chrome with the appropriate - - command line flags -to produce the profiling log. -

      -

      Usage:

      -

      -Click on the button and browse to the profiling log file (usually, v8.log). -Process will start automatically and the output will be visible in the below -text area. -

      -

      Limitations and disclaimer:

      -

      -This page offers a subset of the functionalities of the command-line tick -processor utility in the V8 repository. In particular, this page cannot -access the command-line utility that provides library symbol information, -hence the [C++] section of the output stays empty. Also consider that this -web-based tool is provided only for convenience and quick reference, you -should refer to the - - command-line -version for full output. -

      -

      - -

      -

      - -

      -

      -Copyright the V8 Authors - Last change to this page: 12/12/2012 -

      - - - - diff --git a/deps/v8/tools/tickprocessor.mjs b/deps/v8/tools/tickprocessor.mjs index 1929c3069de64a..6afcea07978927 100644 --- a/deps/v8/tools/tickprocessor.mjs +++ b/deps/v8/tools/tickprocessor.mjs @@ -510,10 +510,9 @@ export class TickProcessor extends LogReader { onlySummary, runtimeTimerFilter, preprocessJson) { - super({}, - timedRange, - pairwiseTimedRange); - this.dispatchTable_ = { + super(timedRange, pairwiseTimedRange); + this.setDispatchTable({ + __proto__: null, 'shared-library': { parsers: [parseString, parseInt, parseInt, parseInt], processor: this.processSharedLibrary @@ -575,17 +574,17 @@ export class TickProcessor extends LogReader { processor: this.advanceDistortion }, // Ignored events. - 'profiler': null, - 'function-creation': null, - 'function-move': null, - 'function-delete': null, - 'heap-sample-item': null, - 'current-time': null, // Handled specially, not parsed. + 'profiler': undefined, + 'function-creation': undefined, + 'function-move': undefined, + 'function-delete': undefined, + 'heap-sample-item': undefined, + 'current-time': undefined, // Handled specially, not parsed. // Obsolete row types. - 'code-allocate': null, - 'begin-code-region': null, - 'end-code-region': null - }; + 'code-allocate': undefined, + 'begin-code-region': undefined, + 'end-code-region': undefined + }); this.preprocessJson = preprocessJson; this.cppEntriesProvider_ = cppEntriesProvider; diff --git a/deps/v8/tools/torque/format-torque.py b/deps/v8/tools/torque/format-torque.py index 638ca100fb9efb..30404a8151ced0 100755 --- a/deps/v8/tools/torque/format-torque.py +++ b/deps/v8/tools/torque/format-torque.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright 2014 the V8 project authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -7,14 +7,17 @@ """This program either generates the parser files for Torque, generating the source and header files directly in V8's src directory.""" -# for py2/py3 compatibility -from __future__ import print_function - import subprocess import sys import re from subprocess import Popen, PIPE +def decode(arg, encoding="utf-8"): + return arg.decode(encoding) + +def encode(arg, encoding="utf-8"): + return arg.encode(encoding) + kPercentEscape = r'α'; # Unicode alpha kDerefEscape = r'☆'; # Unicode star kAddressofEscape = r'⌂'; # Unicode house @@ -103,8 +106,8 @@ def process(filename, lint, should_format): p = Popen(['clang-format', '-assume-filename=.ts'], stdin=PIPE, stdout=PIPE, stderr=PIPE, shell=True) else: p = Popen(['clang-format', '-assume-filename=.ts'], stdin=PIPE, stdout=PIPE, stderr=PIPE) - output, err = p.communicate(preprocess(content)) - output = postprocess(output) + output, err = p.communicate(encode(preprocess(content))) + output = postprocess(decode(output)) rc = p.returncode if (rc != 0): print("error code " + str(rc) + " running clang-format. Exiting...") @@ -116,7 +119,7 @@ def process(filename, lint, should_format): if should_format: output_file = open(filename, 'wb') - output_file.write(output); + output_file.write(encode(output)) output_file.close() def print_usage(): diff --git a/deps/v8/tools/turbolizer/OWNERS b/deps/v8/tools/turbolizer/OWNERS index b7694bd267bcab..fc52961eff7c55 100644 --- a/deps/v8/tools/turbolizer/OWNERS +++ b/deps/v8/tools/turbolizer/OWNERS @@ -1,2 +1 @@ danno@chromium.org -sigurds@chromium.org diff --git a/deps/v8/tools/turbolizer/info-view.html b/deps/v8/tools/turbolizer/info-view.html index 534860d54adad9..aceb59569497c0 100644 --- a/deps/v8/tools/turbolizer/info-view.html +++ b/deps/v8/tools/turbolizer/info-view.html @@ -22,6 +22,14 @@
+ + + + + + + + diff --git a/deps/v8/tools/turbolizer/src/edge.ts b/deps/v8/tools/turbolizer/src/edge.ts index 30d265c5614da4..c94ef9112e69e8 100644 --- a/deps/v8/tools/turbolizer/src/edge.ts +++ b/deps/v8/tools/turbolizer/src/edge.ts @@ -2,10 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import { GNode, DEFAULT_NODE_BUBBLE_RADIUS } from "../src/node"; +import { GNode, MINIMUM_EDGE_SEPARATION, DEFAULT_NODE_BUBBLE_RADIUS } from "../src/node"; import { Graph } from "./graph"; -export const MINIMUM_EDGE_SEPARATION = 20; +const BEZIER_CONSTANT = 0.3; export class Edge { target: GNode; @@ -64,20 +64,31 @@ export class Edge { const outputApproach = source.getOutputApproach(showTypes); const horizontalPos = this.getInputHorizontalPosition(graph, showTypes); - let result = "M" + outputX + "," + outputY + - "L" + outputX + "," + outputApproach + - "L" + horizontalPos + "," + outputApproach; + let result: string; - if (horizontalPos != inputX) { - result += "L" + horizontalPos + "," + inputApproach; - } else { - if (inputApproach < outputApproach) { - inputApproach = outputApproach; + if (inputY < outputY) { + result = `M ${outputX} ${outputY} + L ${outputX} ${outputApproach} + L ${horizontalPos} ${outputApproach}`; + + if (horizontalPos != inputX) { + result += `L ${horizontalPos} ${inputApproach}`; + } else { + if (inputApproach < outputApproach) { + inputApproach = outputApproach; + } } + + result += `L ${inputX} ${inputApproach} + L ${inputX} ${inputY}`; + } else { + const controlY = outputY + (inputY - outputY) * BEZIER_CONSTANT; + result = `M ${outputX} ${outputY} + C ${outputX} ${controlY}, + ${inputX} ${outputY}, + ${inputX} ${inputY}`; } - result += "L" + inputX + "," + inputApproach + - "L" + inputX + "," + inputY; return result; } diff --git a/deps/v8/tools/turbolizer/src/graph-layout.ts b/deps/v8/tools/turbolizer/src/graph-layout.ts index 3687c28c86a8bf..ad32557e905062 100644 --- a/deps/v8/tools/turbolizer/src/graph-layout.ts +++ b/deps/v8/tools/turbolizer/src/graph-layout.ts @@ -3,11 +3,11 @@ // found in the LICENSE file. import { MAX_RANK_SENTINEL } from "../src/constants"; -import { MINIMUM_EDGE_SEPARATION, Edge } from "../src/edge"; -import { NODE_INPUT_WIDTH, MINIMUM_NODE_OUTPUT_APPROACH, DEFAULT_NODE_BUBBLE_RADIUS, GNode } from "../src/node"; +import { Edge } from "../src/edge"; +import { GNode, MINIMUM_EDGE_SEPARATION, NODE_INPUT_WIDTH, MINIMUM_NODE_OUTPUT_APPROACH, DEFAULT_NODE_BUBBLE_RADIUS } from "../src/node"; import { Graph } from "./graph"; -const DEFAULT_NODE_ROW_SEPARATION = 130; +const DEFAULT_NODE_ROW_SEPARATION = 150; const traceLayout = false; function newGraphOccupation(graph: Graph) { diff --git a/deps/v8/tools/turbolizer/src/graph-view.ts b/deps/v8/tools/turbolizer/src/graph-view.ts index 3cb5e6fbc22259..798a5cd340eb6f 100644 --- a/deps/v8/tools/turbolizer/src/graph-view.ts +++ b/deps/v8/tools/turbolizer/src/graph-view.ts @@ -19,6 +19,13 @@ function nodeToStringKey(n: GNode) { return "" + n.id; } +function nodeOriginToStringKey(n: GNode): string | undefined { + if (n.nodeLabel && n.nodeLabel.origin) { + return "" + n.nodeLabel.origin.nodeId; + } + return undefined; +} + interface GraphState { showTypes: boolean; selection: MySelection; @@ -132,7 +139,7 @@ export class GraphView extends PhaseView { } }; - view.state.selection = new MySelection(nodeToStringKey); + view.state.selection = new MySelection(nodeToStringKey, nodeOriginToStringKey); const defs = svg.append('svg:defs'); defs.append('svg:marker') @@ -254,12 +261,14 @@ export class GraphView extends PhaseView { this.toolbox.appendChild(createImgInput("toggle-types", "toggle types", partial(this.toggleTypesAction, this))); + const adaptedSelection = this.adaptSelectionToCurrentPhase(data.data, rememberedSelection); + this.phaseName = data.name; - this.createGraph(data.data, rememberedSelection); + this.createGraph(data.data, adaptedSelection); this.broker.addNodeHandler(this.selectionHandler); - if (rememberedSelection != null && rememberedSelection.size > 0) { - this.attachSelection(rememberedSelection); + if (adaptedSelection != null && adaptedSelection.size > 0) { + this.attachSelection(adaptedSelection); this.connectVisibleSelectedNodes(); this.viewSelection(); } else { @@ -286,14 +295,14 @@ export class GraphView extends PhaseView { this.deleteContent(); } - createGraph(data, rememberedSelection) { + createGraph(data, selection) { this.graph = new Graph(data); this.showControlAction(this); - if (rememberedSelection != undefined) { + if (selection != undefined) { for (const n of this.graph.nodes()) { - n.visible = n.visible || rememberedSelection.has(nodeToStringKey(n)); + n.visible = n.visible || selection.has(nodeToStringKey(n)); } } @@ -359,6 +368,33 @@ export class GraphView extends PhaseView { }); } + adaptSelectionToCurrentPhase(data, selection) { + const updatedGraphSelection = new Set(); + if (!data || !(selection instanceof Map)) return updatedGraphSelection; + // Adding survived nodes (with the same id) + for (const node of data.nodes) { + const stringKey = this.state.selection.stringKey(node); + if (selection.has(stringKey)) { + updatedGraphSelection.add(stringKey); + } + } + // Adding children of nodes + for (const node of data.nodes) { + const originStringKey = this.state.selection.originStringKey(node); + if (originStringKey && selection.has(originStringKey)) { + updatedGraphSelection.add(this.state.selection.stringKey(node)); + } + } + // Adding ancestors of nodes + selection.forEach(selectedNode => { + const originStringKey = this.state.selection.originStringKey(selectedNode); + if (originStringKey) { + updatedGraphSelection.add(originStringKey); + } + }); + return updatedGraphSelection; + } + attachSelection(s) { if (!(s instanceof Set)) return; this.selectionHandler.clear(); diff --git a/deps/v8/tools/turbolizer/src/graph.ts b/deps/v8/tools/turbolizer/src/graph.ts index 0eb2e3e1e698a7..8eb0d26d20d04e 100644 --- a/deps/v8/tools/turbolizer/src/graph.ts +++ b/deps/v8/tools/turbolizer/src/graph.ts @@ -1,5 +1,5 @@ -import { GNode } from "./node"; -import { Edge, MINIMUM_EDGE_SEPARATION } from "./edge"; +import { GNode, MINIMUM_EDGE_SEPARATION } from "./node"; +import { Edge } from "./edge"; export class Graph { nodeMap: Array; diff --git a/deps/v8/tools/turbolizer/src/graphmultiview.ts b/deps/v8/tools/turbolizer/src/graphmultiview.ts index 4f8f6339199543..19113eef2bd1ba 100644 --- a/deps/v8/tools/turbolizer/src/graphmultiview.ts +++ b/deps/v8/tools/turbolizer/src/graphmultiview.ts @@ -8,6 +8,7 @@ import { SequenceView } from "../src/sequence-view"; import { SourceResolver } from "../src/source-resolver"; import { SelectionBroker } from "../src/selection-broker"; import { View, PhaseView } from "../src/view"; +import { GNode } from "./node"; const multiviewID = "multiview"; @@ -61,6 +62,10 @@ export class GraphMultiView extends View { view.divNode.addEventListener("keyup", (e: KeyboardEvent) => { if (e.keyCode == 191) { // keyCode == '/' searchInput.focus(); + } else if (e.keyCode == 78) { // keyCode == 'n' + view.displayNextGraphPhase(); + } else if (e.keyCode == 66) { // keyCode == 'b' + view.displayPreviousGraphPhase(); } }); searchInput.setAttribute("value", window.sessionStorage.getItem("lastSearch") || ""); @@ -101,7 +106,7 @@ export class GraphMultiView extends View { this.displayPhase(this.sourceResolver.getPhase(initialPhaseIndex)); } - displayPhase(phase, selection?: Set) { + displayPhase(phase, selection?: Map) { if (phase.type == "graph") { this.displayPhaseView(this.graph, phase, selection); } else if (phase.type == "schedule") { @@ -111,18 +116,46 @@ export class GraphMultiView extends View { } } - displayPhaseView(view: PhaseView, data, selection?: Set) { + displayPhaseView(view: PhaseView, data, selection?: Map) { const rememberedSelection = selection ? selection : this.hideCurrentPhase(); view.initializeContent(data, rememberedSelection); this.currentPhaseView = view; } - displayPhaseByName(phaseName, selection?: Set) { + displayPhaseByName(phaseName, selection?: Map) { const phaseId = this.sourceResolver.getPhaseIdByName(phaseName); this.selectMenu.selectedIndex = phaseId; this.displayPhase(this.sourceResolver.getPhase(phaseId), selection); } + displayNextGraphPhase() { + let nextPhaseIndex = this.selectMenu.selectedIndex + 1; + while (nextPhaseIndex < this.sourceResolver.phases.length) { + const nextPhase = this.sourceResolver.getPhase(nextPhaseIndex); + if (nextPhase.type == "graph") { + this.selectMenu.selectedIndex = nextPhaseIndex; + window.sessionStorage.setItem("lastSelectedPhase", nextPhaseIndex.toString()); + this.displayPhase(nextPhase); + break; + } + nextPhaseIndex += 1; + } + } + + displayPreviousGraphPhase() { + let previousPhaseIndex = this.selectMenu.selectedIndex - 1; + while (previousPhaseIndex >= 0) { + const previousPhase = this.sourceResolver.getPhase(previousPhaseIndex); + if (previousPhase.type == "graph") { + this.selectMenu.selectedIndex = previousPhaseIndex; + window.sessionStorage.setItem("lastSelectedPhase", previousPhaseIndex.toString()); + this.displayPhase(previousPhase); + break; + } + previousPhaseIndex -= 1; + } + } + hideCurrentPhase() { let rememberedSelection = null; if (this.currentPhaseView != null) { diff --git a/deps/v8/tools/turbolizer/src/node.ts b/deps/v8/tools/turbolizer/src/node.ts index 02906d1204850c..90db8adb1ad6a4 100644 --- a/deps/v8/tools/turbolizer/src/node.ts +++ b/deps/v8/tools/turbolizer/src/node.ts @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import { MINIMUM_EDGE_SEPARATION, Edge } from "../src/edge"; +import { Edge } from "../src/edge"; import { NodeLabel } from "./node-label"; import { MAX_RANK_SENTINEL } from "./constants"; import { alignUp, measureText } from "./util"; @@ -10,6 +10,7 @@ import { alignUp, measureText } from "./util"; export const DEFAULT_NODE_BUBBLE_RADIUS = 12; export const NODE_INPUT_WIDTH = 50; export const MINIMUM_NODE_OUTPUT_APPROACH = 15; +export const MINIMUM_EDGE_SEPARATION = 20; const MINIMUM_NODE_INPUT_APPROACH = 15 + 2 * DEFAULT_NODE_BUBBLE_RADIUS; export class GNode { diff --git a/deps/v8/tools/turbolizer/src/selection.ts b/deps/v8/tools/turbolizer/src/selection.ts index 044a1969c31e56..a3f3dd941fbab1 100644 --- a/deps/v8/tools/turbolizer/src/selection.ts +++ b/deps/v8/tools/turbolizer/src/selection.ts @@ -2,13 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import { GNode } from "./node"; + export class MySelection { selection: any; stringKey: (o: any) => string; + originStringKey: (node: GNode) => string; - constructor(stringKeyFnc) { + constructor(stringKeyFnc, originStringKeyFnc?) { this.selection = new Map(); this.stringKey = stringKeyFnc; + this.originStringKey = originStringKeyFnc; } isEmpty(): boolean { @@ -50,7 +54,7 @@ export class MySelection { } detachSelection() { - const result = this.selectedKeys(); + const result = this.selection; this.clear(); return result; } diff --git a/deps/v8/tools/turbolizer/src/source-resolver.ts b/deps/v8/tools/turbolizer/src/source-resolver.ts index 4d5bd250b75681..4632ad306aafc4 100644 --- a/deps/v8/tools/turbolizer/src/source-resolver.ts +++ b/deps/v8/tools/turbolizer/src/source-resolver.ts @@ -81,7 +81,7 @@ interface InstructionsPhase { name: string; data: any; instructionOffsetToPCOffset?: any; - blockIdtoInstructionRange?: any; + blockIdToInstructionRange?: any; nodeIdToInstructionRange?: any; codeOffsetsInfo?: CodeOffsetsInfo; } @@ -595,8 +595,8 @@ export class SourceResolver { if (phase.nodeIdToInstructionRange) { this.readNodeIdToInstructionRange(phase.nodeIdToInstructionRange); } - if (phase.blockIdtoInstructionRange) { - this.readBlockIdToInstructionRange(phase.blockIdtoInstructionRange); + if (phase.blockIdToInstructionRange) { + this.readBlockIdToInstructionRange(phase.blockIdToInstructionRange); } if (phase.instructionOffsetToPCOffset) { this.readInstructionOffsetToPCOffset(phase.instructionOffsetToPCOffset); diff --git a/deps/v8/tools/turbolizer/src/text-view.ts b/deps/v8/tools/turbolizer/src/text-view.ts index dcda2db2de9dc4..fbf43f03d977a2 100644 --- a/deps/v8/tools/turbolizer/src/text-view.ts +++ b/deps/v8/tools/turbolizer/src/text-view.ts @@ -20,7 +20,7 @@ export abstract class TextView extends PhaseView { instructionIdToHtmlElementsMap: Map>; nodeIdToHtmlElementsMap: Map>; blockIdToHtmlElementsMap: Map>; - blockIdtoNodeIds: Map>; + blockIdToNodeIds: Map>; nodeIdToBlockId: Array; patterns: any; sourceResolver: SourceResolver; @@ -34,7 +34,7 @@ export abstract class TextView extends PhaseView { view.instructionIdToHtmlElementsMap = new Map(); view.nodeIdToHtmlElementsMap = new Map(); view.blockIdToHtmlElementsMap = new Map(); - view.blockIdtoNodeIds = new Map(); + view.blockIdToNodeIds = new Map(); view.nodeIdToBlockId = []; view.selection = new MySelection(anyToString); view.blockSelection = new MySelection(anyToString); @@ -147,10 +147,10 @@ export abstract class TextView extends PhaseView { addNodeIdToBlockId(anyNodeId, anyBlockId) { const blockId = anyToString(anyBlockId); - if (!this.blockIdtoNodeIds.has(blockId)) { - this.blockIdtoNodeIds.set(blockId, []); + if (!this.blockIdToNodeIds.has(blockId)) { + this.blockIdToNodeIds.set(blockId, []); } - this.blockIdtoNodeIds.get(blockId).push(anyToString(anyNodeId)); + this.blockIdToNodeIds.get(blockId).push(anyToString(anyNodeId)); this.nodeIdToBlockId[anyNodeId] = blockId; } diff --git a/deps/v8/tools/turbolizer/src/view.ts b/deps/v8/tools/turbolizer/src/view.ts index d93eeeda8f6522..cd76c6de441cd0 100644 --- a/deps/v8/tools/turbolizer/src/view.ts +++ b/deps/v8/tools/turbolizer/src/view.ts @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import { GNode } from "./node"; + export abstract class View { protected container: HTMLElement; protected divNode: HTMLElement; @@ -22,8 +24,8 @@ export abstract class View { } export abstract class PhaseView extends View { - public abstract initializeContent(data: any, rememberedSelection: Set): void; - public abstract detachSelection(): Set; + public abstract initializeContent(data: any, rememberedSelection: Map): void; + public abstract detachSelection(): Map; public abstract onresize(): void; public abstract searchInputAction(searchInput: HTMLInputElement, e: Event, onlyVisible: boolean): void; diff --git a/deps/v8/tools/unittests/__init__.py b/deps/v8/tools/unittests/__init__.py index 3841a861c8396a..e1bbf0cdfc21f9 100644 --- a/deps/v8/tools/unittests/__init__.py +++ b/deps/v8/tools/unittests/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright 2018 the V8 project authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. diff --git a/deps/v8/tools/unittests/compare_torque_output_test.py b/deps/v8/tools/unittests/compare_torque_output_test.py index a6086d96c9a8d7..f5f240cdaf3eae 100644 --- a/deps/v8/tools/unittests/compare_torque_output_test.py +++ b/deps/v8/tools/unittests/compare_torque_output_test.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright 2020 the V8 project authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -24,7 +24,7 @@ def _compare_from(self, test_folder): file1 = os.path.join(TEST_DATA, test_folder, 'f1') file2 = os.path.join(TEST_DATA, test_folder, 'f2') proc = subprocess.Popen([ - 'python', '-u', + sys.executable, '-u', COMPARE_SCRIPT, file1, file2, self.tmp_file ], stdout=subprocess.PIPE, stderr=subprocess.PIPE) _, err = proc.communicate() @@ -34,7 +34,7 @@ def test_content_diff(self): exitcode, output = self._compare_from('test1') self.assertEqual(1, exitcode) full_match = r'^Found.*-line 2\+line 2 with diff.*\+line 3\n\n$' - self.assertRegexpMatches(output, re.compile(full_match, re.M | re.S)) + self.assertRegex(output.decode('utf-8'), re.compile(full_match, re.M | re.S)) def test_no_diff(self): exitcode, output = self._compare_from('test2') @@ -44,12 +44,12 @@ def test_no_diff(self): def test_right_only(self): exitcode, output = self._compare_from('test3') self.assertEqual(1, exitcode) - self.assertRegexpMatches(output, r'Some files exist only in.*f2\nfile3') + self.assertRegex(output.decode('utf-8'), r'Some files exist only in.*f2\nfile3') def test_left_only(self): exitcode, output = self._compare_from('test4') self.assertEqual(1, exitcode) - self.assertRegexpMatches(output, r'Some files exist only in.*f1\nfile4') + self.assertRegex(output.decode('utf-8'), r'Some files exist only in.*f1\nfile4') def tearDown(self): os.unlink(self.tmp_file) diff --git a/deps/v8/tools/unittests/predictable_wrapper_test.py b/deps/v8/tools/unittests/predictable_wrapper_test.py index c085fb8879f954..2f806e7c111797 100755 --- a/deps/v8/tools/unittests/predictable_wrapper_test.py +++ b/deps/v8/tools/unittests/predictable_wrapper_test.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright 2017 the V8 project authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. diff --git a/deps/v8/tools/unittests/run_perf_test.py b/deps/v8/tools/unittests/run_perf_test.py index 28f71b2b339115..18f91d7bda2ad9 100755 --- a/deps/v8/tools/unittests/run_perf_test.py +++ b/deps/v8/tools/unittests/run_perf_test.py @@ -1,11 +1,8 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright 2014 the V8 project authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -# for py2/py3 compatibility -from __future__ import print_function - from collections import namedtuple import json import os @@ -28,6 +25,8 @@ TEST_WORKSPACE = os.path.join(tempfile.gettempdir(), 'test-v8-run-perf') +SORT_KEY = lambda x: x['graphs'] + V8_JSON = { 'path': ['.'], 'owners': ['username@chromium.org'], @@ -101,8 +100,8 @@ def setUpClass(cls): def testBuildDirectory(self): base_path = os.path.join(TEST_DATA, 'builddirs', 'dir1', 'out') expected_path = os.path.join(base_path, 'build') - self.assertEquals( - expected_path, run_perf.find_build_directory(base_path, 'x64')) + self.assertEqual(expected_path, + run_perf.find_build_directory(base_path, 'x64')) class PerfTest(unittest.TestCase): @@ -196,8 +195,8 @@ def _VerifyResults(self, suite, units, traces, file_name=None): {'units': units, 'graphs': [suite, trace['name']], 'results': trace['results'], - 'stddev': trace['stddev']} for trace in traces]), - sorted(self._LoadResults(file_name)['traces'])) + 'stddev': trace['stddev']} for trace in traces], key=SORT_KEY), + sorted(self._LoadResults(file_name)['traces'], key=SORT_KEY)) def _VerifyRunnableDurations(self, runs, timeout, file_name=None): self.assertListEqual([ @@ -368,7 +367,7 @@ def testNestedSuite(self): 'graphs': ['test', 'DeltaBlue'], 'results': [200.0], 'stddev': ''}, - ]), sorted(self._LoadResults()['traces'])) + ], key=SORT_KEY), sorted(self._LoadResults()['traces'], key=SORT_KEY)) self._VerifyErrors([]) self._VerifyMockMultiple( (os.path.join('out', 'x64.release', 'd7'), '--flag', 'run.js'), @@ -381,7 +380,7 @@ def testNestedSuite(self): def testOneRunStdDevRegExp(self): test_input = dict(V8_JSON) - test_input['stddev_regexp'] = '^%s\-stddev: (.+)$' + test_input['stddev_regexp'] = r'^%s-stddev: (.+)$' self._WriteTestInput(test_input) self._MockCommand(['.'], ['Richards: 1.234\nRichards-stddev: 0.23\n' 'DeltaBlue: 10657567\nDeltaBlue-stddev: 106\n']) @@ -396,7 +395,7 @@ def testOneRunStdDevRegExp(self): def testTwoRunsStdDevRegExp(self): test_input = dict(V8_JSON) - test_input['stddev_regexp'] = '^%s\-stddev: (.+)$' + test_input['stddev_regexp'] = r'^%s-stddev: (.+)$' test_input['run_count'] = 2 self._WriteTestInput(test_input) self._MockCommand(['.'], ['Richards: 3\nRichards-stddev: 0.7\n' @@ -408,13 +407,14 @@ def testTwoRunsStdDevRegExp(self): {'name': 'Richards', 'results': [2.0, 3.0], 'stddev': '0.7'}, {'name': 'DeltaBlue', 'results': [5.0, 6.0], 'stddev': '0.8'}, ]) - self._VerifyErrors( - ['Test test/Richards should only run once since a stddev is provided ' - 'by the test.', - 'Test test/DeltaBlue should only run once since a stddev is provided ' - 'by the test.', - 'Regexp "^DeltaBlue\-stddev: (.+)$" did not match for test ' - 'test/DeltaBlue.']) + self._VerifyErrors([ + 'Test test/Richards should only run once since a stddev is provided ' + 'by the test.', + 'Test test/DeltaBlue should only run once since a stddev is provided ' + 'by the test.', + r'Regexp "^DeltaBlue-stddev: (.+)$" did not match for test ' + r'test/DeltaBlue.' + ]) self._VerifyMock( os.path.join('out', 'x64.release', 'd7'), '--flag', 'run.js') @@ -605,7 +605,7 @@ def testNormal(self): 'results': [2.1, 2.1], 'stddev': '', }, - ]), sorted(results['traces'])) + ], key=SORT_KEY), sorted(results['traces'], key=SORT_KEY)) def testResultsProcessor(self): results = self._RunPerf('d8_mocked2.py', 'test2.json') diff --git a/deps/v8/tools/unittests/run_tests_test.py b/deps/v8/tools/unittests/run_tests_test.py index 89acacaaa36e88..14daae6865a052 100755 --- a/deps/v8/tools/unittests/run_tests_test.py +++ b/deps/v8/tools/unittests/run_tests_test.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright 2017 the V8 project authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -17,9 +17,6 @@ # TODO(machenbach): Coverage data from multiprocessing doesn't work. # TODO(majeski): Add some tests for the fuzzers. -# for py2/py3 compatibility -from __future__ import print_function - import collections import contextlib import json @@ -30,7 +27,7 @@ import tempfile import unittest -from cStringIO import StringIO +from io import StringIO TOOLS_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) TEST_DATA_ROOT = os.path.join(TOOLS_ROOT, 'unittests', 'testdata') @@ -277,7 +274,9 @@ def replace_variable_data(data): # We need lexicographic sorting here to avoid non-deterministic behaviour # The original sorting key is duration, but in our fake test we have # non-deterministic durations before we reset them to 1 - json_output['slowest_tests'].sort(key= lambda x: str(x)) + def sort_key(x): + return str(sorted(x.items())) + json_output['slowest_tests'].sort(key=sort_key) with open(os.path.join(TEST_DATA_ROOT, expected_results_name)) as f: expected_test_results = json.load(f) @@ -351,7 +350,8 @@ def testAutoDetect(self): v8_enable_verify_csa=False, v8_enable_lite_mode=False, v8_enable_pointer_compression=False, v8_enable_pointer_compression_shared_cage=False, - v8_enable_virtual_memory_cage=False) + v8_enable_shared_ro_heap=False, + v8_enable_sandbox=False) result = run_tests( basedir, '--progress=verbose', diff --git a/deps/v8/tools/unittests/testdata/predictable_mocked.py b/deps/v8/tools/unittests/testdata/predictable_mocked.py index b9e73f64542766..cd1a54d54e1408 100644 --- a/deps/v8/tools/unittests/testdata/predictable_mocked.py +++ b/deps/v8/tools/unittests/testdata/predictable_mocked.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright 2017 the V8 project authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. diff --git a/deps/v8/tools/unittests/testdata/testroot1/v8_build_config.json b/deps/v8/tools/unittests/testdata/testroot1/v8_build_config.json index d5d0f9981d6571..a1cff614429147 100644 --- a/deps/v8/tools/unittests/testdata/testroot1/v8_build_config.json +++ b/deps/v8/tools/unittests/testdata/testroot1/v8_build_config.json @@ -22,7 +22,8 @@ "v8_enable_lite_mode": false, "v8_enable_pointer_compression": true, "v8_enable_pointer_compression_shared_cage": true, - "v8_enable_virtual_memory_cage": false, + "v8_enable_sandbox": false, + "v8_enable_shared_ro_heap": true, "v8_control_flow_integrity": false, "v8_enable_single_generation": false, "v8_enable_third_party_heap": false, diff --git a/deps/v8/tools/unittests/testdata/testroot2/v8_build_config.json b/deps/v8/tools/unittests/testdata/testroot2/v8_build_config.json index 590af4d59ad11a..049078cb703ee9 100644 --- a/deps/v8/tools/unittests/testdata/testroot2/v8_build_config.json +++ b/deps/v8/tools/unittests/testdata/testroot2/v8_build_config.json @@ -22,7 +22,8 @@ "v8_enable_lite_mode": false, "v8_enable_pointer_compression": false, "v8_enable_pointer_compression_shared_cage": false, - "v8_enable_virtual_memory_cage": false, + "v8_enable_sandbox": false, + "v8_enable_shared_ro_heap": false, "v8_control_flow_integrity": false, "v8_enable_single_generation": false, "v8_enable_third_party_heap": false, diff --git a/deps/v8/tools/unittests/testdata/testroot3/v8_build_config.json b/deps/v8/tools/unittests/testdata/testroot3/v8_build_config.json index d5d0f9981d6571..a1cff614429147 100644 --- a/deps/v8/tools/unittests/testdata/testroot3/v8_build_config.json +++ b/deps/v8/tools/unittests/testdata/testroot3/v8_build_config.json @@ -22,7 +22,8 @@ "v8_enable_lite_mode": false, "v8_enable_pointer_compression": true, "v8_enable_pointer_compression_shared_cage": true, - "v8_enable_virtual_memory_cage": false, + "v8_enable_sandbox": false, + "v8_enable_shared_ro_heap": true, "v8_control_flow_integrity": false, "v8_enable_single_generation": false, "v8_enable_third_party_heap": false, diff --git a/deps/v8/tools/unittests/v8_presubmit_test.py b/deps/v8/tools/unittests/v8_presubmit_test.py index 2c66d1891b8ff8..7b784b2d8cf502 100755 --- a/deps/v8/tools/unittests/v8_presubmit_test.py +++ b/deps/v8/tools/unittests/v8_presubmit_test.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright 2018 the V8 project authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. diff --git a/deps/v8/tools/v8_presubmit.py b/deps/v8/tools/v8_presubmit.py index f4212794513667..ac6d06f6c11add 100755 --- a/deps/v8/tools/v8_presubmit.py +++ b/deps/v8/tools/v8_presubmit.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Copyright 2012 the V8 project authors. All rights reserved. # Redistribution and use in source and binary forms, with or without @@ -27,34 +27,28 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -# for py2/py3 compatibility -from __future__ import absolute_import -from __future__ import print_function - -try: - import hashlib - md5er = hashlib.md5 -except ImportError as e: - import md5 - md5er = md5.new +import hashlib +md5er = hashlib.md5 import json +import multiprocessing import optparse import os from os.path import abspath, join, dirname, basename, exists import pickle import re -import sys import subprocess -import multiprocessing from subprocess import PIPE +import sys from testrunner.local import statusfile from testrunner.local import testsuite from testrunner.local import utils +def decode(arg, encoding="utf-8"): + return arg.decode(encoding) + # Special LINT rules diverging from default and reason. # build/header_guard: Our guards have the form "V8_FOO_H_", not "SRC_FOO_H_". # We now run our own header guard check in PRESUBMIT.py. @@ -76,7 +70,7 @@ -whitespace/comments """.split() -LINT_OUTPUT_PATTERN = re.compile(r'^.+[:(]\d+[:)]|^Done processing') +LINT_OUTPUT_PATTERN = re.compile(r'^.+[:(]\d+[:)]') FLAGS_LINE = re.compile("//\s*Flags:.*--([A-z0-9-])+_[A-z0-9].*\n") ASSERT_OPTIMIZED_PATTERN = re.compile("assertOptimized") FLAGS_ENABLE_OPT = re.compile("//\s*Flags:.*--opt[^-].*\n") @@ -84,6 +78,9 @@ FLAGS_NO_ALWAYS_OPT = re.compile("//\s*Flags:.*--no-?always-opt.*\n") TOOLS_PATH = dirname(abspath(__file__)) +DEPS_DEPOT_TOOLS_PATH = abspath( + join(TOOLS_PATH, '..', 'third_party', 'depot_tools')) + def CppLintWorker(command): try: @@ -92,23 +89,27 @@ def CppLintWorker(command): out_lines = "" error_count = -1 while True: - out_line = process.stderr.readline() + out_line = decode(process.stderr.readline()) if out_line == '' and process.poll() != None: if error_count == -1: print("Failed to process %s" % command.pop()) return 1 break - m = LINT_OUTPUT_PATTERN.match(out_line) - if m: - out_lines += out_line + if out_line.strip() == 'Total errors found: 0': + out_lines += "Done processing %s\n" % command.pop() error_count += 1 + else: + m = LINT_OUTPUT_PATTERN.match(out_line) + if m: + out_lines += out_line + error_count += 1 sys.stdout.write(out_lines) return error_count except KeyboardInterrupt: process.kill() except: print('Error running cpplint.py. Please make sure you have depot_tools' + - ' in your $PATH. Lint check skipped.') + ' in your third_party directory. Lint check skipped.') process.kill() def TorqueLintWorker(command): @@ -118,14 +119,14 @@ def TorqueLintWorker(command): out_lines = "" error_count = 0 while True: - out_line = process.stderr.readline() + out_line = decode(process.stderr.readline()) if out_line == '' and process.poll() != None: break out_lines += out_line error_count += 1 sys.stdout.write(out_lines) if error_count != 0: - sys.stdout.write( + sys.stdout.write( "warning: formatting and overwriting unformatted Torque files\n") return error_count except KeyboardInterrupt: @@ -148,15 +149,16 @@ def format_file(command): sys.stdout.write("error code " + str(rc) + " running clang-format.\n") return rc - if output != contents: + if decode(output) != contents: return 1 return 0 except KeyboardInterrupt: process.kill() except Exception: - print('Error running clang-format. Please make sure you have depot_tools' + - ' in your $PATH. Lint check skipped.') + print( + 'Error running clang-format. Please make sure you have depot_tools' + + ' in your third_party directory. Lint check skipped.') process.kill() rc = format_file(command) @@ -177,7 +179,7 @@ def Load(self): try: sums_file = None try: - sums_file = open(self.sums_file_name, 'r') + sums_file = open(self.sums_file_name, 'rb') self.sums = pickle.load(sums_file) except: # Cannot parse pickle for any reason. Not much we can do about it. @@ -188,7 +190,7 @@ def Load(self): def Save(self): try: - sums_file = open(self.sums_file_name, 'w') + sums_file = open(self.sums_file_name, 'wb') pickle.dump(self.sums, sums_file) except: # Failed to write pickle. Try to clean-up behind us. @@ -205,7 +207,7 @@ def FilterUnchangedFiles(self, files): changed_or_new = [] for file in files: try: - handle = open(file, "r") + handle = open(file, "rb") file_sum = md5er(handle.read()).digest() if not file in self.sums or self.sums[file] != file_sum: changed_or_new.append(file) @@ -389,13 +391,9 @@ def GetProcessorWorker(self): def GetProcessorScript(self): filters = ','.join([n for n in LINT_RULES]) arguments = ['--filter', filters] - for path in [TOOLS_PATH] + os.environ["PATH"].split(os.pathsep): - path = path.strip('"') - cpplint = os.path.join(path, 'cpplint.py') - if os.path.isfile(cpplint): - return cpplint, arguments - return None, arguments + cpplint = os.path.join(DEPS_DEPOT_TOOLS_PATH, 'cpplint.py') + return cpplint, arguments class TorqueLintProcessor(CacheableSourceFileProcessor): @@ -441,19 +439,15 @@ def IsRelevant(self, name): return name.endswith('.js') or name.endswith('.mjs') def GetPathsToSearch(self): - return ['tools/system-analyzer'] + return ['tools/system-analyzer', 'tools/heap-layout', 'tools/js'] def GetProcessorWorker(self): return JSLintWorker def GetProcessorScript(self): - for path in [TOOLS_PATH] + os.environ["PATH"].split(os.pathsep): - path = path.strip('"') - clang_format = os.path.join(path, 'clang_format.py') - if os.path.isfile(clang_format): - return clang_format, [] + jslint = os.path.join(DEPS_DEPOT_TOOLS_PATH, 'clang_format.py') + return jslint, [] - return None, [] COPYRIGHT_HEADER_PATTERN = re.compile( r'Copyright [\d-]*20[0-2][0-9] the V8 project authors. All rights reserved.') @@ -490,7 +484,7 @@ def FindFilesIn(self, path): output = subprocess.Popen('git ls-files --full-name', stdout=PIPE, cwd=path, shell=True) result = [] - for file in output.stdout.read().split(): + for file in decode(output.stdout.read()).split(): for dir_part in os.path.dirname(file).replace(os.sep, '/').split('/'): if self.IgnoreDir(dir_part): break @@ -623,8 +617,8 @@ def ProcessFiles(self, files): violations = 0 for file in files: try: - handle = open(file) - contents = handle.read() + handle = open(file, "rb") + contents = decode(handle.read(), "ISO-8859-1") if len(contents) > 0 and not self.ProcessContents(file, contents): success = False violations += 1 @@ -730,15 +724,32 @@ def CheckDeps(workspace): return subprocess.call([sys.executable, checkdeps_py, workspace]) == 0 +def FindTests(workspace): + scripts = [] + # TODO(almuthanna): unskip valid tests when they are properly migrated + exclude = [ + 'tools/clang', + 'tools/mb/mb_test.py', + 'tools/cppgc/gen_cmake_test.py', + 'tools/ignition/linux_perf_report_test.py', + 'tools/ignition/bytecode_dispatches_report_test.py', + 'tools/ignition/linux_perf_bytecode_annotate_test.py', + ] + scripts_without_excluded = [] + for root, dirs, files in os.walk(join(workspace, 'tools')): + for f in files: + if f.endswith('_test.py'): + fullpath = os.path.join(root, f) + scripts.append(fullpath) + for script in scripts: + if not any(exc_dir in script for exc_dir in exclude): + scripts_without_excluded.append(script) + return scripts_without_excluded + + def PyTests(workspace): result = True - for script in [ - join(workspace, 'tools', 'clusterfuzz', 'v8_foozzie_test.py'), - join(workspace, 'tools', 'release', 'test_scripts.py'), - join(workspace, 'tools', 'unittests', 'run_tests_test.py'), - join(workspace, 'tools', 'unittests', 'run_perf_test.py'), - join(workspace, 'tools', 'testrunner', 'testproc', 'variant_unittest.py'), - ]: + for script in FindTests(workspace): print('Running ' + script) result &= subprocess.call( [sys.executable, script], stdout=subprocess.PIPE) == 0 diff --git a/deps/v8/tools/v8heapconst.py b/deps/v8/tools/v8heapconst.py index 306eeb7aa279d3..66466b7e820e02 100644 --- a/deps/v8/tools/v8heapconst.py +++ b/deps/v8/tools/v8heapconst.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python3 # Copyright 2019 the V8 project authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can # be found in the LICENSE file. @@ -6,6 +7,8 @@ # be modified manually. # List of known V8 instance types. +# yapf: disable + INSTANCE_TYPES = { 0: "INTERNALIZED_STRING_TYPE", 2: "EXTERNAL_INTERNALIZED_STRING_TYPE", @@ -55,497 +58,514 @@ 151: "BREAK_POINT_INFO_TYPE", 152: "CACHED_TEMPLATE_OBJECT_TYPE", 153: "CALL_HANDLER_INFO_TYPE", - 154: "CLASS_POSITIONS_TYPE", - 155: "DEBUG_INFO_TYPE", - 156: "ENUM_CACHE_TYPE", - 157: "FEEDBACK_CELL_TYPE", - 158: "FUNCTION_TEMPLATE_RARE_DATA_TYPE", - 159: "INTERCEPTOR_INFO_TYPE", - 160: "INTERPRETER_DATA_TYPE", - 161: "MODULE_REQUEST_TYPE", - 162: "PROMISE_CAPABILITY_TYPE", - 163: "PROMISE_REACTION_TYPE", - 164: "PROPERTY_DESCRIPTOR_OBJECT_TYPE", - 165: "PROTOTYPE_INFO_TYPE", - 166: "REG_EXP_BOILERPLATE_DESCRIPTION_TYPE", - 167: "SCRIPT_TYPE", - 168: "SCRIPT_OR_MODULE_TYPE", - 169: "SOURCE_TEXT_MODULE_INFO_ENTRY_TYPE", - 170: "STACK_FRAME_INFO_TYPE", - 171: "TEMPLATE_OBJECT_DESCRIPTION_TYPE", - 172: "TUPLE2_TYPE", - 173: "WASM_CONTINUATION_OBJECT_TYPE", - 174: "WASM_EXCEPTION_TAG_TYPE", - 175: "WASM_INDIRECT_FUNCTION_TABLE_TYPE", - 176: "FIXED_ARRAY_TYPE", - 177: "HASH_TABLE_TYPE", - 178: "EPHEMERON_HASH_TABLE_TYPE", - 179: "GLOBAL_DICTIONARY_TYPE", - 180: "NAME_DICTIONARY_TYPE", - 181: "NUMBER_DICTIONARY_TYPE", - 182: "ORDERED_HASH_MAP_TYPE", - 183: "ORDERED_HASH_SET_TYPE", - 184: "ORDERED_NAME_DICTIONARY_TYPE", - 185: "SIMPLE_NUMBER_DICTIONARY_TYPE", - 186: "CLOSURE_FEEDBACK_CELL_ARRAY_TYPE", - 187: "OBJECT_BOILERPLATE_DESCRIPTION_TYPE", - 188: "SCRIPT_CONTEXT_TABLE_TYPE", - 189: "BYTE_ARRAY_TYPE", - 190: "BYTECODE_ARRAY_TYPE", - 191: "FIXED_DOUBLE_ARRAY_TYPE", - 192: "INTERNAL_CLASS_WITH_SMI_ELEMENTS_TYPE", - 193: "SLOPPY_ARGUMENTS_ELEMENTS_TYPE", - 194: "AWAIT_CONTEXT_TYPE", - 195: "BLOCK_CONTEXT_TYPE", - 196: "CATCH_CONTEXT_TYPE", - 197: "DEBUG_EVALUATE_CONTEXT_TYPE", - 198: "EVAL_CONTEXT_TYPE", - 199: "FUNCTION_CONTEXT_TYPE", - 200: "MODULE_CONTEXT_TYPE", - 201: "NATIVE_CONTEXT_TYPE", - 202: "SCRIPT_CONTEXT_TYPE", - 203: "WITH_CONTEXT_TYPE", + 154: "CALL_SITE_INFO_TYPE", + 155: "CLASS_POSITIONS_TYPE", + 156: "DEBUG_INFO_TYPE", + 157: "ENUM_CACHE_TYPE", + 158: "ERROR_STACK_DATA_TYPE", + 159: "FEEDBACK_CELL_TYPE", + 160: "FUNCTION_TEMPLATE_RARE_DATA_TYPE", + 161: "INTERCEPTOR_INFO_TYPE", + 162: "INTERPRETER_DATA_TYPE", + 163: "MODULE_REQUEST_TYPE", + 164: "PROMISE_CAPABILITY_TYPE", + 165: "PROMISE_ON_STACK_TYPE", + 166: "PROMISE_REACTION_TYPE", + 167: "PROPERTY_DESCRIPTOR_OBJECT_TYPE", + 168: "PROTOTYPE_INFO_TYPE", + 169: "REG_EXP_BOILERPLATE_DESCRIPTION_TYPE", + 170: "SCRIPT_TYPE", + 171: "SCRIPT_OR_MODULE_TYPE", + 172: "SOURCE_TEXT_MODULE_INFO_ENTRY_TYPE", + 173: "STACK_FRAME_INFO_TYPE", + 174: "TEMPLATE_OBJECT_DESCRIPTION_TYPE", + 175: "TUPLE2_TYPE", + 176: "WASM_CONTINUATION_OBJECT_TYPE", + 177: "WASM_EXCEPTION_TAG_TYPE", + 178: "WASM_INDIRECT_FUNCTION_TABLE_TYPE", + 179: "FIXED_ARRAY_TYPE", + 180: "HASH_TABLE_TYPE", + 181: "EPHEMERON_HASH_TABLE_TYPE", + 182: "GLOBAL_DICTIONARY_TYPE", + 183: "NAME_DICTIONARY_TYPE", + 184: "NAME_TO_INDEX_HASH_TABLE_TYPE", + 185: "NUMBER_DICTIONARY_TYPE", + 186: "ORDERED_HASH_MAP_TYPE", + 187: "ORDERED_HASH_SET_TYPE", + 188: "ORDERED_NAME_DICTIONARY_TYPE", + 189: "REGISTERED_SYMBOL_TABLE_TYPE", + 190: "SIMPLE_NUMBER_DICTIONARY_TYPE", + 191: "CLOSURE_FEEDBACK_CELL_ARRAY_TYPE", + 192: "OBJECT_BOILERPLATE_DESCRIPTION_TYPE", + 193: "SCRIPT_CONTEXT_TABLE_TYPE", + 194: "BYTE_ARRAY_TYPE", + 195: "BYTECODE_ARRAY_TYPE", + 196: "FIXED_DOUBLE_ARRAY_TYPE", + 197: "INTERNAL_CLASS_WITH_SMI_ELEMENTS_TYPE", + 198: "SLOPPY_ARGUMENTS_ELEMENTS_TYPE", + 199: "TURBOFAN_BITSET_TYPE_TYPE", + 200: "TURBOFAN_HEAP_CONSTANT_TYPE_TYPE", + 201: "TURBOFAN_OTHER_NUMBER_CONSTANT_TYPE_TYPE", + 202: "TURBOFAN_RANGE_TYPE_TYPE", + 203: "TURBOFAN_UNION_TYPE_TYPE", 204: "FOREIGN_TYPE", 205: "WASM_INTERNAL_FUNCTION_TYPE", 206: "WASM_TYPE_INFO_TYPE", - 207: "TURBOFAN_BITSET_TYPE_TYPE", - 208: "TURBOFAN_HEAP_CONSTANT_TYPE_TYPE", - 209: "TURBOFAN_OTHER_NUMBER_CONSTANT_TYPE_TYPE", - 210: "TURBOFAN_RANGE_TYPE_TYPE", - 211: "TURBOFAN_UNION_TYPE_TYPE", - 212: "UNCOMPILED_DATA_WITH_PREPARSE_DATA_TYPE", - 213: "UNCOMPILED_DATA_WITH_PREPARSE_DATA_AND_JOB_TYPE", - 214: "UNCOMPILED_DATA_WITHOUT_PREPARSE_DATA_TYPE", - 215: "UNCOMPILED_DATA_WITHOUT_PREPARSE_DATA_WITH_JOB_TYPE", - 216: "WASM_FUNCTION_DATA_TYPE", - 217: "WASM_CAPI_FUNCTION_DATA_TYPE", - 218: "WASM_EXPORTED_FUNCTION_DATA_TYPE", - 219: "WASM_JS_FUNCTION_DATA_TYPE", - 220: "EXPORTED_SUB_CLASS_BASE_TYPE", - 221: "EXPORTED_SUB_CLASS_TYPE", - 222: "EXPORTED_SUB_CLASS2_TYPE", - 223: "SMALL_ORDERED_HASH_MAP_TYPE", - 224: "SMALL_ORDERED_HASH_SET_TYPE", - 225: "SMALL_ORDERED_NAME_DICTIONARY_TYPE", - 226: "ABSTRACT_INTERNAL_CLASS_SUBCLASS1_TYPE", - 227: "ABSTRACT_INTERNAL_CLASS_SUBCLASS2_TYPE", - 228: "DESCRIPTOR_ARRAY_TYPE", - 229: "STRONG_DESCRIPTOR_ARRAY_TYPE", - 230: "SOURCE_TEXT_MODULE_TYPE", - 231: "SYNTHETIC_MODULE_TYPE", - 232: "WEAK_FIXED_ARRAY_TYPE", - 233: "TRANSITION_ARRAY_TYPE", - 234: "CELL_TYPE", - 235: "CODE_TYPE", - 236: "CODE_DATA_CONTAINER_TYPE", - 237: "COVERAGE_INFO_TYPE", - 238: "EMBEDDER_DATA_ARRAY_TYPE", - 239: "FEEDBACK_METADATA_TYPE", - 240: "FEEDBACK_VECTOR_TYPE", - 241: "FILLER_TYPE", - 242: "FREE_SPACE_TYPE", - 243: "INTERNAL_CLASS_TYPE", - 244: "INTERNAL_CLASS_WITH_STRUCT_ELEMENTS_TYPE", - 245: "MAP_TYPE", - 246: "MEGA_DOM_HANDLER_TYPE", - 247: "ON_HEAP_BASIC_BLOCK_PROFILER_DATA_TYPE", - 248: "PREPARSE_DATA_TYPE", - 249: "PROPERTY_ARRAY_TYPE", - 250: "PROPERTY_CELL_TYPE", - 251: "SCOPE_INFO_TYPE", - 252: "SHARED_FUNCTION_INFO_TYPE", - 253: "SMI_BOX_TYPE", - 254: "SMI_PAIR_TYPE", - 255: "SORT_STATE_TYPE", - 256: "SWISS_NAME_DICTIONARY_TYPE", - 257: "WASM_API_FUNCTION_REF_TYPE", - 258: "WEAK_ARRAY_LIST_TYPE", - 259: "WEAK_CELL_TYPE", - 260: "WASM_ARRAY_TYPE", - 261: "WASM_STRUCT_TYPE", - 262: "JS_PROXY_TYPE", + 207: "AWAIT_CONTEXT_TYPE", + 208: "BLOCK_CONTEXT_TYPE", + 209: "CATCH_CONTEXT_TYPE", + 210: "DEBUG_EVALUATE_CONTEXT_TYPE", + 211: "EVAL_CONTEXT_TYPE", + 212: "FUNCTION_CONTEXT_TYPE", + 213: "MODULE_CONTEXT_TYPE", + 214: "NATIVE_CONTEXT_TYPE", + 215: "SCRIPT_CONTEXT_TYPE", + 216: "WITH_CONTEXT_TYPE", + 217: "UNCOMPILED_DATA_WITH_PREPARSE_DATA_TYPE", + 218: "UNCOMPILED_DATA_WITH_PREPARSE_DATA_AND_JOB_TYPE", + 219: "UNCOMPILED_DATA_WITHOUT_PREPARSE_DATA_TYPE", + 220: "UNCOMPILED_DATA_WITHOUT_PREPARSE_DATA_WITH_JOB_TYPE", + 221: "WASM_FUNCTION_DATA_TYPE", + 222: "WASM_CAPI_FUNCTION_DATA_TYPE", + 223: "WASM_EXPORTED_FUNCTION_DATA_TYPE", + 224: "WASM_JS_FUNCTION_DATA_TYPE", + 225: "EXPORTED_SUB_CLASS_BASE_TYPE", + 226: "EXPORTED_SUB_CLASS_TYPE", + 227: "EXPORTED_SUB_CLASS2_TYPE", + 228: "SMALL_ORDERED_HASH_MAP_TYPE", + 229: "SMALL_ORDERED_HASH_SET_TYPE", + 230: "SMALL_ORDERED_NAME_DICTIONARY_TYPE", + 231: "ABSTRACT_INTERNAL_CLASS_SUBCLASS1_TYPE", + 232: "ABSTRACT_INTERNAL_CLASS_SUBCLASS2_TYPE", + 233: "DESCRIPTOR_ARRAY_TYPE", + 234: "STRONG_DESCRIPTOR_ARRAY_TYPE", + 235: "SOURCE_TEXT_MODULE_TYPE", + 236: "SYNTHETIC_MODULE_TYPE", + 237: "WEAK_FIXED_ARRAY_TYPE", + 238: "TRANSITION_ARRAY_TYPE", + 239: "CELL_TYPE", + 240: "CODE_TYPE", + 241: "CODE_DATA_CONTAINER_TYPE", + 242: "COVERAGE_INFO_TYPE", + 243: "EMBEDDER_DATA_ARRAY_TYPE", + 244: "FEEDBACK_METADATA_TYPE", + 245: "FEEDBACK_VECTOR_TYPE", + 246: "FILLER_TYPE", + 247: "FREE_SPACE_TYPE", + 248: "INTERNAL_CLASS_TYPE", + 249: "INTERNAL_CLASS_WITH_STRUCT_ELEMENTS_TYPE", + 250: "MAP_TYPE", + 251: "MEGA_DOM_HANDLER_TYPE", + 252: "ON_HEAP_BASIC_BLOCK_PROFILER_DATA_TYPE", + 253: "PREPARSE_DATA_TYPE", + 254: "PROPERTY_ARRAY_TYPE", + 255: "PROPERTY_CELL_TYPE", + 256: "SCOPE_INFO_TYPE", + 257: "SHARED_FUNCTION_INFO_TYPE", + 258: "SMI_BOX_TYPE", + 259: "SMI_PAIR_TYPE", + 260: "SORT_STATE_TYPE", + 261: "SWISS_NAME_DICTIONARY_TYPE", + 262: "WASM_API_FUNCTION_REF_TYPE", + 263: "WASM_ON_FULFILLED_DATA_TYPE", + 264: "WEAK_ARRAY_LIST_TYPE", + 265: "WEAK_CELL_TYPE", + 266: "WASM_ARRAY_TYPE", + 267: "WASM_STRUCT_TYPE", + 268: "JS_PROXY_TYPE", 1057: "JS_OBJECT_TYPE", - 263: "JS_GLOBAL_OBJECT_TYPE", - 264: "JS_GLOBAL_PROXY_TYPE", - 265: "JS_MODULE_NAMESPACE_TYPE", + 269: "JS_GLOBAL_OBJECT_TYPE", + 270: "JS_GLOBAL_PROXY_TYPE", + 271: "JS_MODULE_NAMESPACE_TYPE", 1040: "JS_SPECIAL_API_OBJECT_TYPE", 1041: "JS_PRIMITIVE_WRAPPER_TYPE", 1058: "JS_API_OBJECT_TYPE", 2058: "JS_LAST_DUMMY_API_OBJECT_TYPE", - 2059: "JS_BOUND_FUNCTION_TYPE", - 2060: "JS_FUNCTION_TYPE", - 2061: "BIGINT64_TYPED_ARRAY_CONSTRUCTOR_TYPE", - 2062: "BIGUINT64_TYPED_ARRAY_CONSTRUCTOR_TYPE", - 2063: "FLOAT32_TYPED_ARRAY_CONSTRUCTOR_TYPE", - 2064: "FLOAT64_TYPED_ARRAY_CONSTRUCTOR_TYPE", - 2065: "INT16_TYPED_ARRAY_CONSTRUCTOR_TYPE", - 2066: "INT32_TYPED_ARRAY_CONSTRUCTOR_TYPE", - 2067: "INT8_TYPED_ARRAY_CONSTRUCTOR_TYPE", - 2068: "UINT16_TYPED_ARRAY_CONSTRUCTOR_TYPE", - 2069: "UINT32_TYPED_ARRAY_CONSTRUCTOR_TYPE", - 2070: "UINT8_CLAMPED_TYPED_ARRAY_CONSTRUCTOR_TYPE", - 2071: "UINT8_TYPED_ARRAY_CONSTRUCTOR_TYPE", - 2072: "JS_ARRAY_CONSTRUCTOR_TYPE", - 2073: "JS_PROMISE_CONSTRUCTOR_TYPE", - 2074: "JS_REG_EXP_CONSTRUCTOR_TYPE", - 2075: "JS_CLASS_CONSTRUCTOR_TYPE", - 2076: "JS_ARRAY_ITERATOR_PROTOTYPE_TYPE", - 2077: "JS_ITERATOR_PROTOTYPE_TYPE", - 2078: "JS_MAP_ITERATOR_PROTOTYPE_TYPE", - 2079: "JS_OBJECT_PROTOTYPE_TYPE", - 2080: "JS_PROMISE_PROTOTYPE_TYPE", - 2081: "JS_REG_EXP_PROTOTYPE_TYPE", - 2082: "JS_SET_ITERATOR_PROTOTYPE_TYPE", - 2083: "JS_SET_PROTOTYPE_TYPE", - 2084: "JS_STRING_ITERATOR_PROTOTYPE_TYPE", - 2085: "JS_TYPED_ARRAY_PROTOTYPE_TYPE", - 2086: "JS_MAP_KEY_ITERATOR_TYPE", - 2087: "JS_MAP_KEY_VALUE_ITERATOR_TYPE", - 2088: "JS_MAP_VALUE_ITERATOR_TYPE", - 2089: "JS_SET_KEY_VALUE_ITERATOR_TYPE", - 2090: "JS_SET_VALUE_ITERATOR_TYPE", - 2091: "JS_GENERATOR_OBJECT_TYPE", - 2092: "JS_ASYNC_FUNCTION_OBJECT_TYPE", - 2093: "JS_ASYNC_GENERATOR_OBJECT_TYPE", - 2094: "JS_DATA_VIEW_TYPE", - 2095: "JS_TYPED_ARRAY_TYPE", - 2096: "JS_MAP_TYPE", - 2097: "JS_SET_TYPE", - 2098: "JS_WEAK_MAP_TYPE", - 2099: "JS_WEAK_SET_TYPE", - 2100: "JS_ARGUMENTS_OBJECT_TYPE", - 2101: "JS_ARRAY_TYPE", - 2102: "JS_ARRAY_BUFFER_TYPE", - 2103: "JS_ARRAY_ITERATOR_TYPE", - 2104: "JS_ASYNC_FROM_SYNC_ITERATOR_TYPE", - 2105: "JS_COLLATOR_TYPE", - 2106: "JS_CONTEXT_EXTENSION_OBJECT_TYPE", - 2107: "JS_DATE_TYPE", - 2108: "JS_DATE_TIME_FORMAT_TYPE", - 2109: "JS_DISPLAY_NAMES_TYPE", - 2110: "JS_ERROR_TYPE", - 2111: "JS_FINALIZATION_REGISTRY_TYPE", - 2112: "JS_LIST_FORMAT_TYPE", - 2113: "JS_LOCALE_TYPE", - 2114: "JS_MESSAGE_OBJECT_TYPE", - 2115: "JS_NUMBER_FORMAT_TYPE", - 2116: "JS_PLURAL_RULES_TYPE", - 2117: "JS_PROMISE_TYPE", - 2118: "JS_REG_EXP_TYPE", - 2119: "JS_REG_EXP_STRING_ITERATOR_TYPE", - 2120: "JS_RELATIVE_TIME_FORMAT_TYPE", - 2121: "JS_SEGMENT_ITERATOR_TYPE", - 2122: "JS_SEGMENTER_TYPE", - 2123: "JS_SEGMENTS_TYPE", - 2124: "JS_STRING_ITERATOR_TYPE", - 2125: "JS_TEMPORAL_CALENDAR_TYPE", - 2126: "JS_TEMPORAL_DURATION_TYPE", - 2127: "JS_TEMPORAL_INSTANT_TYPE", - 2128: "JS_TEMPORAL_PLAIN_DATE_TYPE", - 2129: "JS_TEMPORAL_PLAIN_DATE_TIME_TYPE", - 2130: "JS_TEMPORAL_PLAIN_MONTH_DAY_TYPE", - 2131: "JS_TEMPORAL_PLAIN_TIME_TYPE", - 2132: "JS_TEMPORAL_PLAIN_YEAR_MONTH_TYPE", - 2133: "JS_TEMPORAL_TIME_ZONE_TYPE", - 2134: "JS_TEMPORAL_ZONED_DATE_TIME_TYPE", - 2135: "JS_V8_BREAK_ITERATOR_TYPE", - 2136: "JS_WEAK_REF_TYPE", - 2137: "WASM_GLOBAL_OBJECT_TYPE", - 2138: "WASM_INSTANCE_OBJECT_TYPE", - 2139: "WASM_MEMORY_OBJECT_TYPE", - 2140: "WASM_MODULE_OBJECT_TYPE", - 2141: "WASM_SUSPENDER_OBJECT_TYPE", - 2142: "WASM_TABLE_OBJECT_TYPE", - 2143: "WASM_TAG_OBJECT_TYPE", - 2144: "WASM_VALUE_OBJECT_TYPE", + 2059: "JS_DATA_VIEW_TYPE", + 2060: "JS_TYPED_ARRAY_TYPE", + 2061: "JS_ARRAY_BUFFER_TYPE", + 2062: "JS_PROMISE_TYPE", + 2063: "JS_BOUND_FUNCTION_TYPE", + 2064: "JS_WRAPPED_FUNCTION_TYPE", + 2065: "JS_FUNCTION_TYPE", + 2066: "BIGINT64_TYPED_ARRAY_CONSTRUCTOR_TYPE", + 2067: "BIGUINT64_TYPED_ARRAY_CONSTRUCTOR_TYPE", + 2068: "FLOAT32_TYPED_ARRAY_CONSTRUCTOR_TYPE", + 2069: "FLOAT64_TYPED_ARRAY_CONSTRUCTOR_TYPE", + 2070: "INT16_TYPED_ARRAY_CONSTRUCTOR_TYPE", + 2071: "INT32_TYPED_ARRAY_CONSTRUCTOR_TYPE", + 2072: "INT8_TYPED_ARRAY_CONSTRUCTOR_TYPE", + 2073: "UINT16_TYPED_ARRAY_CONSTRUCTOR_TYPE", + 2074: "UINT32_TYPED_ARRAY_CONSTRUCTOR_TYPE", + 2075: "UINT8_CLAMPED_TYPED_ARRAY_CONSTRUCTOR_TYPE", + 2076: "UINT8_TYPED_ARRAY_CONSTRUCTOR_TYPE", + 2077: "JS_ARRAY_CONSTRUCTOR_TYPE", + 2078: "JS_PROMISE_CONSTRUCTOR_TYPE", + 2079: "JS_REG_EXP_CONSTRUCTOR_TYPE", + 2080: "JS_CLASS_CONSTRUCTOR_TYPE", + 2081: "JS_ARRAY_ITERATOR_PROTOTYPE_TYPE", + 2082: "JS_ITERATOR_PROTOTYPE_TYPE", + 2083: "JS_MAP_ITERATOR_PROTOTYPE_TYPE", + 2084: "JS_OBJECT_PROTOTYPE_TYPE", + 2085: "JS_PROMISE_PROTOTYPE_TYPE", + 2086: "JS_REG_EXP_PROTOTYPE_TYPE", + 2087: "JS_SET_ITERATOR_PROTOTYPE_TYPE", + 2088: "JS_SET_PROTOTYPE_TYPE", + 2089: "JS_STRING_ITERATOR_PROTOTYPE_TYPE", + 2090: "JS_TYPED_ARRAY_PROTOTYPE_TYPE", + 2091: "JS_MAP_KEY_ITERATOR_TYPE", + 2092: "JS_MAP_KEY_VALUE_ITERATOR_TYPE", + 2093: "JS_MAP_VALUE_ITERATOR_TYPE", + 2094: "JS_SET_KEY_VALUE_ITERATOR_TYPE", + 2095: "JS_SET_VALUE_ITERATOR_TYPE", + 2096: "JS_GENERATOR_OBJECT_TYPE", + 2097: "JS_ASYNC_FUNCTION_OBJECT_TYPE", + 2098: "JS_ASYNC_GENERATOR_OBJECT_TYPE", + 2099: "JS_MAP_TYPE", + 2100: "JS_SET_TYPE", + 2101: "JS_WEAK_MAP_TYPE", + 2102: "JS_WEAK_SET_TYPE", + 2103: "JS_ARGUMENTS_OBJECT_TYPE", + 2104: "JS_ARRAY_TYPE", + 2105: "JS_ARRAY_ITERATOR_TYPE", + 2106: "JS_ASYNC_FROM_SYNC_ITERATOR_TYPE", + 2107: "JS_COLLATOR_TYPE", + 2108: "JS_CONTEXT_EXTENSION_OBJECT_TYPE", + 2109: "JS_DATE_TYPE", + 2110: "JS_DATE_TIME_FORMAT_TYPE", + 2111: "JS_DISPLAY_NAMES_TYPE", + 2112: "JS_ERROR_TYPE", + 2113: "JS_EXTERNAL_OBJECT_TYPE", + 2114: "JS_FINALIZATION_REGISTRY_TYPE", + 2115: "JS_LIST_FORMAT_TYPE", + 2116: "JS_LOCALE_TYPE", + 2117: "JS_MESSAGE_OBJECT_TYPE", + 2118: "JS_NUMBER_FORMAT_TYPE", + 2119: "JS_PLURAL_RULES_TYPE", + 2120: "JS_REG_EXP_TYPE", + 2121: "JS_REG_EXP_STRING_ITERATOR_TYPE", + 2122: "JS_RELATIVE_TIME_FORMAT_TYPE", + 2123: "JS_SEGMENT_ITERATOR_TYPE", + 2124: "JS_SEGMENTER_TYPE", + 2125: "JS_SEGMENTS_TYPE", + 2126: "JS_SHADOW_REALM_TYPE", + 2127: "JS_SHARED_STRUCT_TYPE", + 2128: "JS_STRING_ITERATOR_TYPE", + 2129: "JS_TEMPORAL_CALENDAR_TYPE", + 2130: "JS_TEMPORAL_DURATION_TYPE", + 2131: "JS_TEMPORAL_INSTANT_TYPE", + 2132: "JS_TEMPORAL_PLAIN_DATE_TYPE", + 2133: "JS_TEMPORAL_PLAIN_DATE_TIME_TYPE", + 2134: "JS_TEMPORAL_PLAIN_MONTH_DAY_TYPE", + 2135: "JS_TEMPORAL_PLAIN_TIME_TYPE", + 2136: "JS_TEMPORAL_PLAIN_YEAR_MONTH_TYPE", + 2137: "JS_TEMPORAL_TIME_ZONE_TYPE", + 2138: "JS_TEMPORAL_ZONED_DATE_TIME_TYPE", + 2139: "JS_V8_BREAK_ITERATOR_TYPE", + 2140: "JS_WEAK_REF_TYPE", + 2141: "WASM_GLOBAL_OBJECT_TYPE", + 2142: "WASM_INSTANCE_OBJECT_TYPE", + 2143: "WASM_MEMORY_OBJECT_TYPE", + 2144: "WASM_MODULE_OBJECT_TYPE", + 2145: "WASM_SUSPENDER_OBJECT_TYPE", + 2146: "WASM_TABLE_OBJECT_TYPE", + 2147: "WASM_TAG_OBJECT_TYPE", + 2148: "WASM_VALUE_OBJECT_TYPE", } # List of known V8 maps. KNOWN_MAPS = { - ("read_only_space", 0x02119): (245, "MetaMap"), - ("read_only_space", 0x02141): (131, "NullMap"), - ("read_only_space", 0x02169): (229, "StrongDescriptorArrayMap"), - ("read_only_space", 0x02191): (258, "WeakArrayListMap"), - ("read_only_space", 0x021d5): (156, "EnumCacheMap"), - ("read_only_space", 0x02209): (176, "FixedArrayMap"), - ("read_only_space", 0x02255): (8, "OneByteInternalizedStringMap"), - ("read_only_space", 0x022a1): (242, "FreeSpaceMap"), - ("read_only_space", 0x022c9): (241, "OnePointerFillerMap"), - ("read_only_space", 0x022f1): (241, "TwoPointerFillerMap"), - ("read_only_space", 0x02319): (131, "UninitializedMap"), - ("read_only_space", 0x02391): (131, "UndefinedMap"), - ("read_only_space", 0x023d5): (130, "HeapNumberMap"), - ("read_only_space", 0x02409): (131, "TheHoleMap"), - ("read_only_space", 0x02469): (131, "BooleanMap"), - ("read_only_space", 0x0250d): (189, "ByteArrayMap"), - ("read_only_space", 0x02535): (176, "FixedCOWArrayMap"), - ("read_only_space", 0x0255d): (177, "HashTableMap"), - ("read_only_space", 0x02585): (128, "SymbolMap"), - ("read_only_space", 0x025ad): (40, "OneByteStringMap"), - ("read_only_space", 0x025d5): (251, "ScopeInfoMap"), - ("read_only_space", 0x025fd): (252, "SharedFunctionInfoMap"), - ("read_only_space", 0x02625): (235, "CodeMap"), - ("read_only_space", 0x0264d): (234, "CellMap"), - ("read_only_space", 0x02675): (250, "GlobalPropertyCellMap"), - ("read_only_space", 0x0269d): (204, "ForeignMap"), - ("read_only_space", 0x026c5): (233, "TransitionArrayMap"), - ("read_only_space", 0x026ed): (45, "ThinOneByteStringMap"), - ("read_only_space", 0x02715): (240, "FeedbackVectorMap"), - ("read_only_space", 0x0274d): (131, "ArgumentsMarkerMap"), - ("read_only_space", 0x027ad): (131, "ExceptionMap"), - ("read_only_space", 0x02809): (131, "TerminationExceptionMap"), - ("read_only_space", 0x02871): (131, "OptimizedOutMap"), - ("read_only_space", 0x028d1): (131, "StaleRegisterMap"), - ("read_only_space", 0x02931): (188, "ScriptContextTableMap"), - ("read_only_space", 0x02959): (186, "ClosureFeedbackCellArrayMap"), - ("read_only_space", 0x02981): (239, "FeedbackMetadataArrayMap"), - ("read_only_space", 0x029a9): (176, "ArrayListMap"), - ("read_only_space", 0x029d1): (129, "BigIntMap"), - ("read_only_space", 0x029f9): (187, "ObjectBoilerplateDescriptionMap"), - ("read_only_space", 0x02a21): (190, "BytecodeArrayMap"), - ("read_only_space", 0x02a49): (236, "CodeDataContainerMap"), - ("read_only_space", 0x02a71): (237, "CoverageInfoMap"), - ("read_only_space", 0x02a99): (191, "FixedDoubleArrayMap"), - ("read_only_space", 0x02ac1): (179, "GlobalDictionaryMap"), - ("read_only_space", 0x02ae9): (157, "ManyClosuresCellMap"), - ("read_only_space", 0x02b11): (246, "MegaDomHandlerMap"), - ("read_only_space", 0x02b39): (176, "ModuleInfoMap"), - ("read_only_space", 0x02b61): (180, "NameDictionaryMap"), - ("read_only_space", 0x02b89): (157, "NoClosuresCellMap"), - ("read_only_space", 0x02bb1): (181, "NumberDictionaryMap"), - ("read_only_space", 0x02bd9): (157, "OneClosureCellMap"), - ("read_only_space", 0x02c01): (182, "OrderedHashMapMap"), - ("read_only_space", 0x02c29): (183, "OrderedHashSetMap"), - ("read_only_space", 0x02c51): (184, "OrderedNameDictionaryMap"), - ("read_only_space", 0x02c79): (248, "PreparseDataMap"), - ("read_only_space", 0x02ca1): (249, "PropertyArrayMap"), - ("read_only_space", 0x02cc9): (153, "SideEffectCallHandlerInfoMap"), - ("read_only_space", 0x02cf1): (153, "SideEffectFreeCallHandlerInfoMap"), - ("read_only_space", 0x02d19): (153, "NextCallSideEffectFreeCallHandlerInfoMap"), - ("read_only_space", 0x02d41): (185, "SimpleNumberDictionaryMap"), - ("read_only_space", 0x02d69): (223, "SmallOrderedHashMapMap"), - ("read_only_space", 0x02d91): (224, "SmallOrderedHashSetMap"), - ("read_only_space", 0x02db9): (225, "SmallOrderedNameDictionaryMap"), - ("read_only_space", 0x02de1): (230, "SourceTextModuleMap"), - ("read_only_space", 0x02e09): (256, "SwissNameDictionaryMap"), - ("read_only_space", 0x02e31): (231, "SyntheticModuleMap"), - ("read_only_space", 0x02e59): (257, "WasmApiFunctionRefMap"), - ("read_only_space", 0x02e81): (217, "WasmCapiFunctionDataMap"), - ("read_only_space", 0x02ea9): (218, "WasmExportedFunctionDataMap"), - ("read_only_space", 0x02ed1): (205, "WasmInternalFunctionMap"), - ("read_only_space", 0x02ef9): (219, "WasmJSFunctionDataMap"), - ("read_only_space", 0x02f21): (206, "WasmTypeInfoMap"), - ("read_only_space", 0x02f49): (232, "WeakFixedArrayMap"), - ("read_only_space", 0x02f71): (178, "EphemeronHashTableMap"), - ("read_only_space", 0x02f99): (238, "EmbedderDataArrayMap"), - ("read_only_space", 0x02fc1): (259, "WeakCellMap"), - ("read_only_space", 0x02fe9): (32, "StringMap"), - ("read_only_space", 0x03011): (41, "ConsOneByteStringMap"), - ("read_only_space", 0x03039): (33, "ConsStringMap"), - ("read_only_space", 0x03061): (37, "ThinStringMap"), - ("read_only_space", 0x03089): (35, "SlicedStringMap"), - ("read_only_space", 0x030b1): (43, "SlicedOneByteStringMap"), - ("read_only_space", 0x030d9): (34, "ExternalStringMap"), - ("read_only_space", 0x03101): (42, "ExternalOneByteStringMap"), - ("read_only_space", 0x03129): (50, "UncachedExternalStringMap"), - ("read_only_space", 0x03151): (0, "InternalizedStringMap"), - ("read_only_space", 0x03179): (2, "ExternalInternalizedStringMap"), - ("read_only_space", 0x031a1): (10, "ExternalOneByteInternalizedStringMap"), - ("read_only_space", 0x031c9): (18, "UncachedExternalInternalizedStringMap"), - ("read_only_space", 0x031f1): (26, "UncachedExternalOneByteInternalizedStringMap"), - ("read_only_space", 0x03219): (58, "UncachedExternalOneByteStringMap"), - ("read_only_space", 0x03241): (104, "SharedOneByteStringMap"), - ("read_only_space", 0x03269): (96, "SharedStringMap"), - ("read_only_space", 0x03291): (109, "SharedThinOneByteStringMap"), - ("read_only_space", 0x032b9): (101, "SharedThinStringMap"), - ("read_only_space", 0x032e1): (96, "TwoByteSeqStringMigrationSentinelMap"), - ("read_only_space", 0x03309): (104, "OneByteSeqStringMigrationSentinelMap"), - ("read_only_space", 0x03331): (131, "SelfReferenceMarkerMap"), - ("read_only_space", 0x03359): (131, "BasicBlockCountersMarkerMap"), - ("read_only_space", 0x0339d): (147, "ArrayBoilerplateDescriptionMap"), - ("read_only_space", 0x0349d): (159, "InterceptorInfoMap"), - ("read_only_space", 0x05d11): (132, "PromiseFulfillReactionJobTaskMap"), - ("read_only_space", 0x05d39): (133, "PromiseRejectReactionJobTaskMap"), - ("read_only_space", 0x05d61): (134, "CallableTaskMap"), - ("read_only_space", 0x05d89): (135, "CallbackTaskMap"), - ("read_only_space", 0x05db1): (136, "PromiseResolveThenableJobTaskMap"), - ("read_only_space", 0x05dd9): (139, "FunctionTemplateInfoMap"), - ("read_only_space", 0x05e01): (140, "ObjectTemplateInfoMap"), - ("read_only_space", 0x05e29): (141, "AccessCheckInfoMap"), - ("read_only_space", 0x05e51): (142, "AccessorInfoMap"), - ("read_only_space", 0x05e79): (143, "AccessorPairMap"), - ("read_only_space", 0x05ea1): (144, "AliasedArgumentsEntryMap"), - ("read_only_space", 0x05ec9): (145, "AllocationMementoMap"), - ("read_only_space", 0x05ef1): (148, "AsmWasmDataMap"), - ("read_only_space", 0x05f19): (149, "AsyncGeneratorRequestMap"), - ("read_only_space", 0x05f41): (150, "BreakPointMap"), - ("read_only_space", 0x05f69): (151, "BreakPointInfoMap"), - ("read_only_space", 0x05f91): (152, "CachedTemplateObjectMap"), - ("read_only_space", 0x05fb9): (154, "ClassPositionsMap"), - ("read_only_space", 0x05fe1): (155, "DebugInfoMap"), - ("read_only_space", 0x06009): (158, "FunctionTemplateRareDataMap"), - ("read_only_space", 0x06031): (160, "InterpreterDataMap"), - ("read_only_space", 0x06059): (161, "ModuleRequestMap"), - ("read_only_space", 0x06081): (162, "PromiseCapabilityMap"), - ("read_only_space", 0x060a9): (163, "PromiseReactionMap"), - ("read_only_space", 0x060d1): (164, "PropertyDescriptorObjectMap"), - ("read_only_space", 0x060f9): (165, "PrototypeInfoMap"), - ("read_only_space", 0x06121): (166, "RegExpBoilerplateDescriptionMap"), - ("read_only_space", 0x06149): (167, "ScriptMap"), - ("read_only_space", 0x06171): (168, "ScriptOrModuleMap"), - ("read_only_space", 0x06199): (169, "SourceTextModuleInfoEntryMap"), - ("read_only_space", 0x061c1): (170, "StackFrameInfoMap"), - ("read_only_space", 0x061e9): (171, "TemplateObjectDescriptionMap"), - ("read_only_space", 0x06211): (172, "Tuple2Map"), - ("read_only_space", 0x06239): (173, "WasmContinuationObjectMap"), - ("read_only_space", 0x06261): (174, "WasmExceptionTagMap"), - ("read_only_space", 0x06289): (175, "WasmIndirectFunctionTableMap"), - ("read_only_space", 0x062b1): (193, "SloppyArgumentsElementsMap"), - ("read_only_space", 0x062d9): (228, "DescriptorArrayMap"), - ("read_only_space", 0x06301): (214, "UncompiledDataWithoutPreparseDataMap"), - ("read_only_space", 0x06329): (212, "UncompiledDataWithPreparseDataMap"), - ("read_only_space", 0x06351): (215, "UncompiledDataWithoutPreparseDataWithJobMap"), - ("read_only_space", 0x06379): (213, "UncompiledDataWithPreparseDataAndJobMap"), - ("read_only_space", 0x063a1): (247, "OnHeapBasicBlockProfilerDataMap"), - ("read_only_space", 0x063c9): (207, "TurbofanBitsetTypeMap"), - ("read_only_space", 0x063f1): (211, "TurbofanUnionTypeMap"), - ("read_only_space", 0x06419): (210, "TurbofanRangeTypeMap"), - ("read_only_space", 0x06441): (208, "TurbofanHeapConstantTypeMap"), - ("read_only_space", 0x06469): (209, "TurbofanOtherNumberConstantTypeMap"), - ("read_only_space", 0x06491): (243, "InternalClassMap"), - ("read_only_space", 0x064b9): (254, "SmiPairMap"), - ("read_only_space", 0x064e1): (253, "SmiBoxMap"), - ("read_only_space", 0x06509): (220, "ExportedSubClassBaseMap"), - ("read_only_space", 0x06531): (221, "ExportedSubClassMap"), - ("read_only_space", 0x06559): (226, "AbstractInternalClassSubclass1Map"), - ("read_only_space", 0x06581): (227, "AbstractInternalClassSubclass2Map"), - ("read_only_space", 0x065a9): (192, "InternalClassWithSmiElementsMap"), - ("read_only_space", 0x065d1): (244, "InternalClassWithStructElementsMap"), - ("read_only_space", 0x065f9): (222, "ExportedSubClass2Map"), - ("read_only_space", 0x06621): (255, "SortStateMap"), - ("read_only_space", 0x06649): (146, "AllocationSiteWithWeakNextMap"), - ("read_only_space", 0x06671): (146, "AllocationSiteWithoutWeakNextMap"), - ("read_only_space", 0x06699): (137, "LoadHandler1Map"), - ("read_only_space", 0x066c1): (137, "LoadHandler2Map"), - ("read_only_space", 0x066e9): (137, "LoadHandler3Map"), - ("read_only_space", 0x06711): (138, "StoreHandler0Map"), - ("read_only_space", 0x06739): (138, "StoreHandler1Map"), - ("read_only_space", 0x06761): (138, "StoreHandler2Map"), - ("read_only_space", 0x06789): (138, "StoreHandler3Map"), - ("map_space", 0x02119): (1057, "ExternalMap"), - ("map_space", 0x02141): (2114, "JSMessageObjectMap"), + ("read_only_space", 0x02149): (250, "MetaMap"), + ("read_only_space", 0x02171): (131, "NullMap"), + ("read_only_space", 0x02199): (234, "StrongDescriptorArrayMap"), + ("read_only_space", 0x021c1): (264, "WeakArrayListMap"), + ("read_only_space", 0x02205): (157, "EnumCacheMap"), + ("read_only_space", 0x02239): (179, "FixedArrayMap"), + ("read_only_space", 0x02285): (8, "OneByteInternalizedStringMap"), + ("read_only_space", 0x022d1): (247, "FreeSpaceMap"), + ("read_only_space", 0x022f9): (246, "OnePointerFillerMap"), + ("read_only_space", 0x02321): (246, "TwoPointerFillerMap"), + ("read_only_space", 0x02349): (131, "UninitializedMap"), + ("read_only_space", 0x023c1): (131, "UndefinedMap"), + ("read_only_space", 0x02405): (130, "HeapNumberMap"), + ("read_only_space", 0x02439): (131, "TheHoleMap"), + ("read_only_space", 0x02499): (131, "BooleanMap"), + ("read_only_space", 0x0253d): (194, "ByteArrayMap"), + ("read_only_space", 0x02565): (179, "FixedCOWArrayMap"), + ("read_only_space", 0x0258d): (180, "HashTableMap"), + ("read_only_space", 0x025b5): (128, "SymbolMap"), + ("read_only_space", 0x025dd): (40, "OneByteStringMap"), + ("read_only_space", 0x02605): (256, "ScopeInfoMap"), + ("read_only_space", 0x0262d): (257, "SharedFunctionInfoMap"), + ("read_only_space", 0x02655): (240, "CodeMap"), + ("read_only_space", 0x0267d): (239, "CellMap"), + ("read_only_space", 0x026a5): (255, "GlobalPropertyCellMap"), + ("read_only_space", 0x026cd): (204, "ForeignMap"), + ("read_only_space", 0x026f5): (238, "TransitionArrayMap"), + ("read_only_space", 0x0271d): (45, "ThinOneByteStringMap"), + ("read_only_space", 0x02745): (245, "FeedbackVectorMap"), + ("read_only_space", 0x0277d): (131, "ArgumentsMarkerMap"), + ("read_only_space", 0x027dd): (131, "ExceptionMap"), + ("read_only_space", 0x02839): (131, "TerminationExceptionMap"), + ("read_only_space", 0x028a1): (131, "OptimizedOutMap"), + ("read_only_space", 0x02901): (131, "StaleRegisterMap"), + ("read_only_space", 0x02961): (193, "ScriptContextTableMap"), + ("read_only_space", 0x02989): (191, "ClosureFeedbackCellArrayMap"), + ("read_only_space", 0x029b1): (244, "FeedbackMetadataArrayMap"), + ("read_only_space", 0x029d9): (179, "ArrayListMap"), + ("read_only_space", 0x02a01): (129, "BigIntMap"), + ("read_only_space", 0x02a29): (192, "ObjectBoilerplateDescriptionMap"), + ("read_only_space", 0x02a51): (195, "BytecodeArrayMap"), + ("read_only_space", 0x02a79): (241, "CodeDataContainerMap"), + ("read_only_space", 0x02aa1): (242, "CoverageInfoMap"), + ("read_only_space", 0x02ac9): (196, "FixedDoubleArrayMap"), + ("read_only_space", 0x02af1): (182, "GlobalDictionaryMap"), + ("read_only_space", 0x02b19): (159, "ManyClosuresCellMap"), + ("read_only_space", 0x02b41): (251, "MegaDomHandlerMap"), + ("read_only_space", 0x02b69): (179, "ModuleInfoMap"), + ("read_only_space", 0x02b91): (183, "NameDictionaryMap"), + ("read_only_space", 0x02bb9): (159, "NoClosuresCellMap"), + ("read_only_space", 0x02be1): (185, "NumberDictionaryMap"), + ("read_only_space", 0x02c09): (159, "OneClosureCellMap"), + ("read_only_space", 0x02c31): (186, "OrderedHashMapMap"), + ("read_only_space", 0x02c59): (187, "OrderedHashSetMap"), + ("read_only_space", 0x02c81): (184, "NameToIndexHashTableMap"), + ("read_only_space", 0x02ca9): (189, "RegisteredSymbolTableMap"), + ("read_only_space", 0x02cd1): (188, "OrderedNameDictionaryMap"), + ("read_only_space", 0x02cf9): (253, "PreparseDataMap"), + ("read_only_space", 0x02d21): (254, "PropertyArrayMap"), + ("read_only_space", 0x02d49): (153, "SideEffectCallHandlerInfoMap"), + ("read_only_space", 0x02d71): (153, "SideEffectFreeCallHandlerInfoMap"), + ("read_only_space", 0x02d99): (153, "NextCallSideEffectFreeCallHandlerInfoMap"), + ("read_only_space", 0x02dc1): (190, "SimpleNumberDictionaryMap"), + ("read_only_space", 0x02de9): (228, "SmallOrderedHashMapMap"), + ("read_only_space", 0x02e11): (229, "SmallOrderedHashSetMap"), + ("read_only_space", 0x02e39): (230, "SmallOrderedNameDictionaryMap"), + ("read_only_space", 0x02e61): (235, "SourceTextModuleMap"), + ("read_only_space", 0x02e89): (261, "SwissNameDictionaryMap"), + ("read_only_space", 0x02eb1): (236, "SyntheticModuleMap"), + ("read_only_space", 0x02ed9): (262, "WasmApiFunctionRefMap"), + ("read_only_space", 0x02f01): (222, "WasmCapiFunctionDataMap"), + ("read_only_space", 0x02f29): (223, "WasmExportedFunctionDataMap"), + ("read_only_space", 0x02f51): (205, "WasmInternalFunctionMap"), + ("read_only_space", 0x02f79): (224, "WasmJSFunctionDataMap"), + ("read_only_space", 0x02fa1): (263, "WasmOnFulfilledDataMap"), + ("read_only_space", 0x02fc9): (206, "WasmTypeInfoMap"), + ("read_only_space", 0x02ff1): (237, "WeakFixedArrayMap"), + ("read_only_space", 0x03019): (181, "EphemeronHashTableMap"), + ("read_only_space", 0x03041): (243, "EmbedderDataArrayMap"), + ("read_only_space", 0x03069): (265, "WeakCellMap"), + ("read_only_space", 0x03091): (32, "StringMap"), + ("read_only_space", 0x030b9): (41, "ConsOneByteStringMap"), + ("read_only_space", 0x030e1): (33, "ConsStringMap"), + ("read_only_space", 0x03109): (37, "ThinStringMap"), + ("read_only_space", 0x03131): (35, "SlicedStringMap"), + ("read_only_space", 0x03159): (43, "SlicedOneByteStringMap"), + ("read_only_space", 0x03181): (34, "ExternalStringMap"), + ("read_only_space", 0x031a9): (42, "ExternalOneByteStringMap"), + ("read_only_space", 0x031d1): (50, "UncachedExternalStringMap"), + ("read_only_space", 0x031f9): (0, "InternalizedStringMap"), + ("read_only_space", 0x03221): (2, "ExternalInternalizedStringMap"), + ("read_only_space", 0x03249): (10, "ExternalOneByteInternalizedStringMap"), + ("read_only_space", 0x03271): (18, "UncachedExternalInternalizedStringMap"), + ("read_only_space", 0x03299): (26, "UncachedExternalOneByteInternalizedStringMap"), + ("read_only_space", 0x032c1): (58, "UncachedExternalOneByteStringMap"), + ("read_only_space", 0x032e9): (104, "SharedOneByteStringMap"), + ("read_only_space", 0x03311): (96, "SharedStringMap"), + ("read_only_space", 0x03339): (109, "SharedThinOneByteStringMap"), + ("read_only_space", 0x03361): (101, "SharedThinStringMap"), + ("read_only_space", 0x03389): (96, "TwoByteSeqStringMigrationSentinelMap"), + ("read_only_space", 0x033b1): (104, "OneByteSeqStringMigrationSentinelMap"), + ("read_only_space", 0x033d9): (131, "SelfReferenceMarkerMap"), + ("read_only_space", 0x03401): (131, "BasicBlockCountersMarkerMap"), + ("read_only_space", 0x03445): (147, "ArrayBoilerplateDescriptionMap"), + ("read_only_space", 0x03545): (161, "InterceptorInfoMap"), + ("read_only_space", 0x06015): (132, "PromiseFulfillReactionJobTaskMap"), + ("read_only_space", 0x0603d): (133, "PromiseRejectReactionJobTaskMap"), + ("read_only_space", 0x06065): (134, "CallableTaskMap"), + ("read_only_space", 0x0608d): (135, "CallbackTaskMap"), + ("read_only_space", 0x060b5): (136, "PromiseResolveThenableJobTaskMap"), + ("read_only_space", 0x060dd): (139, "FunctionTemplateInfoMap"), + ("read_only_space", 0x06105): (140, "ObjectTemplateInfoMap"), + ("read_only_space", 0x0612d): (141, "AccessCheckInfoMap"), + ("read_only_space", 0x06155): (142, "AccessorInfoMap"), + ("read_only_space", 0x0617d): (143, "AccessorPairMap"), + ("read_only_space", 0x061a5): (144, "AliasedArgumentsEntryMap"), + ("read_only_space", 0x061cd): (145, "AllocationMementoMap"), + ("read_only_space", 0x061f5): (148, "AsmWasmDataMap"), + ("read_only_space", 0x0621d): (149, "AsyncGeneratorRequestMap"), + ("read_only_space", 0x06245): (150, "BreakPointMap"), + ("read_only_space", 0x0626d): (151, "BreakPointInfoMap"), + ("read_only_space", 0x06295): (152, "CachedTemplateObjectMap"), + ("read_only_space", 0x062bd): (154, "CallSiteInfoMap"), + ("read_only_space", 0x062e5): (155, "ClassPositionsMap"), + ("read_only_space", 0x0630d): (156, "DebugInfoMap"), + ("read_only_space", 0x06335): (158, "ErrorStackDataMap"), + ("read_only_space", 0x0635d): (160, "FunctionTemplateRareDataMap"), + ("read_only_space", 0x06385): (162, "InterpreterDataMap"), + ("read_only_space", 0x063ad): (163, "ModuleRequestMap"), + ("read_only_space", 0x063d5): (164, "PromiseCapabilityMap"), + ("read_only_space", 0x063fd): (165, "PromiseOnStackMap"), + ("read_only_space", 0x06425): (166, "PromiseReactionMap"), + ("read_only_space", 0x0644d): (167, "PropertyDescriptorObjectMap"), + ("read_only_space", 0x06475): (168, "PrototypeInfoMap"), + ("read_only_space", 0x0649d): (169, "RegExpBoilerplateDescriptionMap"), + ("read_only_space", 0x064c5): (170, "ScriptMap"), + ("read_only_space", 0x064ed): (171, "ScriptOrModuleMap"), + ("read_only_space", 0x06515): (172, "SourceTextModuleInfoEntryMap"), + ("read_only_space", 0x0653d): (173, "StackFrameInfoMap"), + ("read_only_space", 0x06565): (174, "TemplateObjectDescriptionMap"), + ("read_only_space", 0x0658d): (175, "Tuple2Map"), + ("read_only_space", 0x065b5): (176, "WasmContinuationObjectMap"), + ("read_only_space", 0x065dd): (177, "WasmExceptionTagMap"), + ("read_only_space", 0x06605): (178, "WasmIndirectFunctionTableMap"), + ("read_only_space", 0x0662d): (198, "SloppyArgumentsElementsMap"), + ("read_only_space", 0x06655): (233, "DescriptorArrayMap"), + ("read_only_space", 0x0667d): (219, "UncompiledDataWithoutPreparseDataMap"), + ("read_only_space", 0x066a5): (217, "UncompiledDataWithPreparseDataMap"), + ("read_only_space", 0x066cd): (220, "UncompiledDataWithoutPreparseDataWithJobMap"), + ("read_only_space", 0x066f5): (218, "UncompiledDataWithPreparseDataAndJobMap"), + ("read_only_space", 0x0671d): (252, "OnHeapBasicBlockProfilerDataMap"), + ("read_only_space", 0x06745): (199, "TurbofanBitsetTypeMap"), + ("read_only_space", 0x0676d): (203, "TurbofanUnionTypeMap"), + ("read_only_space", 0x06795): (202, "TurbofanRangeTypeMap"), + ("read_only_space", 0x067bd): (200, "TurbofanHeapConstantTypeMap"), + ("read_only_space", 0x067e5): (201, "TurbofanOtherNumberConstantTypeMap"), + ("read_only_space", 0x0680d): (248, "InternalClassMap"), + ("read_only_space", 0x06835): (259, "SmiPairMap"), + ("read_only_space", 0x0685d): (258, "SmiBoxMap"), + ("read_only_space", 0x06885): (225, "ExportedSubClassBaseMap"), + ("read_only_space", 0x068ad): (226, "ExportedSubClassMap"), + ("read_only_space", 0x068d5): (231, "AbstractInternalClassSubclass1Map"), + ("read_only_space", 0x068fd): (232, "AbstractInternalClassSubclass2Map"), + ("read_only_space", 0x06925): (197, "InternalClassWithSmiElementsMap"), + ("read_only_space", 0x0694d): (249, "InternalClassWithStructElementsMap"), + ("read_only_space", 0x06975): (227, "ExportedSubClass2Map"), + ("read_only_space", 0x0699d): (260, "SortStateMap"), + ("read_only_space", 0x069c5): (146, "AllocationSiteWithWeakNextMap"), + ("read_only_space", 0x069ed): (146, "AllocationSiteWithoutWeakNextMap"), + ("read_only_space", 0x06a15): (137, "LoadHandler1Map"), + ("read_only_space", 0x06a3d): (137, "LoadHandler2Map"), + ("read_only_space", 0x06a65): (137, "LoadHandler3Map"), + ("read_only_space", 0x06a8d): (138, "StoreHandler0Map"), + ("read_only_space", 0x06ab5): (138, "StoreHandler1Map"), + ("read_only_space", 0x06add): (138, "StoreHandler2Map"), + ("read_only_space", 0x06b05): (138, "StoreHandler3Map"), + ("map_space", 0x02149): (2113, "ExternalMap"), + ("map_space", 0x02171): (2117, "JSMessageObjectMap"), } # List of known V8 objects. KNOWN_OBJECTS = { - ("read_only_space", 0x021b9): "EmptyWeakArrayList", - ("read_only_space", 0x021c5): "EmptyDescriptorArray", - ("read_only_space", 0x021fd): "EmptyEnumCache", - ("read_only_space", 0x02231): "EmptyFixedArray", - ("read_only_space", 0x02239): "NullValue", - ("read_only_space", 0x02341): "UninitializedValue", - ("read_only_space", 0x023b9): "UndefinedValue", - ("read_only_space", 0x023fd): "NanValue", - ("read_only_space", 0x02431): "TheHoleValue", - ("read_only_space", 0x0245d): "HoleNanValue", - ("read_only_space", 0x02491): "TrueValue", - ("read_only_space", 0x024d1): "FalseValue", - ("read_only_space", 0x02501): "empty_string", - ("read_only_space", 0x0273d): "EmptyScopeInfo", - ("read_only_space", 0x02775): "ArgumentsMarker", - ("read_only_space", 0x027d5): "Exception", - ("read_only_space", 0x02831): "TerminationException", - ("read_only_space", 0x02899): "OptimizedOut", - ("read_only_space", 0x028f9): "StaleRegister", - ("read_only_space", 0x03381): "EmptyPropertyArray", - ("read_only_space", 0x03389): "EmptyByteArray", - ("read_only_space", 0x03391): "EmptyObjectBoilerplateDescription", - ("read_only_space", 0x033c5): "EmptyArrayBoilerplateDescription", - ("read_only_space", 0x033d1): "EmptyClosureFeedbackCellArray", - ("read_only_space", 0x033d9): "EmptySlowElementDictionary", - ("read_only_space", 0x033fd): "EmptyOrderedHashMap", - ("read_only_space", 0x03411): "EmptyOrderedHashSet", - ("read_only_space", 0x03425): "EmptyFeedbackMetadata", - ("read_only_space", 0x03431): "EmptyPropertyDictionary", - ("read_only_space", 0x03459): "EmptyOrderedPropertyDictionary", - ("read_only_space", 0x03471): "EmptySwissPropertyDictionary", - ("read_only_space", 0x034c5): "NoOpInterceptorInfo", - ("read_only_space", 0x034ed): "EmptyWeakFixedArray", - ("read_only_space", 0x034f5): "InfinityValue", - ("read_only_space", 0x03501): "MinusZeroValue", - ("read_only_space", 0x0350d): "MinusInfinityValue", - ("read_only_space", 0x03519): "SelfReferenceMarker", - ("read_only_space", 0x03559): "BasicBlockCountersMarker", - ("read_only_space", 0x0359d): "OffHeapTrampolineRelocationInfo", - ("read_only_space", 0x035a9): "TrampolineTrivialCodeDataContainer", - ("read_only_space", 0x035b5): "TrampolinePromiseRejectionCodeDataContainer", - ("read_only_space", 0x035c1): "GlobalThisBindingScopeInfo", - ("read_only_space", 0x035f1): "EmptyFunctionScopeInfo", - ("read_only_space", 0x03615): "NativeScopeInfo", - ("read_only_space", 0x0362d): "HashSeed", - ("old_space", 0x04211): "ArgumentsIteratorAccessor", - ("old_space", 0x04255): "ArrayLengthAccessor", - ("old_space", 0x04299): "BoundFunctionLengthAccessor", - ("old_space", 0x042dd): "BoundFunctionNameAccessor", - ("old_space", 0x04321): "ErrorStackAccessor", - ("old_space", 0x04365): "FunctionArgumentsAccessor", - ("old_space", 0x043a9): "FunctionCallerAccessor", - ("old_space", 0x043ed): "FunctionNameAccessor", - ("old_space", 0x04431): "FunctionLengthAccessor", - ("old_space", 0x04475): "FunctionPrototypeAccessor", - ("old_space", 0x044b9): "StringLengthAccessor", - ("old_space", 0x044fd): "InvalidPrototypeValidityCell", - ("old_space", 0x04505): "EmptyScript", - ("old_space", 0x04545): "ManyClosuresCell", - ("old_space", 0x04551): "ArrayConstructorProtector", - ("old_space", 0x04565): "NoElementsProtector", - ("old_space", 0x04579): "MegaDOMProtector", - ("old_space", 0x0458d): "IsConcatSpreadableProtector", - ("old_space", 0x045a1): "ArraySpeciesProtector", - ("old_space", 0x045b5): "TypedArraySpeciesProtector", - ("old_space", 0x045c9): "PromiseSpeciesProtector", - ("old_space", 0x045dd): "RegExpSpeciesProtector", - ("old_space", 0x045f1): "StringLengthProtector", - ("old_space", 0x04605): "ArrayIteratorProtector", - ("old_space", 0x04619): "ArrayBufferDetachingProtector", - ("old_space", 0x0462d): "PromiseHookProtector", - ("old_space", 0x04641): "PromiseResolveProtector", - ("old_space", 0x04655): "MapIteratorProtector", - ("old_space", 0x04669): "PromiseThenProtector", - ("old_space", 0x0467d): "SetIteratorProtector", - ("old_space", 0x04691): "StringIteratorProtector", - ("old_space", 0x046a5): "SingleCharacterStringCache", - ("old_space", 0x04aad): "StringSplitCache", - ("old_space", 0x04eb5): "RegExpMultipleCache", - ("old_space", 0x052bd): "BuiltinsConstantsTable", - ("old_space", 0x056e5): "AsyncFunctionAwaitRejectSharedFun", - ("old_space", 0x05709): "AsyncFunctionAwaitResolveSharedFun", - ("old_space", 0x0572d): "AsyncGeneratorAwaitRejectSharedFun", - ("old_space", 0x05751): "AsyncGeneratorAwaitResolveSharedFun", - ("old_space", 0x05775): "AsyncGeneratorYieldResolveSharedFun", - ("old_space", 0x05799): "AsyncGeneratorReturnResolveSharedFun", - ("old_space", 0x057bd): "AsyncGeneratorReturnClosedRejectSharedFun", - ("old_space", 0x057e1): "AsyncGeneratorReturnClosedResolveSharedFun", - ("old_space", 0x05805): "AsyncIteratorValueUnwrapSharedFun", - ("old_space", 0x05829): "PromiseAllResolveElementSharedFun", - ("old_space", 0x0584d): "PromiseAllSettledResolveElementSharedFun", - ("old_space", 0x05871): "PromiseAllSettledRejectElementSharedFun", - ("old_space", 0x05895): "PromiseAnyRejectElementSharedFun", - ("old_space", 0x058b9): "PromiseCapabilityDefaultRejectSharedFun", - ("old_space", 0x058dd): "PromiseCapabilityDefaultResolveSharedFun", - ("old_space", 0x05901): "PromiseCatchFinallySharedFun", - ("old_space", 0x05925): "PromiseGetCapabilitiesExecutorSharedFun", - ("old_space", 0x05949): "PromiseThenFinallySharedFun", - ("old_space", 0x0596d): "PromiseThrowerFinallySharedFun", - ("old_space", 0x05991): "PromiseValueThunkFinallySharedFun", - ("old_space", 0x059b5): "ProxyRevokeSharedFun", + ("read_only_space", 0x021e9): "EmptyWeakArrayList", + ("read_only_space", 0x021f5): "EmptyDescriptorArray", + ("read_only_space", 0x0222d): "EmptyEnumCache", + ("read_only_space", 0x02261): "EmptyFixedArray", + ("read_only_space", 0x02269): "NullValue", + ("read_only_space", 0x02371): "UninitializedValue", + ("read_only_space", 0x023e9): "UndefinedValue", + ("read_only_space", 0x0242d): "NanValue", + ("read_only_space", 0x02461): "TheHoleValue", + ("read_only_space", 0x0248d): "HoleNanValue", + ("read_only_space", 0x024c1): "TrueValue", + ("read_only_space", 0x02501): "FalseValue", + ("read_only_space", 0x02531): "empty_string", + ("read_only_space", 0x0276d): "EmptyScopeInfo", + ("read_only_space", 0x027a5): "ArgumentsMarker", + ("read_only_space", 0x02805): "Exception", + ("read_only_space", 0x02861): "TerminationException", + ("read_only_space", 0x028c9): "OptimizedOut", + ("read_only_space", 0x02929): "StaleRegister", + ("read_only_space", 0x03429): "EmptyPropertyArray", + ("read_only_space", 0x03431): "EmptyByteArray", + ("read_only_space", 0x03439): "EmptyObjectBoilerplateDescription", + ("read_only_space", 0x0346d): "EmptyArrayBoilerplateDescription", + ("read_only_space", 0x03479): "EmptyClosureFeedbackCellArray", + ("read_only_space", 0x03481): "EmptySlowElementDictionary", + ("read_only_space", 0x034a5): "EmptyOrderedHashMap", + ("read_only_space", 0x034b9): "EmptyOrderedHashSet", + ("read_only_space", 0x034cd): "EmptyFeedbackMetadata", + ("read_only_space", 0x034d9): "EmptyPropertyDictionary", + ("read_only_space", 0x03501): "EmptyOrderedPropertyDictionary", + ("read_only_space", 0x03519): "EmptySwissPropertyDictionary", + ("read_only_space", 0x0356d): "NoOpInterceptorInfo", + ("read_only_space", 0x03595): "EmptyArrayList", + ("read_only_space", 0x035a1): "EmptyWeakFixedArray", + ("read_only_space", 0x035a9): "InfinityValue", + ("read_only_space", 0x035b5): "MinusZeroValue", + ("read_only_space", 0x035c1): "MinusInfinityValue", + ("read_only_space", 0x035cd): "SelfReferenceMarker", + ("read_only_space", 0x0360d): "BasicBlockCountersMarker", + ("read_only_space", 0x03651): "OffHeapTrampolineRelocationInfo", + ("read_only_space", 0x0365d): "GlobalThisBindingScopeInfo", + ("read_only_space", 0x0368d): "EmptyFunctionScopeInfo", + ("read_only_space", 0x036b1): "NativeScopeInfo", + ("read_only_space", 0x036c9): "HashSeed", + ("old_space", 0x04215): "ArgumentsIteratorAccessor", + ("old_space", 0x04259): "ArrayLengthAccessor", + ("old_space", 0x0429d): "BoundFunctionLengthAccessor", + ("old_space", 0x042e1): "BoundFunctionNameAccessor", + ("old_space", 0x04325): "ErrorStackAccessor", + ("old_space", 0x04369): "FunctionArgumentsAccessor", + ("old_space", 0x043ad): "FunctionCallerAccessor", + ("old_space", 0x043f1): "FunctionNameAccessor", + ("old_space", 0x04435): "FunctionLengthAccessor", + ("old_space", 0x04479): "FunctionPrototypeAccessor", + ("old_space", 0x044bd): "StringLengthAccessor", + ("old_space", 0x04501): "WrappedFunctionLengthAccessor", + ("old_space", 0x04545): "WrappedFunctionNameAccessor", + ("old_space", 0x04589): "InvalidPrototypeValidityCell", + ("old_space", 0x04591): "EmptyScript", + ("old_space", 0x045d1): "ManyClosuresCell", + ("old_space", 0x045dd): "ArrayConstructorProtector", + ("old_space", 0x045f1): "NoElementsProtector", + ("old_space", 0x04605): "MegaDOMProtector", + ("old_space", 0x04619): "IsConcatSpreadableProtector", + ("old_space", 0x0462d): "ArraySpeciesProtector", + ("old_space", 0x04641): "TypedArraySpeciesProtector", + ("old_space", 0x04655): "PromiseSpeciesProtector", + ("old_space", 0x04669): "RegExpSpeciesProtector", + ("old_space", 0x0467d): "StringLengthProtector", + ("old_space", 0x04691): "ArrayIteratorProtector", + ("old_space", 0x046a5): "ArrayBufferDetachingProtector", + ("old_space", 0x046b9): "PromiseHookProtector", + ("old_space", 0x046cd): "PromiseResolveProtector", + ("old_space", 0x046e1): "MapIteratorProtector", + ("old_space", 0x046f5): "PromiseThenProtector", + ("old_space", 0x04709): "SetIteratorProtector", + ("old_space", 0x0471d): "StringIteratorProtector", + ("old_space", 0x04731): "SingleCharacterStringCache", + ("old_space", 0x04b39): "StringSplitCache", + ("old_space", 0x04f41): "RegExpMultipleCache", + ("old_space", 0x05349): "BuiltinsConstantsTable", + ("old_space", 0x05775): "AsyncFunctionAwaitRejectSharedFun", + ("old_space", 0x05799): "AsyncFunctionAwaitResolveSharedFun", + ("old_space", 0x057bd): "AsyncGeneratorAwaitRejectSharedFun", + ("old_space", 0x057e1): "AsyncGeneratorAwaitResolveSharedFun", + ("old_space", 0x05805): "AsyncGeneratorYieldResolveSharedFun", + ("old_space", 0x05829): "AsyncGeneratorReturnResolveSharedFun", + ("old_space", 0x0584d): "AsyncGeneratorReturnClosedRejectSharedFun", + ("old_space", 0x05871): "AsyncGeneratorReturnClosedResolveSharedFun", + ("old_space", 0x05895): "AsyncIteratorValueUnwrapSharedFun", + ("old_space", 0x058b9): "PromiseAllResolveElementSharedFun", + ("old_space", 0x058dd): "PromiseAllSettledResolveElementSharedFun", + ("old_space", 0x05901): "PromiseAllSettledRejectElementSharedFun", + ("old_space", 0x05925): "PromiseAnyRejectElementSharedFun", + ("old_space", 0x05949): "PromiseCapabilityDefaultRejectSharedFun", + ("old_space", 0x0596d): "PromiseCapabilityDefaultResolveSharedFun", + ("old_space", 0x05991): "PromiseCatchFinallySharedFun", + ("old_space", 0x059b5): "PromiseGetCapabilitiesExecutorSharedFun", + ("old_space", 0x059d9): "PromiseThenFinallySharedFun", + ("old_space", 0x059fd): "PromiseThrowerFinallySharedFun", + ("old_space", 0x05a21): "PromiseValueThunkFinallySharedFun", + ("old_space", 0x05a45): "ProxyRevokeSharedFun", } # Lower 32 bits of first page addresses for various heap spaces. HEAP_FIRST_PAGES = { - 0x080c0000: "old_space", - 0x08100000: "map_space", - 0x08000000: "read_only_space", + 0x000c0000: "old_space", + 0x00100000: "map_space", + 0x00000000: "read_only_space", } # List of known V8 Frame Markers. @@ -556,7 +576,7 @@ "WASM", "WASM_TO_JS", "JS_TO_WASM", - "RETURN_PROMISE_ON_SUSPEND", + "STACK_SWITCH", "WASM_DEBUG_BREAK", "C_WASM_ENTRY", "WASM_EXIT", diff --git a/deps/v8/tools/v8windbg/BUILD.gn b/deps/v8/tools/v8windbg/BUILD.gn index e30b826b0f4962..5516a6109f2b1b 100644 --- a/deps/v8/tools/v8windbg/BUILD.gn +++ b/deps/v8/tools/v8windbg/BUILD.gn @@ -75,8 +75,8 @@ action("copy_prereqs") { outputs = [ "$root_out_dir/dbgeng.dll" ] args = [ - rebase_path("//build"), - rebase_path(root_out_dir), + rebase_path("//build", root_build_dir), + rebase_path(root_out_dir, root_build_dir), target_cpu, ] } diff --git a/deps/v8/tools/wasm/code-size-factors.py b/deps/v8/tools/wasm/code-size-factors.py new file mode 100755 index 00000000000000..57c691988ca61c --- /dev/null +++ b/deps/v8/tools/wasm/code-size-factors.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +# vim:fenc=utf-8:ts=2:sw=2:softtabstop=2:expandtab: +# Copyright 2022 the V8 project authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import sys +import re + +liftoff_regex = re.compile('^Compiled function .* using Liftoff, ' + '.*bodysize ([0-9]+) codesize ([0-9]+)$') +turbofan_regex = re.compile('^Compiled function .* using TurboFan, ' + '.*bodysize ([0-9]+) codesize ([0-9]+) ') +wasm2js_regex = re.compile('^Compiled WasmToJS wrapper .* ' + 'codesize ([0-9]+)$') + + +def main(): + print('Reading --trace-wasm-compilation-times lines from stdin...') + liftoff_values = [] + turbofan_values = [] + wasm2js_values = [] + for line in sys.stdin: + match(line, liftoff_regex, liftoff_values) + match(line, turbofan_regex, turbofan_values) + match_wasm2js(line, wasm2js_values) + + evaluate('Liftoff', liftoff_values) + evaluate('TurboFan', turbofan_values) + evaluate_wasm2js(wasm2js_values) + + +def match(line, regex, array): + m = regex.match(line) + if m: + array.append([int(m.group(1)), int(m.group(2))]) + + +def match_wasm2js(line, array): + m = wasm2js_regex.match(line) + if m: + array.append(int(m.group(1))) + + +def evaluate(name, values): + n = len(values) + if n == 0: + print(f'No values for {name}') + return + + print(f'Computing base and factor for {name} based on {n} values') + sum_xy = sum(x * y for [x, y] in values) + sum_x = sum(x for [x, y] in values) + sum_y = sum(y for [x, y] in values) + sum_xx = sum(x * x for [x, y] in values) + + factor = (n * sum_xy - sum_x * sum_y) / (n * sum_xx - sum_x * sum_x) + base = (sum_y - factor * sum_x) / n + + print(f'--> [{name}] Trend line: base: {base:.2f}, factor {factor:.2f}') + + min_y = min(y for [x, y] in values) + + simple_factor = (sum_y - n * min_y) / sum_x + print(f'--> [{name}] Simple analysis: Min {min_y}, ' + f'factor {simple_factor:.2f}') + + +def evaluate_wasm2js(values): + n = len(values) + if n == 0: + print('No wasm2js wrappers') + return + + print(f'--> [Wasm2js wrappers] {n} compiled, size min {min(values)}, ' + f'max {max(values)}, avg {(sum(values) / n):.2f}') + + +main() diff --git a/doc/.eslintrc.yaml b/doc/.eslintrc.yaml index fbc115669a7166..e8d24adb6e00aa 100644 --- a/doc/.eslintrc.yaml +++ b/doc/.eslintrc.yaml @@ -9,7 +9,6 @@ rules: symbol-description: off # Add new ECMAScript features gradually - no-var: error prefer-const: error prefer-rest-params: error prefer-template: error diff --git a/doc/README.md b/doc/README.md index a0f050ecd5cfc8..3d8ad6246839e6 100644 --- a/doc/README.md +++ b/doc/README.md @@ -35,6 +35,8 @@ this guide. (`[a link](http://example.com)`). * When documenting APIs, update the YAML comment associated with the API as appropriate. This is especially true when introducing or deprecating an API. +* When documenting APIs, every function should have a usage example or + link to an example that uses the function. * For code blocks: * Use [language][]-aware fences. (\`\`\`js) diff --git a/doc/abi_version_registry.json b/doc/abi_version_registry.json index 8f7690c283cc04..0ed54e8e717fe5 100644 --- a/doc/abi_version_registry.json +++ b/doc/abi_version_registry.json @@ -1,5 +1,6 @@ { "NODE_MODULE_VERSION": [ + { "modules": 108,"runtime": "node", "variant": "v8_10.1", "versions": "18.0.0" }, { "modules": 107,"runtime": "electron", "variant": "electron", "versions": "20" }, { "modules": 106,"runtime": "electron", "variant": "electron", "versions": "19" }, { "modules": 105,"runtime": "node", "variant": "v8_9.8", "versions": "18.0.0-pre" }, diff --git a/doc/api/addons.md b/doc/api/addons.md index 3635d5bd54d8fb..d047bd230b6d88 100644 --- a/doc/api/addons.md +++ b/doc/api/addons.md @@ -1367,7 +1367,7 @@ console.log(result); ``` [Electron]: https://electronjs.org/ -[Embedder's Guide]: https://github.com/v8/v8/wiki/Embedder's%20Guide +[Embedder's Guide]: https://v8.dev/docs/embed [Linking to libraries included with Node.js]: #linking-to-libraries-included-with-nodejs [Native Abstractions for Node.js]: https://github.com/nodejs/nan [V8]: https://v8.dev/ diff --git a/doc/api/assert.md b/doc/api/assert.md index 1987402958fe22..cc71195985791c 100644 --- a/doc/api/assert.md +++ b/doc/api/assert.md @@ -6,7 +6,7 @@ -The `assert` module provides a set of assertion functions for verifying +The `node:assert` module provides a set of assertion functions for verifying invariants. ## Strict assertion mode @@ -16,7 +16,7 @@ added: v9.9.0 changes: - version: v15.0.0 pr-url: https://github.com/nodejs/node/pull/34001 - description: Exposed as `require('assert/strict')`. + description: Exposed as `require('node:assert/strict')`. - version: - v13.9.0 - v12.16.2 @@ -42,25 +42,25 @@ assertion mode, error messages for objects display the objects, often truncated. To use strict assertion mode: ```mjs -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; ``` ```cjs -const assert = require('assert').strict; +const assert = require('node:assert').strict; ``` ```mjs -import assert from 'assert/strict'; +import assert from 'node:assert/strict'; ``` ```cjs -const assert = require('assert/strict'); +const assert = require('node:assert/strict'); ``` Example error diff: ```mjs -import { strict as assert } from 'assert'; +import { strict as assert } from 'node:assert'; assert.deepEqual([[[1, 2, 3]], 4, 5], [[[1, 2, '3']], 4, 5]); // AssertionError: Expected inputs to be strictly deep-equal: @@ -79,7 +79,7 @@ assert.deepEqual([[[1, 2, 3]], 4, 5], [[[1, 2, '3']], 4, 5]); ``` ```cjs -const assert = require('assert/strict'); +const assert = require('node:assert/strict'); assert.deepEqual([[[1, 2, 3]], 4, 5], [[[1, 2, '3']], 4, 5]); // AssertionError: Expected inputs to be strictly deep-equal: @@ -114,11 +114,11 @@ Legacy assertion mode uses the [`==` operator][] in: To use legacy assertion mode: ```mjs -import assert from 'assert'; +import assert from 'node:assert'; ``` ```cjs -const assert = require('assert'); +const assert = require('node:assert'); ``` Legacy assertion mode may have surprising results, especially when using @@ -133,8 +133,8 @@ assert.deepEqual(/a/gi, new Date()); * Extends: {errors.Error} -Indicates the failure of an assertion. All errors thrown by the `assert` module -will be instances of the `AssertionError` class. +Indicates the failure of an assertion. All errors thrown by the `node:assert` +module will be instances of the `AssertionError` class. ### `new assert.AssertionError(options)` @@ -166,7 +166,7 @@ and: * `operator` {string} Set to the passed in operator value. ```mjs -import assert from 'assert'; +import assert from 'node:assert'; // Generate an AssertionError to compare the error message later: const { message } = new assert.AssertionError({ @@ -191,7 +191,7 @@ try { ``` ```cjs -const assert = require('assert'); +const assert = require('node:assert'); // Generate an AssertionError to compare the error message later: const { message } = new assert.AssertionError({ @@ -241,8 +241,8 @@ for the verification to take place. The usual pattern would be to call it in a [`process.on('exit')`][] handler. ```mjs -import assert from 'assert'; -import process from 'process'; +import assert from 'node:assert'; +import process from 'node:process'; const tracker = new assert.CallTracker(); @@ -261,7 +261,7 @@ process.on('exit', () => { ``` ```cjs -const assert = require('assert'); +const assert = require('node:assert'); const tracker = new assert.CallTracker(); @@ -297,7 +297,7 @@ function has not been called exactly `exact` times when error. ```mjs -import assert from 'assert'; +import assert from 'node:assert'; // Creates call tracker. const tracker = new assert.CallTracker(); @@ -310,7 +310,7 @@ const callsfunc = tracker.calls(func); ``` ```cjs -const assert = require('assert'); +const assert = require('node:assert'); // Creates call tracker. const tracker = new assert.CallTracker(); @@ -344,7 +344,7 @@ The arrays contains information about the expected and actual number of calls of the functions that have not been called the expected number of times. ```mjs -import assert from 'assert'; +import assert from 'node:assert'; // Creates call tracker. const tracker = new assert.CallTracker(); @@ -372,7 +372,7 @@ tracker.report(); ``` ```cjs -const assert = require('assert'); +const assert = require('node:assert'); // Creates call tracker. const tracker = new assert.CallTracker(); @@ -412,7 +412,7 @@ Iterates through the list of functions passed to have not been called the expected number of times. ```mjs -import assert from 'assert'; +import assert from 'node:assert'; // Creates call tracker. const tracker = new assert.CallTracker(); @@ -430,7 +430,7 @@ tracker.verify(); ``` ```cjs -const assert = require('assert'); +const assert = require('node:assert'); // Creates call tracker. const tracker = new assert.CallTracker(); @@ -463,7 +463,7 @@ An alias of [`assert.ok()`][]. -The `async_hooks` module provides an API to track asynchronous resources. It -can be accessed using: +The `node:async_hooks` module provides an API to track asynchronous resources. +It can be accessed using: ```mjs -import async_hooks from 'async_hooks'; +import async_hooks from 'node:async_hooks'; ``` ```cjs -const async_hooks = require('async_hooks'); +const async_hooks = require('node:async_hooks'); ``` ## Terminology An asynchronous resource represents an object with an associated callback. -This callback may be called multiple times, for example, the `'connection'` +This callback may be called multiple times, such as the `'connection'` event in `net.createServer()`, or just a single time like in `fs.open()`. A resource can also be closed before the callback is called. `AsyncHook` does not explicitly distinguish between these different cases but will represent them @@ -34,7 +34,7 @@ interface, and each thread will use a new set of async IDs. Following is a simple overview of the public API. ```mjs -import async_hooks from 'async_hooks'; +import async_hooks from 'node:async_hooks'; // Return the ID of the current execution context. const eid = async_hooks.executionAsyncId(); @@ -59,30 +59,30 @@ asyncHook.disable(); // The following are the callbacks that can be passed to createHook(). // -// init is called during object construction. The resource may not have -// completed construction when this callback runs, therefore all fields of the +// init() is called during object construction. The resource may not have +// completed construction when this callback runs. Therefore, all fields of the // resource referenced by "asyncId" may not have been populated. function init(asyncId, type, triggerAsyncId, resource) { } -// Before is called just before the resource's callback is called. It can be +// before() is called just before the resource's callback is called. It can be // called 0-N times for handles (such as TCPWrap), and will be called exactly 1 // time for requests (such as FSReqCallback). function before(asyncId) { } -// After is called just after the resource's callback has finished. +// after() is called just after the resource's callback has finished. function after(asyncId) { } -// Destroy is called when the resource is destroyed. +// destroy() is called when the resource is destroyed. function destroy(asyncId) { } -// promiseResolve is called only for promise resources, when the -// `resolve` function passed to the `Promise` constructor is invoked +// promiseResolve() is called only for promise resources, when the +// resolve() function passed to the Promise constructor is invoked // (either directly or through other means of resolving a promise). function promiseResolve(asyncId) { } ``` ```cjs -const async_hooks = require('async_hooks'); +const async_hooks = require('node:async_hooks'); // Return the ID of the current execution context. const eid = async_hooks.executionAsyncId(); @@ -107,24 +107,24 @@ asyncHook.disable(); // The following are the callbacks that can be passed to createHook(). // -// init is called during object construction. The resource may not have -// completed construction when this callback runs, therefore all fields of the +// init() is called during object construction. The resource may not have +// completed construction when this callback runs. Therefore, all fields of the // resource referenced by "asyncId" may not have been populated. function init(asyncId, type, triggerAsyncId, resource) { } -// Before is called just before the resource's callback is called. It can be +// before() is called just before the resource's callback is called. It can be // called 0-N times for handles (such as TCPWrap), and will be called exactly 1 // time for requests (such as FSReqCallback). function before(asyncId) { } -// After is called just after the resource's callback has finished. +// after() is called just after the resource's callback has finished. function after(asyncId) { } -// Destroy is called when the resource is destroyed. +// destroy() is called when the resource is destroyed. function destroy(asyncId) { } -// promiseResolve is called only for promise resources, when the -// `resolve` function passed to the `Promise` constructor is invoked +// promiseResolve() is called only for promise resources, when the +// resolve() function passed to the Promise constructor is invoked // (either directly or through other means of resolving a promise). function promiseResolve(asyncId) { } ``` @@ -155,7 +155,7 @@ specifics of all functions that can be passed to `callbacks` is in the [Hook Callbacks][] section. ```mjs -import { createHook } from 'async_hooks'; +import { createHook } from 'node:async_hooks'; const asyncHook = createHook({ init(asyncId, type, triggerAsyncId, resource) { }, @@ -164,7 +164,7 @@ const asyncHook = createHook({ ``` ```cjs -const async_hooks = require('async_hooks'); +const async_hooks = require('node:async_hooks'); const asyncHook = async_hooks.createHook({ init(asyncId, type, triggerAsyncId, resource) { }, @@ -209,41 +209,41 @@ future. This is subject to change in the future if a comprehensive analysis is performed to ensure an exception can follow the normal control flow without unintentional side effects. -### Printing in AsyncHooks callbacks +### Printing in `AsyncHook` callbacks Because printing to the console is an asynchronous operation, `console.log()` -will cause the AsyncHooks callbacks to be called. Using `console.log()` or -similar asynchronous operations inside an AsyncHooks callback function will thus +will cause `AsyncHook` callbacks to be called. Using `console.log()` or +similar asynchronous operations inside an `AsyncHook` callback function will cause an infinite recursion. An easy solution to this when debugging is to use a synchronous logging operation such as `fs.writeFileSync(file, msg, flag)`. -This will print to the file and will not invoke AsyncHooks recursively because +This will print to the file and will not invoke `AsyncHook` recursively because it is synchronous. ```mjs -import { writeFileSync } from 'fs'; -import { format } from 'util'; +import { writeFileSync } from 'node:fs'; +import { format } from 'node:util'; function debug(...args) { - // Use a function like this one when debugging inside an AsyncHooks callback + // Use a function like this one when debugging inside an AsyncHook callback writeFileSync('log.out', `${format(...args)}\n`, { flag: 'a' }); } ``` ```cjs -const fs = require('fs'); -const util = require('util'); +const fs = require('node:fs'); +const util = require('node:util'); function debug(...args) { - // Use a function like this one when debugging inside an AsyncHooks callback + // Use a function like this one when debugging inside an AsyncHook callback fs.writeFileSync('log.out', `${util.format(...args)}\n`, { flag: 'a' }); } ``` If an asynchronous operation is needed for logging, it is possible to keep track of what caused the asynchronous operation using the information -provided by AsyncHooks itself. The logging should then be skipped when -it was the logging itself that caused AsyncHooks callback to call. By -doing this the otherwise infinite recursion is broken. +provided by `AsyncHook` itself. The logging should then be skipped when +it was the logging itself that caused the `AsyncHook` callback to be called. By +doing this, the otherwise infinite recursion is broken. ## Class: `AsyncHook` @@ -261,13 +261,13 @@ The `AsyncHook` instance is disabled by default. If the `AsyncHook` instance should be enabled immediately after creation, the following pattern can be used. ```mjs -import { createHook } from 'async_hooks'; +import { createHook } from 'node:async_hooks'; const hook = createHook(callbacks).enable(); ``` ```cjs -const async_hooks = require('async_hooks'); +const async_hooks = require('node:async_hooks'); const hook = async_hooks.createHook(callbacks).enable(); ``` @@ -307,7 +307,7 @@ closing it before the resource can be used. The following snippet demonstrates this. ```mjs -import { createServer } from 'net'; +import { createServer } from 'node:net'; createServer().listen(function() { this.close(); }); // OR @@ -315,7 +315,7 @@ clearTimeout(setTimeout(() => {}, 10)); ``` ```cjs -require('net').createServer().listen(function() { this.close(); }); +require('node:net').createServer().listen(function() { this.close(); }); // OR clearTimeout(setTimeout(() => {}, 10)); ``` @@ -361,9 +361,9 @@ created, while `triggerAsyncId` shows _why_ a resource was created. The following is a simple demonstration of `triggerAsyncId`: ```mjs -import { createHook, executionAsyncId } from 'async_hooks'; -import { stdout } from 'process'; -import net from 'net'; +import { createHook, executionAsyncId } from 'node:async_hooks'; +import { stdout } from 'node:process'; +import net from 'node:net'; createHook({ init(asyncId, type, triggerAsyncId) { @@ -378,9 +378,9 @@ net.createServer((conn) => {}).listen(8080); ``` ```cjs -const { createHook, executionAsyncId } = require('async_hooks'); -const { stdout } = require('process'); -const net = require('net'); +const { createHook, executionAsyncId } = require('node:async_hooks'); +const { stdout } = require('node:process'); +const net = require('node:net'); createHook({ init(asyncId, type, triggerAsyncId) { @@ -433,9 +433,9 @@ callback to `listen()` will look like. The output formatting is slightly more elaborate to make calling context easier to see. ```js -const async_hooks = require('async_hooks'); -const fs = require('fs'); -const net = require('net'); +const async_hooks = require('node:async_hooks'); +const fs = require('node:fs'); +const net = require('node:net'); const { fd } = process.stdout; let indent = 0; @@ -619,8 +619,8 @@ return an empty object as there is no handle or request object to use, but having an object representing the top-level can be helpful. ```mjs -import { open } from 'fs'; -import { executionAsyncId, executionAsyncResource } from 'async_hooks'; +import { open } from 'node:fs'; +import { executionAsyncId, executionAsyncResource } from 'node:async_hooks'; console.log(executionAsyncId(), executionAsyncResource()); // 1 {} open(new URL(import.meta.url), 'r', (err, fd) => { @@ -629,8 +629,8 @@ open(new URL(import.meta.url), 'r', (err, fd) => { ``` ```cjs -const { open } = require('fs'); -const { executionAsyncId, executionAsyncResource } = require('async_hooks'); +const { open } = require('node:fs'); +const { executionAsyncId, executionAsyncResource } = require('node:async_hooks'); console.log(executionAsyncId(), executionAsyncResource()); // 1 {} open(__filename, 'r', (err, fd) => { @@ -642,7 +642,7 @@ This can be used to implement continuation local storage without the use of a tracking `Map` to store the metadata: ```mjs -import { createServer } from 'http'; +import { createServer } from 'node:http'; import { executionAsyncId, executionAsyncResource, @@ -668,12 +668,12 @@ const server = createServer((req, res) => { ``` ```cjs -const { createServer } = require('http'); +const { createServer } = require('node:http'); const { executionAsyncId, executionAsyncResource, createHook -} = require('async_hooks'); +} = require('node:async_hooks'); const sym = Symbol('state'); // Private symbol to avoid pollution createHook({ @@ -707,7 +707,7 @@ changes: track when something calls. ```mjs -import { executionAsyncId } from 'async_hooks'; +import { executionAsyncId } from 'node:async_hooks'; console.log(executionAsyncId()); // 1 - bootstrap fs.open(path, 'r', (err, fd) => { @@ -716,7 +716,7 @@ fs.open(path, 'r', (err, fd) => { ``` ```cjs -const async_hooks = require('async_hooks'); +const async_hooks = require('node:async_hooks'); console.log(async_hooks.executionAsyncId()); // 1 - bootstrap fs.open(path, 'r', (err, fd) => { @@ -788,7 +788,7 @@ V8. This means that programs using promises or `async`/`await` will not get correct execution and trigger ids for promise callback contexts by default. ```mjs -import { executionAsyncId, triggerAsyncId } from 'async_hooks'; +import { executionAsyncId, triggerAsyncId } from 'node:async_hooks'; Promise.resolve(1729).then(() => { console.log(`eid ${executionAsyncId()} tid ${triggerAsyncId()}`); @@ -798,7 +798,7 @@ Promise.resolve(1729).then(() => { ``` ```cjs -const { executionAsyncId, triggerAsyncId } = require('async_hooks'); +const { executionAsyncId, triggerAsyncId } = require('node:async_hooks'); Promise.resolve(1729).then(() => { console.log(`eid ${executionAsyncId()} tid ${triggerAsyncId()}`); @@ -816,7 +816,7 @@ Installing async hooks via `async_hooks.createHook` enables promise execution tracking: ```mjs -import { createHook, executionAsyncId, triggerAsyncId } from 'async_hooks'; +import { createHook, executionAsyncId, triggerAsyncId } from 'node:async_hooks'; createHook({ init() {} }).enable(); // forces PromiseHooks to be enabled. Promise.resolve(1729).then(() => { console.log(`eid ${executionAsyncId()} tid ${triggerAsyncId()}`); @@ -826,7 +826,7 @@ Promise.resolve(1729).then(() => { ``` ```cjs -const { createHook, executionAsyncId, triggerAsyncId } = require('async_hooks'); +const { createHook, executionAsyncId, triggerAsyncId } = require('node:async_hooks'); createHook({ init() {} }).enable(); // forces PromiseHooks to be enabled. Promise.resolve(1729).then(() => { diff --git a/doc/api/buffer.md b/doc/api/buffer.md index 3f4c80015cf512..1bdc976fd12e42 100644 --- a/doc/api/buffer.md +++ b/doc/api/buffer.md @@ -17,7 +17,7 @@ While the `Buffer` class is available within the global scope, it is still recommended to explicitly reference it via an import or require statement. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; // Creates a zero-filled Buffer of length 10. const buf1 = Buffer.alloc(10); @@ -50,7 +50,7 @@ const buf7 = Buffer.from('tést', 'latin1'); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); // Creates a zero-filled Buffer of length 10. const buf1 = Buffer.alloc(10); @@ -104,7 +104,7 @@ specified. If no character encoding is specified, UTF-8 will be used as the default. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.from('hello world', 'utf8'); @@ -120,7 +120,7 @@ console.log(Buffer.from('fhqwhgads', 'utf16le')); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.from('hello world', 'utf8'); @@ -199,7 +199,7 @@ The following legacy character encodings are also supported: U+FFFF. In Node.js, these code points are always supported. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; Buffer.from('1ag123', 'hex'); // Prints , data truncated when first non-hexadecimal value @@ -213,7 +213,7 @@ Buffer.from('1634', 'hex'); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); Buffer.from('1ag123', 'hex'); // Prints , data truncated when first non-hexadecimal value @@ -265,7 +265,7 @@ There are two ways to create new [`TypedArray`][] instances from a `Buffer`: of the target type. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.from([1, 2, 3, 4]); const uint32array = new Uint32Array(buf); @@ -276,7 +276,7 @@ console.log(uint32array); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.from([1, 2, 3, 4]); const uint32array = new Uint32Array(buf); @@ -290,7 +290,7 @@ console.log(uint32array); [`TypedArray`][] that shares its memory with the `Buffer`. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.from('hello', 'utf16le'); const uint16array = new Uint16Array( @@ -304,7 +304,7 @@ console.log(uint16array); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.from('hello', 'utf16le'); const uint16array = new Uint16Array( @@ -323,7 +323,7 @@ memory as a [`TypedArray`][] instance by using the `TypedArray` object’s behaves like `new Uint8Array()` in this context. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const arr = new Uint16Array(2); @@ -350,7 +350,7 @@ console.log(buf2); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const arr = new Uint16Array(2); @@ -381,7 +381,7 @@ possible to use only a portion of the underlying [`ArrayBuffer`][] by passing in `byteOffset` and `length` parameters. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const arr = new Uint16Array(20); const buf = Buffer.from(arr.buffer, 0, 16); @@ -391,7 +391,7 @@ console.log(buf.length); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const arr = new Uint16Array(20); const buf = Buffer.from(arr.buffer, 0, 16); @@ -420,7 +420,7 @@ function: `Buffer` instances can be iterated over using `for..of` syntax: ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.from([1, 2, 3]); @@ -434,7 +434,7 @@ for (const b of buf) { ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.from([1, 2, 3]); @@ -457,7 +457,7 @@ added: - v15.7.0 - v14.18.0 changes: - - version: REPLACEME + - version: v18.0.0 pr-url: https://github.com/nodejs/node/pull/41270 description: No longer experimental. --> @@ -484,7 +484,7 @@ changes: * `options` {Object} * `endings` {string} One of either `'transparent'` or `'native'`. When set to `'native'`, line endings in string source parts will be converted to - the platform native line-ending as specified by `require('os').EOL`. + the platform native line-ending as specified by `require('node:os').EOL`. * `type` {string} The Blob content-type. The intent is for `type` to convey the MIME media type of the data, however no validation of the type format is performed. @@ -579,8 +579,8 @@ contained by the `Blob` is copied only when the `arrayBuffer()` or `text()` methods are called. ```mjs -import { Blob, Buffer } from 'buffer'; -import { setTimeout as delay } from 'timers/promises'; +import { Blob, Buffer } from 'node:buffer'; +import { setTimeout as delay } from 'node:timers/promises'; const blob = new Blob(['hello there']); @@ -606,8 +606,8 @@ blob.text().then(console.log); ``` ```cjs -const { Blob, Buffer } = require('buffer'); -const { setTimeout: delay } = require('timers/promises'); +const { Blob, Buffer } = require('node:buffer'); +const { setTimeout: delay } = require('node:timers/promises'); const blob = new Blob(['hello there']); @@ -670,7 +670,7 @@ Allocates a new `Buffer` of `size` bytes. If `fill` is `undefined`, the `Buffer` will be zero-filled. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.alloc(5); @@ -679,7 +679,7 @@ console.log(buf); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.alloc(5); @@ -695,7 +695,7 @@ If `fill` is specified, the allocated `Buffer` will be initialized by calling [`buf.fill(fill)`][`buf.fill()`]. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.alloc(5, 'a'); @@ -704,7 +704,7 @@ console.log(buf); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.alloc(5, 'a'); @@ -716,7 +716,7 @@ If both `fill` and `encoding` are specified, the allocated `Buffer` will be initialized by calling [`buf.fill(fill, encoding)`][`buf.fill()`]. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.alloc(11, 'aGVsbG8gd29ybGQ=', 'base64'); @@ -725,7 +725,7 @@ console.log(buf); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.alloc(11, 'aGVsbG8gd29ybGQ=', 'base64'); @@ -766,7 +766,7 @@ _may contain sensitive data_. Use [`Buffer.alloc()`][] instead to initialize `Buffer` instances with zeroes. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.allocUnsafe(10); @@ -780,7 +780,7 @@ console.log(buf); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.allocUnsafe(10); @@ -833,7 +833,7 @@ _may contain sensitive data_. Use [`buf.fill(0)`][`buf.fill()`] to initialize such `Buffer` instances with zeroes. When using [`Buffer.allocUnsafe()`][] to allocate new `Buffer` instances, -allocations under 4 KB are sliced from a single pre-allocated `Buffer`. This +allocations under 4 KiB are sliced from a single pre-allocated `Buffer`. This allows applications to avoid the garbage collection overhead of creating many individually allocated `Buffer` instances. This approach improves both performance and memory usage by eliminating the need to track and clean up as @@ -845,7 +845,7 @@ to create an un-pooled `Buffer` instance using `Buffer.allocUnsafeSlow()` and then copying out the relevant bits. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; // Need to keep around a few small chunks of memory. const store = []; @@ -865,7 +865,7 @@ socket.on('readable', () => { ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); // Need to keep around a few small chunks of memory. const store = []; @@ -916,7 +916,7 @@ return value might be greater than the length of a `Buffer` created from the string. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const str = '\u00bd + \u00bc = \u00be'; @@ -926,7 +926,7 @@ console.log(`${str}: ${str.length} characters, ` + ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const str = '\u00bd + \u00bc = \u00be'; @@ -959,7 +959,7 @@ Compares `buf1` to `buf2`, typically for the purpose of sorting arrays of [`buf1.compare(buf2)`][`buf.compare()`]. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf1 = Buffer.from('1234'); const buf2 = Buffer.from('0123'); @@ -971,7 +971,7 @@ console.log(arr.sort(Buffer.compare)); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf1 = Buffer.from('1234'); const buf2 = Buffer.from('0123'); @@ -1012,7 +1012,7 @@ combined length of the `Buffer`s in `list` exceeds `totalLength`, the result is truncated to `totalLength`. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; // Create a single `Buffer` from a list of three `Buffer` instances. @@ -1033,7 +1033,7 @@ console.log(bufA.length); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); // Create a single `Buffer` from a list of three `Buffer` instances. @@ -1068,14 +1068,14 @@ Allocates a new `Buffer` using an `array` of bytes in the range `0` – `255`. Array entries outside that range will be truncated to fit into it. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; // Creates a new Buffer containing the UTF-8 bytes of the string 'buffer'. const buf = Buffer.from([0x62, 0x75, 0x66, 0x66, 0x65, 0x72]); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); // Creates a new Buffer containing the UTF-8 bytes of the string 'buffer'. const buf = Buffer.from([0x62, 0x75, 0x66, 0x66, 0x65, 0x72]); @@ -1106,7 +1106,7 @@ memory. For example, when passed a reference to the `.buffer` property of a allocated memory as the [`TypedArray`][]'s underlying `ArrayBuffer`. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const arr = new Uint16Array(2); @@ -1127,7 +1127,7 @@ console.log(buf); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const arr = new Uint16Array(2); @@ -1151,7 +1151,7 @@ The optional `byteOffset` and `length` arguments specify a memory range within the `arrayBuffer` that will be shared by the `Buffer`. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const ab = new ArrayBuffer(10); const buf = Buffer.from(ab, 0, 2); @@ -1161,7 +1161,7 @@ console.log(buf.length); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const ab = new ArrayBuffer(10); const buf = Buffer.from(ab, 0, 2); @@ -1180,7 +1180,7 @@ of memory that extends beyond the bounds of a `TypedArray` view. A new beyond the range of the `TypedArray`: ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const arrA = Uint8Array.from([0x63, 0x64, 0x65, 0x66]); // 4 elements const arrB = new Uint8Array(arrA.buffer, 1, 2); // 2 elements @@ -1192,7 +1192,7 @@ console.log(buf); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const arrA = Uint8Array.from([0x63, 0x64, 0x65, 0x66]); // 4 elements const arrB = new Uint8Array(arrA.buffer, 1, 2); // 2 elements @@ -1215,7 +1215,7 @@ added: v5.10.0 Copies the passed `buffer` data onto a new `Buffer` instance. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf1 = Buffer.from('buffer'); const buf2 = Buffer.from(buf1); @@ -1229,7 +1229,7 @@ console.log(buf2.toString()); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf1 = Buffer.from('buffer'); const buf2 = Buffer.from(buf1); @@ -1259,14 +1259,14 @@ For objects whose `valueOf()` function returns a value not strictly equal to `object`, returns `Buffer.from(object.valueOf(), offsetOrEncoding, length)`. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.from(new String('this is a test')); // Prints: ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.from(new String('this is a test')); // Prints: @@ -1276,7 +1276,7 @@ For objects that support `Symbol.toPrimitive`, returns `Buffer.from(object[Symbol.toPrimitive]('string'), offsetOrEncoding)`. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; class Foo { [Symbol.toPrimitive]() { @@ -1289,7 +1289,7 @@ const buf = Buffer.from(new Foo(), 'utf8'); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); class Foo { [Symbol.toPrimitive]() { @@ -1317,7 +1317,7 @@ Creates a new `Buffer` containing `string`. The `encoding` parameter identifies the character encoding to be used when converting `string` into bytes. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf1 = Buffer.from('this is a tést'); const buf2 = Buffer.from('7468697320697320612074c3a97374', 'hex'); @@ -1331,7 +1331,7 @@ console.log(buf1.toString('latin1')); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf1 = Buffer.from('this is a tést'); const buf2 = Buffer.from('7468697320697320612074c3a97374', 'hex'); @@ -1359,7 +1359,7 @@ added: v0.1.101 Returns `true` if `obj` is a `Buffer`, `false` otherwise. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; Buffer.isBuffer(Buffer.alloc(10)); // true Buffer.isBuffer(Buffer.from('foo')); // true @@ -1369,7 +1369,7 @@ Buffer.isBuffer(new Uint8Array(1024)); // false ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); Buffer.isBuffer(Buffer.alloc(10)); // true Buffer.isBuffer(Buffer.from('foo')); // true @@ -1391,7 +1391,7 @@ Returns `true` if `encoding` is the name of a supported character encoding, or `false` otherwise. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; console.log(Buffer.isEncoding('utf8')); // Prints: true @@ -1407,7 +1407,7 @@ console.log(Buffer.isEncoding('')); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); console.log(Buffer.isEncoding('utf8')); // Prints: true @@ -1448,7 +1448,7 @@ access is the same as `Uint8Array`. In other words, `buf[index]` returns `>= buf.length`. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; // Copy an ASCII string into a `Buffer` one byte at a time. // (This only works for ASCII-only strings. In general, one should use @@ -1466,7 +1466,7 @@ console.log(buf.toString('utf8')); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); // Copy an ASCII string into a `Buffer` one byte at a time. // (This only works for ASCII-only strings. In general, one should use @@ -1492,7 +1492,7 @@ This `ArrayBuffer` is not guaranteed to correspond exactly to the original `Buffer`. See the notes on `buf.byteOffset` for details. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const arrayBuffer = new ArrayBuffer(16); const buffer = Buffer.from(arrayBuffer); @@ -1502,7 +1502,7 @@ console.log(buffer.buffer === arrayBuffer); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const arrayBuffer = new ArrayBuffer(16); const buffer = Buffer.from(arrayBuffer); @@ -1527,10 +1527,10 @@ A common issue when creating a `TypedArray` object that shares its memory with a `Buffer` is that in this case one needs to specify the `byteOffset` correctly: ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; // Create a buffer smaller than `Buffer.poolSize`. -const nodeBuffer = new Buffer.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); +const nodeBuffer = Buffer.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); // When casting the Node.js Buffer to an Int8Array, use the byteOffset // to refer only to the part of `nodeBuffer.buffer` that contains the memory @@ -1539,10 +1539,10 @@ new Int8Array(nodeBuffer.buffer, nodeBuffer.byteOffset, nodeBuffer.length); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); // Create a buffer smaller than `Buffer.poolSize`. -const nodeBuffer = new Buffer.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); +const nodeBuffer = Buffer.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); // When casting the Node.js Buffer to an Int8Array, use the byteOffset // to refer only to the part of `nodeBuffer.buffer` that contains the memory @@ -1584,7 +1584,7 @@ Comparison is based on the actual sequence of bytes in each `Buffer`. * `-1` is returned if `target` should come _after_ `buf` when sorted. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf1 = Buffer.from('ABC'); const buf2 = Buffer.from('BCD'); @@ -1606,7 +1606,7 @@ console.log([buf1, buf2, buf3].sort(Buffer.compare)); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf1 = Buffer.from('ABC'); const buf2 = Buffer.from('BCD'); @@ -1632,7 +1632,7 @@ arguments can be used to limit the comparison to specific ranges within `target` and `buf` respectively. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf1 = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8, 9]); const buf2 = Buffer.from([5, 6, 7, 8, 9, 1, 2, 3, 4]); @@ -1646,7 +1646,7 @@ console.log(buf1.compare(buf2, 5, 6, 5)); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf1 = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8, 9]); const buf2 = Buffer.from([5, 6, 7, 8, 9, 1, 2, 3, 4]); @@ -1685,7 +1685,7 @@ for all TypedArrays, including Node.js `Buffer`s, although it takes different function arguments. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; // Create two `Buffer` instances. const buf1 = Buffer.allocUnsafe(26); @@ -1706,7 +1706,7 @@ console.log(buf2.toString('ascii', 0, 25)); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); // Create two `Buffer` instances. const buf1 = Buffer.allocUnsafe(26); @@ -1727,7 +1727,7 @@ console.log(buf2.toString('ascii', 0, 25)); ``` ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; // Create a `Buffer` and copy data from one region to an overlapping region // within the same `Buffer`. @@ -1746,7 +1746,7 @@ console.log(buf.toString()); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); // Create a `Buffer` and copy data from one region to an overlapping region // within the same `Buffer`. @@ -1776,7 +1776,7 @@ Creates and returns an [iterator][] of `[index, byte]` pairs from the contents of `buf`. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; // Log the entire contents of a `Buffer`. @@ -1795,7 +1795,7 @@ for (const pair of buf.entries()) { ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); // Log the entire contents of a `Buffer`. @@ -1832,7 +1832,7 @@ Returns `true` if both `buf` and `otherBuffer` have exactly the same bytes, [`buf.compare(otherBuffer) === 0`][`buf.compare()`]. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf1 = Buffer.from('ABC'); const buf2 = Buffer.from('414243', 'hex'); @@ -1845,7 +1845,7 @@ console.log(buf1.equals(buf3)); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf1 = Buffer.from('ABC'); const buf2 = Buffer.from('414243', 'hex'); @@ -1894,7 +1894,7 @@ Fills `buf` with the specified `value`. If the `offset` and `end` are not given, the entire `buf` will be filled: ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; // Fill a `Buffer` with the ASCII character 'h'. @@ -1905,7 +1905,7 @@ console.log(b.toString()); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); // Fill a `Buffer` with the ASCII character 'h'. @@ -1923,7 +1923,7 @@ If the final write of a `fill()` operation falls on a multi-byte character, then only the bytes of that character that fit into `buf` are written: ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; // Fill a `Buffer` with character that takes up two bytes in UTF-8. @@ -1932,7 +1932,7 @@ console.log(Buffer.allocUnsafe(5).fill('\u0222')); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); // Fill a `Buffer` with character that takes up two bytes in UTF-8. @@ -1944,7 +1944,7 @@ If `value` contains invalid characters, it is truncated; if no valid fill data remains, an exception is thrown: ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.allocUnsafe(5); @@ -1957,7 +1957,7 @@ console.log(buf.fill('zz', 'hex')); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.allocUnsafe(5); @@ -1985,7 +1985,7 @@ added: v5.3.0 Equivalent to [`buf.indexOf() !== -1`][`buf.indexOf()`]. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.from('this is a buffer'); @@ -2006,7 +2006,7 @@ console.log(buf.includes('this', 4)); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.from('this is a buffer'); @@ -2061,7 +2061,7 @@ If `value` is: value between `0` and `255`. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.from('this is a buffer'); @@ -2087,7 +2087,7 @@ console.log(utf16Buffer.indexOf('\u03a3', -4, 'utf16le')); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.from('this is a buffer'); @@ -2121,7 +2121,7 @@ of coercion is `NaN` or `0`, then the entire buffer will be searched. This behavior matches [`String.prototype.indexOf()`][]. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const b = Buffer.from('abcdef'); @@ -2139,7 +2139,7 @@ console.log(b.indexOf('b', [])); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const b = Buffer.from('abcdef'); @@ -2171,7 +2171,7 @@ added: v1.1.0 Creates and returns an [iterator][] of `buf` keys (indices). ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.from('buffer'); @@ -2188,7 +2188,7 @@ for (const key of buf.keys()) { ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.from('buffer'); @@ -2228,7 +2228,7 @@ Identical to [`buf.indexOf()`][], except the last occurrence of `value` is found rather than the first occurrence. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.from('this buffer is a buffer'); @@ -2256,7 +2256,7 @@ console.log(utf16Buffer.lastIndexOf('\u03a3', -5, 'utf16le')); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.from('this buffer is a buffer'); @@ -2292,7 +2292,7 @@ that coerce to `NaN`, like `{}` or `undefined`, will search the whole buffer. This behavior matches [`String.prototype.lastIndexOf()`][]. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const b = Buffer.from('abcdef'); @@ -2313,7 +2313,7 @@ console.log(b.lastIndexOf('b', [])); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const b = Buffer.from('abcdef'); @@ -2346,7 +2346,7 @@ added: v0.1.90 Returns the number of bytes in `buf`. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; // Create a `Buffer` and write a shorter string to it using UTF-8. @@ -2362,7 +2362,7 @@ console.log(buf.length); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); // Create a `Buffer` and write a shorter string to it using UTF-8. @@ -2446,7 +2446,7 @@ Reads an unsigned, big-endian 64-bit integer from `buf` at the specified This function is also available under the `readBigUint64BE` alias. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.from([0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff]); @@ -2455,7 +2455,7 @@ console.log(buf.readBigUInt64BE(0)); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.from([0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff]); @@ -2487,7 +2487,7 @@ Reads an unsigned, little-endian 64-bit integer from `buf` at the specified This function is also available under the `readBigUint64LE` alias. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.from([0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff]); @@ -2496,7 +2496,7 @@ console.log(buf.readBigUInt64LE(0)); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.from([0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff]); @@ -2522,7 +2522,7 @@ changes: Reads a 64-bit, big-endian double from `buf` at the specified `offset`. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]); @@ -2531,7 +2531,7 @@ console.log(buf.readDoubleBE(0)); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]); @@ -2557,7 +2557,7 @@ changes: Reads a 64-bit, little-endian double from `buf` at the specified `offset`. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]); @@ -2568,7 +2568,7 @@ console.log(buf.readDoubleLE(1)); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]); @@ -2596,7 +2596,7 @@ changes: Reads a 32-bit, big-endian float from `buf` at the specified `offset`. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.from([1, 2, 3, 4]); @@ -2605,7 +2605,7 @@ console.log(buf.readFloatBE(0)); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.from([1, 2, 3, 4]); @@ -2631,7 +2631,7 @@ changes: Reads a 32-bit, little-endian float from `buf` at the specified `offset`. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.from([1, 2, 3, 4]); @@ -2642,7 +2642,7 @@ console.log(buf.readFloatLE(1)); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.from([1, 2, 3, 4]); @@ -2672,7 +2672,7 @@ Reads a signed 8-bit integer from `buf` at the specified `offset`. Integers read from a `Buffer` are interpreted as two's complement signed values. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.from([-1, 5]); @@ -2685,7 +2685,7 @@ console.log(buf.readInt8(2)); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.from([-1, 5]); @@ -2717,7 +2717,7 @@ Reads a signed, big-endian 16-bit integer from `buf` at the specified `offset`. Integers read from a `Buffer` are interpreted as two's complement signed values. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.from([0, 5]); @@ -2726,7 +2726,7 @@ console.log(buf.readInt16BE(0)); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.from([0, 5]); @@ -2755,7 +2755,7 @@ Reads a signed, little-endian 16-bit integer from `buf` at the specified Integers read from a `Buffer` are interpreted as two's complement signed values. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.from([0, 5]); @@ -2766,7 +2766,7 @@ console.log(buf.readInt16LE(1)); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.from([0, 5]); @@ -2796,7 +2796,7 @@ Reads a signed, big-endian 32-bit integer from `buf` at the specified `offset`. Integers read from a `Buffer` are interpreted as two's complement signed values. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.from([0, 0, 0, 5]); @@ -2805,7 +2805,7 @@ console.log(buf.readInt32BE(0)); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.from([0, 0, 0, 5]); @@ -2834,7 +2834,7 @@ Reads a signed, little-endian 32-bit integer from `buf` at the specified Integers read from a `Buffer` are interpreted as two's complement signed values. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.from([0, 0, 0, 5]); @@ -2845,7 +2845,7 @@ console.log(buf.readInt32LE(1)); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.from([0, 0, 0, 5]); @@ -2877,7 +2877,7 @@ and interprets the result as a big-endian, two's complement signed value supporting up to 48 bits of accuracy. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.from([0x12, 0x34, 0x56, 0x78, 0x90, 0xab]); @@ -2890,7 +2890,7 @@ console.log(buf.readIntBE(1, 0).toString(16)); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.from([0x12, 0x34, 0x56, 0x78, 0x90, 0xab]); @@ -2924,7 +2924,7 @@ and interprets the result as a little-endian, two's complement signed value supporting up to 48 bits of accuracy. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.from([0x12, 0x34, 0x56, 0x78, 0x90, 0xab]); @@ -2933,7 +2933,7 @@ console.log(buf.readIntLE(0, 6).toString(16)); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.from([0x12, 0x34, 0x56, 0x78, 0x90, 0xab]); @@ -2966,7 +2966,7 @@ Reads an unsigned 8-bit integer from `buf` at the specified `offset`. This function is also available under the `readUint8` alias. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.from([1, -2]); @@ -2979,7 +2979,7 @@ console.log(buf.readUInt8(2)); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.from([1, -2]); @@ -3017,7 +3017,7 @@ Reads an unsigned, big-endian 16-bit integer from `buf` at the specified This function is also available under the `readUint16BE` alias. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.from([0x12, 0x34, 0x56]); @@ -3028,7 +3028,7 @@ console.log(buf.readUInt16BE(1).toString(16)); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.from([0x12, 0x34, 0x56]); @@ -3064,7 +3064,7 @@ Reads an unsigned, little-endian 16-bit integer from `buf` at the specified This function is also available under the `readUint16LE` alias. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.from([0x12, 0x34, 0x56]); @@ -3077,7 +3077,7 @@ console.log(buf.readUInt16LE(2).toString(16)); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.from([0x12, 0x34, 0x56]); @@ -3115,7 +3115,7 @@ Reads an unsigned, big-endian 32-bit integer from `buf` at the specified This function is also available under the `readUint32BE` alias. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.from([0x12, 0x34, 0x56, 0x78]); @@ -3124,7 +3124,7 @@ console.log(buf.readUInt32BE(0).toString(16)); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.from([0x12, 0x34, 0x56, 0x78]); @@ -3158,7 +3158,7 @@ Reads an unsigned, little-endian 32-bit integer from `buf` at the specified This function is also available under the `readUint32LE` alias. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.from([0x12, 0x34, 0x56, 0x78]); @@ -3169,7 +3169,7 @@ console.log(buf.readUInt32LE(1).toString(16)); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.from([0x12, 0x34, 0x56, 0x78]); @@ -3208,7 +3208,7 @@ up to 48 bits of accuracy. This function is also available under the `readUintBE` alias. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.from([0x12, 0x34, 0x56, 0x78, 0x90, 0xab]); @@ -3219,7 +3219,7 @@ console.log(buf.readUIntBE(1, 6).toString(16)); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.from([0x12, 0x34, 0x56, 0x78, 0x90, 0xab]); @@ -3258,7 +3258,7 @@ up to 48 bits of accuracy. This function is also available under the `readUintLE` alias. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.from([0x12, 0x34, 0x56, 0x78, 0x90, 0xab]); @@ -3267,7 +3267,7 @@ console.log(buf.readUIntLE(0, 6).toString(16)); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.from([0x12, 0x34, 0x56, 0x78, 0x90, 0xab]); @@ -3298,7 +3298,7 @@ Modifying the new `Buffer` slice will modify the memory in the original `Buffer` because the allocated memory of the two objects overlap. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; // Create a `Buffer` with the ASCII alphabet, take a slice, and modify one byte // from the original `Buffer`. @@ -3322,7 +3322,7 @@ console.log(buf2.toString('ascii', 0, buf2.length)); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); // Create a `Buffer` with the ASCII alphabet, take a slice, and modify one byte // from the original `Buffer`. @@ -3349,7 +3349,7 @@ Specifying negative indexes causes the slice to be generated relative to the end of `buf` rather than the beginning. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.from('buffer'); @@ -3367,7 +3367,7 @@ console.log(buf.subarray(-5, -2).toString()); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.from('buffer'); @@ -3419,7 +3419,7 @@ which is a superclass of `Buffer`. To copy the slice, use `Uint8Array.prototype.slice()`. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.from('buffer'); @@ -3441,7 +3441,7 @@ console.log(buf.toString()); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.from('buffer'); @@ -3475,7 +3475,7 @@ byte order _in-place_. Throws [`ERR_INVALID_BUFFER_SIZE`][] if [`buf.length`][] is not a multiple of 2. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf1 = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8]); @@ -3494,7 +3494,7 @@ buf2.swap16(); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf1 = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8]); @@ -3516,14 +3516,14 @@ One convenient use of `buf.swap16()` is to perform a fast in-place conversion between UTF-16 little-endian and UTF-16 big-endian: ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.from('This is little-endian UTF-16', 'utf16le'); buf.swap16(); // Convert to big-endian UTF-16 text. ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.from('This is little-endian UTF-16', 'utf16le'); buf.swap16(); // Convert to big-endian UTF-16 text. @@ -3542,7 +3542,7 @@ byte order _in-place_. Throws [`ERR_INVALID_BUFFER_SIZE`][] if [`buf.length`][] is not a multiple of 4. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf1 = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8]); @@ -3561,7 +3561,7 @@ buf2.swap32(); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf1 = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8]); @@ -3591,7 +3591,7 @@ Interprets `buf` as an array of 64-bit numbers and swaps byte order _in-place_. Throws [`ERR_INVALID_BUFFER_SIZE`][] if [`buf.length`][] is not a multiple of 8. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf1 = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8]); @@ -3610,7 +3610,7 @@ buf2.swap64(); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf1 = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8]); @@ -3643,7 +3643,7 @@ this function when stringifying a `Buffer` instance. In particular, `Buffer.from(buf.toJSON())` works like `Buffer.from(buf)`. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5]); const json = JSON.stringify(buf); @@ -3662,7 +3662,7 @@ console.log(copy); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5]); const json = JSON.stringify(buf); @@ -3702,7 +3702,7 @@ The maximum length of a string instance (in UTF-16 code units) is available as [`buffer.constants.MAX_STRING_LENGTH`][]. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf1 = Buffer.allocUnsafe(26); @@ -3727,7 +3727,7 @@ console.log(buf2.toString(undefined, 0, 3)); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf1 = Buffer.allocUnsafe(26); @@ -3763,7 +3763,7 @@ Creates and returns an [iterator][] for `buf` values (bytes). This function is called automatically when a `Buffer` is used in a `for..of` statement. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.from('buffer'); @@ -3791,7 +3791,7 @@ for (const value of buf) { ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.from('buffer'); @@ -3838,7 +3838,7 @@ not contain enough space to fit the entire string, only part of `string` will be written. However, partially encoded characters will not be written. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.alloc(256); @@ -3856,7 +3856,7 @@ console.log(`${length} bytes: ${buffer.toString('utf8', 8, 10)}`); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.alloc(256); @@ -3891,7 +3891,7 @@ Writes `value` to `buf` at the specified `offset` as big-endian. `value` is interpreted and written as a two's complement signed integer. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.allocUnsafe(8); @@ -3902,7 +3902,7 @@ console.log(buf); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.allocUnsafe(8); @@ -3930,7 +3930,7 @@ Writes `value` to `buf` at the specified `offset` as little-endian. `value` is interpreted and written as a two's complement signed integer. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.allocUnsafe(8); @@ -3941,7 +3941,7 @@ console.log(buf); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.allocUnsafe(8); @@ -3975,7 +3975,7 @@ Writes `value` to `buf` at the specified `offset` as big-endian. This function is also available under the `writeBigUint64BE` alias. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.allocUnsafe(8); @@ -3986,7 +3986,7 @@ console.log(buf); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.allocUnsafe(8); @@ -4018,7 +4018,7 @@ changes: Writes `value` to `buf` at the specified `offset` as little-endian ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.allocUnsafe(8); @@ -4029,7 +4029,7 @@ console.log(buf); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.allocUnsafe(8); @@ -4062,7 +4062,7 @@ must be a JavaScript number. Behavior is undefined when `value` is anything other than a JavaScript number. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.allocUnsafe(8); @@ -4073,7 +4073,7 @@ console.log(buf); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.allocUnsafe(8); @@ -4104,7 +4104,7 @@ must be a JavaScript number. Behavior is undefined when `value` is anything other than a JavaScript number. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.allocUnsafe(8); @@ -4115,7 +4115,7 @@ console.log(buf); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.allocUnsafe(8); @@ -4145,7 +4145,7 @@ Writes `value` to `buf` at the specified `offset` as big-endian. Behavior is undefined when `value` is anything other than a JavaScript number. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.allocUnsafe(4); @@ -4156,7 +4156,7 @@ console.log(buf); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.allocUnsafe(4); @@ -4186,7 +4186,7 @@ Writes `value` to `buf` at the specified `offset` as little-endian. Behavior is undefined when `value` is anything other than a JavaScript number. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.allocUnsafe(4); @@ -4197,7 +4197,7 @@ console.log(buf); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.allocUnsafe(4); @@ -4230,7 +4230,7 @@ a signed 8-bit integer. `value` is interpreted and written as a two's complement signed integer. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.allocUnsafe(2); @@ -4242,7 +4242,7 @@ console.log(buf); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.allocUnsafe(2); @@ -4276,7 +4276,7 @@ anything other than a signed 16-bit integer. The `value` is interpreted and written as a two's complement signed integer. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.allocUnsafe(2); @@ -4287,7 +4287,7 @@ console.log(buf); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.allocUnsafe(2); @@ -4320,7 +4320,7 @@ anything other than a signed 16-bit integer. The `value` is interpreted and written as a two's complement signed integer. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.allocUnsafe(2); @@ -4331,7 +4331,7 @@ console.log(buf); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.allocUnsafe(2); @@ -4364,7 +4364,7 @@ anything other than a signed 32-bit integer. The `value` is interpreted and written as a two's complement signed integer. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.allocUnsafe(4); @@ -4375,7 +4375,7 @@ console.log(buf); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.allocUnsafe(4); @@ -4408,7 +4408,7 @@ anything other than a signed 32-bit integer. The `value` is interpreted and written as a two's complement signed integer. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.allocUnsafe(4); @@ -4419,7 +4419,7 @@ console.log(buf); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.allocUnsafe(4); @@ -4452,7 +4452,7 @@ as big-endian. Supports up to 48 bits of accuracy. Behavior is undefined when `value` is anything other than a signed integer. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.allocUnsafe(6); @@ -4463,7 +4463,7 @@ console.log(buf); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.allocUnsafe(6); @@ -4496,7 +4496,7 @@ as little-endian. Supports up to 48 bits of accuracy. Behavior is undefined when `value` is anything other than a signed integer. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.allocUnsafe(6); @@ -4507,7 +4507,7 @@ console.log(buf); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.allocUnsafe(6); @@ -4545,7 +4545,7 @@ other than an unsigned 8-bit integer. This function is also available under the `writeUint8` alias. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.allocUnsafe(4); @@ -4559,7 +4559,7 @@ console.log(buf); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.allocUnsafe(4); @@ -4600,7 +4600,7 @@ is anything other than an unsigned 16-bit integer. This function is also available under the `writeUint16BE` alias. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.allocUnsafe(4); @@ -4612,7 +4612,7 @@ console.log(buf); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.allocUnsafe(4); @@ -4651,7 +4651,7 @@ anything other than an unsigned 16-bit integer. This function is also available under the `writeUint16LE` alias. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.allocUnsafe(4); @@ -4663,7 +4663,7 @@ console.log(buf); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.allocUnsafe(4); @@ -4702,7 +4702,7 @@ is anything other than an unsigned 32-bit integer. This function is also available under the `writeUint32BE` alias. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.allocUnsafe(4); @@ -4713,7 +4713,7 @@ console.log(buf); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.allocUnsafe(4); @@ -4751,7 +4751,7 @@ anything other than an unsigned 32-bit integer. This function is also available under the `writeUint32LE` alias. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.allocUnsafe(4); @@ -4762,7 +4762,7 @@ console.log(buf); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.allocUnsafe(4); @@ -4802,7 +4802,7 @@ when `value` is anything other than an unsigned integer. This function is also available under the `writeUintBE` alias. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.allocUnsafe(6); @@ -4813,7 +4813,7 @@ console.log(buf); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.allocUnsafe(6); @@ -4853,7 +4853,7 @@ when `value` is anything other than an unsigned integer. This function is also available under the `writeUintLE` alias. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const buf = Buffer.allocUnsafe(6); @@ -4864,7 +4864,7 @@ console.log(buf); ``` ```cjs -const { Buffer } = require('buffer'); +const { Buffer } = require('node:buffer'); const buf = Buffer.allocUnsafe(6); @@ -5009,11 +5009,11 @@ changes: See [`Buffer.from(string[, encoding])`][`Buffer.from(string)`]. -## `buffer` module APIs +## `node:buffer` module APIs While, the `Buffer` object is available as a global, there are additional -`Buffer`-related APIs that are available only via the `buffer` module -accessed using `require('buffer')`. +`Buffer`-related APIs that are available only via the `node:buffer` module +accessed using `require('node:buffer')`. ### `buffer.atob(data)` @@ -5138,7 +5138,7 @@ The transcoding process will use substitution characters if a given byte sequence cannot be adequately represented in the target encoding. For instance: ```mjs -import { Buffer, transcode } from 'buffer'; +import { Buffer, transcode } from 'node:buffer'; const newBuf = transcode(Buffer.from('€'), 'utf8', 'ascii'); console.log(newBuf.toString('ascii')); @@ -5146,7 +5146,7 @@ console.log(newBuf.toString('ascii')); ``` ```cjs -const { Buffer, transcode } = require('buffer'); +const { Buffer, transcode } = require('node:buffer'); const newBuf = transcode(Buffer.from('€'), 'utf8', 'ascii'); console.log(newBuf.toString('ascii')); @@ -5204,9 +5204,9 @@ changes: * {integer} The largest size allowed for a single `Buffer` instance. On 32-bit architectures, this value currently is 230 - 1 (about 1 -GB). +GiB). -On 64-bit architectures, this value currently is 232 (about 4 GB). +On 64-bit architectures, this value currently is 232 (about 4 GiB). It reflects [`v8::TypedArray::kMaxLength`][] under the hood. diff --git a/doc/api/child_process.md b/doc/api/child_process.md index f92636a7ebce44..0f98613f729891 100644 --- a/doc/api/child_process.md +++ b/doc/api/child_process.md @@ -6,12 +6,12 @@ -The `child_process` module provides the ability to spawn subprocesses in +The `node:child_process` module provides the ability to spawn subprocesses in a manner that is similar, but not identical, to popen(3). This capability is primarily provided by the [`child_process.spawn()`][] function: ```js -const { spawn } = require('child_process'); +const { spawn } = require('node:child_process'); const ls = spawn('ls', ['-lh', '/usr']); ls.stdout.on('data', (data) => { @@ -36,8 +36,11 @@ identical to the behavior of pipes in the shell. Use the `{ stdio: 'ignore' }` option if the output will not be consumed. The command lookup is performed using the `options.env.PATH` environment -variable if it is in the `options` object. Otherwise, `process.env.PATH` is -used. +variable if `env` is in the `options` object. Otherwise, `process.env.PATH` is +used. If `options.env` is set without `PATH`, lookup on Unix is performed +on a default search path search of `/usr/bin:/bin` (see your operating system's +manual for execvpe/execvp), on Windows the current processes environment +variable `PATH` is used. On Windows, environment variables are case-insensitive. Node.js lexicographically sorts the `env` keys and uses the first one that @@ -51,8 +54,8 @@ without blocking the Node.js event loop. The [`child_process.spawnSync()`][] function provides equivalent functionality in a synchronous manner that blocks the event loop until the spawned process either exits or is terminated. -For convenience, the `child_process` module provides a handful of synchronous -and asynchronous alternatives to [`child_process.spawn()`][] and +For convenience, the `node:child_process` module provides a handful of +synchronous and asynchronous alternatives to [`child_process.spawn()`][] and [`child_process.spawnSync()`][]. Each of these alternatives are implemented on top of [`child_process.spawn()`][] or [`child_process.spawnSync()`][]. @@ -107,7 +110,7 @@ spaces it needs to be quoted. ```js // On Windows Only... -const { spawn } = require('child_process'); +const { spawn } = require('node:child_process'); const bat = spawn('cmd.exe', ['/c', 'my.bat']); bat.stdout.on('data', (data) => { @@ -125,7 +128,7 @@ bat.on('exit', (code) => { ```js // OR... -const { exec, spawn } = require('child_process'); +const { exec, spawn } = require('node:child_process'); exec('my.bat', (err, stdout, stderr) => { if (err) { console.error(err); @@ -195,7 +198,7 @@ directly by the shell and special characters (vary based on need to be dealt with accordingly: ```js -const { exec } = require('child_process'); +const { exec } = require('node:child_process'); exec('"/path/to/test file/test.sh" arg1 arg2'); // Double quotes are used so that the space in the path is not interpreted as @@ -223,7 +226,7 @@ stderr output. If `encoding` is `'buffer'`, or an unrecognized character encoding, `Buffer` objects will be passed to the callback instead. ```js -const { exec } = require('child_process'); +const { exec } = require('node:child_process'); exec('cat *.js missing_file | wc -l', (error, stdout, stderr) => { if (error) { console.error(`exec error: ${error}`); @@ -249,8 +252,8 @@ rejected promise is returned, with the same `error` object given in the callback, but with two additional properties `stdout` and `stderr`. ```js -const util = require('util'); -const exec = util.promisify(require('child_process').exec); +const util = require('node:util'); +const exec = util.promisify(require('node:child_process').exec); async function lsExample() { const { stdout, stderr } = await exec('ls'); @@ -265,7 +268,7 @@ If the `signal` option is enabled, calling `.abort()` on the corresponding the error passed to the callback will be an `AbortError`: ```js -const { exec } = require('child_process'); +const { exec } = require('node:child_process'); const controller = new AbortController(); const { signal } = controller; const child = exec('grep ssh', { signal }, (error) => { @@ -335,7 +338,7 @@ not spawned, behaviors such as I/O redirection and file globbing are not supported. ```js -const { execFile } = require('child_process'); +const { execFile } = require('node:child_process'); const child = execFile('node', ['--version'], (error, stdout, stderr) => { if (error) { throw error; @@ -359,8 +362,8 @@ rejected promise is returned, with the same `error` object given in the callback, but with two additional properties `stdout` and `stderr`. ```js -const util = require('util'); -const execFile = util.promisify(require('child_process').execFile); +const util = require('node:util'); +const execFile = util.promisify(require('node:child_process').execFile); async function getVersion() { const { stdout } = await execFile('node', ['--version']); console.log(stdout); @@ -377,7 +380,7 @@ If the `signal` option is enabled, calling `.abort()` on the corresponding the error passed to the callback will be an `AbortError`: ```js -const { execFile } = require('child_process'); +const { execFile } = require('node:child_process'); const controller = new AbortController(); const { signal } = controller; const child = execFile('node', ['--version'], { signal }, (error) => { @@ -503,7 +506,7 @@ if (process.argv[2] === 'child') { console.log(`Hello from ${process.argv[2]}!`); }, 1_000); } else { - const { fork } = require('child_process'); + const { fork } = require('node:child_process'); const controller = new AbortController(); const { signal } = controller; const child = fork(__filename, ['child'], { signal }); @@ -622,7 +625,7 @@ Example of running `ls -lh /usr`, capturing `stdout`, `stderr`, and the exit code: ```js -const { spawn } = require('child_process'); +const { spawn } = require('node:child_process'); const ls = spawn('ls', ['-lh', '/usr']); ls.stdout.on('data', (data) => { @@ -641,7 +644,7 @@ ls.on('close', (code) => { Example: A very elaborate way to run `ps ax | grep ssh` ```js -const { spawn } = require('child_process'); +const { spawn } = require('node:child_process'); const ps = spawn('ps', ['ax']); const grep = spawn('grep', ['ssh']); @@ -678,7 +681,7 @@ grep.on('close', (code) => { Example of checking for failed `spawn`: ```js -const { spawn } = require('child_process'); +const { spawn } = require('node:child_process'); const subprocess = spawn('bad_command'); subprocess.on('error', (err) => { @@ -699,7 +702,7 @@ If the `signal` option is enabled, calling `.abort()` on the corresponding the error passed to the callback will be an `AbortError`: ```js -const { spawn } = require('child_process'); +const { spawn } = require('node:child_process'); const controller = new AbortController(); const { signal } = controller; const grep = spawn('grep', ['ssh'], { signal }); @@ -742,7 +745,7 @@ Example of a long-running process, by detaching and also ignoring its parent `stdio` file descriptors, in order to ignore the parent's termination: ```js -const { spawn } = require('child_process'); +const { spawn } = require('node:child_process'); const subprocess = spawn(process.argv[0], ['child_program.js'], { detached: true, @@ -755,8 +758,8 @@ subprocess.unref(); Alternatively one can redirect the child process' output into files: ```js -const fs = require('fs'); -const { spawn } = require('child_process'); +const fs = require('node:fs'); +const { spawn } = require('node:child_process'); const out = fs.openSync('./out.log', 'a'); const err = fs.openSync('./out.log', 'a'); @@ -807,6 +810,9 @@ pipes between the parent and child. The value is one of the following: `child_process` object as [`subprocess.stdio[fd]`][`subprocess.stdio`]. Pipes created for fds 0, 1, and 2 are also available as [`subprocess.stdin`][], [`subprocess.stdout`][] and [`subprocess.stderr`][], respectively. + Currently, these are not actual Unix pipes and therefore the child process + can not use them by their descriptor files, + e.g. `/dev/fd/2` or `/dev/stdout`. 2. `'overlapped'`: Same as `'pipe'` except that the `FILE_FLAG_OVERLAPPED` flag is set on the handle. This is necessary for overlapped I/O on the child process's stdio handles. See the @@ -847,7 +853,7 @@ pipes between the parent and child. The value is one of the following: default is `'ignore'`. ```js -const { spawn } = require('child_process'); +const { spawn } = require('node:child_process'); // Child will use parent's stdios. spawn('prg', [], { stdio: 'inherit' }); @@ -1146,7 +1152,7 @@ streams. The `'close'` event will always emit after [`'exit'`][] was already emitted, or [`'error'`][] if the child failed to spawn. ```js -const { spawn } = require('child_process'); +const { spawn } = require('node:child_process'); const ls = spawn('ls', ['-lh', '/usr']); ls.stdout.on('data', (data) => { @@ -1342,7 +1348,7 @@ signal(7) for a list of available signals. This function returns `true` if kill(2) succeeds, and `false` otherwise. ```js -const { spawn } = require('child_process'); +const { spawn } = require('node:child_process'); const grep = spawn('grep', ['ssh']); grep.on('close', (code, signal) => { @@ -1376,7 +1382,7 @@ new process in a shell or with the use of the `shell` option of `ChildProcess`: ```js 'use strict'; -const { spawn } = require('child_process'); +const { spawn } = require('node:child_process'); const subprocess = spawn( 'sh', @@ -1421,7 +1427,7 @@ fails to spawn due to errors, then the value is `undefined` and `error` is emitted. ```js -const { spawn } = require('child_process'); +const { spawn } = require('node:child_process'); const grep = spawn('grep', ['ssh']); console.log(`Spawned child pid: ${grep.pid}`); @@ -1439,7 +1445,7 @@ restore the removed reference count for the child process, forcing the parent to wait for the child to exit before exiting itself. ```js -const { spawn } = require('child_process'); +const { spawn } = require('node:child_process'); const subprocess = spawn(process.argv[0], ['child_program.js'], { detached: true, @@ -1489,7 +1495,7 @@ message might not be the same as what is originally sent. For example, in the parent script: ```js -const cp = require('child_process'); +const cp = require('node:child_process'); const n = cp.fork(`${__dirname}/sub.js`); n.on('message', (m) => { @@ -1547,10 +1553,10 @@ The `sendHandle` argument can be used, for instance, to pass the handle of a TCP server object to the child process as illustrated in the example below: ```js -const subprocess = require('child_process').fork('subprocess.js'); +const subprocess = require('node:child_process').fork('subprocess.js'); // Open up the server object and send the handle. -const server = require('net').createServer(); +const server = require('node:net').createServer(); server.on('connection', (socket) => { socket.end('handled by parent'); }); @@ -1574,11 +1580,11 @@ process.on('message', (m, server) => { Once the server is now shared between the parent and child, some connections can be handled by the parent and some by the child. -While the example above uses a server created using the `net` module, `dgram` -module servers use exactly the same workflow with the exceptions of listening on -a `'message'` event instead of `'connection'` and using `server.bind()` instead -of `server.listen()`. This is, however, currently only supported on Unix -platforms. +While the example above uses a server created using the `node:net` module, +`node:dgram` module servers use exactly the same workflow with the exceptions of +listening on a `'message'` event instead of `'connection'` and using +`server.bind()` instead of `server.listen()`. This is, however, currently only +supported on Unix platforms. #### Example: sending a socket object @@ -1587,13 +1593,13 @@ socket to the child process. The example below spawns two children that each handle connections with "normal" or "special" priority: ```js -const { fork } = require('child_process'); +const { fork } = require('node:child_process'); const normal = fork('subprocess.js', ['normal']); const special = fork('subprocess.js', ['special']); // Open up the server and send sockets to child. Use pauseOnConnect to prevent // the sockets from being read before they are sent to the child process. -const server = require('net').createServer({ pauseOnConnect: true }); +const server = require('node:net').createServer({ pauseOnConnect: true }); server.on('connection', (socket) => { // If this is special priority... @@ -1718,9 +1724,9 @@ pipe, so only the parent's `subprocess.stdio[1]` is a stream, all other values in the array are `null`. ```js -const assert = require('assert'); -const fs = require('fs'); -const child_process = require('child_process'); +const assert = require('node:assert'); +const fs = require('node:fs'); +const child_process = require('node:child_process'); const subprocess = child_process.spawn('ls', { stdio: [ @@ -1760,7 +1766,7 @@ then this will be `null`. refer to the same value. ```js -const { spawn } = require('child_process'); +const { spawn } = require('node:child_process'); const subprocess = spawn('ls'); @@ -1786,7 +1792,7 @@ independently of the child, unless there is an established IPC channel between the child and the parent. ```js -const { spawn } = require('child_process'); +const { spawn } = require('node:child_process'); const subprocess = spawn(process.argv[0], ['child_program.js'], { detached: true, @@ -1827,7 +1833,7 @@ added: --> Child processes support a serialization mechanism for IPC that is based on the -[serialization API of the `v8` module][v8.serdes], based on the +[serialization API of the `node:v8` module][v8.serdes], based on the [HTML structured clone algorithm][]. This is generally more powerful and supports more built-in JavaScript object types, such as `BigInt`, `Map` and `Set`, `ArrayBuffer` and `TypedArray`, `Buffer`, `Error`, `RegExp` etc. diff --git a/doc/api/cli.md b/doc/api/cli.md index 8bfb1e9792ace5..3db570deb695bc 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -98,7 +98,7 @@ analysis using a debugger (such as `lldb`, `gdb`, and `mdb`). If this flag is passed, the behavior can still be set to not abort through [`process.setUncaughtExceptionCaptureCallback()`][] (and through usage of the -`domain` module that uses it). +`node:domain` module that uses it). ### `--completion-bash` @@ -226,7 +226,7 @@ added: v9.8.0 Make built-in language features like `eval` and `new Function` that generate code from strings throw an exception instead. This does not affect the Node.js -`vm` module. +`node:vm` module. ### `--dns-result-order=order` @@ -301,7 +301,12 @@ Enable experimental `import.meta.resolve()` support. ### `--experimental-loader=module` Specify the `module` of a custom experimental [ECMAScript module loader][]. @@ -328,7 +333,7 @@ Use the specified file as a security policy. ### `--no-experimental-fetch` Disable experimental support for the [Fetch API][]. @@ -364,7 +369,7 @@ See [customizing ESM specifier resolution][] for example usage. added: v9.6.0 --> -Enable experimental ES Module support in the `vm` module. +Enable experimental ES Module support in the `node:vm` module. ### `--experimental-wasi-unstable-preview1` @@ -437,7 +442,7 @@ heap limit. `count` should be a non-negative integer (in which case Node.js will write no more than `max_count` snapshots to disk). When generating snapshots, garbage collection may be triggered and bring -the heap usage down, therefore multiple snapshots may be written to disk +the heap usage down. Therefore multiple snapshots may be written to disk before the Node.js instance finally runs out of memory. These heap snapshots can be compared to determine what objects are being allocated during the time consecutive snapshots are taken. It's not guaranteed that Node.js will @@ -666,10 +671,10 @@ added: changes: - version: v13.13.0 pr-url: https://github.com/nodejs/node/pull/32520 - description: Change maximum default size of HTTP headers from 8 KB to 16 KB. + description: Change maximum default size of HTTP headers from 8 KiB to 16 KiB. --> -Specify the maximum size, in bytes, of HTTP headers. Defaults to 16 KB. +Specify the maximum size, in bytes, of HTTP headers. Defaults to 16 KiB. ### `--napi-modules` @@ -1052,6 +1057,25 @@ minimum allocation from the secure heap. The minimum value is `2`. The maximum value is the lesser of `--secure-heap` or `2147483647`. The value given must be a power of two. +### `--test` + + + +Starts the Node.js command line test runner. This flag cannot be combined with +`--check`, `--eval`, `--interactive`, or the inspector. See the documentation +on [running tests from the command line][] for more details. + +### `--test-only` + + + +Configures the test runner to only execute top level tests that have the `only` +option set. + ### `--throw-deprecation` -* {number} +* {integer} Each new worker is given its own unique id, this id is stored in the `id`. @@ -505,10 +505,10 @@ This function returns `true` if the worker's process has terminated (either because of exiting or being signaled). Otherwise, it returns `false`. ```mjs -import cluster from 'cluster'; -import http from 'http'; -import { cpus } from 'os'; -import process from 'process'; +import cluster from 'node:cluster'; +import http from 'node:http'; +import { cpus } from 'node:os'; +import process from 'node:process'; const numCPUs = cpus().length; @@ -538,10 +538,10 @@ if (cluster.isPrimary) { ``` ```cjs -const cluster = require('cluster'); -const http = require('http'); -const numCPUs = require('os').cpus().length; -const process = require('process'); +const cluster = require('node:cluster'); +const http = require('node:http'); +const numCPUs = require('node:os').cpus().length; +const process = require('node:process'); if (cluster.isPrimary) { console.log(`Primary ${process.pid} is running`); @@ -982,7 +982,7 @@ The defaults above apply to the first call only; the defaults for later calls are the current values at the time of `cluster.setupPrimary()` is called. ```mjs -import cluster from 'cluster'; +import cluster from 'node:cluster'; cluster.setupPrimary({ exec: 'worker.js', @@ -998,7 +998,7 @@ cluster.fork(); // http worker ``` ```cjs -const cluster = require('cluster'); +const cluster = require('node:cluster'); cluster.setupPrimary({ exec: 'worker.js', @@ -1026,7 +1026,7 @@ added: v0.7.0 A reference to the current worker object. Not available in the primary process. ```mjs -import cluster from 'cluster'; +import cluster from 'node:cluster'; if (cluster.isPrimary) { console.log('I am primary'); @@ -1038,7 +1038,7 @@ if (cluster.isPrimary) { ``` ```cjs -const cluster = require('cluster'); +const cluster = require('node:cluster'); if (cluster.isPrimary) { console.log('I am primary'); @@ -1067,7 +1067,7 @@ advance. However, it is guaranteed that the removal from the `cluster.workers` list happens before the last `'disconnect'` or `'exit'` event is emitted. ```mjs -import cluster from 'cluster'; +import cluster from 'node:cluster'; for (const worker of Object.values(cluster.workers)) { worker.send('big announcement to all workers'); @@ -1075,7 +1075,7 @@ for (const worker of Object.values(cluster.workers)) { ``` ```cjs -const cluster = require('cluster'); +const cluster = require('node:cluster'); for (const worker of Object.values(cluster.workers)) { worker.send('big announcement to all workers'); diff --git a/doc/api/console.md b/doc/api/console.md index bd890e20a79a59..685fe370a04028 100644 --- a/doc/api/console.md +++ b/doc/api/console.md @@ -6,8 +6,8 @@ -The `console` module provides a simple debugging console that is similar to the -JavaScript console mechanism provided by web browsers. +The `node:console` module provides a simple debugging console that is similar to +the JavaScript console mechanism provided by web browsers. The module exports two specific components: @@ -15,7 +15,7 @@ The module exports two specific components: `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`][] and [`process.stderr`][]. The global `console` can be used without calling - `require('console')`. + `require('node:console')`. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently @@ -77,11 +77,11 @@ changes: The `Console` class can be used to create a simple logger with configurable -output streams and can be accessed using either `require('console').Console` +output streams and can be accessed using either `require('node:console').Console` or `console.Console` (or their destructured counterparts): ```js -const { Console } = require('console'); +const { Console } = require('node:console'); ``` ```js diff --git a/doc/api/corepack.md b/doc/api/corepack.md index 0f38a998e60fde..c5525108c41826 100644 --- a/doc/api/corepack.md +++ b/doc/api/corepack.md @@ -33,14 +33,14 @@ This feature simplifies two core workflows: ### Enabling the feature Due to its experimental status, Corepack currently needs to be explicitly -enabled to have any effect. To do that simply run [`corepack enable`][], which -will set up the symlinks in your environment, next to the `node` binary +enabled to have any effect. To do that, run [`corepack enable`][], which +will set up the symlinks in your environment next to the `node` binary (and overwrite the existing symlinks if necessary). From this point forward, any call to the [supported binaries][] will work -without further setup. Should you experience a problem, just run +without further setup. Should you experience a problem, run [`corepack disable`][] to remove the proxies from your system (and consider -opening up an issue on the [Corepack repository][] to let us know). +opening an issue on the [Corepack repository][] to let us know). ### Configuring a package @@ -57,7 +57,7 @@ successfully retrieved. When running outside of an existing project (for example when running `yarn init`), Corepack will by default use predefined versions roughly corresponding to the latest stable releases from each tool. Those versions can -be easily overriden by running the [`corepack prepare`][] command along with the +be overriden by running the [`corepack prepare`][] command along with the package manager version you wish to set: ```bash @@ -73,8 +73,8 @@ it can conflict with such environments. To avoid that happening, call the the same time you're preparing your deploy image). This will ensure that the required package managers are available even without network access. -The `prepare` command has [various flags][], consult the detailed -[Corepack documentation][] for more information on the matter. +The `prepare` command has [various flags][]. Consult the detailed +[Corepack documentation][] for more information. ## Supported package managers @@ -87,10 +87,10 @@ The following binaries are provided through Corepack: ## Common questions -### How does Corepack currently interact with npm? +### How does Corepack interact with npm? -While Corepack could easily support npm like any other package manager, its -shims aren't currently enabled by default. This has a few consequences: +While Corepack could support npm like any other package manager, its +shims aren't enabled by default. This has a few consequences: * It's always possible to run a `npm` command within a project configured to be used with another package manager, since Corepack cannot intercept it. @@ -103,13 +103,13 @@ shims aren't currently enabled by default. This has a few consequences: npm prevents accidentally overriding the Corepack binaries when doing a global install. To avoid this problem, consider one of the following options: -* Don't run this command anymore; Corepack will provide the package manager +* Don't run this command; Corepack will provide the package manager binaries anyway and will ensure that the requested versions are always - available, so installing the package managers explicitly isn't needed anymore. + available, so installing the package managers explicitly isn't needed. * Add the `--force` flag to `npm install`; this will tell npm that it's fine to - override binaries, but you'll erase the Corepack ones in the process (should - that happen, run [`corepack enable`][] again to add them back). + override binaries, but you'll erase the Corepack ones in the process. (Run + [`corepack enable`][] to add them back.) [Corepack]: https://github.com/nodejs/corepack [Corepack documentation]: https://github.com/nodejs/corepack#readme diff --git a/doc/api/crypto.md b/doc/api/crypto.md index 470c8f6641839a..930ca11c04c2de 100644 --- a/doc/api/crypto.md +++ b/doc/api/crypto.md @@ -6,11 +6,12 @@ -The `crypto` module provides cryptographic functionality that includes a set of -wrappers for OpenSSL's hash, HMAC, cipher, decipher, sign, and verify functions. +The `node:crypto` module provides cryptographic functionality that includes a +set of wrappers for OpenSSL's hash, HMAC, cipher, decipher, sign, and verify +functions. ```mjs -const { createHmac } = await import('crypto'); +const { createHmac } = await import('node:crypto'); const secret = 'abcdefg'; const hash = createHmac('sha256', secret) @@ -22,7 +23,7 @@ console.log(hash); ``` ```cjs -const crypto = require('crypto'); +const crypto = require('node:crypto'); const secret = 'abcdefg'; const hash = crypto.createHmac('sha256', secret) @@ -36,8 +37,8 @@ console.log(hash); ## Determining if crypto support is unavailable It is possible for Node.js to be built without including support for the -`crypto` module. In such cases, attempting to `import` from `crypto` or -calling `require('crypto')` will result in an error being thrown. +`node:crypto` module. In such cases, attempting to `import` from `crypto` or +calling `require('node:crypto')` will result in an error being thrown. When using CommonJS, the error thrown can be caught using try/catch: @@ -46,7 +47,7 @@ When using CommonJS, the error thrown can be caught using try/catch: ```cjs let crypto; try { - crypto = require('crypto'); + crypto = require('node:crypto'); } catch (err) { console.log('crypto support is disabled!'); } @@ -54,17 +55,17 @@ try { When using the lexical ESM `import` keyword, the error can only be caught if a handler for `process.on('uncaughtException')` is registered -_before_ any attempt to load the module is made -- using, for instance, -a preload module. +_before_ any attempt to load the module is made (using, for instance, +a preload module). When using ESM, if there is a chance that the code may be run on a build of Node.js where crypto support is not enabled, consider using the -`import()` function instead of the lexical `import` keyword: +[`import()`][] function instead of the lexical `import` keyword: ```mjs let crypto; try { - crypto = await import('crypto'); + crypto = await import('node:crypto'); } catch (err) { console.log('crypto support is disabled!'); } @@ -82,7 +83,7 @@ Netscape and was specified formally as part of [HTML5's `keygen` element][]. `` is deprecated since [HTML 5.2][] and new projects should not use this element anymore. -The `crypto` module provides the `Certificate` class for working with SPKAC +The `node:crypto` module provides the `Certificate` class for working with SPKAC data. The most common usage is handling output generated by the HTML5 `` element. Node.js uses [OpenSSL's SPKAC implementation][] internally. @@ -103,7 +104,7 @@ changes: includes a public key and a challenge. ```mjs -const { Certificate } = await import('crypto'); +const { Certificate } = await import('node:crypto'); const spkac = getSpkacSomehow(); const challenge = Certificate.exportChallenge(spkac); console.log(challenge.toString('utf8')); @@ -111,7 +112,7 @@ console.log(challenge.toString('utf8')); ``` ```cjs -const { Certificate } = require('crypto'); +const { Certificate } = require('node:crypto'); const spkac = getSpkacSomehow(); const challenge = Certificate.exportChallenge(spkac); console.log(challenge.toString('utf8')); @@ -135,7 +136,7 @@ changes: which includes a public key and a challenge. ```mjs -const { Certificate } = await import('crypto'); +const { Certificate } = await import('node:crypto'); const spkac = getSpkacSomehow(); const publicKey = Certificate.exportPublicKey(spkac); console.log(publicKey); @@ -143,7 +144,7 @@ console.log(publicKey); ``` ```cjs -const { Certificate } = require('crypto'); +const { Certificate } = require('node:crypto'); const spkac = getSpkacSomehow(); const publicKey = Certificate.exportPublicKey(spkac); console.log(publicKey); @@ -168,8 +169,8 @@ changes: `false` otherwise. ```mjs -import { Buffer } from 'buffer'; -const { Certificate } = await import('crypto'); +import { Buffer } from 'node:buffer'; +const { Certificate } = await import('node:crypto'); const spkac = getSpkacSomehow(); console.log(Certificate.verifySpkac(Buffer.from(spkac))); @@ -177,8 +178,8 @@ console.log(Certificate.verifySpkac(Buffer.from(spkac))); ``` ```cjs -const { Certificate } = require('crypto'); -const { Buffer } = require('buffer'); +const { Certificate } = require('node:crypto'); +const { Buffer } = require('node:buffer'); const spkac = getSpkacSomehow(); console.log(Certificate.verifySpkac(Buffer.from(spkac))); @@ -198,14 +199,14 @@ Instances of the `Certificate` class can be created using the `new` keyword or by calling `crypto.Certificate()` as a function: ```mjs -const { Certificate } = await import('crypto'); +const { Certificate } = await import('node:crypto'); const cert1 = new Certificate(); const cert2 = Certificate(); ``` ```cjs -const { Certificate } = require('crypto'); +const { Certificate } = require('node:crypto'); const cert1 = new Certificate(); const cert2 = Certificate(); @@ -223,7 +224,7 @@ added: v0.11.8 includes a public key and a challenge. ```mjs -const { Certificate } = await import('crypto'); +const { Certificate } = await import('node:crypto'); const cert = Certificate(); const spkac = getSpkacSomehow(); const challenge = cert.exportChallenge(spkac); @@ -232,7 +233,7 @@ console.log(challenge.toString('utf8')); ``` ```cjs -const { Certificate } = require('crypto'); +const { Certificate } = require('node:crypto'); const cert = Certificate(); const spkac = getSpkacSomehow(); const challenge = cert.exportChallenge(spkac); @@ -252,7 +253,7 @@ added: v0.11.8 which includes a public key and a challenge. ```mjs -const { Certificate } = await import('crypto'); +const { Certificate } = await import('node:crypto'); const cert = Certificate(); const spkac = getSpkacSomehow(); const publicKey = cert.exportPublicKey(spkac); @@ -261,7 +262,7 @@ console.log(publicKey); ``` ```cjs -const { Certificate } = require('crypto'); +const { Certificate } = require('node:crypto'); const cert = Certificate(); const spkac = getSpkacSomehow(); const publicKey = cert.exportPublicKey(spkac); @@ -281,8 +282,8 @@ added: v0.11.8 `false` otherwise. ```mjs -import { Buffer } from 'buffer'; -const { Certificate } = await import('crypto'); +import { Buffer } from 'node:buffer'; +const { Certificate } = await import('node:crypto'); const cert = Certificate(); const spkac = getSpkacSomehow(); @@ -291,8 +292,8 @@ console.log(cert.verifySpkac(Buffer.from(spkac))); ``` ```cjs -const { Certificate } = require('crypto'); -const { Buffer } = require('buffer'); +const { Certificate } = require('node:crypto'); +const { Buffer } = require('node:buffer'); const cert = Certificate(); const spkac = getSpkacSomehow(); @@ -327,7 +328,7 @@ const { scrypt, randomFill, createCipheriv -} = await import('crypto'); +} = await import('node:crypto'); const algorithm = 'aes-192-cbc'; const password = 'Password used to generate key'; @@ -360,7 +361,7 @@ const { scrypt, randomFill, createCipheriv -} = require('crypto'); +} = require('node:crypto'); const algorithm = 'aes-192-cbc'; const password = 'Password used to generate key'; @@ -404,7 +405,7 @@ const { scrypt, randomFill, createCipheriv -} = await import('crypto'); +} = await import('node:crypto'); const algorithm = 'aes-192-cbc'; const password = 'Password used to generate key'; @@ -433,17 +434,17 @@ scrypt(password, 'salt', 24, (err, key) => { const { createReadStream, createWriteStream, -} = require('fs'); +} = require('node:fs'); const { pipeline -} = require('stream'); +} = require('node:stream'); const { scrypt, randomFill, createCipheriv, -} = require('crypto'); +} = require('node:crypto'); const algorithm = 'aes-192-cbc'; const password = 'Password used to generate key'; @@ -475,7 +476,7 @@ const { scrypt, randomFill, createCipheriv -} = await import('crypto'); +} = await import('node:crypto'); const algorithm = 'aes-192-cbc'; const password = 'Password used to generate key'; @@ -502,7 +503,7 @@ const { scrypt, randomFill, createCipheriv, -} = require('crypto'); +} = require('node:crypto'); const algorithm = 'aes-192-cbc'; const password = 'Password used to generate key'; @@ -546,7 +547,8 @@ added: v1.0.0 --> * Returns: {Buffer} When using an authenticated encryption mode (`GCM`, `CCM`, - and `OCB` are currently supported), the `cipher.getAuthTag()` method returns a + `OCB`, and `chacha20-poly1305` are currently supported), the + `cipher.getAuthTag()` method returns a [`Buffer`][] containing the _authentication tag_ that has been computed from the given data. @@ -568,7 +570,8 @@ added: v1.0.0 * `encoding` {string} The string encoding to use when `buffer` is a string. * Returns: {Cipher} for method chaining. -When using an authenticated encryption mode (`GCM`, `CCM`, and `OCB` are +When using an authenticated encryption mode (`GCM`, `CCM`, `OCB`, and +`chacha20-poly1305` are currently supported), the `cipher.setAAD()` method sets the value used for the _additional authenticated data_ (AAD) input parameter. @@ -653,11 +656,11 @@ directly using the `new` keyword. Example: Using `Decipher` objects as streams: ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const { scryptSync, createDecipheriv -} = await import('crypto'); +} = await import('node:crypto'); const algorithm = 'aes-192-cbc'; const password = 'Password used to generate key'; @@ -692,8 +695,8 @@ decipher.end(); const { scryptSync, createDecipheriv, -} = require('crypto'); -const { Buffer } = require('buffer'); +} = require('node:crypto'); +const { Buffer } = require('node:buffer'); const algorithm = 'aes-192-cbc'; const password = 'Password used to generate key'; @@ -731,11 +734,11 @@ import { createReadStream, createWriteStream, } from 'fs'; -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const { scryptSync, createDecipheriv -} = await import('crypto'); +} = await import('node:crypto'); const algorithm = 'aes-192-cbc'; const password = 'Password used to generate key'; @@ -756,12 +759,12 @@ input.pipe(decipher).pipe(output); const { createReadStream, createWriteStream, -} = require('fs'); +} = require('node:fs'); const { scryptSync, createDecipheriv, -} = require('crypto'); -const { Buffer } = require('buffer'); +} = require('node:crypto'); +const { Buffer } = require('node:buffer'); const algorithm = 'aes-192-cbc'; const password = 'Password used to generate key'; @@ -781,11 +784,11 @@ input.pipe(decipher).pipe(output); Example: Using the [`decipher.update()`][] and [`decipher.final()`][] methods: ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const { scryptSync, createDecipheriv -} = await import('crypto'); +} = await import('node:crypto'); const algorithm = 'aes-192-cbc'; const password = 'Password used to generate key'; @@ -809,8 +812,8 @@ console.log(decrypted); const { scryptSync, createDecipheriv, -} = require('crypto'); -const { Buffer } = require('buffer'); +} = require('node:crypto'); +const { Buffer } = require('node:buffer'); const algorithm = 'aes-192-cbc'; const password = 'Password used to generate key'; @@ -865,7 +868,8 @@ changes: * `encoding` {string} String encoding to use when `buffer` is a string. * Returns: {Decipher} for method chaining. -When using an authenticated encryption mode (`GCM`, `CCM`, and `OCB` are +When using an authenticated encryption mode (`GCM`, `CCM`, `OCB`, and +`chacha20-poly1305` are currently supported), the `decipher.setAAD()` method sets the value used for the _additional authenticated data_ (AAD) input parameter. @@ -899,7 +903,8 @@ changes: * `encoding` {string} String encoding to use when `buffer` is a string. * Returns: {Decipher} for method chaining. -When using an authenticated encryption mode (`GCM`, `CCM`, and `OCB` are +When using an authenticated encryption mode (`GCM`, `CCM`, `OCB`, and +`chacha20-poly1305` are currently supported), the `decipher.setAuthTag()` method is used to pass in the received _authentication tag_. If no tag is provided, or if the cipher text has been tampered with, [`decipher.final()`][] will throw, indicating that the @@ -908,7 +913,8 @@ is invalid according to [NIST SP 800-38D][] or does not match the value of the `authTagLength` option, `decipher.setAuthTag()` will throw an error. The `decipher.setAuthTag()` method must be called before [`decipher.update()`][] -for `CCM` mode or before [`decipher.final()`][] for `GCM` and `OCB` modes. +for `CCM` mode or before [`decipher.final()`][] for `GCM` and `OCB` modes and +`chacha20-poly1305`. `decipher.setAuthTag()` can only be called once. When passing a string as the authentication tag, please consider @@ -976,11 +982,11 @@ Instances of the `DiffieHellman` class can be created using the [`crypto.createDiffieHellman()`][] function. ```mjs -import assert from 'assert'; +import assert from 'node:assert'; const { createDiffieHellman -} = await import('crypto'); +} = await import('node:crypto'); // Generate Alice's keys... const alice = createDiffieHellman(2048); @@ -999,11 +1005,11 @@ assert.strictEqual(aliceSecret.toString('hex'), bobSecret.toString('hex')); ``` ```cjs -const assert = require('assert'); +const assert = require('node:assert'); const { createDiffieHellman, -} = require('crypto'); +} = require('node:crypto'); // Generate Alice's keys... const alice = createDiffieHellman(2048); @@ -1147,8 +1153,7 @@ added: v0.11.12 A bit field containing any warnings and/or errors resulting from a check performed during initialization of the `DiffieHellman` object. -The following values are valid for this property (as defined in `constants` -module): +The following values are valid for this property (as defined in `node:constants` module): * `DH_CHECK_P_NOT_SAFE_PRIME` * `DH_CHECK_P_NOT_PRIME` @@ -1167,12 +1172,12 @@ its keys after creation. In other words, it does not implement `setPublicKey()` or `setPrivateKey()` methods. ```mjs -const { createDiffieHellmanGroup } = await import('crypto'); +const { createDiffieHellmanGroup } = await import('node:crypto'); const dh = createDiffieHellmanGroup('modp1'); ``` ```cjs -const { createDiffieHellmanGroup } = require('crypto'); +const { createDiffieHellmanGroup } = require('node:crypto'); const dh = createDiffieHellmanGroup('modp1'); ``` @@ -1204,11 +1209,11 @@ Instances of the `ECDH` class can be created using the [`crypto.createECDH()`][] function. ```mjs -import assert from 'assert'; +import assert from 'node:assert'; const { createECDH -} = await import('crypto'); +} = await import('node:crypto'); // Generate Alice's keys... const alice = createECDH('secp521r1'); @@ -1227,11 +1232,11 @@ assert.strictEqual(aliceSecret.toString('hex'), bobSecret.toString('hex')); ``` ```cjs -const assert = require('assert'); +const assert = require('node:assert'); const { createECDH, -} = require('crypto'); +} = require('node:crypto'); // Generate Alice's keys... const alice = createECDH('secp521r1'); @@ -1284,7 +1289,7 @@ Example (uncompressing a key): const { createECDH, ECDH -} = await import('crypto'); +} = await import('node:crypto'); const ecdh = createECDH('secp256k1'); ecdh.generateKeys(); @@ -1305,7 +1310,7 @@ console.log(uncompressedKey === ecdh.getPublicKey('hex')); const { createECDH, ECDH, -} = require('crypto'); +} = require('node:crypto'); const ecdh = createECDH('secp256k1'); ecdh.generateKeys(); @@ -1456,7 +1461,7 @@ Example (obtaining a shared secret): const { createECDH, createHash -} = await import('crypto'); +} = await import('node:crypto'); const alice = createECDH('secp256k1'); const bob = createECDH('secp256k1'); @@ -1483,7 +1488,7 @@ console.log(aliceSecret === bobSecret); const { createECDH, createHash, -} = require('crypto'); +} = require('node:crypto'); const alice = createECDH('secp256k1'); const bob = createECDH('secp256k1'); @@ -1530,7 +1535,7 @@ Example: Using `Hash` objects as streams: ```mjs const { createHash -} = await import('crypto'); +} = await import('node:crypto'); const hash = createHash('sha256'); @@ -1552,7 +1557,7 @@ hash.end(); ```cjs const { createHash, -} = require('crypto'); +} = require('node:crypto'); const hash = createHash('sha256'); @@ -1574,9 +1579,9 @@ hash.end(); Example: Using `Hash` and piped streams: ```mjs -import { createReadStream } from 'fs'; -import { stdout } from 'process'; -const { createHash } = await import('crypto'); +import { createReadStream } from 'node:fs'; +import { stdout } from 'node:process'; +const { createHash } = await import('node:crypto'); const hash = createHash('sha256'); @@ -1585,9 +1590,9 @@ input.pipe(hash).setEncoding('hex').pipe(stdout); ``` ```cjs -const { createReadStream } = require('fs'); -const { createHash } = require('crypto'); -const { stdout } = require('process'); +const { createReadStream } = require('node:fs'); +const { createHash } = require('node:crypto'); +const { stdout } = require('node:process'); const hash = createHash('sha256'); @@ -1600,7 +1605,7 @@ Example: Using the [`hash.update()`][] and [`hash.digest()`][] methods: ```mjs const { createHash -} = await import('crypto'); +} = await import('node:crypto'); const hash = createHash('sha256'); @@ -1613,7 +1618,7 @@ console.log(hash.digest('hex')); ```cjs const { createHash, -} = require('crypto'); +} = require('node:crypto'); const hash = createHash('sha256'); @@ -1646,7 +1651,7 @@ its [`hash.digest()`][] method has been called. // Calculate a rolling hash. const { createHash -} = await import('crypto'); +} = await import('node:crypto'); const hash = createHash('sha256'); @@ -1666,7 +1671,7 @@ console.log(hash.copy().digest('hex')); // Calculate a rolling hash. const { createHash, -} = require('crypto'); +} = require('node:crypto'); const hash = createHash('sha256'); @@ -1744,7 +1749,7 @@ Example: Using `Hmac` objects as streams: ```mjs const { createHmac -} = await import('crypto'); +} = await import('node:crypto'); const hmac = createHmac('sha256', 'a secret'); @@ -1766,7 +1771,7 @@ hmac.end(); ```cjs const { createHmac, -} = require('crypto'); +} = require('node:crypto'); const hmac = createHmac('sha256', 'a secret'); @@ -1788,11 +1793,11 @@ hmac.end(); Example: Using `Hmac` and piped streams: ```mjs -import { createReadStream } from 'fs'; -import { stdout } from 'process'; +import { createReadStream } from 'node:fs'; +import { stdout } from 'node:process'; const { createHmac -} = await import('crypto'); +} = await import('node:crypto'); const hmac = createHmac('sha256', 'a secret'); @@ -1803,11 +1808,11 @@ input.pipe(hmac).pipe(stdout); ```cjs const { createReadStream, -} = require('fs'); +} = require('node:fs'); const { createHmac, -} = require('crypto'); -const { stdout } = require('process'); +} = require('node:crypto'); +const { stdout } = require('node:process'); const hmac = createHmac('sha256', 'a secret'); @@ -1820,7 +1825,7 @@ Example: Using the [`hmac.update()`][] and [`hmac.digest()`][] methods: ```mjs const { createHmac -} = await import('crypto'); +} = await import('node:crypto'); const hmac = createHmac('sha256', 'a secret'); @@ -1833,7 +1838,7 @@ console.log(hmac.digest('hex')); ```cjs const { createHmac, -} = require('crypto'); +} = require('node:crypto'); const hmac = createHmac('sha256', 'a secret'); @@ -1922,7 +1927,7 @@ added: v15.0.0 Example: Converting a `CryptoKey` instance to a `KeyObject`: ```mjs -const { webcrypto, KeyObject } = await import('crypto'); +const { webcrypto, KeyObject } = await import('node:crypto'); const { subtle } = webcrypto; const key = await subtle.generateKey({ @@ -1942,7 +1947,7 @@ const { subtle, }, KeyObject, -} = require('crypto'); +} = require('node:crypto'); (async function() { const key = await subtle.generateKey({ @@ -2082,6 +2087,20 @@ encryption mechanism, PEM-level encryption is not supported when encrypting a PKCS#8 key. See [RFC 5208][] for PKCS#8 encryption and [RFC 1421][] for PKCS#1 and SEC1 encryption. +### `keyObject.equals(otherKeyObject)` + + + +* `otherKeyObject`: {KeyObject} A `KeyObject` with which to + compare `keyObject`. +* Returns: {boolean} + +Returns `true` or `false` depending on whether the keys have exactly the same +type, value, and parameters. This method is not +[constant time](https://en.wikipedia.org/wiki/Timing_attack). + ### `keyObject.symmetricKeySize` @@ -2604,6 +2627,10 @@ added: v15.6.0 The SHA-1 fingerprint of this certificate. +Because SHA-1 is cryptographically broken and because the security of SHA-1 is +significantly worse than that of algorithms that are commonly used to sign +certificates, consider using [`x509.fingerprint256`][] instead. + ### `x509.fingerprint256` -### DEP0089: `require('assert')` +### DEP0089: `require('node:assert')` -Type: Runtime +Type: End-of-Life Using a non-nullish non-integer value for `family` option, a non-nullish non-number value for `hints` option, a non-nullish non-boolean value for `all` option, or a non-nullish non-boolean value for `verbatim` option in -[`dns.lookup()`][] and [`dnsPromises.lookup()`][] is deprecated. +[`dns.lookup()`][] and [`dnsPromises.lookup()`][] throws an +`ERR_INVALID_ARG_TYPE` error. ### DEP0154: RSA-PSS generate key pair options @@ -3019,6 +3024,9 @@ it was an aborted or graceful destroy. -Type: Documentation-only +Type: End-of-Life An undocumented feature of Node.js streams was to support thenables in implementation methods. This is now deprecated, use callbacks instead and avoid @@ -3065,7 +3073,7 @@ Use [`buffer.subarray`][] which does the same thing instead. @@ -3079,7 +3087,7 @@ the errors used for value type validation. + +Type: Runtime + +Implicit coercion of objects with own `toString` property, passed as second +parameter in [`fs.write()`][], [`fs.writeFile()`][], [`fs.appendFile()`][], +[`fs.writeFileSync()`][], and [`fs.appendFileSync()`][] is deprecated. +Convert them to primitive strings. + [Legacy URL API]: url.md#legacy-url-api [NIST SP 800-38D]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf [RFC 6066]: https://tools.ietf.org/html/rfc6066#section-3 @@ -3158,6 +3185,8 @@ resources and not the actual references. [`events.listenerCount(emitter, eventName)`]: events.md#eventslistenercountemitter-eventname [`fs.FileHandle`]: fs.md#class-filehandle [`fs.access()`]: fs.md#fsaccesspath-mode-callback +[`fs.appendFile()`]: fs.md#fsappendfilepath-data-options-callback +[`fs.appendFileSync()`]: fs.md#fsappendfilesyncpath-data-options [`fs.createReadStream()`]: fs.md#fscreatereadstreampath-options [`fs.createWriteStream()`]: fs.md#fscreatewritestreampath-options [`fs.exists(path, callback)`]: fs.md#fsexistspath-callback @@ -3168,6 +3197,9 @@ resources and not the actual references. [`fs.read()`]: fs.md#fsreadfd-buffer-offset-length-position-callback [`fs.readSync()`]: fs.md#fsreadsyncfd-buffer-offset-length-position [`fs.stat()`]: fs.md#fsstatpath-options-callback +[`fs.write()`]: fs.md#fswritefd-buffer-offset-length-position-callback +[`fs.writeFile()`]: fs.md#fswritefilefile-data-options-callback +[`fs.writeFileSync()`]: fs.md#fswritefilesyncfile-data-options [`http.ClientRequest`]: http.md#class-httpclientrequest [`http.IncomingMessage`]: http.md#class-httpincomingmessage [`http.ServerResponse`]: http.md#class-httpserverresponse diff --git a/doc/api/dgram.md b/doc/api/dgram.md index 27efdfb186bfd3..185c5f60e9a07d 100644 --- a/doc/api/dgram.md +++ b/doc/api/dgram.md @@ -8,10 +8,10 @@ -The `dgram` module provides an implementation of UDP datagram sockets. +The `node:dgram` module provides an implementation of UDP datagram sockets. ```mjs -import dgram from 'dgram'; +import dgram from 'node:dgram'; const server = dgram.createSocket('udp4'); @@ -34,7 +34,7 @@ server.bind(41234); ``` ```cjs -const dgram = require('dgram'); +const dgram = require('node:dgram'); const server = dgram.createSocket('udp4'); server.on('error', (err) => { @@ -113,6 +113,10 @@ exist and calls such as `socket.address()` and `socket.setTTL()` will fail. The `'message'` event is emitted when a new datagram is available on a socket. @@ -121,7 +125,7 @@ The event handler function is passed two arguments: `msg` and `rinfo`. * `msg` {Buffer} The message. * `rinfo` {Object} Remote address information. * `address` {string} The sender address. - * `family` {string} The address family (`'IPv4'` or `'IPv6'`). + * `family` {number} The address family (`4` for IPv4 or `6` for IPv6). * `port` {number} The sender port. * `size` {number} The message size. @@ -154,8 +158,8 @@ When sharing a UDP socket across multiple `cluster` workers, the `EADDRINUSE` error will occur: ```mjs -import cluster from 'cluster'; -import dgram from 'dgram'; +import cluster from 'node:cluster'; +import dgram from 'node:dgram'; if (cluster.isPrimary) { cluster.fork(); // Works ok. @@ -169,8 +173,8 @@ if (cluster.isPrimary) { ``` ```cjs -const cluster = require('cluster'); -const dgram = require('dgram'); +const cluster = require('node:cluster'); +const dgram = require('node:dgram'); if (cluster.isPrimary) { cluster.fork(); // Works ok. @@ -256,7 +260,7 @@ attempting to bind with a closed socket), an [`Error`][] may be thrown. Example of a UDP server listening on port 41234: ```mjs -import dgram from 'dgram'; +import dgram from 'node:dgram'; const server = dgram.createSocket('udp4'); @@ -279,7 +283,7 @@ server.bind(41234); ``` ```cjs -const dgram = require('dgram'); +const dgram = require('node:dgram'); const server = dgram.createSocket('udp4'); server.on('error', (err) => { @@ -569,8 +573,8 @@ This method throws [`ERR_SOCKET_BAD_PORT`][] if called on an unbound socket. Example of sending a UDP packet to a port on `localhost`; ```mjs -import dgram from 'dgram'; -import { Buffer } from 'buffer'; +import dgram from 'node:dgram'; +import { Buffer } from 'node:buffer'; const message = Buffer.from('Some bytes'); const client = dgram.createSocket('udp4'); @@ -580,8 +584,8 @@ client.send(message, 41234, 'localhost', (err) => { ``` ```cjs -const dgram = require('dgram'); -const { Buffer } = require('buffer'); +const dgram = require('node:dgram'); +const { Buffer } = require('node:buffer'); const message = Buffer.from('Some bytes'); const client = dgram.createSocket('udp4'); @@ -594,8 +598,8 @@ Example of sending a UDP packet composed of multiple buffers to a port on `127.0.0.1`; ```mjs -import dgram from 'dgram'; -import { Buffer } from 'buffer'; +import dgram from 'node:dgram'; +import { Buffer } from 'node:buffer'; const buf1 = Buffer.from('Some '); const buf2 = Buffer.from('bytes'); @@ -606,8 +610,8 @@ client.send([buf1, buf2], 41234, (err) => { ``` ```cjs -const dgram = require('dgram'); -const { Buffer } = require('buffer'); +const dgram = require('node:dgram'); +const { Buffer } = require('node:buffer'); const buf1 = Buffer.from('Some '); const buf2 = Buffer.from('bytes'); @@ -626,8 +630,8 @@ Example of sending a UDP packet using a socket connected to a port on `localhost`: ```mjs -import dgram from 'dgram'; -import { Buffer } from 'buffer'; +import dgram from 'node:dgram'; +import { Buffer } from 'node:buffer'; const message = Buffer.from('Some bytes'); const client = dgram.createSocket('udp4'); @@ -639,8 +643,8 @@ client.connect(41234, 'localhost', (err) => { ``` ```cjs -const dgram = require('dgram'); -const { Buffer } = require('buffer'); +const dgram = require('node:dgram'); +const { Buffer } = require('node:buffer'); const message = Buffer.from('Some bytes'); const client = dgram.createSocket('udp4'); @@ -844,7 +848,7 @@ travel through. Each router or gateway that forwards a packet decrements the TTL. If the TTL is decremented to 0 by a router, it will not be forwarded. Changing TTL values is typically done for network probes or when multicasting. -The `ttl` argument may be between between 1 and 255. The default on most systems +The `ttl` argument may be between 1 and 255. The default on most systems is 64. This method throws `EBADF` if called on an unbound socket. @@ -868,7 +872,7 @@ Calling `socket.unref()` multiple times will have no addition effect. The `socket.unref()` method returns a reference to the socket so calls can be chained. -## `dgram` module functions +## `node:dgram` module functions ### `dgram.createSocket(options[, callback])` diff --git a/doc/api/diagnostics_channel.md b/doc/api/diagnostics_channel.md index fc13f0d38e4393..eff642f96743b4 100644 --- a/doc/api/diagnostics_channel.md +++ b/doc/api/diagnostics_channel.md @@ -6,17 +6,17 @@ -The `diagnostics_channel` module provides an API to create named channels +The `node:diagnostics_channel` module provides an API to create named channels to report arbitrary message data for diagnostics purposes. It can be accessed using: ```mjs -import diagnostics_channel from 'diagnostics_channel'; +import diagnostics_channel from 'node:diagnostics_channel'; ``` ```cjs -const diagnostics_channel = require('diagnostics_channel'); +const diagnostics_channel = require('node:diagnostics_channel'); ``` It is intended that a module writer wanting to report diagnostics messages @@ -38,7 +38,7 @@ other modules. Following is a simple overview of the public API. ```mjs -import diagnostics_channel from 'diagnostics_channel'; +import diagnostics_channel from 'node:diagnostics_channel'; // Get a reusable channel object const channel = diagnostics_channel.channel('my-channel'); @@ -58,7 +58,7 @@ if (channel.hasSubscribers) { ``` ```cjs -const diagnostics_channel = require('diagnostics_channel'); +const diagnostics_channel = require('node:diagnostics_channel'); // Get a reusable channel object const channel = diagnostics_channel.channel('my-channel'); @@ -95,7 +95,7 @@ This API is optional but helpful when trying to publish messages from very performance-sensitive code. ```mjs -import diagnostics_channel from 'diagnostics_channel'; +import diagnostics_channel from 'node:diagnostics_channel'; if (diagnostics_channel.hasSubscribers('my-channel')) { // There are subscribers, prepare and publish message @@ -103,7 +103,7 @@ if (diagnostics_channel.hasSubscribers('my-channel')) { ``` ```cjs -const diagnostics_channel = require('diagnostics_channel'); +const diagnostics_channel = require('node:diagnostics_channel'); if (diagnostics_channel.hasSubscribers('my-channel')) { // There are subscribers, prepare and publish message @@ -126,13 +126,13 @@ channel. It produces a channel object which is optimized to reduce overhead at publish time as much as possible. ```mjs -import diagnostics_channel from 'diagnostics_channel'; +import diagnostics_channel from 'node:diagnostics_channel'; const channel = diagnostics_channel.channel('my-channel'); ``` ```cjs -const diagnostics_channel = require('diagnostics_channel'); +const diagnostics_channel = require('node:diagnostics_channel'); const channel = diagnostics_channel.channel('my-channel'); ``` @@ -170,7 +170,7 @@ This API is optional but helpful when trying to publish messages from very performance-sensitive code. ```mjs -import diagnostics_channel from 'diagnostics_channel'; +import diagnostics_channel from 'node:diagnostics_channel'; const channel = diagnostics_channel.channel('my-channel'); @@ -180,7 +180,7 @@ if (channel.hasSubscribers) { ``` ```cjs -const diagnostics_channel = require('diagnostics_channel'); +const diagnostics_channel = require('node:diagnostics_channel'); const channel = diagnostics_channel.channel('my-channel'); @@ -203,7 +203,7 @@ Publish a message to any subscribers to the channel. This will trigger message handlers synchronously so they will execute within the same context. ```mjs -import diagnostics_channel from 'diagnostics_channel'; +import diagnostics_channel from 'node:diagnostics_channel'; const channel = diagnostics_channel.channel('my-channel'); @@ -213,7 +213,7 @@ channel.publish({ ``` ```cjs -const diagnostics_channel = require('diagnostics_channel'); +const diagnostics_channel = require('node:diagnostics_channel'); const channel = diagnostics_channel.channel('my-channel'); @@ -239,7 +239,7 @@ will be run synchronously whenever a message is published to the channel. Any errors thrown in the message handler will trigger an [`'uncaughtException'`][]. ```mjs -import diagnostics_channel from 'diagnostics_channel'; +import diagnostics_channel from 'node:diagnostics_channel'; const channel = diagnostics_channel.channel('my-channel'); @@ -249,7 +249,7 @@ channel.subscribe((message, name) => { ``` ```cjs -const diagnostics_channel = require('diagnostics_channel'); +const diagnostics_channel = require('node:diagnostics_channel'); const channel = diagnostics_channel.channel('my-channel'); @@ -280,7 +280,7 @@ Remove a message handler previously registered to this channel with [`channel.subscribe(onMessage)`][]. ```mjs -import diagnostics_channel from 'diagnostics_channel'; +import diagnostics_channel from 'node:diagnostics_channel'; const channel = diagnostics_channel.channel('my-channel'); @@ -294,7 +294,7 @@ channel.unsubscribe(onMessage); ``` ```cjs -const diagnostics_channel = require('diagnostics_channel'); +const diagnostics_channel = require('node:diagnostics_channel'); const channel = diagnostics_channel.channel('my-channel'); diff --git a/doc/api/dns.md b/doc/api/dns.md index e5a1b5e3ff82c6..c150c8a8abb8eb 100644 --- a/doc/api/dns.md +++ b/doc/api/dns.md @@ -6,7 +6,7 @@ -The `dns` module enables name resolution. For example, use it to look up IP +The `node:dns` module enables name resolution. For example, use it to look up IP addresses of host names. Although named for the [Domain Name System (DNS)][], it does not always use the @@ -16,7 +16,7 @@ communication. To perform name resolution the way other applications on the same system do, use [`dns.lookup()`][]. ```js -const dns = require('dns'); +const dns = require('node:dns'); dns.lookup('example.org', (err, address, family) => { console.log('address: %j family: IPv%s', address, family); @@ -24,14 +24,14 @@ dns.lookup('example.org', (err, address, family) => { // address: "93.184.216.34" family: IPv4 ``` -All other functions in the `dns` module connect to an actual DNS server to +All other functions in the `node:dns` module connect to an actual DNS server to perform name resolution. They will always use the network to perform DNS queries. These functions do not use the same set of configuration files used by [`dns.lookup()`][] (e.g. `/etc/hosts`). Use these functions to always perform DNS queries, bypassing other name-resolution facilities. ```js -const dns = require('dns'); +const dns = require('node:dns'); dns.resolve4('archive.org', (err, addresses) => { if (err) throw err; @@ -65,7 +65,7 @@ the servers used for a resolver using other resolvers: ```js -const { Resolver } = require('dns'); +const { Resolver } = require('node:dns'); const resolver = new Resolver(); resolver.setServers(['4.4.4.4']); @@ -75,7 +75,7 @@ resolver.resolve4('example.org', (err, addresses) => { }); ``` -The following methods from the `dns` module are available: +The following methods from the `node:dns` module are available: * [`resolver.getServers()`][`dns.getServers()`] * [`resolver.resolve()`][`dns.resolve()`] @@ -179,7 +179,7 @@ section if a custom port is used. > Stability: 1 - Experimental. The feature is not subject to -> [Semantic Versioning][] rules. Non-backward compatible changes or removal may +> [semantic versioning][] rules. Non-backward compatible changes or removal may > occur in any future release. Use of the feature is not recommended in > production environments. @@ -40,9 +40,13 @@ The stability indices are as follows: -> Stability: 3 - Legacy. The feature is no longer recommended for use. While it -> likely will not be removed, and is still covered by semantic-versioning -> guarantees, use of the feature should be avoided. +> Stability: 3 - Legacy. Although this feature is unlikely to be removed and is +> still covered by semantic versioning guarantees, it is no longer actively +> maintained, and other alternatives are available. + +Features are marked as legacy rather than being deprecated if their use does no +harm, and they are widely relied upon within the npm ecosystem. Bugs found in +legacy features are unlikely to be fixed. Use caution when making use of Experimental features, particularly within modules. Users may not be aware that experimental features are being used. @@ -73,8 +77,8 @@ to the corresponding man pages which describe how the system call works. Most Unix system calls have Windows analogues. Still, behavior differences may be unavoidable. -[Semantic Versioning]: https://semver.org/ [V8 JavaScript engine]: https://v8.dev/ +[semantic versioning]: https://semver.org/ [the contributing guide]: https://github.com/nodejs/node/blob/HEAD/CONTRIBUTING.md [the issue tracker]: https://github.com/nodejs/node/issues/new [warning]: process.md#event-warning diff --git a/doc/api/domain.md b/doc/api/domain.md index 9552b3bbcd478c..d88eeef7419d8f 100644 --- a/doc/api/domain.md +++ b/doc/api/domain.md @@ -66,7 +66,7 @@ For example, this is not a good idea: ```js // XXX WARNING! BAD IDEA! -const d = require('domain').create(); +const d = require('node:domain').create(); d.on('error', (er) => { // The error won't crash the process, but what it does is worse! // Though we've prevented abrupt process restarting, we are leaking @@ -75,7 +75,7 @@ d.on('error', (er) => { console.log(`error, but oh well ${er.message}`); }); d.run(() => { - require('http').createServer((req, res) => { + require('node:http').createServer((req, res) => { handleRequest(req, res); }).listen(PORT); }); @@ -88,7 +88,7 @@ appropriately, and handle errors with much greater safety. ```js // Much better! -const cluster = require('cluster'); +const cluster = require('node:cluster'); const PORT = +process.env.PORT || 1337; if (cluster.isPrimary) { @@ -117,12 +117,12 @@ if (cluster.isPrimary) { // // This is where we put our bugs! - const domain = require('domain'); + const domain = require('node:domain'); // See the cluster documentation for more details about using // worker processes to serve requests. How it works, caveats, etc. - const server = require('http').createServer((req, res) => { + const server = require('node:http').createServer((req, res) => { const d = domain.create(); d.on('error', (er) => { console.error(`error ${er.stack}`); @@ -246,8 +246,8 @@ That is possible via explicit binding. ```js // Create a top-level domain for the server -const domain = require('domain'); -const http = require('http'); +const domain = require('node:domain'); +const http = require('node:http'); const serverDomain = domain.create(); serverDomain.run(() => { @@ -416,8 +416,8 @@ the function. This is the most basic way to use a domain. ```js -const domain = require('domain'); -const fs = require('fs'); +const domain = require('node:domain'); +const fs = require('node:fs'); const d = domain.create(); d.on('error', (er) => { console.error('Caught error!', er); diff --git a/doc/api/embedding.md b/doc/api/embedding.md index e6bcc5d63b8b02..d07bec744df2e5 100644 --- a/doc/api/embedding.md +++ b/doc/api/embedding.md @@ -124,6 +124,7 @@ int RunNodeInstance(MultiIsolatePlatform* platform, { Locker locker(isolate); Isolate::Scope isolate_scope(isolate); + HandleScope handle_scope(isolate); // The v8::Context needs to be entered when node::CreateEnvironment() and // node::LoadEnvironment() are being called. Context::Scope context_scope(setup->context()); @@ -140,9 +141,9 @@ int RunNodeInstance(MultiIsolatePlatform* platform, MaybeLocal loadenv_ret = node::LoadEnvironment( env, "const publicRequire =" - " require('module').createRequire(process.cwd() + '/');" + " require('node:module').createRequire(process.cwd() + '/');" "globalThis.require = publicRequire;" - "require('vm').runInThisContext(process.argv[1]);"); + "require('node:vm').runInThisContext(process.argv[1]);"); if (loadenv_ret.IsEmpty()) // There has been a JS exception. return 1; diff --git a/doc/api/errors.md b/doc/api/errors.md index b26829be0008da..b174db7b6151b6 100644 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -15,7 +15,7 @@ errors: * User-specified errors triggered by application code. * `AssertionError`s are a special class of error that can be triggered when Node.js detects an exceptional logic violation that should never occur. These - are raised typically by the `assert` module. + are raised typically by the `node:assert` module. All JavaScript and system errors raised by Node.js inherit from, or are instances of, the standard JavaScript {Error} class and are guaranteed @@ -63,7 +63,7 @@ Errors that occur within _Asynchronous APIs_ may be reported in multiple ways: ```js - const fs = require('fs'); + const fs = require('node:fs'); fs.readFile('a file that does not exist', (err, data) => { if (err) { console.error('There was an error reading the file!', err); @@ -77,7 +77,7 @@ Errors that occur within _Asynchronous APIs_ may be reported in multiple ways: [`EventEmitter`][], errors can be routed to that object's `'error'` event. ```js - const net = require('net'); + const net = require('node:net'); const connection = net.connect('localhost'); // Adding an 'error' event handler to a stream: @@ -109,7 +109,7 @@ used appropriately or a handler has been registered for the [`'uncaughtException'`][] event. ```js -const EventEmitter = require('events'); +const EventEmitter = require('node:events'); const ee = new EventEmitter(); setImmediate(() => { @@ -137,7 +137,7 @@ completes or an error is raised, the callback function is called with the the first argument will be passed as `null`. ```js -const fs = require('fs'); +const fs = require('node:fs'); function errorFirstCallback(err, data) { if (err) { @@ -157,7 +157,7 @@ use `throw` inside an error-first callback: ```js // THIS WILL NOT WORK: -const fs = require('fs'); +const fs = require('node:fs'); try { fs.readFile('/some/file/that/does-not-exist', (err, data) => { @@ -372,7 +372,7 @@ acceptable values for a function; whether that is a numeric range, or outside the set of options for a given function parameter. ```js -require('net').connect(-1); +require('node:net').connect(-1); // Throws "RangeError: "port" option should be >= 0 and < 65536: -1" ``` @@ -409,7 +409,7 @@ are almost always indicative of a broken program. ```js try { - require('vm').runInThisContext('binary ! isNotOk'); + require('node:vm').runInThisContext('binary ! isNotOk'); } catch (err) { // 'err' will be a SyntaxError. } @@ -570,7 +570,7 @@ Indicates that a provided argument is not an allowable type. For example, passing a function to a parameter which expects a string would be a `TypeError`. ```js -require('url').parse(() => { }); +require('node:url').parse(() => { }); // Throws TypeError, since it expected a string. ``` @@ -638,11 +638,11 @@ order to be compatible with the web platform's `AbortError`. ### `ERR_AMBIGUOUS_ARGUMENT` A function argument is being used in a way that suggests that the function -signature may be misunderstood. This is thrown by the `assert` module when the -`message` parameter in `assert.throws(block, message)` matches the error message -thrown by `block` because that usage suggests that the user believes `message` -is the expected message rather than the message the `AssertionError` will -display if `block` does not throw. +signature may be misunderstood. This is thrown by the `node:assert` module when +the `message` parameter in `assert.throws(block, message)` matches the error +message thrown by `block` because that usage suggests that the user believes +`message` is the expected message rather than the message the `AssertionError` +will display if `block` does not throw. @@ -657,7 +657,7 @@ required, but not provided to a Node.js API. A special type of error that can be triggered whenever Node.js detects an exceptional logic violation that should never occur. These are raised typically -by the `assert` module. +by the `node:assert` module. @@ -818,14 +818,14 @@ key lies outside of the elliptic curve. ### `ERR_CRYPTO_ENGINE_UNKNOWN` An invalid crypto engine identifier was passed to -[`require('crypto').setEngine()`][]. +[`require('node:crypto').setEngine()`][]. ### `ERR_CRYPTO_FIPS_FORCED` The [`--force-fips`][] command-line argument was used but there was an attempt -to enable or disable FIPS mode in the `crypto` module. +to enable or disable FIPS mode in the `node:crypto` module. @@ -1164,8 +1164,8 @@ ongoing asynchronous operations. ### `ERR_DOMAIN_CALLBACK_NOT_AVAILABLE` -The `domain` module was not usable since it could not establish the required -error handling hooks, because +The `node:domain` module was not usable since it could not establish the +required error handling hooks, because [`process.setUncaughtExceptionCaptureCallback()`][] had been called at an earlier point in time. @@ -1174,10 +1174,10 @@ earlier point in time. ### `ERR_DOMAIN_CANNOT_SET_UNCAUGHT_EXCEPTION_CAPTURE` [`process.setUncaughtExceptionCaptureCallback()`][] could not be called -because the `domain` module has been loaded at an earlier point in time. +because the `node:domain` module has been loaded at an earlier point in time. The stack trace is extended to include the point in time at which the -`domain` module had been loaded. +`node:domain` module had been loaded. @@ -1750,7 +1750,7 @@ only be used with input via `--eval`, `--print` or `STDIN`. ### `ERR_INSPECTOR_ALREADY_ACTIVATED` -While using the `inspector` module, an attempt was made to activate the +While using the `node:inspector` module, an attempt was made to activate the inspector when it already started to listen on a port. Use `inspector.close()` before activating it on a different address. @@ -1758,21 +1758,21 @@ before activating it on a different address. ### `ERR_INSPECTOR_ALREADY_CONNECTED` -While using the `inspector` module, an attempt was made to connect when the +While using the `node:inspector` module, an attempt was made to connect when the inspector was already connected. ### `ERR_INSPECTOR_CLOSED` -While using the `inspector` module, an attempt was made to use the inspector -after the session had already closed. +While using the `node:inspector` module, an attempt was made to use the +inspector after the session had already closed. ### `ERR_INSPECTOR_COMMAND` -An error occurred while issuing a command via the `inspector` module. +An error occurred while issuing a command via the `node:inspector` module. @@ -1784,14 +1784,14 @@ The `inspector` is not active when `inspector.waitForDebugger()` is called. ### `ERR_INSPECTOR_NOT_AVAILABLE` -The `inspector` module is not available for use. +The `node:inspector` module is not available for use. ### `ERR_INSPECTOR_NOT_CONNECTED` -While using the `inspector` module, an attempt was made to use the inspector -before it was connected. +While using the `node:inspector` module, an attempt was made to use the +inspector before it was connected. @@ -1921,6 +1921,13 @@ valid. The imported module string is an invalid URL, package name, or package subpath specifier. + + +### `ERR_INVALID_OBJECT_DEFINE_PROPERTY` + +An error occurred while setting an invalid attribute on the property of +an object. + ### `ERR_INVALID_PACKAGE_CONFIG` @@ -2516,7 +2523,7 @@ Prevents an abort if a string decoder was set on the Socket or if the decoder is in `objectMode`. ```js -const Socket = require('net').Socket; +const Socket = require('node:net').Socket; const instance = new Socket(); instance.setEncoding('utf8'); @@ -2551,6 +2558,14 @@ An unspecified or non-specific system error has occurred within the Node.js process. The error object will have an `err.info` object property with additional details. + + +### `ERR_TEST_FAILURE` + +This error represents a failed test. Additional information about the failure +is available via the `cause` property. The `failureType` property specifies +what the test was doing when the failure occurred. + ### `ERR_TLS_CERT_ALTNAME_FORMAT` @@ -2670,8 +2685,8 @@ category. ### `ERR_TRACE_EVENTS_UNAVAILABLE` -The `trace_events` module could not be loaded because Node.js was compiled with -the `--without-v8-platform` flag. +The `node:trace_events` module could not be loaded because Node.js was compiled +with the `--without-v8-platform` flag. @@ -2787,6 +2802,14 @@ import 'package-name'; // supported `import` with URL schemes other than `file` and `data` is unsupported. + + +### `ERR_USE_AFTER_CLOSE` + +> Stability: 1 - Experimental + +An attempt was made to use something that was already closed. + ### `ERR_VALID_PERFORMANCE_ENTRY_TYPE` @@ -2830,12 +2853,6 @@ Cached data cannot be created for modules which have already been evaluated. The module being returned from the linker function is from a different context than the parent module. Linked modules must share the same context. - - -### `ERR_VM_MODULE_LINKING_ERRORED` - -The linker function returned a module for which linking has failed. - ### `ERR_VM_MODULE_LINK_FAILURE` @@ -2867,6 +2884,17 @@ The WASI instance has already started. The WASI instance has not been started. + + +### `ERR_WEBASSEMBLY_RESPONSE` + + + +The `Response` that has been passed to `WebAssembly.compileStreaming` or to +`WebAssembly.instantiateStreaming` is not a valid WebAssembly response. + ### `ERR_WORKER_INIT_FAILED` @@ -2928,11 +2956,11 @@ changes: - v10.15.0 commit: 186035243fad247e3955f pr-url: https://github.com/nodejs-private/node-private/pull/143 - description: Max header size in `http_parser` was set to 8 KB. + description: Max header size in `http_parser` was set to 8 KiB. --> Too much HTTP header data was received. In order to protect against malicious or -malconfigured clients, if more than 8 KB of HTTP header data is received then +malconfigured clients, if more than 8 KiB of HTTP header data is received then HTTP parsing will abort without a request or response object being created, and an `Error` with this code will be emitted. @@ -3175,7 +3203,7 @@ added: v9.0.0 removed: v10.0.0 --> -The `repl` module was unable to parse data from the REPL history file. +The `node:repl` module was unable to parse data from the REPL history file. @@ -3310,6 +3338,17 @@ Used when a given value is out of the accepted range. The module must be successfully linked before instantiation. + + +### `ERR_VM_MODULE_LINKING_ERRORED` + + + +The linker function returned a module for which linking has failed. + ### `ERR_WORKER_UNSUPPORTED_EXTENSION` @@ -3403,7 +3442,7 @@ The native call from `process.cpuUsage` could not be processed. [`process.send()`]: process.md#processsendmessage-sendhandle-options-callback [`process.setUncaughtExceptionCaptureCallback()`]: process.md#processsetuncaughtexceptioncapturecallbackfn [`readable._read()`]: stream.md#readable_readsize -[`require('crypto').setEngine()`]: crypto.md#cryptosetengineengine-flags +[`require('node:crypto').setEngine()`]: crypto.md#cryptosetengineengine-flags [`require()`]: modules.md#requireid [`server.close()`]: net.md#serverclosecallback [`server.listen()`]: net.md#serverlisten diff --git a/doc/api/esm.md b/doc/api/esm.md index 122fddc4854d8c..7e423634183d5d 100644 --- a/doc/api/esm.md +++ b/doc/api/esm.md @@ -116,7 +116,7 @@ This section was moved to [Modules: Packages](packages.md). ### Terminology The _specifier_ of an `import` statement is the string after the `from` keyword, -e.g. `'path'` in `import { sep } from 'path'`. Specifiers are also used in +e.g. `'path'` in `import { sep } from 'node:path'`. Specifiers are also used in `export from` statements, and as the argument to an `import()` expression. There are three types of specifiers: @@ -178,7 +178,7 @@ The volume root may be referenced via `/`, `//` or `file:///`. Given the differences between [URL][] and path resolution (such as percent encoding details), it is recommended to use [url.pathToFileURL][] when importing a path. -#### `data:` Imports +#### `data:` imports + > Stability: 1 - Experimental -**Note: This API is currently being redesigned and will still change.** +> This API is currently being redesigned and will still change. @@ -700,7 +708,7 @@ changes: description: Add support for import assertions. --> -> Note: The loaders API is being redesigned. This hook may disappear or its +> The loaders API is being redesigned. This hook may disappear or its > signature may change. Do not rely on the API described below. * `specifier` {string} @@ -773,10 +781,10 @@ export async function resolve(specifier, context, defaultResolve) { #### `load(url, context, defaultLoad)` -> Note: The loaders API is being redesigned. This hook may disappear or its +> The loaders API is being redesigned. This hook may disappear or its > signature may change. Do not rely on the API described below. -> Note: In a previous version of this API, this was split across 3 separate, now +> In a previous version of this API, this was split across 3 separate, now > deprecated, hooks (`getFormat`, `getSource`, and `transformSource`). * `url` {string} @@ -814,7 +822,7 @@ overcome in the future. > are incompatible. Attempting to use them together will result in an empty > object from the import. This may be addressed in the future. -> Note: These types all correspond to classes defined in ECMAScript. +> These types all correspond to classes defined in ECMAScript. * The specific [`ArrayBuffer`][] object is a [`SharedArrayBuffer`][]. * The specific [`TypedArray`][] object is a [`Uint8Array`][]. @@ -864,10 +872,10 @@ source to a supported one (see [Examples](#examples) below). #### `globalPreload()` -> Note: The loaders API is being redesigned. This hook may disappear or its +> The loaders API is being redesigned. This hook may disappear or its > signature may change. Do not rely on the API described below. -> Note: In a previous version of this API, this hook was named +> In a previous version of this API, this hook was named > `getGlobalPreloadCode`. * Returns: {string} @@ -949,7 +957,7 @@ and there is no security. ```js // https-loader.mjs -import { get } from 'https'; +import { get } from 'node:https'; export function resolve(specifier, context, defaultResolve) { const { parentURL = null } = context; @@ -1010,7 +1018,7 @@ prints the current version of CoffeeScript per the module at the URL in Sources that are in formats Node.js doesn’t understand can be converted into JavaScript using the [`load` hook][load hook]. Before that hook gets called, -however, a [`resolve` hook][resolve hook] hook needs to tell Node.js not to +however, a [`resolve` hook][resolve hook] needs to tell Node.js not to throw an error on unknown file types. This is less performant than transpiling source files before running @@ -1091,7 +1099,7 @@ async function getPackageType(url) { // required by the spec // this simple truthy check for whether `url` contains a file extension will // work for most projects but does not cover some edge-cases (such as - // extension-less files or a url ending in a trailing space) + // extensionless files or a url ending in a trailing space) const isFilePath = !!extname(url); // If it is a file path, get the directory it's in const dir = isFilePath ? @@ -1119,7 +1127,7 @@ async function getPackageType(url) { import { scream } from './scream.coffee' console.log scream 'hello, world' -import { version } from 'process' +import { version } from 'node:process' console.log "Brought to you by Node.js version #{version}" ``` @@ -1456,6 +1464,10 @@ _internal_, _conditions_) > Stability: 1 - Experimental +> Do not rely on this flag. We plan to remove it once the +> [Loaders API][] has advanced to the point that equivalent functionality can +> be achieved via custom loaders. + The current specifier resolution does not support all default behavior of the CommonJS loader. One of the behavior differences is automatic resolution of file extensions and the ability to import directories that have an index @@ -1488,6 +1500,7 @@ success! [Import Assertions]: #import-assertions [Import Assertions proposal]: https://github.com/tc39/proposal-import-assertions [JSON modules]: #json-modules +[Loaders API]: #loaders [Node.js Module Resolution Algorithm]: #resolver-algorithm-specification [Terminology]: #terminology [URL]: https://url.spec.whatwg.org/ diff --git a/doc/api/events.md b/doc/api/events.md index 603c2407380c6b..839b076a74438d 100644 --- a/doc/api/events.md +++ b/doc/api/events.md @@ -31,7 +31,7 @@ listener. The `eventEmitter.on()` method is used to register listeners, while the `eventEmitter.emit()` method is used to trigger the event. ```js -const EventEmitter = require('events'); +const EventEmitter = require('node:events'); class MyEmitter extends EventEmitter {} @@ -144,7 +144,7 @@ myEmitter.emit('error', new Error('whoops!')); ``` To guard against crashing the Node.js process the [`domain`][] module can be -used. (Note, however, that the `domain` module is deprecated.) +used. (Note, however, that the `node:domain` module is deprecated.) As a best practice, listeners should always be added for the `'error'` events. @@ -161,7 +161,7 @@ It is possible to monitor `'error'` events without consuming the emitted error by installing a listener using the symbol `events.errorMonitor`. ```js -const { EventEmitter, errorMonitor } = require('events'); +const { EventEmitter, errorMonitor } = require('node:events'); const myEmitter = new EventEmitter(); myEmitter.on(errorMonitor, (err) => { @@ -209,7 +209,7 @@ Setting `events.captureRejections = true` will change the default for all new instances of `EventEmitter`. ```js -const events = require('events'); +const events = require('node:events'); events.captureRejections = true; const ee1 = new events.EventEmitter(); ee1.on('something', async (value) => { @@ -235,10 +235,10 @@ changes: description: Added captureRejections option. --> -The `EventEmitter` class is defined and exposed by the `events` module: +The `EventEmitter` class is defined and exposed by the `node:events` module: ```js -const EventEmitter = require('events'); +const EventEmitter = require('node:events'); ``` All `EventEmitter`s emit the event `'newListener'` when new listeners are @@ -338,7 +338,7 @@ to each. Returns `true` if the event had listeners, `false` otherwise. ```js -const EventEmitter = require('events'); +const EventEmitter = require('node:events'); const myEmitter = new EventEmitter(); // First listener @@ -382,7 +382,7 @@ Returns an array listing the events for which the emitter has registered listeners. The values in the array are strings or `Symbol`s. ```js -const EventEmitter = require('events'); +const EventEmitter = require('node:events'); const myEE = new EventEmitter(); myEE.on('foo', () => {}); myEE.on('bar', () => {}); @@ -758,7 +758,7 @@ It is possible to use [`events.captureRejectionSymbol`][rejectionsymbol] in place of `Symbol.for('nodejs.rejection')`. ```js -const { EventEmitter, captureRejectionSymbol } = require('events'); +const { EventEmitter, captureRejectionSymbol } = require('node:events'); class MyClass extends EventEmitter { constructor() { @@ -830,7 +830,7 @@ events. Listeners installed using this symbol are called before the regular `'error'` listeners are called. Installing a listener using this symbol does not change the behavior once an -`'error'` event is emitted, therefore the process will still crash if no +`'error'` event is emitted. Therefore, the process will still crash if no regular `'error'` listener is installed. ## `events.getEventListeners(emitterOrTarget, eventName)` @@ -854,7 +854,7 @@ For `EventTarget`s this is the only way to get the event listeners for the event target. This is useful for debugging and diagnostic purposes. ```js -const { getEventListeners, EventEmitter } = require('events'); +const { getEventListeners, EventEmitter } = require('node:events'); { const ee = new EventEmitter(); @@ -898,7 +898,7 @@ This method is intentionally generic and works with the web platform `'error'` event semantics and does not listen to the `'error'` event. ```js -const { once, EventEmitter } = require('events'); +const { once, EventEmitter } = require('node:events'); async function run() { const ee = new EventEmitter(); @@ -931,7 +931,7 @@ is used to wait for another event. If `events.once()` is used to wait for the special handling: ```js -const { EventEmitter, once } = require('events'); +const { EventEmitter, once } = require('node:events'); const ee = new EventEmitter(); @@ -947,7 +947,7 @@ ee.emit('error', new Error('boom')); An {AbortSignal} can be used to cancel waiting for the event: ```js -const { EventEmitter, once } = require('events'); +const { EventEmitter, once } = require('node:events'); const ee = new EventEmitter(); const ac = new AbortController(); @@ -980,7 +980,7 @@ queue, and because `EventEmitter` emits all events synchronously, it is possible for `events.once()` to miss an event. ```js -const { EventEmitter, once } = require('events'); +const { EventEmitter, once } = require('node:events'); const myEE = new EventEmitter(); @@ -1007,7 +1007,7 @@ of them, then it becomes possible to use `Promise.all()`, `Promise.race()`, or `Promise.allSettled()`: ```js -const { EventEmitter, once } = require('events'); +const { EventEmitter, once } = require('node:events'); const myEE = new EventEmitter(); @@ -1076,7 +1076,7 @@ A class method that returns the number of listeners for the given `eventName` registered on the given `emitter`. ```js -const { EventEmitter, listenerCount } = require('events'); +const { EventEmitter, listenerCount } = require('node:events'); const myEmitter = new EventEmitter(); myEmitter.on('event', () => {}); myEmitter.on('event', () => {}); @@ -1099,7 +1099,7 @@ added: * Returns: {AsyncIterator} that iterates `eventName` events emitted by the `emitter` ```js -const { on, EventEmitter } = require('events'); +const { on, EventEmitter } = require('node:events'); (async () => { const ee = new EventEmitter(); @@ -1128,7 +1128,7 @@ composed of the emitted event arguments. An {AbortSignal} can be used to cancel waiting on events: ```js -const { on, EventEmitter } = require('events'); +const { on, EventEmitter } = require('node:events'); const ac = new AbortController(); (async () => { @@ -1168,7 +1168,7 @@ added: v15.4.0 const { setMaxListeners, EventEmitter -} = require('events'); +} = require('node:events'); const target = new EventTarget(); const emitter = new EventEmitter(); @@ -1189,9 +1189,9 @@ require manual async tracking. Specifically, all events emitted by instances of `events.EventEmitterAsyncResource` will run within its [async context][]. ```js -const { EventEmitterAsyncResource } = require('events'); -const { notStrictEqual, strictEqual } = require('assert'); -const { executionAsyncId } = require('async_hooks'); +const { EventEmitterAsyncResource } = require('node:events'); +const { notStrictEqual, strictEqual } = require('node:assert'); +const { executionAsyncId } = require('node:async_hooks'); // Async tracking tooling will identify this as 'Q'. const ee1 = new EventEmitterAsyncResource({ name: 'Q' }); diff --git a/doc/api/fs.md b/doc/api/fs.md index f2e4e2757cb4eb..46a15ba5473287 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -8,27 +8,27 @@ -The `fs` module enables interacting with the file system in a +The `node:fs` module enables interacting with the file system in a way modeled on standard POSIX functions. To use the promise-based APIs: ```mjs -import * as fs from 'fs/promises'; +import * as fs from 'node:fs/promises'; ``` ```cjs -const fs = require('fs/promises'); +const fs = require('node:fs/promises'); ``` To use the callback and sync APIs: ```mjs -import * as fs from 'fs'; +import * as fs from 'node:fs'; ``` ```cjs -const fs = require('fs'); +const fs = require('node:fs'); ``` All file system operations have synchronous, callback, and promise-based @@ -40,7 +40,7 @@ Promise-based operations return a promise that is fulfilled when the asynchronous operation is complete. ```mjs -import { unlink } from 'fs/promises'; +import { unlink } from 'node:fs/promises'; try { await unlink('/tmp/hello'); @@ -51,7 +51,7 @@ try { ``` ```cjs -const { unlink } = require('fs/promises'); +const { unlink } = require('node:fs/promises'); (async function(path) { try { @@ -72,7 +72,7 @@ reserved for an exception. If the operation is completed successfully, then the first argument is `null` or `undefined`. ```mjs -import { unlink } from 'fs'; +import { unlink } from 'node:fs'; unlink('/tmp/hello', (err) => { if (err) throw err; @@ -81,7 +81,7 @@ unlink('/tmp/hello', (err) => { ``` ```cjs -const { unlink } = require('fs'); +const { unlink } = require('node:fs'); unlink('/tmp/hello', (err) => { if (err) throw err; @@ -89,7 +89,7 @@ unlink('/tmp/hello', (err) => { }); ``` -The callback-based versions of the `fs` module APIs are preferable over +The callback-based versions of the `node:fs` module APIs are preferable over the use of the promise APIs when maximal performance (both in terms of execution time and memory allocation) is required. @@ -100,7 +100,7 @@ execution until the operation is complete. Exceptions are thrown immediately and can be handled using `try…catch`, or can be allowed to bubble up. ```mjs -import { unlinkSync } from 'fs'; +import { unlinkSync } from 'node:fs'; try { unlinkSync('/tmp/hello'); @@ -111,7 +111,7 @@ try { ``` ```cjs -const { unlinkSync } = require('fs'); +const { unlinkSync } = require('node:fs'); try { unlinkSync('/tmp/hello'); @@ -128,7 +128,7 @@ added: v10.0.0 changes: - version: v14.0.0 pr-url: https://github.com/nodejs/node/pull/31553 - description: Exposed as `require('fs/promises')`. + description: Exposed as `require('node:fs/promises')`. - version: - v11.14.0 - v10.17.0 @@ -136,7 +136,7 @@ changes: description: This API is no longer experimental. - version: v10.1.0 pr-url: https://github.com/nodejs/node/pull/20504 - description: The API is accessible via `require('fs').promises` only. + description: The API is accessible via `require('node:fs').promises` only. --> The `fs/promises` API provides asynchronous file system methods that return @@ -179,9 +179,19 @@ longer be used. -* `data` {string|Buffer|TypedArray|DataView} +* `data` {string|Buffer|TypedArray|DataView|AsyncIterable|Iterable|Stream} * `options` {Object|string} * `encoding` {string|null} **Default:** `'utf8'` * Returns: {Promise} Fulfills with `undefined` upon success. @@ -227,7 +237,7 @@ Closes the file handle after waiting for any pending operation on the handle to complete. ```mjs -import { open } from 'fs/promises'; +import { open } from 'node:fs/promises'; let filehandle; try { @@ -252,8 +262,8 @@ added: v16.11.0 * `highWaterMark` {integer} **Default:** `64 * 1024` * Returns: {fs.ReadStream} -Unlike the 16 kb default `highWaterMark` for a {stream.Readable}, the stream -returned by this method has a default `highWaterMark` of 64 kb. +Unlike the 16 KiB default `highWaterMark` for a {stream.Readable}, the stream +returned by this method has a default `highWaterMark` of 64 KiB. `options` can include `start` and `end` values to read a range of bytes from the file instead of the entire file. Both `start` and `end` are inclusive and @@ -272,7 +282,7 @@ By default, the stream will emit a `'close'` event after it has been destroyed. Set the `emitClose` option to `false` to change this behavior. ```mjs -import { open } from 'fs/promises'; +import { open } from 'node:fs/promises'; const fd = await open('/dev/input/event0'); // Create a stream from some character device. @@ -298,7 +308,7 @@ automatically. An example to read the last 10 bytes of a file which is 100 bytes long: ```mjs -import { open } from 'fs/promises'; +import { open } from 'node:fs/promises'; const fd = await open('sample.txt'); fd.createReadStream({ start: 90, end: 99 }); @@ -438,7 +448,7 @@ await file.close(); ```cjs const { open, -} = require('fs/promises'); +} = require('node:fs/promises'); (async () => { const file = await open('./some/file/to/read'); @@ -542,7 +552,7 @@ retained in the file. The following example retains only the first four bytes of the file: ```mjs -import { open } from 'fs/promises'; +import { open } from 'node:fs/promises'; let filehandle = null; try { @@ -576,21 +586,17 @@ then resolves the promise with no arguments upon success. -* `buffer` {Buffer|TypedArray|DataView|string|Object} +* `buffer` {Buffer|TypedArray|DataView} * `offset` {integer} The start position from within `buffer` where the data to write begins. **Default:** `0` * `length` {integer} The number of bytes from `buffer` to write. **Default:** - `buffer.byteLength` + `buffer.byteLength - offset` * `position` {integer} The offset from the beginning of the file where the data from `buffer` should be written. If `position` is not a `number`, the data will be written at the current position. See the POSIX pwrite(2) @@ -599,18 +605,15 @@ changes: Write `buffer` to the file. -If `buffer` is a plain object, it must have an own (not inherited) `toString` -function property. - The promise is resolved with an object containing two properties: * `bytesWritten` {integer} the number of bytes written -* `buffer` {Buffer|TypedArray|DataView|string|Object} a reference to the +* `buffer` {Buffer|TypedArray|DataView} a reference to the `buffer` written. It is unsafe to use `filehandle.write()` multiple times on the same file without waiting for the promise to be resolved (or rejected). For this -scenario, use [`fs.createWriteStream()`][]. +scenario, use [`filehandle.createWriteStream()`][]. On Linux, positional writes do not work when the file is opened in append mode. The kernel ignores the position argument and always appends the data to @@ -621,17 +624,13 @@ the end of the file. -* `string` {string|Object} +* `string` {string} * `position` {integer} The offset from the beginning of the file where the data from `string` should be written. If `position` is not a `number` the data will be written at the current position. See the POSIX pwrite(2) @@ -639,17 +638,17 @@ changes: * `encoding` {string} The expected string encoding. **Default:** `'utf8'` * Returns: {Promise} -Write `string` to the file. If `string` is not a string, or an object with an -own `toString` function property, the promise is rejected with an error. +Write `string` to the file. If `string` is not a string, the promise is +rejected with an error. The promise is resolved with an object containing two properties: * `bytesWritten` {integer} the number of bytes written -* `buffer` {string|Object} a reference to the `string` written. +* `buffer` {string} a reference to the `string` written. It is unsafe to use `filehandle.write()` multiple times on the same file without waiting for the promise to be resolved (or rejected). For this -scenario, use [`fs.createWriteStream()`][]. +scenario, use [`filehandle.createWriteStream()`][]. On Linux, positional writes do not work when the file is opened in append mode. The kernel ignores the position argument and always appends the data to @@ -665,27 +664,21 @@ changes: - v14.18.0 pr-url: https://github.com/nodejs/node/pull/37490 description: The `data` argument supports `AsyncIterable`, `Iterable` and `Stream`. - - version: v14.12.0 - pr-url: https://github.com/nodejs/node/pull/34993 - description: The `data` parameter will stringify an object with an - explicit `toString` function. - version: v14.0.0 pr-url: https://github.com/nodejs/node/pull/31030 description: The `data` parameter won't coerce unsupported input to strings anymore. --> -* `data` {string|Buffer|TypedArray|DataView|Object|AsyncIterable|Iterable - |Stream} +* `data` {string|Buffer|TypedArray|DataView|AsyncIterable|Iterable|Stream} * `options` {Object|string} * `encoding` {string|null} The expected character encoding when `data` is a string. **Default:** `'utf8'` * Returns: {Promise} Asynchronously writes data to a file, replacing the file if it already exists. -`data` can be a string, a buffer, an {AsyncIterable} or {Iterable} object, or an -object with an own `toString` function -property. The promise is resolved with no arguments upon success. +`data` can be a string, a buffer, an {AsyncIterable} or {Iterable} object. +The promise is resolved with no arguments upon success. If `options` is a string, then it specifies the `encoding`. @@ -750,8 +743,8 @@ with an {Error} object. The following example checks if the file `/etc/passwd` can be read and written by the current process. ```mjs -import { access } from 'fs/promises'; -import { constants } from 'fs'; +import { access } from 'node:fs/promises'; +import { constants } from 'node:fs'; try { await access('/etc/passwd', constants.R_OK | constants.W_OK); @@ -853,8 +846,8 @@ error occurs after the destination file has been opened for writing, an attempt will be made to remove the destination. ```mjs -import { constants } from 'fs'; -import { copyFile } from 'fs/promises'; +import { constants } from 'node:fs'; +import { copyFile } from 'node:fs/promises'; try { await copyFile('source.txt', 'destination.txt'); @@ -1044,7 +1037,7 @@ The optional `options` argument can be a string specifying an encoding, or an object with an `encoding` property specifying the character encoding to use. ```mjs -import { mkdtemp } from 'fs/promises'; +import { mkdtemp } from 'node:fs/promises'; try { await mkdtemp(path.join(os.tmpdir(), 'foo-')); @@ -1057,7 +1050,7 @@ The `fsPromises.mkdtemp()` method will append the six randomly selected characters directly to the `prefix` string. For instance, given a directory `/tmp`, if the intention is to create a temporary directory _within_ `/tmp`, the `prefix` must end with a trailing platform-specific path separator -(`require('path').sep`). +(`require('node:path').sep`). ### `fsPromises.open(path, flags[, mode])` @@ -1117,7 +1110,7 @@ directory and subsequent read operations. Example using async iteration: ```mjs -import { opendir } from 'fs/promises'; +import { opendir } from 'node:fs/promises'; try { const dir = await opendir('./'); @@ -1159,7 +1152,7 @@ If `options.withFileTypes` is set to `true`, the resolved array will contain {fs.Dirent} objects. ```mjs -import { readdir } from 'fs/promises'; +import { readdir } from 'node:fs/promises'; try { const files = await readdir(path); @@ -1206,7 +1199,7 @@ It is possible to abort an ongoing `readFile` using an {AbortSignal}. If a request is aborted the promise returned is rejected with an `AbortError`: ```mjs -import { readFile } from 'fs/promises'; +import { readFile } from 'node:fs/promises'; try { const controller = new AbortController(); @@ -1481,7 +1474,7 @@ Returns an async iterator that watches for changes on `filename`, where `filenam is either a file or a directory. ```js -const { watch } = require('fs/promises'); +const { watch } = require('node:fs/promises'); const ac = new AbortController(); const { signal } = ac; @@ -1521,10 +1514,6 @@ changes: pr-url: https://github.com/nodejs/node/pull/35993 description: The options argument may include an AbortSignal to abort an ongoing writeFile request. - - version: v14.12.0 - pr-url: https://github.com/nodejs/node/pull/34993 - description: The `data` parameter will stringify an object with an - explicit `toString` function. - version: v14.0.0 pr-url: https://github.com/nodejs/node/pull/31030 description: The `data` parameter won't coerce unsupported input to @@ -1532,8 +1521,7 @@ changes: --> * `file` {string|Buffer|URL|FileHandle} filename or `FileHandle` -* `data` {string|Buffer|TypedArray|DataView|Object|AsyncIterable|Iterable - |Stream} +* `data` {string|Buffer|TypedArray|DataView|AsyncIterable|Iterable|Stream} * `options` {Object|string} * `encoding` {string|null} **Default:** `'utf8'` * `mode` {integer} **Default:** `0o666` @@ -1542,8 +1530,7 @@ changes: * Returns: {Promise} Fulfills with `undefined` upon success. Asynchronously writes data to a file, replacing the file if it already exists. -`data` can be a string, a {Buffer}, or, an object with an own (not inherited) -`toString` function property. +`data` can be a string, a buffer, an {AsyncIterable} or {Iterable} object. The `encoding` option is ignored if `data` is a buffer. @@ -1560,15 +1547,15 @@ without waiting for the promise to be settled. Similarly to `fsPromises.readFile` - `fsPromises.writeFile` is a convenience method that performs multiple `write` calls internally to write the buffer passed to it. For performance sensitive code consider using -[`fs.createWriteStream()`][]. +[`fs.createWriteStream()`][] or [`filehandle.createWriteStream()`][]. It is possible to use an {AbortSignal} to cancel an `fsPromises.writeFile()`. Cancelation is "best effort", and some amount of data is likely still to be written. ```mjs -import { writeFile } from 'fs/promises'; -import { Buffer } from 'buffer'; +import { writeFile } from 'node:fs/promises'; +import { Buffer } from 'node:buffer'; try { const controller = new AbortController(); @@ -1604,7 +1591,7 @@ concurrent modifications on the same file or data corruption may occur. * `fd` {integer} -* `buffer` {Buffer|TypedArray|DataView|string|Object} +* `buffer` {Buffer|TypedArray|DataView} * `offset` {integer} * `length` {integer} * `position` {integer} @@ -4384,8 +4367,7 @@ changes: * `bytesWritten` {integer} * `buffer` {Buffer|TypedArray|DataView} -Write `buffer` to the file specified by `fd`. If `buffer` is a normal object, it -must have an own `toString` function property. +Write `buffer` to the file specified by `fd`. `offset` determines the part of the buffer to be written, and `length` is an integer specifying the number of bytes to write. @@ -4413,6 +4395,10 @@ the end of the file. * `fd` {integer} -* `buffer` {Buffer|TypedArray|DataView|string|Object} +* `buffer` {Buffer|TypedArray|DataView} * `offset` {integer} * `length` {integer} * `position` {integer} * Returns: {number} The number of bytes written. -If `buffer` is a plain object, it must have an own (not inherited) `toString` -function property. - For detailed information, see the documentation of the asynchronous version of this API: [`fs.write(fd, buffer...)`][]. @@ -5793,10 +5774,6 @@ this API: [`fs.write(fd, buffer...)`][]. * `fd` {integer} -* `string` {string|Object} +* `string` {string} * `position` {integer} * `encoding` {string} * Returns: {number} The number of bytes written. -If `string` is a plain object, it must have an own (not inherited) `toString` -function property. - For detailed information, see the documentation of the asynchronous version of this API: [`fs.write(fd, string...)`][]. @@ -5849,7 +5823,7 @@ Created by [`fs.opendir()`][], [`fs.opendirSync()`][], or [`fsPromises.opendir()`][]. ```mjs -import { opendir } from 'fs/promises'; +import { opendir } from 'node:fs/promises'; try { const dir = await opendir('./'); @@ -5882,7 +5856,7 @@ closed. -For most `fs` module functions, the `path` or `filename` argument may be passed -as a {URL} object using the `file:` protocol. +For most `node:fs` module functions, the `path` or `filename` argument may be +passed as a {URL} object using the `file:` protocol. ```mjs -import { readFileSync } from 'fs'; +import { readFileSync } from 'node:fs'; readFileSync(new URL('file:///tmp/hello')); ``` @@ -7220,7 +7210,7 @@ On Windows, `file:` {URL}s with a host name convert to UNC paths, while `file:` with no host name and no drive letter will result in an error: ```mjs -import { readFileSync } from 'fs'; +import { readFileSync } from 'node:fs'; // On Windows : // - WHATWG file URLs with hostname convert to UNC path @@ -7244,7 +7234,7 @@ On all other platforms, `file:` {URL}s with a host name are unsupported and will result in an error: ```mjs -import { readFileSync } from 'fs'; +import { readFileSync } from 'node:fs'; // On other platforms: // - WHATWG file URLs with hostname are unsupported @@ -7261,7 +7251,7 @@ A `file:` {URL} having encoded slash characters will result in an error on all platforms: ```mjs -import { readFileSync } from 'fs'; +import { readFileSync } from 'node:fs'; // On Windows readFileSync(new URL('file:///C:/p/a/t/h/%2F')); @@ -7279,7 +7269,7 @@ readFileSync(new URL('file:///p/a/t/h/%2f')); On Windows, `file:` {URL}s having encoded backslash will result in an error: ```mjs -import { readFileSync } from 'fs'; +import { readFileSync } from 'node:fs'; // On Windows readFileSync(new URL('file:///C:/path/%5C')); @@ -7299,8 +7289,8 @@ be relative or absolute: Example using an absolute path on POSIX: ```mjs -import { open } from 'fs/promises'; -import { Buffer } from 'buffer'; +import { open } from 'node:fs/promises'; +import { Buffer } from 'node:buffer'; let fd; try { @@ -7340,7 +7330,7 @@ are completed. Failure to do so will result in a memory leak that will eventually cause an application to crash. ```mjs -import { open, close, fstat } from 'fs'; +import { open, close, fstat } from 'node:fs'; function closeFd(fd) { close(fd, (err) => { @@ -7374,7 +7364,7 @@ that resources are not leaked. However, it is still required that they are closed when operations are completed: ```mjs -import { open } from 'fs/promises'; +import { open } from 'node:fs/promises'; let file; try { @@ -7497,6 +7487,7 @@ the file contents. [`ReadDirectoryChangesW`]: https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-readdirectorychangesw [`UV_THREADPOOL_SIZE`]: cli.md#uv_threadpool_sizesize [`event ports`]: https://illumos.org/man/port_create +[`filehandle.createWriteStream()`]: #filehandlecreatewritestreamoptions [`filehandle.writeFile()`]: #filehandlewritefiledata-options [`fs.access()`]: #fsaccesspath-mode-callback [`fs.accessSync()`]: #fsaccesssyncpath-mode diff --git a/doc/api/globals.md b/doc/api/globals.md index 5d9e81d9afcfff..4aef31aa24ac91 100644 --- a/doc/api/globals.md +++ b/doc/api/globals.md @@ -210,7 +210,7 @@ If `abortSignal.aborted` is `true`, throws `abortSignal.reason`. ## Class: `Blob` @@ -229,6 +229,16 @@ added: v0.1.103 Used to handle binary data. See the [buffer section][]. +## Class: `ByteLengthQueuingStrategy` + + + +> Stability: 1 - Experimental. + +A browser-compatible implementation of [`ByteLengthQueuingStrategy`][]. + ## `__dirname` This variable may appear to be global but is not. See [`__dirname`][]. @@ -250,7 +260,7 @@ Global alias for [`buffer.atob()`][]. ## `BroadcastChannel` See {BroadcastChannel}. @@ -295,6 +305,16 @@ added: v0.0.1 [`clearTimeout`][] is described in the [timers][] section. +## Class: `CompressionStream` + + + +> Stability: 1 - Experimental. + +A browser-compatible implementation of [`CompressionStream`][]. + ## `console` + +> Stability: 1 - Experimental. + +A browser-compatible implementation of [`CountQueuingStrategy`][]. + ## `Crypto` + +> Stability: 1 - Experimental. + +A browser-compatible implementation of [`DecompressionStream`][]. ## `Event` @@ -516,6 +556,66 @@ DataHandler.prototype.load = async function load(key) { }; ``` +## Class: `ReadableByteStreamController` + + + +> Stability: 1 - Experimental. + +A browser-compatible implementation of [`ReadableByteStreamController`][]. + +## Class: `ReadableStream` + + + +> Stability: 1 - Experimental. + +A browser-compatible implementation of [`ReadableStream`][]. + +## Class: `ReadableStreamBYOBReader` + + + +> Stability: 1 - Experimental. + +A browser-compatible implementation of [`ReadableStreamBYOBReader`][]. + +## Class: `ReadableStreamBYOBRequest` + + + +> Stability: 1 - Experimental. + +A browser-compatible implementation of [`ReadableStreamBYOBRequest`][]. + +## Class: `ReadableStreamDefaultController` + + + +> Stability: 1 - Experimental. + +A browser-compatible implementation of [`ReadableStreamDefaultController`][]. + +## Class: `ReadableStreamDefaultReader` + + + +> Stability: 1 - Experimental. + +A browser-compatible implementation of [`ReadableStreamDefaultReader`][]. + ## `require()` This variable may appear to be global but is not. See [`require()`][]. @@ -593,7 +693,7 @@ added: v17.6.0 A browser-compatible implementation of {SubtleCrypto}. This global is available only if the Node.js binary was compiled with including support for the -`crypto` module. +`node:crypto` module. ## `DOMException` @@ -615,6 +715,16 @@ added: v11.0.0 The WHATWG `TextDecoder` class. See the [`TextDecoder`][] section. +## Class: `TextDecoderStream` + + + +> Stability: 1 - Experimental. + +A browser-compatible implementation of [`TextDecoderStream`][]. + ## `TextEncoder` + +> Stability: 1 - Experimental. + +A browser-compatible implementation of [`TextEncoderStream`][]. + +## Class: `TransformStream` + + + +> Stability: 1 - Experimental. + +A browser-compatible implementation of [`TransformStream`][]. + +## Class: `TransformStreamDefaultController` + + + +> Stability: 1 - Experimental. + +A browser-compatible implementation of [`TransformStreamDefaultController`][]. + ## `URL` + +> Stability: 1 - Experimental. + +A browser-compatible implementation of [`WritableStream`][]. + +## Class: `WritableStreamDefaultController` + + + +> Stability: 1 - Experimental. + +A browser-compatible implementation of [`WritableStreamDefaultController`][]. + +## Class: `WritableStreamDefaultWriter` + + + +> Stability: 1 - Experimental. + +A browser-compatible implementation of [`WritableStreamDefaultWriter`][]. + [Web Crypto API]: webcrypto.md [`--experimental-global-webcrypto`]: cli.md#--experimental-global-webcrypto [`--no-experimental-fetch`]: cli.md#--no-experimental-fetch [`AbortController`]: https://developer.mozilla.org/en-US/docs/Web/API/AbortController +[`ByteLengthQueuingStrategy`]: webstreams.md#class-bytelengthqueuingstrategy +[`CompressionStream`]: webstreams.md#class-compressionstream +[`CountQueuingStrategy`]: webstreams.md#class-countqueuingstrategy [`DOMException`]: https://developer.mozilla.org/en-US/docs/Web/API/DOMException +[`DecompressionStream`]: webstreams.md#class-decompressionstream [`EventTarget` and `Event` API]: events.md#eventtarget-and-event-api [`MessageChannel`]: worker_threads.md#class-messagechannel [`MessageEvent`]: https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent/MessageEvent [`MessagePort`]: worker_threads.md#class-messageport +[`ReadableByteStreamController`]: webstreams.md#class-readablebytestreamcontroller +[`ReadableStreamBYOBReader`]: webstreams.md#class-readablestreambyobreader +[`ReadableStreamBYOBRequest`]: webstreams.md#class-readablestreambyobrequest +[`ReadableStreamDefaultController`]: webstreams.md#class-readablestreamdefaultcontroller +[`ReadableStreamDefaultReader`]: webstreams.md#class-readablestreamdefaultreader +[`ReadableStream`]: webstreams.md#class-readablestream +[`TextDecoderStream`]: webstreams.md#class-textdecoderstream [`TextDecoder`]: util.md#class-utiltextdecoder +[`TextEncoderStream`]: webstreams.md#class-textencoderstream [`TextEncoder`]: util.md#class-utiltextencoder +[`TransformStreamDefaultController`]: webstreams.md#class-transformstreamdefaultcontroller +[`TransformStream`]: webstreams.md#class-transformstream [`URLSearchParams`]: url.md#class-urlsearchparams [`URL`]: url.md#class-url +[`WritableStreamDefaultController`]: webstreams.md#class-writablestreamdefaultcontroller +[`WritableStreamDefaultWriter`]: webstreams.md#class-writablestreamdefaultwriter +[`WritableStream`]: webstreams.md#class-writablestream [`__dirname`]: modules.md#__dirname [`__filename`]: modules.md#__filename [`buffer.atob()`]: buffer.md#bufferatobdata diff --git a/doc/api/http.md b/doc/api/http.md index 8c60b43c7e365b..543a25dfdf80c2 100644 --- a/doc/api/http.md +++ b/doc/api/http.md @@ -6,7 +6,7 @@ -To use the HTTP server and client one must `require('http')`. +To use the HTTP server and client one must `require('node:http')`. The HTTP interfaces in Node.js are designed to support many features of the protocol which have been traditionally difficult to use. @@ -188,7 +188,7 @@ of these values set to their respective defaults. To configure any of them, a custom [`http.Agent`][] instance must be created. ```js -const http = require('http'); +const http = require('node:http'); const keepAliveAgent = new http.Agent({ keepAlive: true }); options.agent = keepAliveAgent; http.request(options, onResponseCallback); @@ -296,10 +296,14 @@ the agent when `keepAlive` is enabled. Do not modify. Sockets in the `freeSockets` list will be automatically destroyed and removed from the array on `'timeout'`. -### `agent.getName(options)` +### `agent.getName([options])` * `options` {Object} A set of options providing information for name generation @@ -389,7 +393,7 @@ agent. Do not modify. added: v0.1.17 --> -* Extends: {Stream} +* Extends: {http.OutgoingMessage} This object is created internally and returned from [`http.request()`][]. It represents an _in-progress_ request whose header has already been queued. The @@ -434,6 +438,15 @@ deprecated: Emitted when the request has been aborted by the client. This event is only emitted on the first call to `abort()`. +### Event: `'close'` + + + +Indicates that the request is completed, or its underlying connection was +terminated prematurely (before the response completion). + ### Event: `'connect'` + +Emitted when the request has been sent. More specifically, this event is emitted +when the last segment of the response headers and body have been handed off to +the operating system for transmission over the network. It does not imply that +the server has received anything yet. + ### Event: `'information'` + +See [`writable.cork()`][]. + ### `request.end([data[, encoding]][, callback])` + +* Returns: {string\[]} + +Returns an array containing the unique names of the current outgoing headers. +All header names are lowercase. + +```js +request.setHeader('Foo', 'bar'); +request.setHeader('Cookie', ['foo=bar', 'bar=baz']); + +const headerNames = request.getHeaderNames(); +// headerNames === ['foo', 'Cookie'] +``` + +### `request.getHeaders()` + + + +* Returns: {Object} + +Returns a shallow copy of the current outgoing headers. Since a shallow copy +is used, array values may be mutated without additional calls to various +header-related http module methods. The keys of the returned object are the +header names and the values are the respective header values. All header names +are lowercase. + +The object returned by the `response.getHeaders()` method _does not_ +prototypically inherit from the JavaScript `Object`. This means that typical +`Object` methods such as `obj.toString()`, `obj.hasOwnProperty()`, and others +are not defined and _will not work_. + +```js +request.setHeader('Foo', 'bar'); +request.setHeader('Cookie', ['foo=bar', 'bar=baz']); + +const headers = response.getHeaders(); +// headers === { foo: 'bar', 'cookie': ['foo=bar', 'bar=baz'] } +``` + ### `request.getRawHeaderNames()` + +* `name` {string} +* Returns: {boolean} + +Returns `true` if the header identified by `name` is currently set in the +outgoing headers. The header name matching is case-insensitive. + +```js +const hasContentType = request.hasHeader('content-type'); +``` + ### `request.maxHeadersCount` * {number} **Default:** `2000` @@ -914,7 +1010,7 @@ might be reused. But if server closes connection at unfortunate time, client may run into a 'ECONNRESET' error. ```js -const http = require('http'); +const http = require('node:http'); // Server has a 5 seconds keep-alive timeout by default http @@ -938,7 +1034,7 @@ By marking a request whether it reused socket or not, we can do automatic error retry base on it. ```js -const http = require('http'); +const http = require('node:http'); const agent = new http.Agent({ keepAlive: true }); function retriableRequest() { @@ -983,6 +1079,17 @@ or request.setHeader('Cookie', ['type=ninja', 'language=javascript']); ``` +When the value is a string an exception will be thrown if it contains +characters outside the `latin1` encoding. + +If you need to pass UTF-8 characters in the value please encode the value +using the [RFC 8187][] standard. + +```js +const filename = 'Rock 🎵.txt'; +request.setHeader('Content-Disposition', `attachment; filename*=utf-8''${encodeURIComponent(filename)}`); +``` + ### `request.setNoDelay([noDelay])` + +See [`writable.uncork()`][]. + ### `request.writableEnded` -* {number} **Default:** `0` +* {number} **Default:** `300000` Sets the timeout value in milliseconds for receiving the entire request from the client. @@ -1490,7 +1609,7 @@ affects new connections to the server, not any existing connections. added: v0.1.17 --> -* Extends: {Stream} +* Extends: {http.OutgoingMessage} This object is created internally by an HTTP server, not by the user. It is passed as the second parameter to the [`'request'`][] event. @@ -1838,7 +1957,7 @@ because of how the protocol parser attaches to the socket. After `response.end()`, the property is nulled. ```js -const http = require('http'); +const http = require('node:http'); const server = http.createServer((req, res) => { const ip = res.socket.remoteAddress; const port = res.socket.remotePort; @@ -1939,7 +2058,7 @@ it will switch to implicit header mode and flush the implicit headers. This sends a chunk of the response body. This method may be called multiple times to provide successive parts of the body. -In the `http` module, the response body is omitted when the +In the `node:http` module, the response body is omitted when the request is a HEAD request. Similarly, the `204` and `304` responses _must not_ include a message body. @@ -2108,9 +2227,14 @@ Emitted when the request has been aborted. -Indicates that the underlying connection was closed. +Emitted when the request has been completed. ### `message.aborted` @@ -2197,7 +2321,7 @@ changes: pr-url: https://github.com/nodejs/node/pull/35281 description: >- `message.headers` is now lazily computed using an accessor property - on the prototype. + on the prototype and is no longer enumerable. --> * {Object} @@ -2212,7 +2336,7 @@ Key-value pairs of header names and values. Header names are lower-cased. // { 'user-agent': 'curl/7.22.0', // host: '127.0.0.1:8000', // accept: '*/*' } -console.log(request.headers); +console.log(request.getHeaders()); ``` Duplicates in raw headers are handled in the following ways, depending on the @@ -2379,15 +2503,15 @@ Accept: text/plain To parse the URL into its parts: ```js -new URL(request.url, `http://${request.headers.host}`); +new URL(request.url, `http://${request.getHeaders().host}`); ``` When `request.url` is `'/status?name=ryan'` and -`request.headers.host` is `'localhost:3000'`: +`request.getHeaders().host` is `'localhost:3000'`: ```console $ node -> new URL(request.url, `http://${request.headers.host}`) +> new URL(request.url, `http://${request.getHeaders().host}`) URL { href: 'http://localhost:3000/status?name=ryan', origin: 'http://localhost:3000', @@ -2413,10 +2537,10 @@ added: v0.1.17 * Extends: {Stream} This class serves as the parent class of [`http.ClientRequest`][] -and [`http.ServerResponse`][]. It is an abstract of outgoing message from -the perspective of the participants of HTTP transaction. +and [`http.ServerResponse`][]. It is an abstract outgoing message from +the perspective of the participants of an HTTP transaction. -### Event: `drain` +### Event: `'drain'` -Emitted when `outgoingMessage.end` was called. +Emitted after `outgoingMessage.end()` is called. When the event is emitted, all data has been processed but not necessarily completely flushed. @@ -2452,11 +2576,11 @@ added: v0.3.0 Adds HTTP trailers (headers but at the end of the message) to the message. -Trailers are **only** be emitted if the message is chunked encoded. If not, -the trailer will be silently discarded. +Trailers will **only** be emitted if the message is chunked encoded. If not, +the trailers will be silently discarded. HTTP requires the `Trailer` header to be sent to emit trailers, -with a list of header fields in its value, e.g. +with a list of header field names in its value, e.g. ```js message.writeHead(200, { 'Content-Type': 'text/plain', @@ -2480,12 +2604,14 @@ deprecated: > Stability: 0 - Deprecated: Use [`outgoingMessage.socket`][] instead. -Aliases of `outgoingMessage.socket` +Alias of [`outgoingMessage.socket`][]. ### `outgoingMessage.cork()` See [`writable.cork()`][]. @@ -2518,14 +2644,14 @@ changes: Finishes the outgoing message. If any parts of the body are unsent, it will flush them to the underlying system. If the message is chunked, it will -send the terminating chunk `0\r\n\r\n`, and send the trailer (if any). +send the terminating chunk `0\r\n\r\n`, and send the trailers (if any). -If `chunk` is specified, it is equivalent to call +If `chunk` is specified, it is equivalent to calling `outgoingMessage.write(chunk, encoding)`, followed by `outgoingMessage.end(callback)`. -If `callback` is provided, it will be called when the message is finished. -(equivalent to the callback to event `finish`) +If `callback` is provided, it will be called when the message is finished +(equivalent to a listener of the `'finish'` event). ### `outgoingMessage.flushHeaders()` @@ -2533,7 +2659,7 @@ If `callback` is provided, it will be called when the message is finished. added: v1.6.0 --> -Compulsorily flushes the message headers +Flushes the message headers. For efficiency reason, Node.js normally buffers the message headers until `outgoingMessage.end()` is called or the first chunk of message data @@ -2542,7 +2668,7 @@ packet. It is usually desired (it saves a TCP round-trip), but not when the first data is not sent until possibly much later. `outgoingMessage.flushHeaders()` -bypasses the optimization and kickstarts the request. +bypasses the optimization and kickstarts the message. ### `outgoingMessage.getHeader(name)` @@ -2553,24 +2679,24 @@ added: v0.4.0 * `name` {string} Name of header * Returns {string | undefined} -Gets the value of HTTP header with the given name. If such a name doesn't -exist in message, it will be `undefined`. +Gets the value of the HTTP header with the given name. If that header is not +set, the returned value will be `undefined`. ### `outgoingMessage.getHeaderNames()` * Returns {string\[]} -Returns an array of names of headers of the outgoing outgoingMessage. All -names are lowercase. +Returns an array containing the unique names of the current outgoing headers. +All names are lowercase. ### `outgoingMessage.getHeaders()` * Returns: {Object} @@ -2582,8 +2708,8 @@ object are the header names and the values are the respective header values. All header names are lowercase. The object returned by the `outgoingMessage.getHeaders()` method does -not prototypically inherit from the JavaScript Object. This means that -typical Object methods such as `obj.toString()`, `obj.hasOwnProperty()`, +not prototypically inherit from the JavaScript `Object`. This means that +typical `Object` methods such as `obj.toString()`, `obj.hasOwnProperty()`, and others are not defined and will not work. ```js @@ -2597,7 +2723,7 @@ const headers = outgoingMessage.getHeaders(); ### `outgoingMessage.hasHeader(name)` * `name` {string} @@ -2626,21 +2752,20 @@ Read-only. `true` if the headers were sent, otherwise `false`. added: v9.0.0 --> -Overrides the pipe method of legacy `Stream` which is the parent class of -`http.outgoingMessage`. +Overrides the `stream.pipe()` method inherited from the legacy `Stream` class +which is the parent class of `http.OutgoingMessage`. -Since `OutgoingMessage` should be a write-only stream, -call this function will throw an `Error`. Thus, it disabled the pipe method -it inherits from `Stream`. +Calling this method will throw an `Error` because `outgoingMessage` is a +write-only stream. -The User should not call this function directly. - -### `outgoingMessage.removeHeader()` +### `outgoingMessage.removeHeader(name)` +* `name` {string} Header name + Removes a header that is queued for implicit sending. ```js @@ -2654,10 +2779,12 @@ added: v0.4.0 --> * `name` {string} Header name -* `value` {string} Header value +* `value` {any} Header value * Returns: {this} -Sets a single header value for the header object. +Sets a single header value. If the header already exists in the to-be-sent +headers, its value will be replaced. Use an array of strings to send multiple +headers with the same name. ### `outgoingMessage.setTimeout(msesc[, callback])` @@ -2689,7 +2816,9 @@ After calling `outgoingMessage.end()`, this property will be nulled. ### `outgoingMessage.uncork()` See [`writable.uncork()`][] @@ -2697,71 +2826,67 @@ See [`writable.uncork()`][] ### `outgoingMessage.writableCorked` * {number} -This `outgoingMessage.writableCorked` will return the time how many -`outgoingMessage.cork()` have been called. +The number of times `outgoingMessage.cork()` has been called. ### `outgoingMessage.writableEnded` * {boolean} -Readonly, `true` if `outgoingMessage.end()` has been called. Noted that -this property does not reflect whether the data has been flush. For that -purpose, use `message.writableFinished` instead. +Is `true` if `outgoingMessage.end()` has been called. This property does +not indicate whether the data has been flushed. For that purpose, use +`message.writableFinished` instead. ### `outgoingMessage.writableFinished` * {boolean} -Readonly. `true` if all data has been flushed to the underlying system. +Is `true` if all data has been flushed to the underlying system. ### `outgoingMessage.writableHighWaterMark` * {number} -This `outgoingMessage.writableHighWaterMark` will be the `highWaterMark` of -underlying socket if socket exists. Else, it would be the default -`highWaterMark`. - -`highWaterMark` is the maximum amount of data that can be potentially -buffered by the socket. +The `highWaterMark` of the underlying socket if assigned. Otherwise, the default +buffer level when [`writable.write()`][] starts returning false (`16384`). ### `outgoingMessage.writableLength` * {number} -Readonly, This `outgoingMessage.writableLength` contains the number of -bytes (or objects) in the buffer ready to send. +The number of buffered bytes. ### `outgoingMessage.writableObjectMode` * {boolean} -Readonly, always returns `false`. +Always `false`. ### `outgoingMessage.write(chunk[, encoding][, callback])` @@ -2769,7 +2894,7 @@ Readonly, always returns `false`. added: v0.1.29 changes: - version: v0.11.6 - description: add `callback` argument. + description: The `callback` argument was added. --> * `chunk` {string | Buffer} @@ -2777,35 +2902,17 @@ changes: * `callback` {Function} * Returns {boolean} -If this method is called and the header is not sent, it will call -`this._implicitHeader` to flush implicit header. -If the message should not have a body (indicated by `this._hasBody`), -the call is ignored and `chunk` will not be sent. It could be useful -when handling a particular message which must not include a body. -e.g. response to `HEAD` request, `204` and `304` response. - -`chunk` can be a string or a buffer. When `chunk` is a string, the -`encoding` parameter specifies how to encode `chunk` into a byte stream. -`callback` will be called when the `chunk` is flushed. +Sends a chunk of the body. This method can be called multiple times. -If the message is transferred in chucked encoding -(indicated by `this.chunkedEncoding`), `chunk` will be flushed as -one chunk among a stream of chunks. Otherwise, it will be flushed as the -body of message. +The `encoding` argument is only relevant when `chunk` is a string. Defaults to +`'utf8'`. -This method handles the raw body of the HTTP message and has nothing to do -with higher-level multi-part body encodings that may be used. - -If it is the first call to this method of a message, it will send the -buffered header first, then flush the `chunk` as described above. - -The second and successive calls to this method will assume the data -will be streamed and send the new data separately. It means that the response -is buffered up to the first chunk of the body. +The `callback` argument is optional and will be called when this chunk of data +is flushed. Returns `true` if the entire data was flushed successfully to the kernel buffer. Returns `false` if all or part of the data was queued in the user -memory. Event `drain` will be emitted when the buffer is free again. +memory. The `'drain'` event will be emitted when the buffer is free again. ## `http.METHODS` @@ -2834,6 +2941,17 @@ Found'`. -The `http2` module provides an implementation of the [HTTP/2][] protocol. It -can be accessed using: +The `node:http2` module provides an implementation of the [HTTP/2][] protocol. +It can be accessed using: ```js -const http2 = require('http2'); +const http2 = require('node:http2'); +``` + +## Determining if crypto support is unavailable + +It is possible for Node.js to be built without including support for the +`node:crypto` module. In such cases, attempting to `import` from `node:http2` or +calling `require('node:http2')` will result in an error being thrown. + +When using CommonJS, the error thrown can be caught using try/catch: + +```cjs +let http2; +try { + http2 = require('node:http2'); +} catch (err) { + console.log('http2 support is disabled!'); +} +``` + +When using the lexical ESM `import` keyword, the error can only be +caught if a handler for `process.on('uncaughtException')` is registered +_before_ any attempt to load the module is made (using, for instance, +a preload module). + +When using ESM, if there is a chance that the code may be run on a build +of Node.js where crypto support is not enabled, consider using the +[`import()`][] function instead of the lexical `import` keyword: + +```mjs +let http2; +try { + http2 = await import('node:http2'); +} catch (err) { + console.log('http2 support is disabled!'); +} ``` ## Core API @@ -50,8 +85,8 @@ Since there are no browsers known that support with browser clients. ```js -const http2 = require('http2'); -const fs = require('fs'); +const http2 = require('node:http2'); +const fs = require('node:fs'); const server = http2.createSecureServer({ key: fs.readFileSync('localhost-privkey.pem'), @@ -83,8 +118,8 @@ openssl req -x509 -newkey rsa:2048 -nodes -sha256 -subj '/CN=localhost' \ The following illustrates an HTTP/2 client: ```js -const http2 = require('http2'); -const fs = require('fs'); +const http2 = require('node:http2'); +const fs = require('node:fs'); const client = http2.connect('https://localhost:8443', { ca: fs.readFileSync('localhost-cert.pem') }); @@ -285,7 +320,7 @@ added: v8.4.0 The `'stream'` event is emitted when a new `Http2Stream` is created. ```js -const http2 = require('http2'); +const http2 = require('node:http2'); session.on('stream', (stream, headers, flags) => { const method = headers[':method']; const path = headers[':path']; @@ -305,7 +340,7 @@ and would instead register a handler for the `'stream'` event emitted by the `http2.createSecureServer()`, respectively, as in the example below: ```js -const http2 = require('http2'); +const http2 = require('node:http2'); // Create an unencrypted HTTP/2 server const server = http2.createServer(); @@ -497,7 +532,7 @@ frames have been acknowledged. + +```cjs +let https; +try { + https = require('node:https'); +} catch (err) { + console.log('https support is disabled!'); +} +``` + +When using the lexical ESM `import` keyword, the error can only be +caught if a handler for `process.on('uncaughtException')` is registered +_before_ any attempt to load the module is made (using, for instance, +a preload module). + +When using ESM, if there is a chance that the code may be run on a build +of Node.js where crypto support is not enabled, consider using the +[`import()`][] function instead of the lexical `import` keyword: + +```mjs +let https; +try { + https = await import('node:https'); +} catch (err) { + console.log('https support is disabled!'); +} +``` + ## Class: `https.Agent` -The `inspector` module provides an API for interacting with the V8 inspector. +The `node:inspector` module provides an API for interacting with the V8 +inspector. It can be accessed using: ```js -const inspector = require('inspector'); +const inspector = require('node:inspector'); ``` ## `inspector.close()` @@ -23,7 +24,7 @@ Deactivate the inspector. Blocks until there are no active connections. * {Object} An object to send messages to the remote inspector console. ```js -require('inspector').console.log('a message'); +require('node:inspector').console.log('a message'); ``` The inspector console does not have API parity with Node.js @@ -170,7 +171,7 @@ enabled agents or configured breakpoints. @@ -12,7 +12,7 @@ added: v0.3.7 Provides general utility methods when interacting with instances of `Module`, the [`module`][] variable often seen in [CommonJS][] modules. Accessed -via `import 'module'` or `require('module')`. +via `import 'node:module'` or `require('node:module')`. ### `module.builtinModules` @@ -34,13 +34,13 @@ by the [module wrapper][]. To access it, require the `Module` module: ```mjs // module.mjs // In an ECMAScript module -import { builtinModules as builtin } from 'module'; +import { builtinModules as builtin } from 'node:module'; ``` ```cjs // module.cjs // In a CommonJS module -const builtin = require('module').builtinModules; +const builtin = require('node:module').builtinModules; ``` ### `module.createRequire(filename)` @@ -55,7 +55,7 @@ added: v12.2.0 * Returns: {require} Require function ```mjs -import { createRequire } from 'module'; +import { createRequire } from 'node:module'; const require = createRequire(import.meta.url); // sibling-module.js is a CommonJS module. @@ -73,9 +73,9 @@ builtin [ES Modules][] to match the properties of the [CommonJS][] exports. It does not add or remove exported names from the [ES Modules][]. ```js -const fs = require('fs'); -const assert = require('assert'); -const { syncBuiltinESMExports } = require('module'); +const fs = require('node:fs'); +const assert = require('node:assert'); +const { syncBuiltinESMExports } = require('node:module'); fs.readFile = newAPI; @@ -89,7 +89,7 @@ fs.newAPI = newAPI; syncBuiltinESMExports(); -import('fs').then((esmFS) => { +import('node:fs').then((esmFS) => { // It syncs the existing readFile property with the new value assert.strictEqual(esmFS.readFile, newAPI); // readFileSync has been deleted from the required fs @@ -122,13 +122,13 @@ To enable source map parsing, Node.js must be run with the flag ```mjs // module.mjs // In an ECMAScript module -import { findSourceMap, SourceMap } from 'module'; +import { findSourceMap, SourceMap } from 'node:module'; ``` ```cjs // module.cjs // In a CommonJS module -const { findSourceMap, SourceMap } = require('module'); +const { findSourceMap, SourceMap } = require('node:module'); ``` diff --git a/doc/api/modules.md b/doc/api/modules.md index de17eea503cb5c..0c860af60d1446 100644 --- a/doc/api/modules.md +++ b/doc/api/modules.md @@ -6,7 +6,11 @@ -In the Node.js module system, each file is treated as a separate module. For +CommonJS modules are the original way to package JavaScript code for Node.js. +Node.js also supports the [ECMAScript modules][] standard used by browsers +and other JavaScript runtimes. + +In Node.js, each file is treated as a separate module. For example, consider a file named `foo.js`: ```js @@ -329,15 +333,17 @@ described in greater detail elsewhere in this documentation. The core modules are defined within the Node.js source and are located in the `lib/` folder. -Core modules are always preferentially loaded if their identifier is -passed to `require()`. For instance, `require('http')` will always -return the built in HTTP module, even if there is a file by that name. - -Core modules can also be identified using the `node:` prefix, in which case +Core modules can be identified using the `node:` prefix, in which case it bypasses the `require` cache. For instance, `require('node:http')` will always return the built in HTTP module, even if there is `require.cache` entry by that name. +Some core modules are always preferentially loaded if their identifier is +passed to `require()`. For instance, `require('http')` will always +return the built-in HTTP module, even if there is a file by that name. The list +of core modules that can be loaded without using the `node:` prefix is exposed +as [`module.builtinModules`][]. + ## Cycles @@ -684,7 +690,7 @@ const myLocalModule = require('./path/myLocalModule'); const jsonData = require('./path/filename.json'); // Importing a module from node_modules or Node.js built-in module: -const crypto = require('crypto'); +const crypto = require('node:crypto'); ``` #### `require.cache` @@ -708,13 +714,13 @@ Use with care! ```js -const assert = require('assert'); -const realFs = require('fs'); +const assert = require('node:assert'); +const realFs = require('node:fs'); const fakeFs = {}; require.cache.fs = { exports: fakeFs }; -assert.strictEqual(require('fs'), fakeFs); +assert.strictEqual(require('node:fs'), fakeFs); assert.strictEqual(require('node:fs'), realFs); ``` @@ -867,7 +873,7 @@ which is probably not what is desired. For example, suppose we were making a module called `a.js`: ```js -const EventEmitter = require('events'); +const EventEmitter = require('node:events'); module.exports = new EventEmitter(); @@ -1088,6 +1094,7 @@ This section was moved to [`__dirname`]: #__dirname [`__filename`]: #__filename [`import()`]: https://wiki.developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#Dynamic_Imports +[`module.builtinModules`]: module.md#modulebuiltinmodules [`module.children`]: #modulechildren [`module.id`]: #moduleid [`module` core module]: module.md diff --git a/doc/api/n-api.md b/doc/api/n-api.md index 4a67c680372b33..76f45091fae711 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -1191,6 +1191,7 @@ added: NAPI_EXTERN napi_status node_api_throw_syntax_error(napi_env env, const char* code, const char* msg); +``` * `[in] env`: The environment that the API is invoked under. * `[in] code`: Optional error code to be set on the error. @@ -5123,6 +5124,11 @@ invocation. If it is deleted before then, then the finalize callback may never be invoked. Therefore, when obtaining a reference a finalize callback is also required in order to enable correct disposal of the reference. +Finalizer callbacks may be deferred, leaving a window where the object has +been garbage collected (and the weak reference is invalid) but the finalizer +hasn't been called yet. When using `napi_get_reference_value()` on weak +references returned by `napi_wrap()`, you should still handle an empty result. + Calling `napi_wrap()` a second time on an object will return an error. To associate another native instance with the object, use `napi_remove_wrap()` first. diff --git a/doc/api/net.md b/doc/api/net.md index 4ecab258011d7e..39d7cbdac53a19 100644 --- a/doc/api/net.md +++ b/doc/api/net.md @@ -8,19 +8,19 @@ -The `net` module provides an asynchronous network API for creating stream-based +The `node:net` module provides an asynchronous network API for creating stream-based TCP or [IPC][] servers ([`net.createServer()`][]) and clients ([`net.createConnection()`][]). It can be accessed using: ```js -const net = require('net'); +const net = require('node:net'); ``` ## IPC support -The `net` module supports IPC with named pipes on Windows, and Unix domain +The `node:net` module supports IPC with named pipes on Windows, and Unix domain sockets on other operating systems. ### Identifying paths for IPC connections @@ -285,6 +285,10 @@ Emitted when the server has been bound after calling [`server.listen()`][]. * Returns: {Object|string|null} @@ -292,7 +296,7 @@ added: v0.1.90 Returns the bound `address`, the address `family` name, and `port` of the server as reported by the operating system if listening on an IP socket (useful to find which port was assigned when getting an OS-assigned address): -`{ port: 12346, family: 'IPv4', address: '127.0.0.1' }`. +`{ port: 12346, family: 4, address: '127.0.0.1' }`. For a server listening on a pipe or Unix domain socket, the name is returned as a string. @@ -710,7 +714,7 @@ Not applicable to Unix sockets. * `err` {Error|null} The error object. See [`dns.lookup()`][]. * `address` {string} The IP address. -* `family` {string|null} The address type. See [`dns.lookup()`][]. +* `family` {number|null} The address type. See [`dns.lookup()`][]. * `host` {string} The host name. ### Event: `'ready'` @@ -738,13 +742,17 @@ See also: [`socket.setTimeout()`][]. * Returns: {Object} Returns the bound `address`, the address `family` name and `port` of the socket as reported by the operating system: -`{ port: 12346, family: 'IPv4', address: '127.0.0.1' }` +`{ port: 12346, family: 4, address: '127.0.0.1' }` ### `socket.bufferSize` @@ -822,6 +830,10 @@ behavior. -The `os` module provides operating system-related utility methods and +The `node:os` module provides operating system-related utility methods and properties. It can be accessed using: ```js -const os = require('os'); +const os = require('node:os'); ``` ## `os.EOL` @@ -224,6 +224,10 @@ always `[0, 0, 0]`. * Returns: {Object} @@ -238,12 +242,12 @@ The properties available on the assigned network address object include: * `address` {string} The assigned IPv4 or IPv6 address * `netmask` {string} The IPv4 or IPv6 network mask -* `family` {string} Either `IPv4` or `IPv6` +* `family` {number} Either `4` (for IPv4) or `6` (for IPv6) * `mac` {string} The MAC address of the network interface * `internal` {boolean} `true` if the network interface is a loopback or similar interface that is not remotely accessible; otherwise `false` * `scopeid` {number} The numeric IPv6 scope ID (only specified when `family` - is `IPv6`) + is `6`) * `cidr` {string} The assigned IPv4 or IPv6 address with the routing prefix in CIDR notation. If the `netmask` is invalid, this property is set to `null`. @@ -256,7 +260,7 @@ The properties available on the assigned network address object include: { address: '127.0.0.1', netmask: '255.0.0.0', - family: 'IPv4', + family: 4, mac: '00:00:00:00:00:00', internal: true, cidr: '127.0.0.1/8' @@ -264,7 +268,7 @@ The properties available on the assigned network address object include: { address: '::1', netmask: 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', - family: 'IPv6', + family: 6, mac: '00:00:00:00:00:00', scopeid: 0, internal: true, @@ -275,7 +279,7 @@ The properties available on the assigned network address object include: { address: '192.168.1.108', netmask: '255.255.255.0', - family: 'IPv4', + family: 4, mac: '01:02:03:0a:0b:0c', internal: false, cidr: '192.168.1.108/24' @@ -283,7 +287,7 @@ The properties available on the assigned network address object include: { address: 'fe80::a00:27ff:fe4e:66a1', netmask: 'ffff:ffff:ffff:ffff::', - family: 'IPv6', + family: 6, mac: '01:02:03:0a:0b:0c', scopeid: 1, internal: false, diff --git a/doc/api/packages.md b/doc/api/packages.md index e0ba7eef492e63..2271a1621b3fcd 100644 --- a/doc/api/packages.md +++ b/doc/api/packages.md @@ -16,11 +16,16 @@ changes: - v12.19.0 pr-url: https://github.com/nodejs/node/pull/34117 description: Add package `"imports"` field. + - version: + - v13.7.0 + - v12.17.0 + pr-url: https://github.com/nodejs/node/pull/29866 + description: Unflag conditional exports. - version: - v13.7.0 - v12.16.0 pr-url: https://github.com/nodejs/node/pull/31001 - description: Unflag conditional exports. + description: Remove the `--experimental-conditional-exports` option. In 12.16.0, conditional exports are still behind `--experimental-modules`. - version: - v13.6.0 - v12.16.0 @@ -193,9 +198,9 @@ Strings passed in as an argument to `--eval` (or `-e`), or piped to `node` via is set. ```bash -node --input-type=module --eval "import { sep } from 'path'; console.log(sep);" +node --input-type=module --eval "import { sep } from 'node:path'; console.log(sep);" -echo "import { sep } from 'path'; console.log(sep);" | node --input-type=module +echo "import { sep } from 'node:path'; console.log(sep);" | node --input-type=module ``` For completeness there is also `--input-type=commonjs`, for explicitly running @@ -1195,6 +1200,11 @@ changes: - v12.20.0 pr-url: https://github.com/nodejs/node/pull/34718 description: Add support for `"exports"` patterns. + - version: + - v13.7.0 + - v12.17.0 + pr-url: https://github.com/nodejs/node/pull/29866 + description: Unflag conditional exports. - version: - v13.7.0 - v12.16.0 @@ -1204,7 +1214,7 @@ changes: - v13.7.0 - v12.16.0 pr-url: https://github.com/nodejs/node/pull/31001 - description: Remove the `--experimental-conditional-exports` option. + description: Remove the `--experimental-conditional-exports` option. In 12.16.0, conditional exports are still behind `--experimental-modules`. - version: - v13.2.0 - v12.16.0 diff --git a/doc/api/path.md b/doc/api/path.md index cc2bb4d1a8a3e3..149a01cdb1b072 100644 --- a/doc/api/path.md +++ b/doc/api/path.md @@ -6,19 +6,19 @@ -The `path` module provides utilities for working with file and directory paths. -It can be accessed using: +The `node:path` module provides utilities for working with file and directory +paths. It can be accessed using: ```js -const path = require('path'); +const path = require('node:path'); ``` ## Windows vs. POSIX -The default operation of the `path` module varies based on the operating system -on which a Node.js application is running. Specifically, when running on a -Windows operating system, the `path` module will assume that Windows-style -paths are being used. +The default operation of the `node:path` module varies based on the operating +system on which a Node.js application is running. Specifically, when running on +a Windows operating system, the `node:path` module will assume that +Windows-style paths are being used. So using `path.basename()` might yield different results on POSIX and Windows: @@ -447,7 +447,7 @@ added: v0.11.15 changes: - version: v15.3.0 pr-url: https://github.com/nodejs/node/pull/34962 - description: Exposed as `require('path/posix')`. + description: Exposed as `require('node:path/posix')`. --> * {Object} @@ -455,7 +455,7 @@ changes: The `path.posix` property provides access to POSIX specific implementations of the `path` methods. -The API is accessible via `require('path').posix` or `require('path/posix')`. +The API is accessible via `require('node:path').posix` or `require('node:path/posix')`. ## `path.relative(from, to)` @@ -592,7 +592,7 @@ added: v0.11.15 changes: - version: v15.3.0 pr-url: https://github.com/nodejs/node/pull/34962 - description: Exposed as `require('path/win32')`. + description: Exposed as `require('node:path/win32')`. --> * {Object} @@ -600,7 +600,7 @@ changes: The `path.win32` property provides access to Windows-specific implementations of the `path` methods. -The API is accessible via `require('path').win32` or `require('path/win32')`. +The API is accessible via `require('node:path').win32` or `require('node:path/win32')`. [MSDN-Rel-Path]: https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#fully-qualified-vs-relative-paths [`TypeError`]: errors.md#class-typeerror diff --git a/doc/api/perf_hooks.md b/doc/api/perf_hooks.md index ea6ffbef53689d..8c398a3aee71eb 100644 --- a/doc/api/perf_hooks.md +++ b/doc/api/perf_hooks.md @@ -17,7 +17,7 @@ Node.js supports the following [Web Performance APIs][]: * [User Timing][] ```js -const { PerformanceObserver, performance } = require('perf_hooks'); +const { PerformanceObserver, performance } = require('node:perf_hooks'); const obs = new PerformanceObserver((items) => { console.log(items.getEntries()[0].duration); @@ -111,8 +111,8 @@ of how a mostly idle process will have a high ELU. ```js 'use strict'; -const { eventLoopUtilization } = require('perf_hooks').performance; -const { spawnSync } = require('child_process'); +const { eventLoopUtilization } = require('node:perf_hooks').performance; +const { spawnSync } = require('node:child_process'); setImmediate(() => { const elu = eventLoopUtilization(); @@ -312,7 +312,7 @@ event type in order for the timing details to be accessed. const { performance, PerformanceObserver -} = require('perf_hooks'); +} = require('node:perf_hooks'); function someFunction() { console.log('hello world'); @@ -531,6 +531,30 @@ When `performanceEntry.type` is equal to `'function'`, the `performanceEntry.detail` property will be an {Array} listing the input arguments to the timed function. +### Net ('net') Details + +When `performanceEntry.type` is equal to `'net'`, the +`performanceEntry.detail` property will be an {Object} containing +additional information. + +If `performanceEntry.name` is equal to `connect`, the `detail` +will contain the following properties: `host`, `port`. + +### DNS ('dns') Details + +When `performanceEntry.type` is equal to `'dns'`, the +`performanceEntry.detail` property will be an {Object} containing +additional information. + +If `performanceEntry.name` is equal to `lookup`, the `detail` +will contain the following properties: `hostname`, `family`, `hints`, `verbatim`. + +If `performanceEntry.name` is equal to `lookupService`, the `detail` will +contain the following properties: `host`, `port`. + +If `performanceEntry.name` is equal to `queryxxx` or `getHostByAddr`, the `detail` will +contain the following properties: `host`, `ttl`. + ## Class: `PerformanceNodeTiming` -* `percentile` {number} A percentile value in the range (0, 100). +* `percentile` {number} A percentile value in the range (0, 100]. * Returns: {bigint} Returns the value at the given percentile. @@ -1204,11 +1228,11 @@ to execute the callback). ```js 'use strict'; -const async_hooks = require('async_hooks'); +const async_hooks = require('node:async_hooks'); const { performance, PerformanceObserver -} = require('perf_hooks'); +} = require('node:perf_hooks'); const set = new Set(); const hook = async_hooks.createHook({ @@ -1253,8 +1277,8 @@ dependencies: const { performance, PerformanceObserver -} = require('perf_hooks'); -const mod = require('module'); +} = require('node:perf_hooks'); +const mod = require('node:module'); // Monkey patch the require function mod.Module.prototype.require = @@ -1276,6 +1300,72 @@ obs.observe({ entryTypes: ['function'], buffered: true }); require('some-module'); ``` +### Measuring how long one HTTP round-trip takes + +The following example is used to trace the time spent by HTTP client +(`OutgoingMessage`) and HTTP request (`IncomingMessage`). For HTTP client, +it means the time interval between starting the request and receiving the +response, and for HTTP request, it means the time interval between receiving +the request and sending the response: + +```js +'use strict'; +const { PerformanceObserver } = require('node:perf_hooks'); +const http = require('node:http'); + +const obs = new PerformanceObserver((items) => { + items.getEntries().forEach((item) => { + console.log(item); + }); +}); + +obs.observe({ entryTypes: ['http'] }); + +const PORT = 8080; + +http.createServer((req, res) => { + res.end('ok'); +}).listen(PORT, () => { + http.get(`http://127.0.0.1:${PORT}`); +}); +``` + +### Measuring how long the `net.connect` (only for TCP) takes when the connection is successful + +```js +'use strict'; +const { PerformanceObserver } = require('node:perf_hooks'); +const net = require('node:net'); +const obs = new PerformanceObserver((items) => { + items.getEntries().forEach((item) => { + console.log(item); + }); +}); +obs.observe({ entryTypes: ['net'] }); +const PORT = 8080; +net.createServer((socket) => { + socket.destroy(); +}).listen(PORT, () => { + net.connect(PORT); +}); +``` + +### Measuring how long the DNS takes when the request is successful + +```js +'use strict'; +const { PerformanceObserver } = require('node:perf_hooks'); +const dns = require('node:dns'); +const obs = new PerformanceObserver((items) => { + items.getEntries().forEach((item) => { + console.log(item); + }); +}); +obs.observe({ entryTypes: ['dns'] }); +dns.lookup('localhost', () => {}); +dns.promises.resolve('localhost'); +``` + [Async Hooks]: async_hooks.md [High Resolution Time]: https://www.w3.org/TR/hr-time-2 [Performance Timeline]: https://w3c.github.io/performance-timeline/ diff --git a/doc/api/policy.md b/doc/api/policy.md index 0d53352788a32c..233d6c94640790 100644 --- a/doc/api/policy.md +++ b/doc/api/policy.md @@ -350,7 +350,7 @@ The following example, would allow access to `fs` for all `data:` resources: ```json { "resources": { - "data:text/javascript,import('fs');": { + "data:text/javascript,import('node:fs');": { "cascade": true, "integrity": true } diff --git a/doc/api/process.md b/doc/api/process.md index 38ba85dbc41b9b..f5e105ce43d581 100644 --- a/doc/api/process.md +++ b/doc/api/process.md @@ -7,15 +7,14 @@ The `process` object provides information about, and control over, the current -Node.js process. While it is available as a global, it is recommended to -explicitly access it via require or import: +Node.js process. ```mjs -import process from 'process'; +import process from 'node:process'; ``` ```cjs -const process = require('process'); +const process = require('node:process'); ``` ## Process events @@ -44,7 +43,7 @@ The `'beforeExit'` should _not_ be used as an alternative to the `'exit'` event unless the intention is to schedule additional work. ```mjs -import process from 'process'; +import process from 'node:process'; process.on('beforeExit', (code) => { console.log('Process beforeExit event with code: ', code); @@ -63,7 +62,7 @@ console.log('This message is displayed first.'); ``` ```cjs -const process = require('process'); +const process = require('node:process'); process.on('beforeExit', (code) => { console.log('Process beforeExit event with code: ', code); @@ -113,7 +112,7 @@ by the [`process.exitCode`][] property, or the `exitCode` argument passed to the [`process.exit()`][] method. ```mjs -import process from 'process'; +import process from 'node:process'; process.on('exit', (code) => { console.log(`About to exit with code: ${code}`); @@ -121,7 +120,7 @@ process.on('exit', (code) => { ``` ```cjs -const process = require('process'); +const process = require('node:process'); process.on('exit', (code) => { console.log(`About to exit with code: ${code}`); @@ -134,7 +133,7 @@ causing any additional work still queued in the event loop to be abandoned. In the following example, for instance, the timeout will never occur: ```mjs -import process from 'process'; +import process from 'node:process'; process.on('exit', (code) => { setTimeout(() => { @@ -144,7 +143,7 @@ process.on('exit', (code) => { ``` ```cjs -const process = require('process'); +const process = require('node:process'); process.on('exit', (code) => { setTimeout(() => { @@ -207,7 +206,7 @@ Because of the unreliability of the event in cases like the [`Promise.race()`][] example above it has been deprecated. ```mjs -import process from 'process'; +import process from 'node:process'; process.on('multipleResolves', (type, promise, reason) => { console.error(type, promise, reason); @@ -236,7 +235,7 @@ main().then(console.log); ``` ```cjs -const process = require('process'); +const process = require('node:process'); process.on('multipleResolves', (type, promise, reason) => { console.error(type, promise, reason); @@ -297,7 +296,7 @@ of unhandled rejections grows, and the `'rejectionHandled'` event is emitted when the list of unhandled rejections shrinks. ```mjs -import process from 'process'; +import process from 'node:process'; const unhandledRejections = new Map(); process.on('unhandledRejection', (reason, promise) => { @@ -309,7 +308,7 @@ process.on('rejectionHandled', (promise) => { ``` ```cjs -const process = require('process'); +const process = require('node:process'); const unhandledRejections = new Map(); process.on('unhandledRejection', (reason, promise) => { @@ -358,7 +357,7 @@ provided exit code. Otherwise, in the presence of such handler the process will exit with 0. ```mjs -import process from 'process'; +import process from 'node:process'; process.on('uncaughtException', (err, origin) => { fs.writeSync( @@ -378,7 +377,7 @@ console.log('This will not run.'); ``` ```cjs -const process = require('process'); +const process = require('node:process'); process.on('uncaughtException', (err, origin) => { fs.writeSync( @@ -454,7 +453,7 @@ once an `'uncaughtException'` event is emitted. The process will still crash if no `'uncaughtException'` listener is installed. ```mjs -import process from 'process'; +import process from 'node:process'; process.on('uncaughtExceptionMonitor', (err, origin) => { MyMonitoringTool.logSync(err, origin); @@ -466,7 +465,7 @@ nonexistentFunc(); ``` ```cjs -const process = require('process'); +const process = require('node:process'); process.on('uncaughtExceptionMonitor', (err, origin) => { MyMonitoringTool.logSync(err, origin); @@ -504,7 +503,7 @@ useful for detecting and keeping track of promises that were rejected whose rejections have not yet been handled. ```mjs -import process from 'process'; +import process from 'node:process'; process.on('unhandledRejection', (reason, promise) => { console.log('Unhandled Rejection at:', promise, 'reason:', reason); @@ -517,7 +516,7 @@ somePromise.then((res) => { ``` ```cjs -const process = require('process'); +const process = require('node:process'); process.on('unhandledRejection', (reason, promise) => { console.log('Unhandled Rejection at:', promise, 'reason:', reason); @@ -533,7 +532,7 @@ The following will also trigger the `'unhandledRejection'` event to be emitted: ```mjs -import process from 'process'; +import process from 'node:process'; function SomeResource() { // Initially set the loaded status to a rejected promise @@ -545,7 +544,7 @@ const resource = new SomeResource(); ``` ```cjs -const process = require('process'); +const process = require('node:process'); function SomeResource() { // Initially set the loaded status to a rejected promise @@ -584,7 +583,7 @@ Node.js can emit warnings whenever it detects bad coding practices that could lead to sub-optimal application performance, bugs, or security vulnerabilities. ```mjs -import process from 'process'; +import process from 'node:process'; process.on('warning', (warning) => { console.warn(warning.name); // Print the warning name @@ -594,7 +593,7 @@ process.on('warning', (warning) => { ``` ```cjs -const process = require('process'); +const process = require('node:process'); process.on('warning', (warning) => { console.warn(warning.name); // Print the warning name @@ -705,7 +704,7 @@ The name of each event will be the uppercase common name for the signal (e.g. `'SIGINT'` for `SIGINT` signals). ```mjs -import process from 'process'; +import process from 'node:process'; // Begin reading from stdin so the process does not exit. process.stdin.resume(); @@ -724,7 +723,7 @@ process.on('SIGTERM', handle); ``` ```cjs -const process = require('process'); +const process = require('node:process'); // Begin reading from stdin so the process does not exit. process.stdin.resume(); @@ -831,7 +830,7 @@ passed through to V8 will contain underscores instead of non-leading dashes: ```mjs -import { allowedNodeEnvironmentFlags } from 'process'; +import { allowedNodeEnvironmentFlags } from 'node:process'; allowedNodeEnvironmentFlags.forEach((flag) => { // -r @@ -842,7 +841,7 @@ allowedNodeEnvironmentFlags.forEach((flag) => { ``` ```cjs -const { allowedNodeEnvironmentFlags } = require('process'); +const { allowedNodeEnvironmentFlags } = require('node:process'); allowedNodeEnvironmentFlags.forEach((flag) => { // -r @@ -873,13 +872,13 @@ Possible values are: `'arm'`, `'arm64'`, `'ia32'`, `'mips'`,`'mipsel'`, `'ppc'`, `'ppc64'`, `'s390'`, `'s390x'`, and `'x64'`. ```mjs -import { arch } from 'process'; +import { arch } from 'node:process'; console.log(`This processor architecture is ${arch}`); ``` ```cjs -const { arch } = require('process'); +const { arch } = require('node:process'); console.log(`This processor architecture is ${arch}`); ``` @@ -902,7 +901,7 @@ arguments. For example, assuming the following script for `process-args.js`: ```mjs -import { argv } from 'process'; +import { argv } from 'node:process'; // print process.argv argv.forEach((val, index) => { @@ -911,7 +910,7 @@ argv.forEach((val, index) => { ``` ```cjs -const { argv } = require('process'); +const { argv } = require('node:process'); // print process.argv argv.forEach((val, index) => { @@ -1010,7 +1009,7 @@ Node.js process or throws an exception if doing so fails (for instance, if the specified `directory` does not exist). ```mjs -import { chdir, cwd } from 'process'; +import { chdir, cwd } from 'node:process'; console.log(`Starting directory: ${cwd()}`); try { @@ -1022,7 +1021,7 @@ try { ``` ```cjs -const { chdir, cwd } = require('process'); +const { chdir, cwd } = require('node:process'); console.log(`Starting directory: ${cwd()}`); try { @@ -1130,7 +1129,7 @@ The result of a previous call to `process.cpuUsage()` can be passed as the argument to the function, to get a diff reading. ```mjs -import { cpuUsage } from 'process'; +import { cpuUsage } from 'node:process'; const startUsage = cpuUsage(); // { user: 38579, system: 6986 } @@ -1144,7 +1143,7 @@ console.log(cpuUsage(startUsage)); ``` ```cjs -const { cpuUsage } = require('process'); +const { cpuUsage } = require('node:process'); const startUsage = cpuUsage(); // { user: 38579, system: 6986 } @@ -1169,13 +1168,13 @@ The `process.cwd()` method returns the current working directory of the Node.js process. ```mjs -import { cwd } from 'process'; +import { cwd } from 'node:process'; console.log(`Current directory: ${cwd()}`); ``` ```cjs -const { cwd } = require('process'); +const { cwd } = require('node:process'); console.log(`Current directory: ${cwd()}`); ``` @@ -1191,13 +1190,13 @@ added: v0.7.2 The port used by the Node.js debugger when enabled. ```mjs -import process from 'process'; +import process from 'node:process'; process.debugPort = 5858; ``` ```cjs -const process = require('process'); +const process = require('node:process'); process.debugPort = 5858; ``` @@ -1252,9 +1251,9 @@ the call returns, by passing the `RTLD_NOW` constant. In this example the constant is assumed to be available. ```mjs -import { dlopen } from 'process'; -import { constants } from 'os'; -import { fileURLToPath } from 'url'; +import { dlopen } from 'node:process'; +import { constants } from 'node:os'; +import { fileURLToPath } from 'node:url'; const module = { exports: {} }; dlopen(module, fileURLToPath(new URL('local.node', import.meta.url)), @@ -1263,9 +1262,9 @@ module.exports.foo(); ``` ```cjs -const { dlopen } = require('process'); -const { constants } = require('os'); -const { join } = require('path'); +const { dlopen } = require('node:process'); +const { constants } = require('node:os'); +const { join } = require('node:path'); const module = { exports: {} }; dlopen(module, join(__dirname, 'local.node'), constants.dlopen.RTLD_NOW); @@ -1293,7 +1292,7 @@ specific process warnings. These can be listened for by adding a handler to the [`'warning'`][process_warning] event. ```mjs -import { emitWarning } from 'process'; +import { emitWarning } from 'node:process'; // Emit a warning with a code and additional detail. emitWarning('Something happened!', { @@ -1306,7 +1305,7 @@ emitWarning('Something happened!', { ``` ```cjs -const { emitWarning } = require('process'); +const { emitWarning } = require('node:process'); // Emit a warning with a code and additional detail. emitWarning('Something happened!', { @@ -1323,7 +1322,7 @@ In this example, an `Error` object is generated internally by [`'warning'`][process_warning] handler. ```mjs -import process from 'process'; +import process from 'node:process'; process.on('warning', (warning) => { console.warn(warning.name); // 'Warning' @@ -1335,7 +1334,7 @@ process.on('warning', (warning) => { ``` ```cjs -const process = require('process'); +const process = require('node:process'); process.on('warning', (warning) => { console.warn(warning.name); // 'Warning' @@ -1367,7 +1366,7 @@ specific process warnings. These can be listened for by adding a handler to the [`'warning'`][process_warning] event. ```mjs -import { emitWarning } from 'process'; +import { emitWarning } from 'node:process'; // Emit a warning using a string. emitWarning('Something happened!'); @@ -1375,7 +1374,7 @@ emitWarning('Something happened!'); ``` ```cjs -const { emitWarning } = require('process'); +const { emitWarning } = require('node:process'); // Emit a warning using a string. emitWarning('Something happened!'); @@ -1383,7 +1382,7 @@ emitWarning('Something happened!'); ``` ```mjs -import { emitWarning } from 'process'; +import { emitWarning } from 'node:process'; // Emit a warning using a string and a type. emitWarning('Something Happened!', 'CustomWarning'); @@ -1391,7 +1390,7 @@ emitWarning('Something Happened!', 'CustomWarning'); ``` ```cjs -const { emitWarning } = require('process'); +const { emitWarning } = require('node:process'); // Emit a warning using a string and a type. emitWarning('Something Happened!', 'CustomWarning'); @@ -1399,14 +1398,14 @@ emitWarning('Something Happened!', 'CustomWarning'); ``` ```mjs -import { emitWarning } from 'process'; +import { emitWarning } from 'node:process'; emitWarning('Something happened!', 'CustomWarning', 'WARN001'); // Emits: (node:56338) [WARN001] CustomWarning: Something happened! ``` ```cjs -const { emitWarning } = require('process'); +const { emitWarning } = require('node:process'); process.emitWarning('Something happened!', 'CustomWarning', 'WARN001'); // Emits: (node:56338) [WARN001] CustomWarning: Something happened! @@ -1417,7 +1416,7 @@ In each of the previous examples, an `Error` object is generated internally by handler. ```mjs -import process from 'process'; +import process from 'node:process'; process.on('warning', (warning) => { console.warn(warning.name); @@ -1428,7 +1427,7 @@ process.on('warning', (warning) => { ``` ```cjs -const process = require('process'); +const process = require('node:process'); process.on('warning', (warning) => { console.warn(warning.name); @@ -1443,7 +1442,7 @@ If `warning` is passed as an `Error` object, it will be passed through to the `code` and `ctor` arguments will be ignored): ```mjs -import { emitWarning } from 'process'; +import { emitWarning } from 'node:process'; // Emit a warning using an Error object. const myWarning = new Error('Something happened!'); @@ -1456,7 +1455,7 @@ emitWarning(myWarning); ``` ```cjs -const { emitWarning } = require('process'); +const { emitWarning } = require('node:process'); // Emit a warning using an Error object. const myWarning = new Error('Something happened!'); @@ -1487,11 +1486,10 @@ The following additional handling is implemented if the warning `type` is ### Avoiding duplicate warnings As a best practice, warnings should be emitted only once per process. To do -so, it is recommended to place the `emitWarning()` behind a simple boolean -flag as illustrated in the example below: +so, place the `emitWarning()` behind a boolean. ```mjs -import { emitWarning } from 'process'; +import { emitWarning } from 'node:process'; function emitMyWarning() { if (!emitMyWarning.warned) { @@ -1506,7 +1504,7 @@ emitMyWarning(); ``` ```cjs -const { emitWarning } = require('process'); +const { emitWarning } = require('node:process'); function emitMyWarning() { if (!emitMyWarning.warned) { @@ -1571,14 +1569,14 @@ $ node -e 'process.env.foo = "bar"' && echo $foo While the following will: ```mjs -import { env } from 'process'; +import { env } from 'node:process'; env.foo = 'bar'; console.log(env.foo); ``` ```cjs -const { env } = require('process'); +const { env } = require('node:process'); env.foo = 'bar'; console.log(env.foo); @@ -1589,7 +1587,7 @@ to a string. **This behavior is deprecated.** Future versions of Node.js may throw an error when the value is not a string, number, or boolean. ```mjs -import { env } from 'process'; +import { env } from 'node:process'; env.test = null; console.log(env.test); @@ -1600,7 +1598,7 @@ console.log(env.test); ``` ```cjs -const { env } = require('process'); +const { env } = require('node:process'); env.test = null; console.log(env.test); @@ -1613,7 +1611,7 @@ console.log(env.test); Use `delete` to delete a property from `process.env`. ```mjs -import { env } from 'process'; +import { env } from 'node:process'; env.TEST = 1; delete env.TEST; @@ -1622,7 +1620,7 @@ console.log(env.TEST); ``` ```cjs -const { env } = require('process'); +const { env } = require('node:process'); env.TEST = 1; delete env.TEST; @@ -1633,7 +1631,7 @@ console.log(env.TEST); On Windows operating systems, environment variables are case-insensitive. ```mjs -import { env } from 'process'; +import { env } from 'node:process'; env.TEST = 1; console.log(env.test); @@ -1641,7 +1639,7 @@ console.log(env.test); ``` ```cjs -const { env } = require('process'); +const { env } = require('node:process'); env.TEST = 1; console.log(env.test); @@ -1727,13 +1725,13 @@ called. To exit with a 'failure' code: ```mjs -import { exit } from 'process'; +import { exit } from 'node:process'; exit(1); ``` ```cjs -const { exit } = require('process'); +const { exit } = require('node:process'); exit(1); ``` @@ -1755,7 +1753,7 @@ For instance, the following example illustrates a _misuse_ of the truncated and lost: ```mjs -import { exit } from 'process'; +import { exit } from 'node:process'; // This is an example of what *not* to do: if (someConditionNotMet()) { @@ -1765,7 +1763,7 @@ if (someConditionNotMet()) { ``` ```cjs -const { exit } = require('process'); +const { exit } = require('node:process'); // This is an example of what *not* to do: if (someConditionNotMet()) { @@ -1784,7 +1782,7 @@ Rather than calling `process.exit()` directly, the code _should_ set the scheduling any additional work for the event loop: ```mjs -import process from 'process'; +import process from 'node:process'; // How to properly set the exit code while letting // the process exit gracefully. @@ -1795,7 +1793,7 @@ if (someConditionNotMet()) { ``` ```cjs -const process = require('process'); +const process = require('node:process'); // How to properly set the exit code while letting // the process exit gracefully. @@ -1844,8 +1842,8 @@ containing the types of the active resources that are currently keeping the event loop alive. ```mjs -import { getActiveResourcesInfo } from 'process'; -import { setTimeout } from 'timers'; +import { getActiveResourcesInfo } from 'node:process'; +import { setTimeout } from 'node:timers'; console.log('Before:', getActiveResourcesInfo()); setTimeout(() => {}, 1000); @@ -1856,8 +1854,8 @@ console.log('After:', getActiveResourcesInfo()); ``` ```cjs -const { getActiveResourcesInfo } = require('process'); -const { setTimeout } = require('timers'); +const { getActiveResourcesInfo } = require('node:process'); +const { setTimeout } = require('node:timers'); console.log('Before:', getActiveResourcesInfo()); setTimeout(() => {}, 1000); @@ -1877,7 +1875,7 @@ The `process.getegid()` method returns the numerical effective group identity of the Node.js process. (See getegid(2).) ```mjs -import process from 'process'; +import process from 'node:process'; if (process.getegid) { console.log(`Current gid: ${process.getegid()}`); @@ -1885,7 +1883,7 @@ if (process.getegid) { ``` ```cjs -const process = require('process'); +const process = require('node:process'); if (process.getegid) { console.log(`Current gid: ${process.getegid()}`); @@ -1907,7 +1905,7 @@ The `process.geteuid()` method returns the numerical effective user identity of the process. (See geteuid(2).) ```mjs -import process from 'process'; +import process from 'node:process'; if (process.geteuid) { console.log(`Current uid: ${process.geteuid()}`); @@ -1915,7 +1913,7 @@ if (process.geteuid) { ``` ```cjs -const process = require('process'); +const process = require('node:process'); if (process.geteuid) { console.log(`Current uid: ${process.geteuid()}`); @@ -1937,7 +1935,7 @@ The `process.getgid()` method returns the numerical group identity of the process. (See getgid(2).) ```mjs -import process from 'process'; +import process from 'node:process'; if (process.getgid) { console.log(`Current gid: ${process.getgid()}`); @@ -1945,7 +1943,7 @@ if (process.getgid) { ``` ```cjs -const process = require('process'); +const process = require('node:process'); if (process.getgid) { console.log(`Current gid: ${process.getgid()}`); @@ -1968,7 +1966,7 @@ IDs. POSIX leaves it unspecified if the effective group ID is included but Node.js ensures it always is. ```mjs -import process from 'process'; +import process from 'node:process'; if (process.getgroups) { console.log(process.getgroups()); // [ 16, 21, 297 ] @@ -1976,7 +1974,7 @@ if (process.getgroups) { ``` ```cjs -const process = require('process'); +const process = require('node:process'); if (process.getgroups) { console.log(process.getgroups()); // [ 16, 21, 297 ] @@ -1998,7 +1996,7 @@ The `process.getuid()` method returns the numeric user identity of the process. (See getuid(2).) ```mjs -import process from 'process'; +import process from 'node:process'; if (process.getuid) { console.log(`Current uid: ${process.getuid()}`); @@ -2006,7 +2004,7 @@ if (process.getuid) { ``` ```cjs -const process = require('process'); +const process = require('node:process'); if (process.getuid) { console.log(`Current uid: ${process.getuid()}`); @@ -2056,7 +2054,7 @@ past, and not related to the time of day and therefore not subject to clock drift. The primary use is for measuring performance between intervals: ```mjs -import { hrtime } from 'process'; +import { hrtime } from 'node:process'; const NS_PER_SEC = 1e9; const time = hrtime(); @@ -2072,7 +2070,7 @@ setTimeout(() => { ``` ```cjs -const { hrtime } = require('process'); +const { hrtime } = require('node:process'); const NS_PER_SEC = 1e9; const time = hrtime(); @@ -2103,7 +2101,7 @@ argument since the difference can just be computed directly by subtraction of the two `bigint`s. ```mjs -import { hrtime } from 'process'; +import { hrtime } from 'node:process'; const start = hrtime.bigint(); // 191051479007711n @@ -2118,7 +2116,7 @@ setTimeout(() => { ``` ```cjs -const { hrtime } = require('process'); +const { hrtime } = require('node:process'); const start = hrtime.bigint(); // 191051479007711n @@ -2149,7 +2147,7 @@ access or the `CAP_SETGID` capability. Use care when dropping privileges: ```mjs -import { getgroups, initgroups, setgid } from 'process'; +import { getgroups, initgroups, setgid } from 'node:process'; console.log(getgroups()); // [ 0 ] initgroups('nodeuser', 1000); // switch user @@ -2159,7 +2157,7 @@ console.log(getgroups()); // [ 27, 30, 46, 1000 ] ``` ```cjs -const { getgroups, initgroups, setgid } = require('process'); +const { getgroups, initgroups, setgid } = require('node:process'); console.log(getgroups()); // [ 0 ] initgroups('nodeuser', 1000); // switch user @@ -2198,7 +2196,7 @@ signal sender, like the `kill` system call. The signal sent may do something other than kill the target process. ```mjs -import process, { kill } from 'process'; +import process, { kill } from 'node:process'; process.on('SIGHUP', () => { console.log('Got SIGHUP signal.'); @@ -2213,7 +2211,7 @@ kill(process.pid, 'SIGHUP'); ``` ```cjs -const process = require('process'); +const process = require('node:process'); process.on('SIGHUP', () => { console.log('Got SIGHUP signal.'); @@ -2276,7 +2274,7 @@ Returns an object describing the memory usage of the Node.js process measured in bytes. ```mjs -import { memoryUsage } from 'process'; +import { memoryUsage } from 'node:process'; console.log(memoryUsage()); // Prints: @@ -2290,7 +2288,7 @@ console.log(memoryUsage()); ``` ```cjs -const { memoryUsage } = require('process'); +const { memoryUsage } = require('node:process'); console.log(memoryUsage()); // Prints: @@ -2343,14 +2341,14 @@ This is the same value as the `rss` property provided by `process.memoryUsage()` but `process.memoryUsage.rss()` is faster. ```mjs -import { memoryUsage } from 'process'; +import { memoryUsage } from 'node:process'; console.log(memoryUsage.rss()); // 35655680 ``` ```cjs -const { rss } = require('process'); +const { rss } = require('node:process'); console.log(memoryUsage.rss()); // 35655680 @@ -2361,7 +2359,7 @@ console.log(memoryUsage.rss()); -The `querystring` module provides utilities for parsing and formatting URL +The `node:querystring` module provides utilities for parsing and formatting URL query strings. It can be accessed using: ```js -const querystring = require('querystring'); +const querystring = require('node:querystring'); ``` The `querystring` API is considered Legacy. While it is still maintained, diff --git a/doc/api/readline.md b/doc/api/readline.md index 0ae536ac302715..443d89f8b49390 100644 --- a/doc/api/readline.md +++ b/doc/api/readline.md @@ -6,8 +6,8 @@ -The `readline` module provides an interface for reading data from a [Readable][] -stream (such as [`process.stdin`][]) one line at a time. +The `node:readline` module provides an interface for reading data from a +[Readable][] stream (such as [`process.stdin`][]) one line at a time. To use the promise-based APIs: @@ -16,7 +16,7 @@ import * as readline from 'node:readline/promises'; ``` ```cjs -const readline = require('readline/promises'); +const readline = require('node:readline/promises'); ``` To use the callback and sync APIs: @@ -26,10 +26,11 @@ import * as readline from 'node:readline'; ``` ```cjs -const readline = require('readline'); +const readline = require('node:readline'); ``` -The following simple example illustrates the basic use of the `readline` module. +The following simple example illustrates the basic use of the `node:readline` +module. ```mjs import * as readline from 'node:readline/promises'; @@ -45,8 +46,8 @@ rl.close(); ``` ```cjs -const readline = require('readline'); -const { stdin: input, stdout: output } = require('process'); +const readline = require('node:readline'); +const { stdin: input, stdout: output } = require('node:process'); const rl = readline.createInterface({ input, output }); @@ -111,6 +112,9 @@ The `'line'` event is emitted whenever the `input` stream receives an end-of-line input (`\n`, `\r`, or `\r\n`). This usually occurs when the user presses Enter or Return. +The `'line'` event is also emitted if new data has been read from a stream and +that stream ends without a final end-of-line marker. + The listener function is called with a string containing the single line of received input. @@ -327,6 +331,8 @@ The `callback` function passed to `rl.question()` does not follow the typical pattern of accepting an `Error` object or `null` as the first argument. The `callback` is called with the provided answer as the only argument. +An error will be thrown if calling `rl.question()` after `rl.close()`. + Example usage: ```js @@ -352,25 +358,6 @@ signal.addEventListener('abort', () => { setTimeout(() => ac.abort(), 10000); ``` -If this method is invoked as it's util.promisify()ed version, it returns a -Promise that fulfills with the answer. If the question is canceled using -an `AbortController` it will reject with an `AbortError`. - -```js -const util = require('util'); -const question = util.promisify(rl.question).bind(rl); - -async function questionExample() { - try { - const answer = await question('What is you favorite food? '); - console.log(`Oh, so your favorite food is ${answer}`); - } catch (err) { - console.error('Question rejected', err); - } -} -questionExample(); -``` - ### `rl.resume()` -The `repl` module provides a Read-Eval-Print-Loop (REPL) implementation that -is available both as a standalone program or includible in other applications. -It can be accessed using: +The `node:repl` module provides a Read-Eval-Print-Loop (REPL) implementation +that is available both as a standalone program or includible in other +applications. It can be accessed using: ```js -const repl = require('repl'); +const repl = require('node:repl'); ``` ## Design and features -The `repl` module exports the [`repl.REPLServer`][] class. While running, +The `node:repl` module exports the [`repl.REPLServer`][] class. While running, instances of [`repl.REPLServer`][] will accept individual lines of user input, evaluate those according to a user-defined evaluation function, then output the result. Input and output may be from `stdin` and `stdout`, respectively, or may @@ -107,7 +107,7 @@ scope. It is possible to expose a variable to the REPL explicitly by assigning it to the `context` object associated with each `REPLServer`: ```js -const repl = require('repl'); +const repl = require('node:repl'); const msg = 'message'; repl.start('> ').context.m = msg; @@ -125,7 +125,7 @@ Context properties are not read-only by default. To specify read-only globals, context properties must be defined using `Object.defineProperty()`: ```js -const repl = require('repl'); +const repl = require('node:repl'); const msg = 'message'; const r = repl.start('> '); @@ -141,7 +141,7 @@ Object.defineProperty(r.context, 'm', { The default evaluator will automatically load Node.js core modules into the REPL environment when used. For instance, unless otherwise declared as a global or scoped variable, the input `fs` will be evaluated on-demand as -`global.fs = require('fs')`. +`global.fs = require('node:fs')`. ```console > fs.createReadStream('./some/file'); @@ -284,7 +284,7 @@ The following illustrates a hypothetical example of a REPL that performs translation of text from one language to another: ```js -const repl = require('repl'); +const repl = require('node:repl'); const { Translator } = require('translator'); const myTranslator = new Translator('en', 'fr'); @@ -355,7 +355,7 @@ function for the `writer` option on construction. The following example, for instance, simply converts any input text to upper case: ```js -const repl = require('repl'); +const repl = require('node:repl'); const r = repl.start({ prompt: '> ', eval: myEval, writer: myWriter }); @@ -381,7 +381,7 @@ Instances of `repl.REPLServer` are created using the [`repl.start()`][] method or directly using the JavaScript `new` keyword. ```js -const repl = require('repl'); +const repl = require('node:repl'); const options = { useColors: true }; @@ -425,7 +425,7 @@ This can be used primarily to re-initialize REPL context to some pre-defined state: ```js -const repl = require('repl'); +const repl = require('node:repl'); function initializeContext(context) { context.m = 'test'; @@ -476,7 +476,7 @@ properties: The following example shows two new commands added to the REPL instance: ```js -const repl = require('repl'); +const repl = require('node:repl'); const replServer = repl.start({ prompt: '> ' }); replServer.defineCommand('sayhello', { @@ -654,7 +654,7 @@ The `repl.start()` method creates and starts a [`repl.REPLServer`][] instance. If `options` is a string, then it specifies the input prompt: ```js -const repl = require('repl'); +const repl = require('node:repl'); // a Unix style prompt repl.start('$ '); @@ -662,9 +662,9 @@ repl.start('$ '); ## The Node.js REPL -Node.js itself uses the `repl` module to provide its own interactive interface -for executing JavaScript. This can be used by executing the Node.js binary -without passing any arguments (or by passing the `-i` argument): +Node.js itself uses the `node:repl` module to provide its own interactive +interface for executing JavaScript. This can be used by executing the Node.js +binary without passing any arguments (or by passing the `-i` argument): ```console $ node @@ -726,8 +726,8 @@ The following example, for instance, provides separate REPLs on `stdin`, a Unix socket, and a TCP socket: ```js -const net = require('net'); -const repl = require('repl'); +const net = require('node:net'); +const repl = require('node:repl'); let connections = 0; repl.start({ diff --git a/doc/api/stream.md b/doc/api/stream.md index d59cd6b51629e4..cd8e7e6e7f5c80 100644 --- a/doc/api/stream.md +++ b/doc/api/stream.md @@ -7,7 +7,7 @@ A stream is an abstract interface for working with streaming data in Node.js. -The `stream` module provides an API for implementing the stream interface. +The `node:stream` module provides an API for implementing the stream interface. There are many stream objects provided by Node.js. For instance, a [request to an HTTP server][http-incoming-message] and [`process.stdout`][] @@ -16,14 +16,14 @@ are both stream instances. Streams can be readable, writable, or both. All streams are instances of [`EventEmitter`][]. -To access the `stream` module: +To access the `node:stream` module: ```js -const stream = require('stream'); +const stream = require('node:stream'); ``` -The `stream` module is useful for creating new types of stream instances. It is -usually not necessary to use the `stream` module to consume streams. +The `node:stream` module is useful for creating new types of stream instances. +It is usually not necessary to use the `node:stream` module to consume streams. ## Organization of this document @@ -56,8 +56,8 @@ added: v15.0.0 The `stream/promises` API provides an alternative set of asynchronous utility functions for streams that return `Promise` objects rather than using -callbacks. The API is accessible via `require('stream/promises')` -or `require('stream').promises`. +callbacks. The API is accessible via `require('node:stream/promises')` +or `require('node:stream').promises`. ### Object mode @@ -134,7 +134,7 @@ manner. The following is an example of using streams in a Node.js application that implements an HTTP server: ```js -const http = require('http'); +const http = require('node:http'); const server = http.createServer((req, res) => { // `req` is an http.IncomingMessage, which is a readable stream. @@ -190,7 +190,7 @@ various ways to communicate the current state of the stream. Applications that are either writing data to or consuming data from a stream are not required to implement the stream interfaces directly and will generally -have no reason to call `require('stream')`. +have no reason to call `require('node:stream')`. Developers wishing to implement new types of streams should refer to the section [API for stream implementers][]. @@ -422,7 +422,7 @@ Use `end()` instead of destroy if data should flush before close, or wait for the `'drain'` event before destroying the stream. ```cjs -const { Writable } = require('stream'); +const { Writable } = require('node:stream'); const myStream = new Writable(); @@ -432,7 +432,7 @@ myStream.on('error', (fooErr) => console.error(fooErr.message)); // foo error ``` ```cjs -const { Writable } = require('stream'); +const { Writable } = require('node:stream'); const myStream = new Writable(); @@ -441,7 +441,7 @@ myStream.on('error', function wontHappen() {}); ``` ```cjs -const { Writable } = require('stream'); +const { Writable } = require('node:stream'); const myStream = new Writable(); myStream.destroy(); @@ -459,7 +459,7 @@ but instead implement [`writable._destroy()`][writable-_destroy]. ##### `writable.closed` * {boolean} @@ -477,7 +477,7 @@ added: v8.0.0 Is `true` after [`writable.destroy()`][writable-destroy] has been called. ```cjs -const { Writable } = require('stream'); +const { Writable } = require('node:stream'); const myStream = new Writable(); @@ -523,7 +523,7 @@ Calling the [`stream.write()`][stream-write] method after calling ```js // Write 'hello, ' and then end with 'world!'. -const fs = require('fs'); +const fs = require('node:fs'); const file = fs.createWriteStream('example.txt'); file.write('hello, '); file.end('world!'); @@ -556,8 +556,8 @@ The `writable.uncork()` method flushes all data buffered since [`stream.cork()`][] was called. When using [`writable.cork()`][] and `writable.uncork()` to manage the buffering -of writes to a stream, it is recommended that calls to `writable.uncork()` be -deferred using `process.nextTick()`. Doing so allows batching of all +of writes to a stream, defer calls to `writable.uncork()` using +`process.nextTick()`. Doing so allows batching of all `writable.write()` calls that occur within a given Node.js event loop phase. ```js @@ -599,7 +599,7 @@ the stream has not been destroyed, errored or ended. ##### `writable.writableAborted` > Stability: 1 - Experimental @@ -637,7 +637,7 @@ called in order to fully uncork the stream. * {Error} @@ -736,7 +736,7 @@ stop until the [`'drain'`][] event is emitted. While a stream is not draining, calls to `write()` will buffer `chunk`, and return false. Once all currently buffered chunks are drained (accepted for delivery by the operating system), the `'drain'` event will be emitted. -It is recommended that once `write()` returns false, no more chunks be written +Once `write()` returns false, do not write more chunks until the `'drain'` event is emitted. While calling `write()` on a stream that is not draining is allowed, Node.js will buffer all written chunks until maximum memory usage occurs, at which point it will abort unconditionally. @@ -871,7 +871,7 @@ data. While in this state, attaching a listener for the `'data'` event will not switch `readable.readableFlowing` to `true`. ```js -const { PassThrough, Writable } = require('stream'); +const { PassThrough, Writable } = require('node:stream'); const pass = new PassThrough(); const writable = new Writable(); @@ -896,12 +896,6 @@ to consume data from a single stream. Specifically, using a combination of `on('data')`, `on('readable')`, `pipe()`, or async iterators could lead to unintuitive behavior. -Use of the `readable.pipe()` method is recommended for most users as it has been -implemented to provide the easiest way of consuming stream data. Developers that -require more fine-grained control over the transfer and generation of data can -use the [`EventEmitter`][] and `readable.on('readable')`/`readable.read()` -or the `readable.pause()`/`readable.resume()` APIs. - #### Class: `stream.Readable` * {boolean} @@ -1206,7 +1200,7 @@ The following example pipes all of the data from the `readable` into a file named `file.txt`: ```js -const fs = require('fs'); +const fs = require('node:fs'); const readable = getReadableStreamSomehow(); const writable = fs.createWriteStream('file.txt'); // All the data from readable goes into 'file.txt'. @@ -1220,7 +1214,7 @@ The `readable.pipe()` method returns a reference to the _destination_ stream making it possible to set up chains of piped streams: ```js -const fs = require('fs'); +const fs = require('node:fs'); const r = fs.createReadStream('file.txt'); const z = zlib.createGzip(); const w = fs.createWriteStream('file.txt.gz'); @@ -1393,7 +1387,7 @@ Becomes `true` when [`'end'`][] event is emitted. * {Error} @@ -1524,7 +1518,7 @@ If the `destination` is specified, but no pipe is set up for it, then the method does nothing. ```js -const fs = require('fs'); +const fs = require('node:fs'); const readable = getReadableStreamSomehow(); const writable = fs.createWriteStream('file.txt'); // All the data from readable goes into 'file.txt', @@ -1576,7 +1570,7 @@ section for more information. // Pull off a header delimited by \n\n. // Use unshift() if we get too much. // Call the callback with (error, header, stream). -const { StringDecoder } = require('string_decoder'); +const { StringDecoder } = require('node:string_decoder'); function parseHeader(stream, callback) { stream.on('error', callback); stream.on('readable', onReadable); @@ -1626,8 +1620,9 @@ added: v0.9.4 * `stream` {Stream} An "old style" readable stream * Returns: {this} -Prior to Node.js 0.10, streams did not implement the entire `stream` module API -as it is currently defined. (See [Compatibility][] for more information.) +Prior to Node.js 0.10, streams did not implement the entire `node:stream` +module API as it is currently defined. (See [Compatibility][] for more +information.) When using an older Node.js library that emits [`'data'`][] events and has a [`stream.pause()`][stream-pause] method that is advisory only, the @@ -1640,7 +1635,7 @@ libraries. ```js const { OldReader } = require('./old-api-module.js'); -const { Readable } = require('stream'); +const { Readable } = require('node:stream'); const oreader = new OldReader(); const myReader = new Readable().wrap(oreader); @@ -1662,7 +1657,7 @@ changes: * Returns: {AsyncIterator} to fully consume the stream. ```js -const fs = require('fs'); +const fs = require('node:fs'); async function print(readable) { readable.setEncoding('utf8'); @@ -1680,7 +1675,7 @@ If the loop terminates with a `break`, `return`, or a `throw`, the stream will be destroyed. In other terms, iterating over a stream will consume the stream fully. The stream will be read in chunks of size equal to the `highWaterMark` option. In the code example above, data will be in a single chunk if the file -has less then 64 KB of data because no `highWaterMark` option is provided to +has less then 64 KiB of data because no `highWaterMark` option is provided to [`fs.createReadStream()`][]. ##### `readable.iterator([options])` @@ -1703,7 +1698,7 @@ destruction of the stream if the `for await...of` loop is exited by `return`, emitted an error during iteration. ```js -const { Readable } = require('stream'); +const { Readable } = require('node:stream'); async function printIterator(readable) { for await (const chunk of readable.iterator({ destroyOnReturn: false })) { @@ -1765,8 +1760,8 @@ for every chunk in the stream. If the `fn` function returns a promise - that promise will be `await`ed before being passed to the result stream. ```mjs -import { Readable } from 'stream'; -import { Resolver } from 'dns/promises'; +import { Readable } from 'node:stream'; +import { Resolver } from 'node:dns/promises'; // With a synchronous mapper. for await (const chunk of Readable.from([1, 2, 3, 4]).map((x) => x * 2)) { @@ -1812,8 +1807,8 @@ passed to the result stream. If the `fn` function returns a promise - that promise will be `await`ed. ```mjs -import { Readable } from 'stream'; -import { Resolver } from 'dns/promises'; +import { Readable } from 'node:stream'; +import { Resolver } from 'node:dns/promises'; // With a synchronous predicate. for await (const chunk of Readable.from([1, 2, 3, 4]).filter((x) => x > 2)) { @@ -1870,8 +1865,8 @@ uses the [`readable`][] event in the underlying machinary and can limit the number of concurrent `fn` calls. ```mjs -import { Readable } from 'stream'; -import { Resolver } from 'dns/promises'; +import { Readable } from 'node:stream'; +import { Resolver } from 'node:dns/promises'; // With a synchronous predicate. for await (const chunk of Readable.from([1, 2, 3, 4]).filter((x) => x > 2)) { @@ -1915,8 +1910,8 @@ streams. It's intended for interoperability and convenience, not as the primary way to consume streams. ```mjs -import { Readable } from 'stream'; -import { Resolver } from 'dns/promises'; +import { Readable } from 'node:stream'; +import { Resolver } from 'node:dns/promises'; await Readable.from([1, 2, 3, 4]).toArray(); // [1, 2, 3, 4] @@ -1961,8 +1956,8 @@ calls on the chunks return a truthy value, the promise is fulfilled with `false`. ```mjs -import { Readable } from 'stream'; -import { stat } from 'fs/promises'; +import { Readable } from 'node:stream'; +import { stat } from 'node:fs/promises'; // With a synchronous predicate. await Readable.from([1, 2, 3, 4]).some((x) => x > 2); // true @@ -2010,8 +2005,8 @@ fulfilled with value for which `fn` returned a truthy value. If all of the `undefined`. ```mjs -import { Readable } from 'stream'; -import { stat } from 'fs/promises'; +import { Readable } from 'node:stream'; +import { stat } from 'node:fs/promises'; // With a synchronous predicate. await Readable.from([1, 2, 3, 4]).find((x) => x > 2); // 3 @@ -2059,8 +2054,8 @@ destroyed and the promise is fulfilled with `false`. If all of the `fn` calls on the chunks return a truthy value, the promise is fulfilled with `true`. ```mjs -import { Readable } from 'stream'; -import { stat } from 'fs/promises'; +import { Readable } from 'node:stream'; +import { stat } from 'node:fs/promises'; // With a synchronous predicate. await Readable.from([1, 2, 3, 4]).every((x) => x > 2); // false @@ -2109,8 +2104,8 @@ It is possible to return a stream or another iterable or async iterable from stream. ```mjs -import { Readable } from 'stream'; -import { createReadStream } from 'fs'; +import { Readable } from 'node:stream'; +import { createReadStream } from 'node:fs'; // With a synchronous mapper. for await (const chunk of Readable.from([1, 2, 3, 4]).flatMap((x) => [x, x])) { @@ -2146,7 +2141,7 @@ added: v17.5.0 This method returns a new stream with the first `limit` chunks dropped. ```mjs -import { Readable } from 'stream'; +import { Readable } from 'node:stream'; await Readable.from([1, 2, 3, 4]).drop(2).toArray(); // [3, 4] ``` @@ -2168,7 +2163,7 @@ added: v17.5.0 This method returns a new stream with the first `limit` chunks. ```mjs -import { Readable } from 'stream'; +import { Readable } from 'node:stream'; await Readable.from([1, 2, 3, 4]).take(2).toArray(); // [1, 2] ``` @@ -2191,7 +2186,7 @@ with a counter in the form `[index, chunk]`. The first index value is 0 and it increases by 1 for each chunk produced. ```mjs -import { Readable } from 'stream'; +import { Readable } from 'node:stream'; const pairs = await Readable.from(['a', 'b', 'c']).asIndexedPairs().toArray(); console.log(pairs); // [[0, 'a'], [1, 'b'], [2, 'c']] @@ -2232,7 +2227,7 @@ initial value. If the stream is empty, the promise is rejected with a `TypeError` with the `ERR_INVALID_ARGS` code property. ```mjs -import { Readable } from 'stream'; +import { Readable } from 'node:stream'; const ten = await Readable.from([1, 2, 3, 4]).reduce((previous, data) => { return previous + data; @@ -2367,7 +2362,7 @@ A function to get notified when a stream is no longer readable, writable or has experienced an error or a premature close event. ```js -const { finished } = require('stream'); +const { finished } = require('node:stream'); const rs = fs.createReadStream('archive.tar'); @@ -2389,7 +2384,7 @@ or `'finish'`. The `finished` API provides promise version: ```js -const { finished } = require('stream/promises'); +const { finished } = require('node:stream/promises'); const rs = fs.createReadStream('archive.tar'); @@ -2423,7 +2418,7 @@ const cleanup = finished(rs, (err) => { -The `stream` module API has been designed to make it possible to easily +The `node:stream` module API has been designed to make it possible to easily implement streams using JavaScript's prototypal inheritance model. First, a stream developer would declare a new JavaScript class that extends one @@ -2954,7 +2949,7 @@ parent class constructor: ```js -const { Writable } = require('stream'); +const { Writable } = require('node:stream'); class MyWritable extends Writable { constructor({ highWaterMark, ...options }) { @@ -3005,7 +3000,7 @@ inheritance. This can be accomplished by directly creating instances of the objects and passing appropriate methods as constructor options. ```js -const { Writable } = require('stream'); +const { Writable } = require('node:stream'); const myWritable = new Writable({ construct(callback) { @@ -3053,7 +3048,7 @@ changes: * `options` {Object} * `highWaterMark` {number} Buffer level when [`stream.write()`][stream-write] starts returning `false`. **Default:** - `16384` (16 KB), or `16` for `objectMode` streams. + `16384` (16 KiB), or `16` for `objectMode` streams. * `decodeStrings` {boolean} Whether to encode `string`s passed to [`stream.write()`][stream-write] to `Buffer`s (with the encoding specified in the [`stream.write()`][stream-write] call) before passing @@ -3087,7 +3082,7 @@ changes: ```js -const { Writable } = require('stream'); +const { Writable } = require('node:stream'); class MyWritable extends Writable { constructor(options) { @@ -3101,8 +3096,8 @@ class MyWritable extends Writable { Or, when using pre-ES6 style constructors: ```js -const { Writable } = require('stream'); -const util = require('util'); +const { Writable } = require('node:stream'); +const util = require('node:util'); function MyWritable(options) { if (!(this instanceof MyWritable)) @@ -3115,7 +3110,7 @@ util.inherits(MyWritable, Writable); Or, using the simplified constructor approach: ```js -const { Writable } = require('stream'); +const { Writable } = require('node:stream'); const myWritable = new Writable({ write(chunk, encoding, callback) { @@ -3132,7 +3127,7 @@ Calling `abort` on the `AbortController` corresponding to the passed on the writeable stream. ```js -const { Writable } = require('stream'); +const { Writable } = require('node:stream'); const controller = new AbortController(); const myWritable = new Writable({ @@ -3167,8 +3162,8 @@ has returned, delaying any `_write()`, `_final()` and `_destroy()` calls until initialize resources before the stream can be used. ```js -const { Writable } = require('stream'); -const fs = require('fs'); +const { Writable } = require('node:stream'); +const fs = require('node:fs'); class WriteStream extends Writable { constructor(filename) { @@ -3324,7 +3319,7 @@ If a `Readable` stream pipes into a `Writable` stream when `Writable` emits an error, the `Readable` stream will be unpiped. ```js -const { Writable } = require('stream'); +const { Writable } = require('node:stream'); const myWritable = new Writable({ write(chunk, encoding, callback) { @@ -3345,7 +3340,7 @@ is not of any real particular usefulness, the example illustrates each of the required elements of a custom [`Writable`][] stream instance: ```js -const { Writable } = require('stream'); +const { Writable } = require('node:stream'); class MyWritable extends Writable { _write(chunk, encoding, callback) { @@ -3366,8 +3361,8 @@ characters encoding, such as UTF-8. The following example shows how to decode multi-byte strings using `StringDecoder` and [`Writable`][]. ```js -const { Writable } = require('stream'); -const { StringDecoder } = require('string_decoder'); +const { Writable } = require('node:stream'); +const { StringDecoder } = require('node:string_decoder'); class StringWritable extends Writable { constructor(options) { @@ -3426,7 +3421,7 @@ changes: * `options` {Object} * `highWaterMark` {number} The maximum [number of bytes][hwm-gotcha] to store in the internal buffer before ceasing to read from the underlying resource. - **Default:** `16384` (16 KB), or `16` for `objectMode` streams. + **Default:** `16384` (16 KiB), or `16` for `objectMode` streams. * `encoding` {string} If specified, then buffers will be decoded to strings using the specified encoding. **Default:** `null`. * `objectMode` {boolean} Whether this stream should behave @@ -3447,7 +3442,7 @@ changes: ```js -const { Readable } = require('stream'); +const { Readable } = require('node:stream'); class MyReadable extends Readable { constructor(options) { @@ -3461,8 +3456,8 @@ class MyReadable extends Readable { Or, when using pre-ES6 style constructors: ```js -const { Readable } = require('stream'); -const util = require('util'); +const { Readable } = require('node:stream'); +const util = require('node:util'); function MyReadable(options) { if (!(this instanceof MyReadable)) @@ -3475,7 +3470,7 @@ util.inherits(MyReadable, Readable); Or, using the simplified constructor approach: ```js -const { Readable } = require('stream'); +const { Readable } = require('node:stream'); const myReadable = new Readable({ read(size) { @@ -3489,7 +3484,7 @@ Calling `abort` on the `AbortController` corresponding to the passed on the readable created. ```js -const { Readable } = require('stream'); +const { Readable } = require('node:stream'); const controller = new AbortController(); const read = new Readable({ read(size) { @@ -3520,8 +3515,8 @@ called. This is useful to initialize state or asynchronously initialize resources before the stream can be used. ```js -const { Readable } = require('stream'); -const fs = require('fs'); +const { Readable } = require('node:stream'); +const fs = require('node:fs'); class ReadStream extends Readable { constructor(filename) { @@ -3693,7 +3688,7 @@ Throwing an `Error` from within [`readable._read()`][] or manually emitting an `'error'` event results in undefined behavior. ```js -const { Readable } = require('stream'); +const { Readable } = require('node:stream'); const myReadable = new Readable({ read(size) { @@ -3715,7 +3710,7 @@ The following is a basic example of a `Readable` stream that emits the numerals from 1 to 1,000,000 in ascending order, and then ends. ```js -const { Readable } = require('stream'); +const { Readable } = require('node:stream'); class Counter extends Readable { constructor(opt) { @@ -3786,7 +3781,7 @@ changes: ```js -const { Duplex } = require('stream'); +const { Duplex } = require('node:stream'); class MyDuplex extends Duplex { constructor(options) { @@ -3799,8 +3794,8 @@ class MyDuplex extends Duplex { Or, when using pre-ES6 style constructors: ```js -const { Duplex } = require('stream'); -const util = require('util'); +const { Duplex } = require('node:stream'); +const util = require('node:util'); function MyDuplex(options) { if (!(this instanceof MyDuplex)) @@ -3813,7 +3808,7 @@ util.inherits(MyDuplex, Duplex); Or, using the simplified constructor approach: ```js -const { Duplex } = require('stream'); +const { Duplex } = require('node:stream'); const myDuplex = new Duplex({ read(size) { @@ -3828,8 +3823,8 @@ const myDuplex = new Duplex({ When using pipeline: ```js -const { Transform, pipeline } = require('stream'); -const fs = require('fs'); +const { Transform, pipeline } = require('node:stream'); +const fs = require('node:fs'); pipeline( fs.createReadStream('object.json') @@ -3877,7 +3872,7 @@ incoming written data via the [`Writable`][] interface that is read back out via the [`Readable`][] interface. ```js -const { Duplex } = require('stream'); +const { Duplex } = require('node:stream'); const kSource = Symbol('source'); class MyDuplex extends Duplex { @@ -3918,7 +3913,7 @@ that accepts JavaScript numbers that are converted to hexadecimal strings on the `Readable` side. ```js -const { Transform } = require('stream'); +const { Transform } = require('node:stream'); // All Transform streams are also Duplex Streams. const myTransform = new Transform({ @@ -3983,7 +3978,7 @@ output on the `Readable` side is not consumed. ```js -const { Transform } = require('stream'); +const { Transform } = require('node:stream'); class MyTransform extends Transform { constructor(options) { @@ -3996,8 +3991,8 @@ class MyTransform extends Transform { Or, when using pre-ES6 style constructors: ```js -const { Transform } = require('stream'); -const util = require('util'); +const { Transform } = require('node:stream'); +const util = require('node:util'); function MyTransform(options) { if (!(this instanceof MyTransform)) @@ -4010,7 +4005,7 @@ util.inherits(MyTransform, Transform); Or, using the simplified constructor approach: ```js -const { Transform } = require('stream'); +const { Transform } = require('node:stream'); const myTransform = new Transform({ transform(chunk, encoding, callback) { @@ -4154,7 +4149,7 @@ A Node.js readable stream can be created from an asynchronous generator using the `Readable.from()` utility method: ```js -const { Readable } = require('stream'); +const { Readable } = require('node:stream'); const ac = new AbortController(); const signal = ac.signal; @@ -4183,9 +4178,9 @@ handling of backpressure and errors. [`stream.pipeline()`][] abstracts away the handling of backpressure and backpressure-related errors: ```js -const fs = require('fs'); -const { pipeline } = require('stream'); -const { pipeline: pipelinePromise } = require('stream/promises'); +const fs = require('node:fs'); +const { pipeline } = require('node:stream'); +const { pipeline: pipelinePromise } = require('node:stream/promises'); const writable = fs.createWriteStream('./file'); diff --git a/doc/api/string_decoder.md b/doc/api/string_decoder.md index a628a5a8a14cf4..70387d2edba696 100644 --- a/doc/api/string_decoder.md +++ b/doc/api/string_decoder.md @@ -6,18 +6,18 @@ -The `string_decoder` module provides an API for decoding `Buffer` objects into -strings in a manner that preserves encoded multi-byte UTF-8 and UTF-16 +The `node:string_decoder` module provides an API for decoding `Buffer` objects +into strings in a manner that preserves encoded multi-byte UTF-8 and UTF-16 characters. It can be accessed using: ```js -const { StringDecoder } = require('string_decoder'); +const { StringDecoder } = require('node:string_decoder'); ``` The following example shows the basic use of the `StringDecoder` class. ```js -const { StringDecoder } = require('string_decoder'); +const { StringDecoder } = require('node:string_decoder'); const decoder = new StringDecoder('utf8'); const cent = Buffer.from([0xC2, 0xA2]); @@ -36,7 +36,7 @@ In the following example, the three UTF-8 encoded bytes of the European Euro symbol (`€`) are written over three separate operations: ```js -const { StringDecoder } = require('string_decoder'); +const { StringDecoder } = require('node:string_decoder'); const decoder = new StringDecoder('utf8'); decoder.write(Buffer.from([0xE2])); diff --git a/doc/api/synopsis.md b/doc/api/synopsis.md index 79b881952475f6..3a4a6f35e1e97f 100644 --- a/doc/api/synopsis.md +++ b/doc/api/synopsis.md @@ -55,7 +55,7 @@ Open `hello-world.js` in any preferred text editor and paste in the following content: ```js -const http = require('http'); +const http = require('node:http'); const hostname = '127.0.0.1'; const port = 3000; diff --git a/doc/api/test.md b/doc/api/test.md new file mode 100644 index 00000000000000..7513dc24e26afd --- /dev/null +++ b/doc/api/test.md @@ -0,0 +1,435 @@ +# Test runner + + + +> Stability: 1 - Experimental + + + +The `node:test` module facilitates the creation of JavaScript tests that +report results in [TAP][] format. To access it: + +```mjs +import test from 'node:test'; +``` + +```cjs +const test = require('node:test'); +``` + +This module is only available under the `node:` scheme. The following will not +work: + +```mjs +import test from 'test'; +``` + +```cjs +const test = require('test'); +``` + +Tests created via the `test` module consist of a single function that is +processed in one of three ways: + +1. A synchronous function that is considered failing if it throws an exception, + and is considered passing otherwise. +2. A function that returns a `Promise` that is considered failing if the + `Promise` rejects, and is considered passing if the `Promise` resolves. +3. A function that receives a callback function. If the callback receives any + truthy value as its first argument, the test is considered failing. If a + falsy value is passed as the first argument to the callback, the test is + considered passing. If the test function receives a callback function and + also returns a `Promise`, the test will fail. + +The following example illustrates how tests are written using the +`test` module. + +```js +test('synchronous passing test', (t) => { + // This test passes because it does not throw an exception. + assert.strictEqual(1, 1); +}); + +test('synchronous failing test', (t) => { + // This test fails because it throws an exception. + assert.strictEqual(1, 2); +}); + +test('asynchronous passing test', async (t) => { + // This test passes because the Promise returned by the async + // function is not rejected. + assert.strictEqual(1, 1); +}); + +test('asynchronous failing test', async (t) => { + // This test fails because the Promise returned by the async + // function is rejected. + assert.strictEqual(1, 2); +}); + +test('failing test using Promises', (t) => { + // Promises can be used directly as well. + return new Promise((resolve, reject) => { + setImmediate(() => { + reject(new Error('this will cause the test to fail')); + }); + }); +}); + +test('callback passing test', (t, done) => { + // done() is the callback function. When the setImmediate() runs, it invokes + // done() with no arguments. + setImmediate(done); +}); + +test('callback failing test', (t, done) => { + // When the setImmediate() runs, done() is invoked with an Error object and + // the test fails. + setImmediate(() => { + done(new Error('callback failure')); + }); +}); +``` + +As a test file executes, TAP is written to the standard output of the Node.js +process. This output can be interpreted by any test harness that understands +the TAP format. If any tests fail, the process exit code is set to `1`. + +## Subtests + +The test context's `test()` method allows subtests to be created. This method +behaves identically to the top level `test()` function. The following example +demonstrates the creation of a top level test with two subtests. + +```js +test('top level test', async (t) => { + await t.test('subtest 1', (t) => { + assert.strictEqual(1, 1); + }); + + await t.test('subtest 2', (t) => { + assert.strictEqual(2, 2); + }); +}); +``` + +In this example, `await` is used to ensure that both subtests have completed. +This is necessary because parent tests do not wait for their subtests to +complete. Any subtests that are still outstanding when their parent finishes +are cancelled and treated as failures. Any subtest failures cause the parent +test to fail. + +## Skipping tests + +Individual tests can be skipped by passing the `skip` option to the test, or by +calling the test context's `skip()` method. Both of these options support +including a message that is displayed in the TAP output as shown in the +following example. + +```js +// The skip option is used, but no message is provided. +test('skip option', { skip: true }, (t) => { + // This code is never executed. +}); + +// The skip option is used, and a message is provided. +test('skip option with message', { skip: 'this is skipped' }, (t) => { + // This code is never executed. +}); + +test('skip() method', (t) => { + // Make sure to return here as well if the test contains additional logic. + t.skip(); +}); + +test('skip() method with message', (t) => { + // Make sure to return here as well if the test contains additional logic. + t.skip('this is skipped'); +}); +``` + +### `only` tests + +If Node.js is started with the [`--test-only`][] command-line option, it is +possible to skip all top level tests except for a selected subset by passing +the `only` option to the tests that should be run. When a test with the `only` +option set is run, all subtests are also run. The test context's `runOnly()` +method can be used to implement the same behavior at the subtest level. + +```js +// Assume Node.js is run with the --test-only command-line option. +// The 'only' option is set, so this test is run. +test('this test is run', { only: true }, async (t) => { + // Within this test, all subtests are run by default. + await t.test('running subtest'); + + // The test context can be updated to run subtests with the 'only' option. + t.runOnly(true); + await t.test('this subtest is now skipped'); + await t.test('this subtest is run', { only: true }); + + // Switch the context back to execute all tests. + t.runOnly(false); + await t.test('this subtest is now run'); + + // Explicitly do not run these tests. + await t.test('skipped subtest 3', { only: false }); + await t.test('skipped subtest 4', { skip: true }); +}); + +// The 'only' option is not set, so this test is skipped. +test('this test is not run', () => { + // This code is not run. + throw new Error('fail'); +}); +``` + +## Extraneous asynchronous activity + +Once a test function finishes executing, the TAP results are output as quickly +as possible while maintaining the order of the tests. However, it is possible +for the test function to generate asynchronous activity that outlives the test +itself. The test runner handles this type of activity, but does not delay the +reporting of test results in order to accommodate it. + +In the following example, a test completes with two `setImmediate()` +operations still outstanding. The first `setImmediate()` attempts to create a +new subtest. Because the parent test has already finished and output its +results, the new subtest is immediately marked as failed, and reported in the +top level of the file's TAP output. + +The second `setImmediate()` creates an `uncaughtException` event. +`uncaughtException` and `unhandledRejection` events originating from a completed +test are handled by the `test` module and reported as diagnostic warnings in +the top level of the file's TAP output. + +```js +test('a test that creates asynchronous activity', (t) => { + setImmediate(() => { + t.test('subtest that is created too late', (t) => { + throw new Error('error1'); + }); + }); + + setImmediate(() => { + throw new Error('error2'); + }); + + // The test finishes after this line. +}); +``` + +## Running tests from the command line + +The Node.js test runner can be invoked from the command line by passing the +[`--test`][] flag: + +```bash +node --test +``` + +By default, Node.js will recursively search the current directory for +JavaScript source files matching a specific naming convention. Matching files +are executed as test files. More information on the expected test file naming +convention and behavior can be found in the [test runner execution model][] +section. + +Alternatively, one or more paths can be provided as the final argument(s) to +the Node.js command, as shown below. + +```bash +node --test test1.js test2.mjs custom_test_dir/ +``` + +In this example, the test runner will execute the files `test1.js` and +`test2.mjs`. The test runner will also recursively search the +`custom_test_dir/` directory for test files to execute. + +### Test runner execution model + +When searching for test files to execute, the test runner behaves as follows: + +* Any files explicitly provided by the user are executed. +* If the user did not explicitly specify any paths, the current working + directory is recursively searched for files as specified in the following + steps. +* `node_modules` directories are skipped unless explicitly provided by the + user. +* If a directory named `test` is encountered, the test runner will search it + recursively for all all `.js`, `.cjs`, and `.mjs` files. All of these files + are treated as test files, and do not need to match the specific naming + convention detailed below. This is to accommodate projects that place all of + their tests in a single `test` directory. +* In all other directories, `.js`, `.cjs`, and `.mjs` files matching the + following patterns are treated as test files: + * `^test$` - Files whose basename is the string `'test'`. Examples: + `test.js`, `test.cjs`, `test.mjs`. + * `^test-.+` - Files whose basename starts with the string `'test-'` + followed by one or more characters. Examples: `test-example.js`, + `test-another-example.mjs`. + * `.+[\.\-\_]test$` - Files whose basename ends with `.test`, `-test`, or + `_test`, preceded by one or more characters. Examples: `example.test.js`, + `example-test.cjs`, `example_test.mjs`. + * Other file types understood by Node.js such as `.node` and `.json` are not + automatically executed by the test runner, but are supported if explicitly + provided on the command line. + +Each matching test file is executed in a separate child process. If the child +process finishes with an exit code of 0, the test is considered passing. +Otherwise, the test is considered to be a failure. Test files must be +executable by Node.js, but are not required to use the `node:test` module +internally. + +## `test([name][, options][, fn])` + + + +* `name` {string} The name of the test, which is displayed when reporting test + results. **Default:** The `name` property of `fn`, or `''` if `fn` + does not have a name. +* `options` {Object} Configuration options for the test. The following + properties are supported: + * `concurrency` {number} The number of tests that can be run at the same time. + If unspecified, subtests inherit this value from their parent. + **Default:** `1`. + * `only` {boolean} If truthy, and the test context is configured to run + `only` tests, then this test will be run. Otherwise, the test is skipped. + **Default:** `false`. + * `skip` {boolean|string} If truthy, the test is skipped. If a string is + provided, that string is displayed in the test results as the reason for + skipping the test. **Default:** `false`. + * `todo` {boolean|string} If truthy, the test marked as `TODO`. If a string + is provided, that string is displayed in the test results as the reason why + the test is `TODO`. **Default:** `false`. +* `fn` {Function|AsyncFunction} The function under test. This first argument + to this function is a [`TestContext`][] object. If the test uses callbacks, + the callback function is passed as the second argument. **Default:** A no-op + function. +* Returns: {Promise} Resolved with `undefined` once the test completes. + +The `test()` function is the value imported from the `test` module. Each +invocation of this function results in the creation of a test point in the TAP +output. + +The `TestContext` object passed to the `fn` argument can be used to perform +actions related to the current test. Examples include skipping the test, adding +additional TAP diagnostic information, or creating subtests. + +`test()` returns a `Promise` that resolves once the test completes. The return +value can usually be discarded for top level tests. However, the return value +from subtests should be used to prevent the parent test from finishing first +and cancelling the subtest as shown in the following example. + +```js +test('top level test', async (t) => { + // The setTimeout() in the following subtest would cause it to outlive its + // parent test if 'await' is removed on the next line. Once the parent test + // completes, it will cancel any outstanding subtests. + await t.test('longer running subtest', async (t) => { + return new Promise((resolve, reject) => { + setTimeout(resolve, 1000); + }); + }); +}); +``` + +## Class: `TestContext` + + + +An instance of `TestContext` is passed to each test function in order to +interact with the test runner. However, the `TestContext` constructor is not +exposed as part of the API. + +### `context.diagnostic(message)` + + + +* `message` {string} Message to be displayed as a TAP diagnostic. + +This function is used to write TAP diagnostics to the output. Any diagnostic +information is included at the end of the test's results. This function does +not return a value. + +### `context.runOnly(shouldRunOnlyTests)` + + + +* `shouldRunOnlyTests` {boolean} Whether or not to run `only` tests. + +If `shouldRunOnlyTests` is truthy, the test context will only run tests that +have the `only` option set. Otherwise, all tests are run. If Node.js was not +started with the [`--test-only`][] command-line option, this function is a +no-op. + +### `context.skip([message])` + + + +* `message` {string} Optional skip message to be displayed in TAP output. + +This function causes the test's output to indicate the test as skipped. If +`message` is provided, it is included in the TAP output. Calling `skip()` does +not terminate execution of the test function. This function does not return a +value. + +### `context.todo([message])` + + + +* `message` {string} Optional `TODO` message to be displayed in TAP output. + +This function adds a `TODO` directive to the test's output. If `message` is +provided, it is included in the TAP output. Calling `todo()` does not terminate +execution of the test function. This function does not return a value. + +### `context.test([name][, options][, fn])` + + + +* `name` {string} The name of the subtest, which is displayed when reporting + test results. **Default:** The `name` property of `fn`, or `''` if + `fn` does not have a name. +* `options` {Object} Configuration options for the subtest. The following + properties are supported: + * `concurrency` {number} The number of tests that can be run at the same time. + If unspecified, subtests inherit this value from their parent. + **Default:** `1`. + * `only` {boolean} If truthy, and the test context is configured to run + `only` tests, then this test will be run. Otherwise, the test is skipped. + **Default:** `false`. + * `skip` {boolean|string} If truthy, the test is skipped. If a string is + provided, that string is displayed in the test results as the reason for + skipping the test. **Default:** `false`. + * `todo` {boolean|string} If truthy, the test marked as `TODO`. If a string + is provided, that string is displayed in the test results as the reason why + the test is `TODO`. **Default:** `false`. +* `fn` {Function|AsyncFunction} The function under test. This first argument + to this function is a [`TestContext`][] object. If the test uses callbacks, + the callback function is passed as the second argument. **Default:** A no-op + function. +* Returns: {Promise} Resolved with `undefined` once the test completes. + +This function is used to create subtests under the current test. This function +behaves in the same fashion as the top level [`test()`][] function. + +[TAP]: https://testanything.org/ +[`--test-only`]: cli.md#--test-only +[`--test`]: cli.md#--test +[`TestContext`]: #class-testcontext +[`test()`]: #testname-options-fn +[test runner execution model]: #test-runner-execution-model diff --git a/doc/api/timers.md b/doc/api/timers.md index f1fbfb0ebe2ff2..d054ad7d419c32 100644 --- a/doc/api/timers.md +++ b/doc/api/timers.md @@ -8,7 +8,7 @@ The `timer` module exposes a global API for scheduling functions to be called at some future period of time. Because the timer functions are -globals, there is no need to call `require('timers')` to use the API. +globals, there is no need to call `require('node:timers')` to use the API. The timer functions within Node.js implement a similar API as the timers API provided by Web Browsers but use a different internal implementation that is @@ -140,10 +140,6 @@ to remain active. If there is no other activity keeping the event loop running, the process may exit before the `Timeout` object's callback is invoked. Calling `timeout.unref()` multiple times will have no effect. -Calling `timeout.unref()` creates an internal timer that will wake the Node.js -event loop. Creating too many of these can adversely impact performance -of the Node.js application. - ### `timeout[Symbol.toPrimitive]()` -The `tls` module provides an implementation of the Transport Layer Security +The `node:tls` module provides an implementation of the Transport Layer Security (TLS) and Secure Socket Layer (SSL) protocols that is built on top of OpenSSL. The module can be accessed using: ```js -const tls = require('tls'); +const tls = require('node:tls'); +``` + +## Determining if crypto support is unavailable + +It is possible for Node.js to be built without including support for the +`node:crypto` module. In such cases, attempting to `import` from `tls` or +calling `require('node:tls')` will result in an error being thrown. + +When using CommonJS, the error thrown can be caught using try/catch: + + + +```cjs +let tls; +try { + tls = require('node:tls'); +} catch (err) { + console.log('tls support is disabled!'); +} +``` + +When using the lexical ESM `import` keyword, the error can only be +caught if a handler for `process.on('uncaughtException')` is registered +_before_ any attempt to load the module is made (using, for instance, +a preload module). + +When using ESM, if there is a chance that the code may be run on a build +of Node.js where crypto support is not enabled, consider using the +[`import()`][] function instead of the lexical `import` keyword: + +```mjs +let tls; +try { + tls = await import('node:tls'); +} catch (err) { + console.log('tls support is disabled!'); +} ``` ## TLS/SSL concepts @@ -90,10 +127,10 @@ the character "E" appended to the traditional abbreviations): * [ECDHE][]: An ephemeral version of the Elliptic Curve Diffie-Hellman key-agreement protocol. -To use perfect forward secrecy using `DHE` with the `tls` module, it is required -to generate Diffie-Hellman parameters and specify them with the `dhparam` -option to [`tls.createSecureContext()`][]. The following illustrates the use of -the OpenSSL command-line interface to generate such parameters: +To use perfect forward secrecy using `DHE` with the `node:tls` module, it is +required to generate Diffie-Hellman parameters and specify them with the +`dhparam` option to [`tls.createSecureContext()`][]. The following illustrates +the use of the OpenSSL command-line interface to generate such parameters: ```bash openssl dhparam -outform PEM -out dhparam.pem 2048 @@ -206,7 +243,7 @@ handlers. The servers encrypt the entire session state and send it to the client as a "ticket". When reconnecting, the state is sent to the server -in the initial connection. This mechanism avoids the need for server-side +in the initial connection. This mechanism avoids the need for a server-side session cache. If the server doesn't use the ticket, for any reason (failure to decrypt it, it's too old, etc.), it will create a new session and send a new ticket. See [RFC 5077][] for more information. @@ -228,7 +265,7 @@ To use session tickets across server restarts or load balancers, servers must all have the same ticket keys. There are three 16-byte keys internally, but the tls API exposes them as a single 48-byte buffer for convenience. -Its possible to get the ticket keys by calling [`server.getTicketKeys()`][] on +It's possible to get the ticket keys by calling [`server.getTicketKeys()`][] on one server instance and then distribute them, but it is more reasonable to securely generate 48 bytes of secure random data and set them with the `ticketKeys` option of [`tls.createServer()`][]. The keys should be regularly @@ -242,7 +279,7 @@ on disk, and they should be regenerated regularly. If clients advertise support for tickets, the server will send them. The server can disable tickets by supplying -`require('constants').SSL_OP_NO_TICKET` in `secureOptions`. +`require('node:constants').SSL_OP_NO_TICKET` in `secureOptions`. Both session identifiers and session tickets timeout, causing the server to create new sessions. The timeout can be configured with the `sessionTimeout` @@ -348,9 +385,6 @@ The default cipher suite prefers GCM ciphers for [Chrome's 'modern cryptography' setting][] and also prefers ECDHE and DHE ciphers for perfect forward secrecy, while offering _some_ backward compatibility. -128 bit AES is preferred over 192 and 256 bit AES in light of [specific -attacks affecting larger AES key sizes][]. - Old clients that rely on insecure and deprecated RC4 or DES-based ciphers (like Internet Explorer 6) cannot complete the handshaking process with the default configuration. If these clients _must_ be supported, the @@ -568,7 +602,7 @@ no OCSP response. Calling `callback(err)` will result in a `socket.destroy(err)` call. -The typical flow of an OCSP Request is as follows: +The typical flow of an OCSP request is as follows: 1. Client connects to the server and sends an `'OCSPRequest'` (via the status info extension in ClientHello). @@ -921,13 +955,17 @@ tlsSocket.once('session', (session) => { * Returns: {Object} Returns the bound `address`, the address `family` name, and `port` of the underlying socket as reported by the operating system: -`{ port: 12346, family: 'IPv4', address: '127.0.0.1' }`. +`{ port: 12346, family: 4, address: '127.0.0.1' }`. ### `tlsSocket.authorizationError` @@ -944,10 +982,10 @@ property is set only when `tlsSocket.authorized === false`. added: v0.11.4 --> -* Returns: {boolean} +* {boolean} -Returns `true` if the peer certificate was signed by one of the CAs specified -when creating the `tls.TLSSocket` instance, otherwise `false`. +This property is `true` if the peer certificate was signed by one of the CAs +specified when creating the `tls.TLSSocket` instance, otherwise `false`. ### `tlsSocket.disableRenegotiation()` @@ -1408,7 +1446,7 @@ Returns the numeric representation of the remote port. For example, `443`. -The `trace_events` module provides a mechanism to centralize tracing information -generated by V8, Node.js core, and userspace code. +The `node:trace_events` module provides a mechanism to centralize tracing +information generated by V8, Node.js core, and userspace code. Tracing can be enabled with the `--trace-event-categories` command-line flag -or by using the `trace_events` module. The `--trace-event-categories` flag +or by using the `node:trace_events` module. The `--trace-event-categories` flag accepts a list of comma-separated category names. The available categories are: @@ -32,7 +32,7 @@ The available categories are: measurements. * `node.promises.rejections`: Enables capture of trace data tracking the number of unhandled Promise rejections and handled-after-rejections. -* `node.vm.script`: Enables capture of trace data for the `vm` module's +* `node.vm.script`: Enables capture of trace data for the `node:vm` module's `runInNewContext()`, `runInContext()`, and `runInThisContext()` methods. * `v8`: The [V8][] events are GC, compiling, and execution related. @@ -55,10 +55,10 @@ node --trace-events-enabled node --trace-event-categories v8,node,node.async_hooks ``` -Alternatively, trace events may be enabled using the `trace_events` module: +Alternatively, trace events may be enabled using the `node:trace_events` module: ```js -const trace_events = require('trace_events'); +const trace_events = require('node:trace_events'); const tracing = trace_events.createTracing({ categories: ['node.perf'] }); tracing.enable(); // Enable trace event capture for the 'node.perf' category @@ -98,7 +98,7 @@ unlike `process.hrtime()` which returns nanoseconds. The features from this module are not available in [`Worker`][] threads. -## The `trace_events` module +## The `node:trace_events` module -The `tty` module provides the `tty.ReadStream` and `tty.WriteStream` classes. -In most cases, it will not be necessary or possible to use this module directly. -However, it can be accessed using: +The `node:tty` module provides the `tty.ReadStream` and `tty.WriteStream` +classes. In most cases, it will not be necessary or possible to use this module +directly. However, it can be accessed using: ```js -const tty = require('tty'); +const tty = require('node:tty'); ``` When Node.js detects that it is being run with a text terminal ("TTY") diff --git a/doc/api/url.md b/doc/api/url.md index 3fa589397f77d6..edbfd6829954fe 100644 --- a/doc/api/url.md +++ b/doc/api/url.md @@ -6,15 +6,15 @@ -The `url` module provides utilities for URL resolution and parsing. It can be -accessed using: +The `node:url` module provides utilities for URL resolution and parsing. It can +be accessed using: ```mjs -import url from 'url'; +import url from 'node:url'; ``` ```cjs -const url = require('url'); +const url = require('node:url'); ``` ## URL strings and URL objects @@ -23,8 +23,8 @@ A URL string is a structured string containing multiple meaningful components. When parsed, a URL object is returned containing properties for each of these components. -The `url` module provides two APIs for working with URLs: a legacy API that is -Node.js specific, and a newer API that implements the same +The `node:url` module provides two APIs for working with URLs: a legacy API that +is Node.js specific, and a newer API that implements the same [WHATWG URL Standard][] used by web browsers. A comparison between the WHATWG and Legacy APIs is provided below. Above the URL @@ -66,13 +66,13 @@ const myURL = Parsing the URL string using the Legacy API: ```mjs -import url from 'url'; +import url from 'node:url'; const myURL = url.parse('https://user:pass@sub.example.com:8080/p/a/t/h?query=string#hash'); ``` ```cjs -const url = require('url'); +const url = require('node:url'); const myURL = url.parse('https://user:pass@sub.example.com:8080/p/a/t/h?query=string#hash'); ``` @@ -147,12 +147,12 @@ The URL constructor is accessible as a property on the global object. It can also be imported from the built-in url module: ```mjs -import { URL } from 'url'; +import { URL } from 'node:url'; console.log(URL === globalThis.URL); // Prints 'true'. ``` ```cjs -console.log(URL === require('url').URL); // Prints 'true'. +console.log(URL === require('node:url').URL); // Prints 'true'. ``` A `TypeError` will be thrown if the `input` or `base` are not valid URLs. Note @@ -630,7 +630,7 @@ object and can be used to retrieve the `Blob` later. const { Blob, resolveObjectURL, -} = require('buffer'); +} = require('node:buffer'); const blob = new Blob(['hello']); const id = URL.createObjectURL(blob); @@ -659,7 +659,8 @@ added: v16.7.0 * `id` {string} A `'blob:nodedata:...` URL string returned by a prior call to `URL.createObjectURL()`. -Removes the stored {Blob} identified by the given ID. +Removes the stored {Blob} identified by the given ID. Attempting to revoke a +ID that isn’t registered will silently fail. ### Class: `URLSearchParams` @@ -853,7 +854,7 @@ Alias for [`urlSearchParams[@@iterator]()`][`urlSearchParams@@iterator()`]. -The `util` module supports the needs of Node.js internal APIs. Many of the +The `node:util` module supports the needs of Node.js internal APIs. Many of the utilities are useful for application and module developers as well. To access it: ```js -const util = require('util'); +const util = require('node:util'); ``` ## `util.callbackify(original)` @@ -30,7 +30,7 @@ first argument will be the rejection reason (or `null` if the `Promise` resolved), and the second argument will be the resolved value. ```js -const util = require('util'); +const util = require('node:util'); async function fn() { return 'hello world'; @@ -90,7 +90,7 @@ environment variable, then the returned function operates similar to [`console.error()`][]. If not, then the returned function is a no-op. ```js -const util = require('util'); +const util = require('node:util'); const debuglog = util.debuglog('foo'); debuglog('hello from foo [%d]', 123); @@ -109,7 +109,7 @@ environment variable set, then it will not print anything. The `section` supports wildcard also: ```js -const util = require('util'); +const util = require('node:util'); const debuglog = util.debuglog('foo-bar'); debuglog('hi there, it\'s foo-bar [%d]', 2333); @@ -130,7 +130,7 @@ with a different function that doesn't have any initialization or unnecessary wrapping. ```js -const util = require('util'); +const util = require('node:util'); let debuglog = util.debuglog('internals', (debug) => { // Replace with a logging function that optimizes out // testing if the section is enabled @@ -153,7 +153,7 @@ then the returned value will be `true`. If not, then the returned value will be `false`. ```js -const util = require('util'); +const util = require('node:util'); const enabled = util.debuglog('foo').enabled; if (enabled) { console.log('hello from foo [%d]', 123); @@ -197,7 +197,7 @@ The `util.deprecate()` method wraps `fn` (which may be a function or class) in such a way that it is marked as deprecated. ```js -const util = require('util'); +const util = require('node:util'); exports.obsoleteFunction = util.deprecate(() => { // Do something here. @@ -214,7 +214,7 @@ If the same optional `code` is supplied in multiple calls to `util.deprecate()`, the warning will be emitted only once for that `code`. ```js -const util = require('util'); +const util = require('node:util'); const fn1 = util.deprecate(someFunction, someMessage, 'DEP0001'); const fn2 = util.deprecate(someOtherFunction, someOtherMessage, 'DEP0001'); @@ -435,8 +435,8 @@ As an additional convenience, `superConstructor` will be accessible through the `constructor.super_` property. ```js -const util = require('util'); -const EventEmitter = require('events'); +const util = require('node:util'); +const EventEmitter = require('node:events'); function MyStream() { EventEmitter.call(this); @@ -462,7 +462,7 @@ stream.write('It works!'); // Received data: "It works!" ES6 example using `class` and `extends`: ```js -const EventEmitter = require('events'); +const EventEmitter = require('node:events'); class MyStream extends EventEmitter { write(data) { @@ -536,7 +536,7 @@ changes: description: The `depth` default changed to `20`. - version: v11.0.0 pr-url: https://github.com/nodejs/node/pull/22756 - description: The inspection output is now limited to about 128 MB. Data + description: The inspection output is now limited to about 128 MiB. Data above that size will not be fully inspected. - version: v10.12.0 pr-url: https://github.com/nodejs/node/pull/22788 @@ -642,7 +642,7 @@ util.inspect(baz); // '[foo] {}' Circular references point to their anchor by using a reference index: ```js -const { inspect } = require('util'); +const { inspect } = require('node:util'); const obj = {}; obj.a = [obj]; @@ -660,7 +660,7 @@ console.log(inspect(obj)); The following example inspects all properties of the `util` object: ```js -const util = require('util'); +const util = require('node:util'); console.log(util.inspect(util, { showHidden: true, depth: null })); ``` @@ -668,7 +668,7 @@ console.log(util.inspect(util, { showHidden: true, depth: null })); The following example highlights the effect of the `compact` option: ```js -const util = require('util'); +const util = require('node:util'); const o = { a: [1, 2, [[ @@ -724,7 +724,7 @@ guarantee which entries are displayed. That means retrieving the same with no remaining strong references may be garbage collected at any time. ```js -const { inspect } = require('util'); +const { inspect } = require('node:util'); const obj = { a: 1 }; const obj2 = { b: 2 }; @@ -738,8 +738,8 @@ The `sorted` option ensures that an object's property insertion order does not impact the result of `util.inspect()`. ```js -const { inspect } = require('util'); -const assert = require('assert'); +const { inspect } = require('node:util'); +const assert = require('node:assert'); const o1 = { b: [2, 3, 1], @@ -766,7 +766,7 @@ The `numericSeparator` option adds an underscore every three digits to all numbers. ```js -const { inspect } = require('util'); +const { inspect } = require('node:util'); const thousand = 1_000; const million = 1_000_000; @@ -778,7 +778,7 @@ console.log(thousand, million, bigNumber, bigDecimal); ``` `util.inspect()` is a synchronous method intended for debugging. Its maximum -output length is approximately 128 MB. Inputs that result in longer output will +output length is approximately 128 MiB. Inputs that result in longer output will be truncated. ### Customizing `util.inspect` colors @@ -892,7 +892,7 @@ which `util.inspect()` will invoke and use the result of when inspecting the object. ```js -const util = require('util'); +const util = require('node:util'); class Box { constructor(value) { @@ -927,7 +927,7 @@ a string but may return a value of any type that will be formatted accordingly by `util.inspect()`. ```js -const util = require('util'); +const util = require('node:util'); const obj = { foo: 'this will not show up in the inspect() output' }; obj[util.inspect.custom] = (depth) => { @@ -996,7 +996,7 @@ object containing one or more valid [`util.inspect()`][] options. Setting option properties directly is also supported. ```js -const util = require('util'); +const util = require('node:util'); const arr = Array(101).fill(0); console.log(arr); // Logs the truncated array @@ -1034,8 +1034,8 @@ an `(err, value) => ...` callback as the last argument, and returns a version that returns promises. ```js -const util = require('util'); -const fs = require('fs'); +const util = require('node:util'); +const fs = require('node:fs'); const stat = util.promisify(fs.stat); stat('.').then((stats) => { @@ -1048,8 +1048,8 @@ stat('.').then((stats) => { Or, equivalently using `async function`s: ```js -const util = require('util'); -const fs = require('fs'); +const util = require('node:util'); +const fs = require('node:fs'); const stat = util.promisify(fs.stat); @@ -1072,7 +1072,7 @@ Using `promisify()` on class methods or other methods that use `this` may not work as expected unless handled specially: ```js -const util = require('util'); +const util = require('node:util'); class Foo { constructor() { @@ -1102,7 +1102,7 @@ Using the `util.promisify.custom` symbol one can override the return value of [`util.promisify()`][]: ```js -const util = require('util'); +const util = require('node:util'); function doSomething(foo, callback) { // ... @@ -1397,7 +1397,7 @@ added: v10.0.0 changes: - version: v15.3.0 pr-url: https://github.com/nodejs/node/pull/34055 - description: Exposed as `require('util/types')`. + description: Exposed as `require('node:util/types')`. --> `util.types` provides type checks for different kinds of built-in objects. @@ -1409,7 +1409,7 @@ The result generally does not make any guarantees about what kinds of properties or behavior a value exposes in JavaScript. They are primarily useful for addon developers who prefer to do type checking in JavaScript. -The API is accessible via `require('util').types` or `require('util/types')`. +The API is accessible via `require('node:util').types` or `require('node:util/types')`. ### `util.types.isAnyArrayBuffer(value)` @@ -2210,7 +2210,7 @@ Alias for [`Array.isArray()`][]. Returns `true` if the given `object` is an `Array`. Otherwise, returns `false`. ```js -const util = require('util'); +const util = require('node:util'); util.isArray([]); // Returns: true @@ -2235,7 +2235,7 @@ deprecated: v4.0.0 Returns `true` if the given `object` is a `Boolean`. Otherwise, returns `false`. ```js -const util = require('util'); +const util = require('node:util'); util.isBoolean(1); // Returns: false @@ -2260,7 +2260,7 @@ deprecated: v4.0.0 Returns `true` if the given `object` is a `Buffer`. Otherwise, returns `false`. ```js -const util = require('util'); +const util = require('node:util'); util.isBuffer({ length: 0 }); // Returns: false @@ -2285,7 +2285,7 @@ deprecated: v4.0.0 Returns `true` if the given `object` is a `Date`. Otherwise, returns `false`. ```js -const util = require('util'); +const util = require('node:util'); util.isDate(new Date()); // Returns: true @@ -2311,7 +2311,7 @@ Returns `true` if the given `object` is an [`Error`][]. Otherwise, returns `false`. ```js -const util = require('util'); +const util = require('node:util'); util.isError(new Error()); // Returns: true @@ -2326,7 +2326,7 @@ possible to obtain an incorrect result when the `object` argument manipulates `@@toStringTag`. ```js -const util = require('util'); +const util = require('node:util'); const obj = { name: 'Error', message: 'an error occurred' }; util.isError(obj); @@ -2352,7 +2352,7 @@ Returns `true` if the given `object` is a `Function`. Otherwise, returns `false`. ```js -const util = require('util'); +const util = require('node:util'); function Foo() {} const Bar = () => {}; @@ -2381,7 +2381,7 @@ Returns `true` if the given `object` is strictly `null`. Otherwise, returns `false`. ```js -const util = require('util'); +const util = require('node:util'); util.isNull(0); // Returns: false @@ -2408,7 +2408,7 @@ Returns `true` if the given `object` is `null` or `undefined`. Otherwise, returns `false`. ```js -const util = require('util'); +const util = require('node:util'); util.isNullOrUndefined(0); // Returns: false @@ -2433,7 +2433,7 @@ deprecated: v4.0.0 Returns `true` if the given `object` is a `Number`. Otherwise, returns `false`. ```js -const util = require('util'); +const util = require('node:util'); util.isNumber(false); // Returns: false @@ -2463,7 +2463,7 @@ Returns `true` if the given `object` is strictly an `Object` **and** not a Otherwise, returns `false`. ```js -const util = require('util'); +const util = require('node:util'); util.isObject(5); // Returns: false @@ -2493,7 +2493,7 @@ Returns `true` if the given `object` is a primitive type. Otherwise, returns `false`. ```js -const util = require('util'); +const util = require('node:util'); util.isPrimitive(5); // Returns: true @@ -2530,7 +2530,7 @@ deprecated: v4.0.0 Returns `true` if the given `object` is a `RegExp`. Otherwise, returns `false`. ```js -const util = require('util'); +const util = require('node:util'); util.isRegExp(/some regexp/); // Returns: true @@ -2555,7 +2555,7 @@ deprecated: v4.0.0 Returns `true` if the given `object` is a `string`. Otherwise, returns `false`. ```js -const util = require('util'); +const util = require('node:util'); util.isString(''); // Returns: true @@ -2582,7 +2582,7 @@ deprecated: v4.0.0 Returns `true` if the given `object` is a `Symbol`. Otherwise, returns `false`. ```js -const util = require('util'); +const util = require('node:util'); util.isSymbol(5); // Returns: false @@ -2607,7 +2607,7 @@ deprecated: v4.0.0 Returns `true` if the given `object` is `undefined`. Otherwise, returns `false`. ```js -const util = require('util'); +const util = require('node:util'); const foo = undefined; util.isUndefined(5); @@ -2633,7 +2633,7 @@ The `util.log()` method prints the given `string` to `stdout` with an included timestamp. ```js -const util = require('util'); +const util = require('node:util'); util.log('Timestamped message.'); ``` diff --git a/doc/api/v8.md b/doc/api/v8.md index def7d17d6d089a..03c8e0e9571d7b 100644 --- a/doc/api/v8.md +++ b/doc/api/v8.md @@ -4,11 +4,11 @@ -The `v8` module exposes APIs that are specific to the version of [V8][] +The `node:v8` module exposes APIs that are specific to the version of [V8][] built into the Node.js binary. It can be accessed using: ```js -const v8 = require('v8'); +const v8 = require('node:v8'); ``` ## `v8.cachedDataVersionTag()` @@ -80,7 +80,7 @@ for a duration depending on the heap size. ```js // Print heap snapshot to the console -const v8 = require('v8'); +const v8 = require('node:v8'); const stream = v8.getHeapSnapshot(); stream.pipe(process.stdout); ``` @@ -180,6 +180,9 @@ Returns an object with the following properties: * `does_zap_garbage` {number} * `number_of_native_contexts` {number} * `number_of_detached_contexts` {number} +* `total_global_handles_size` {number} +* `used_global_handles_size` {number} +* `external_memory` {number} `does_zap_garbage` is a 0/1 boolean, which signifies whether the `--zap_code_space` option is enabled or not. This makes V8 overwrite heap @@ -195,6 +198,15 @@ a memory leak. of contexts that were detached and not yet garbage collected. This number being non-zero indicates a potential memory leak. +`total_global_handles_size` The value of total\_global\_handles\_size is the +total memory size of V8 global handles. + +`used_global_handles_size` The value of used\_global\_handles\_size is the +used memory size of V8 global handles. + +`external_memory` The value of external\_memory is the memory size of array +buffers and external strings. + ```js @@ -209,7 +221,10 @@ being non-zero indicates a potential memory leak. peak_malloced_memory: 1127496, does_zap_garbage: 0, number_of_native_contexts: 1, - number_of_detached_contexts: 0 + number_of_detached_contexts: 0, + total_global_handles_size: 8192, + used_global_handles_size: 3296, + external_memory: 318824 } ``` @@ -233,7 +248,7 @@ Usage: ```js // Print GC events to stdout for one minute. -const v8 = require('v8'); +const v8 = require('node:v8'); v8.setFlagsFromString('--trace_gc'); setTimeout(() => { v8.setFlagsFromString('--notrace_gc'); }, 60e3); ``` @@ -275,9 +290,12 @@ disk unless [`v8.stopCoverage()`][] is invoked before the process exits. * `filename` {string} The file path where the V8 heap snapshot is to be @@ -305,12 +323,12 @@ Generating a snapshot is a synchronous operation which blocks the event loop for a duration depending on the heap size. ```js -const { writeHeapSnapshot } = require('v8'); +const { writeHeapSnapshot } = require('node:v8'); const { Worker, isMainThread, parentPort -} = require('worker_threads'); +} = require('node:worker_threads'); if (isMainThread) { const worker = new Worker(__filename); @@ -590,7 +608,7 @@ module to produce promise lifecycle events in addition to events for other async resources. For request context management, see [`AsyncLocalStorage`][]. ```mjs -import { promiseHooks } from 'v8'; +import { promiseHooks } from 'node:v8'; // There are four lifecycle events produced by promises: @@ -659,13 +677,13 @@ added: throw as it would produce an infinite microtask loop.** ```mjs -import { promiseHooks } from 'v8'; +import { promiseHooks } from 'node:v8'; const stop = promiseHooks.onInit((promise, parent) => {}); ``` ```cjs -const { promiseHooks } = require('v8'); +const { promiseHooks } = require('node:v8'); const stop = promiseHooks.onInit((promise, parent) => {}); ``` @@ -686,13 +704,13 @@ added: throw as it would produce an infinite microtask loop.** ```mjs -import { promiseHooks } from 'v8'; +import { promiseHooks } from 'node:v8'; const stop = promiseHooks.onSettled((promise) => {}); ``` ```cjs -const { promiseHooks } = require('v8'); +const { promiseHooks } = require('node:v8'); const stop = promiseHooks.onSettled((promise) => {}); ``` @@ -713,13 +731,13 @@ added: throw as it would produce an infinite microtask loop.** ```mjs -import { promiseHooks } from 'v8'; +import { promiseHooks } from 'node:v8'; const stop = promiseHooks.onBefore((promise) => {}); ``` ```cjs -const { promiseHooks } = require('v8'); +const { promiseHooks } = require('node:v8'); const stop = promiseHooks.onBefore((promise) => {}); ``` @@ -740,13 +758,13 @@ added: throw as it would produce an infinite microtask loop.** ```mjs -import { promiseHooks } from 'v8'; +import { promiseHooks } from 'node:v8'; const stop = promiseHooks.onAfter((promise) => {}); ``` ```cjs -const { promiseHooks } = require('v8'); +const { promiseHooks } = require('node:v8'); const stop = promiseHooks.onAfter((promise) => {}); ``` @@ -780,7 +798,7 @@ specifics of all functions that can be passed to `callbacks` is in the [Hook Callbacks][] section. ```mjs -import { promiseHooks } from 'v8'; +import { promiseHooks } from 'node:v8'; const stopAll = promiseHooks.createHook({ init(promise, parent) {} @@ -788,7 +806,7 @@ const stopAll = promiseHooks.createHook({ ``` ```cjs -const { promiseHooks } = require('v8'); +const { promiseHooks } = require('node:v8'); const stopAll = promiseHooks.createHook({ init(promise, parent) {} @@ -815,7 +833,7 @@ via the promise hooks mechanism, the `init()`, `before()`, `after()`, and promises which would produce an infinite loop. While this API is used to feed promise events into [`async_hooks`][], the -ordering between the two is considered undefined. Both APIs are multi-tenant +ordering between the two is undefined. Both APIs are multi-tenant and therefore could produce events in any order relative to each other. #### `init(promise, parent)` diff --git a/doc/api/vm.md b/doc/api/vm.md index cbb16c602ee71c..aa99c8430767a0 100644 --- a/doc/api/vm.md +++ b/doc/api/vm.md @@ -8,10 +8,10 @@ -The `vm` module enables compiling and running code within V8 Virtual +The `node:vm` module enables compiling and running code within V8 Virtual Machine contexts. -The `vm` module is not a security +The `node:vm` module is not a security mechanism. Do not use it to run untrusted code. JavaScript code can be compiled and run immediately or @@ -26,7 +26,7 @@ global variable. Any changes to global variables caused by the invoked code are reflected in the context object. ```js -const vm = require('vm'); +const vm = require('node:vm'); const x = 1; @@ -177,7 +177,7 @@ the value of another global variable, then execute the code multiple times. The globals are contained in the `context` object. ```js -const vm = require('vm'); +const vm = require('node:vm'); const context = { animal: 'cat', @@ -259,7 +259,7 @@ the code multiple times in different contexts. The globals are set on and contained within each individual `context`. ```js -const vm = require('vm'); +const vm = require('node:vm'); const script = new vm.Script('globalVar = "set"'); @@ -304,7 +304,7 @@ The following example compiles code that increments a `global` variable then executes that code multiple times: ```js -const vm = require('vm'); +const vm = require('node:vm'); global.globalVar = 0; @@ -351,7 +351,7 @@ loader][]. There is also no way to interact with the Loader yet, though support is planned. ```mjs -import vm from 'vm'; +import vm from 'node:vm'; const contextifiedObject = vm.createContext({ secret: 42, @@ -422,7 +422,7 @@ await bar.evaluate(); ``` ```cjs -const vm = require('vm'); +const vm = require('node:vm'); const contextifiedObject = vm.createContext({ secret: 42, @@ -560,6 +560,8 @@ The identifier of the current module, as set in the constructor. // ^^^^^ the module specifier ``` + * `referencingModule` {vm.Module} The `Module` object `link()` is called on. + * `extra` {Object} * `assert` {Object} The data from the assertion: @@ -571,8 +573,6 @@ The identifier of the current module, as set in the constructor. support, as opposed to, for example, triggering an error if an unsupported assertion is present. - * `referencingModule` {vm.Module} The `Module` object `link()` is called on. - * Returns: {vm.Module|Promise} * Returns: {Promise} @@ -712,7 +712,7 @@ allow the module to access information outside the specified `context`. Use `vm.runInContext()` to create objects in a specific context. ```mjs -import vm from 'vm'; +import vm from 'node:vm'; const contextifiedObject = vm.createContext({ secret: 42 }); @@ -740,7 +740,7 @@ await module.evaluate(); ``` ```cjs -const vm = require('vm'); +const vm = require('node:vm'); const contextifiedObject = vm.createContext({ secret: 42 }); (async () => { const module = new vm.SourceTextModule( @@ -812,7 +812,7 @@ provide a generic interface for exposing non-JavaScript sources to ECMAScript module graphs. ```js -const vm = require('vm'); +const vm = require('node:vm'); const source = '{ "a": 1 }'; const module = new vm.SyntheticModule(['default'], function() { @@ -863,7 +863,7 @@ it is called before the module is linked, an [`ERR_VM_MODULE_STATUS`][] error will be thrown. ```mjs -import vm from 'vm'; +import vm from 'node:vm'; const m = new vm.SyntheticModule(['x'], () => { m.setExport('x', 1); @@ -876,7 +876,7 @@ assert.strictEqual(m.namespace.x, 1); ``` ```cjs -const vm = require('vm'); +const vm = require('node:vm'); (async () => { const m = new vm.SyntheticModule(['x'], () => { m.setExport('x', 1); @@ -999,7 +999,7 @@ properties but also having the built-in objects and functions any standard will remain unchanged. ```js -const vm = require('vm'); +const vm = require('node:vm'); global.globalVar = 3; @@ -1053,7 +1053,7 @@ current V8 isolate, or the main context. * `options` {Object} Optional. * `mode` {string} Either `'summary'` or `'detailed'`. In summary mode, only the memory measured for the main context will be returned. In - detailed mode, the measure measured for all contexts known to the + detailed mode, the memory measured for all contexts known to the current V8 isolate will be returned. **Default:** `'summary'` * `execution` {string} Either `'default'` or `'eager'`. With default @@ -1075,7 +1075,7 @@ the V8 engine, while the result of `v8.getHeapSpaceStatistics()` measure the memory occupied by each heap space in the current V8 instance. ```js -const vm = require('vm'); +const vm = require('node:vm'); // Measure the memory used by the main context. vm.measureMemory({ mode: 'summary' }) // This is the same as vm.measureMemory() @@ -1190,7 +1190,7 @@ The following example compiles and executes different scripts using a single [contextified][] object: ```js -const vm = require('vm'); +const vm = require('node:vm'); const contextObject = { globalVar: 1 }; vm.createContext(contextObject); @@ -1302,7 +1302,7 @@ The following example compiles and executes code that increments a global variable and sets a new one. These globals are contained in the `contextObject`. ```js -const vm = require('vm'); +const vm = require('node:vm'); const contextObject = { animal: 'cat', @@ -1388,7 +1388,7 @@ the JavaScript [`eval()`][] function to run the same code: ```js -const vm = require('vm'); +const vm = require('node:vm'); let localVar = 'initial value'; const vmResult = vm.runInThisContext('localVar = "vm";'); @@ -1412,17 +1412,17 @@ When using either [`script.runInThisContext()`][] or [`vm.runInThisContext()`][], the code is executed within the current V8 global context. The code passed to this VM context will have its own isolated scope. -In order to run a simple web server using the `http` module the code passed to -the context must either call `require('http')` on its own, or have a reference -to the `http` module passed to it. For instance: +In order to run a simple web server using the `node:http` module the code passed +to the context must either call `require('node:http')` on its own, or have a +reference to the `node:http` module passed to it. For instance: ```js 'use strict'; -const vm = require('vm'); +const vm = require('node:vm'); const code = ` ((require) => { - const http = require('http'); + const http = require('node:http'); http.createServer((request, response) => { response.writeHead(200, { 'Content-Type': 'text/plain' }); @@ -1451,9 +1451,9 @@ According to the [V8 Embedder's Guide][]: When the method `vm.createContext()` is called, the `contextObject` argument (or a newly-created object if `contextObject` is `undefined`) is associated internally with a new instance of a V8 Context. This V8 Context provides the -`code` run using the `vm` module's methods with an isolated global environment -within which it can operate. The process of creating the V8 Context and -associating it with the `contextObject` is what this document refers to as +`code` run using the `node:vm` module's methods with an isolated global +environment within which it can operate. The process of creating the V8 Context +and associating it with the `contextObject` is what this document refers to as "contextifying" the object. ## Timeout interactions with asynchronous tasks and Promises @@ -1469,7 +1469,7 @@ timeout of 5 milliseconds schedules an infinite loop to run after a promise resolves. The scheduled loop is never interrupted by the timeout: ```js -const vm = require('vm'); +const vm = require('node:vm'); function loop() { console.log('entering loop'); @@ -1489,7 +1489,7 @@ This can be addressed by passing `microtaskMode: 'afterEvaluate'` to the code that creates the `Context`: ```js -const vm = require('vm'); +const vm = require('node:vm'); function loop() { while (1) console.log(Date.now()); diff --git a/doc/api/wasi.md b/doc/api/wasi.md index 644daa217e900e..1b457201345ff0 100644 --- a/doc/api/wasi.md +++ b/doc/api/wasi.md @@ -11,9 +11,9 @@ specification. WASI gives sandboxed WebAssembly applications access to the underlying operating system via a collection of POSIX-like functions. ```mjs -import { readFile } from 'fs/promises'; +import { readFile } from 'node:fs/promises'; import { WASI } from 'wasi'; -import { argv, env } from 'process'; +import { argv, env } from 'node:process'; const wasi = new WASI({ args: argv, @@ -37,10 +37,10 @@ wasi.start(instance); ```cjs 'use strict'; -const { readFile } = require('fs/promises'); +const { readFile } = require('node:fs/promises'); const { WASI } = require('wasi'); -const { argv, env } = require('process'); -const { join } = require('path'); +const { argv, env } = require('node:process'); +const { join } = require('node:path'); const wasi = new WASI({ args: argv, diff --git a/doc/api/webcrypto.md b/doc/api/webcrypto.md index 3d7bf0f946ff05..9454fead349b69 100644 --- a/doc/api/webcrypto.md +++ b/doc/api/webcrypto.md @@ -6,10 +6,10 @@ Node.js provides an implementation of the standard [Web Crypto API][]. -Use `require('crypto').webcrypto` to access this module. +Use `require('node:crypto').webcrypto` to access this module. ```js -const { subtle } = require('crypto').webcrypto; +const { subtle } = require('node:crypto').webcrypto; (async function() { @@ -39,7 +39,7 @@ or asymmetric key pairs (public key and private key). #### AES keys ```js -const { subtle } = require('crypto').webcrypto; +const { subtle } = require('node:crypto').webcrypto; async function generateAesKey(length = 256) { const key = await subtle.generateKey({ @@ -54,7 +54,7 @@ async function generateAesKey(length = 256) { #### ECDSA key pairs ```js -const { subtle } = require('crypto').webcrypto; +const { subtle } = require('node:crypto').webcrypto; async function generateEcKey(namedCurve = 'P-521') { const { @@ -72,7 +72,7 @@ async function generateEcKey(namedCurve = 'P-521') { #### ED25519/ED448/X25519/X448 key pairs ```js -const { subtle } = require('crypto').webcrypto; +const { subtle } = require('node:crypto').webcrypto; async function generateEd25519Key() { return subtle.generateKey({ @@ -92,7 +92,7 @@ async function generateX25519Key() { #### HMAC keys ```js -const { subtle } = require('crypto').webcrypto; +const { subtle } = require('node:crypto').webcrypto; async function generateHmacKey(hash = 'SHA-256') { const key = await subtle.generateKey({ @@ -107,7 +107,7 @@ async function generateHmacKey(hash = 'SHA-256') { #### RSA key pairs ```js -const { subtle } = require('crypto').webcrypto; +const { subtle } = require('node:crypto').webcrypto; const publicExponent = new Uint8Array([1, 0, 1]); async function generateRsaKey(modulusLength = 2048, hash = 'SHA-256') { @@ -128,14 +128,14 @@ async function generateRsaKey(modulusLength = 2048, hash = 'SHA-256') { ### Encryption and decryption ```js -const { subtle, getRandomValues } = require('crypto').webcrypto; +const crypto = require('node:crypto').webcrypto; async function aesEncrypt(plaintext) { const ec = new TextEncoder(); const key = await generateAesKey(); - const iv = getRandomValues(new Uint8Array(16)); + const iv = crypto.getRandomValues(new Uint8Array(16)); - const ciphertext = await subtle.encrypt({ + const ciphertext = await crypto.subtle.encrypt({ name: 'AES-CBC', iv, }, key, ec.encode(plaintext)); @@ -149,7 +149,7 @@ async function aesEncrypt(plaintext) { async function aesDecrypt(ciphertext, key, iv) { const dec = new TextDecoder(); - const plaintext = await subtle.decrypt({ + const plaintext = await crypto.subtle.decrypt({ name: 'AES-CBC', iv, }, key, ciphertext); @@ -161,7 +161,7 @@ async function aesDecrypt(ciphertext, key, iv) { ### Exporting and importing keys ```js -const { subtle } = require('crypto').webcrypto; +const { subtle } = require('node:crypto').webcrypto; async function generateAndExportHmacKey(format = 'jwk', hash = 'SHA-512') { const key = await subtle.generateKey({ @@ -185,7 +185,7 @@ async function importHmacKey(keyData, format = 'jwk', hash = 'SHA-512') { ### Wrapping and unwrapping keys ```js -const { subtle } = require('crypto').webcrypto; +const { subtle } = require('node:crypto').webcrypto; async function generateAndWrapHmacKey(format = 'jwk', hash = 'SHA-512') { const [ @@ -228,7 +228,7 @@ async function unwrapHmacKey( ### Sign and verify ```js -const { subtle } = require('crypto').webcrypto; +const { subtle } = require('node:crypto').webcrypto; async function sign(key, data) { const ec = new TextEncoder(); @@ -252,7 +252,7 @@ async function verify(key, signature, data) { ### Deriving bits and keys ```js -const { subtle } = require('crypto').webcrypto; +const { subtle } = require('node:crypto').webcrypto; async function pbkdf2(pass, salt, iterations = 1000, length = 256) { const ec = new TextEncoder(); @@ -295,7 +295,7 @@ async function pbkdf2Key(pass, salt, iterations = 1000, length = 256) { ### Digest ```js -const { subtle } = require('crypto').webcrypto; +const { subtle } = require('node:crypto').webcrypto; async function digest(data, algorithm = 'SHA-512') { const ec = new TextEncoder(); @@ -338,8 +338,9 @@ implementation and the APIs supported for each: added: v15.0.0 --> -Calling `require('crypto').webcrypto` returns an instance of the `Crypto` class. -`Crypto` is a singleton that provides access to the remainder of the crypto API. +Calling `require('node:crypto').webcrypto` returns an instance of the `Crypto` +class. `Crypto` is a singleton that provides access to the remainder of the +crypto API. ### `crypto.subtle` @@ -393,7 +394,7 @@ added: v15.0.0 -* Type: {AesKeyGenParams|RsaHashedKeyGenParams|EcKeyGenParams|HmacKeyGenParams|NodeDsaKeyGenParams|NodeDhKeyGenParams} +* Type: {AesKeyGenParams|RsaHashedKeyGenParams|EcKeyGenParams|HmacKeyGenParams|NodeDsaKeyGenParams|NodeDhKeyGenParams|NodeEdKeyGenParams} @@ -845,10 +846,10 @@ promise is resolved with a {CryptoKey} object. The wrapping algorithms currently supported include: * `'RSA-OAEP'` -* `'AES-CTR'`[^1] -* `'AES-CBC'`[^1] -* `'AES-GCM'`[^1] -* `'AES-KW'`[^1] +* `'AES-CTR'` +* `'AES-CBC'` +* `'AES-GCM'` +* `'AES-KW'` The unwrapped key algorithms supported include: @@ -1019,9 +1020,14 @@ added: v15.0.0 * Type: {ArrayBuffer|TypedArray|DataView|Buffer} -The initialization vector must be unique for every encryption operation -using a given key. It is recommended by the AES-GCM specification that -this contain at least 12 random bytes. +The initialization vector must be unique for every encryption operation using a +given key. + +Ideally, this is a deterministic 12-byte value that is computed in such a way +that it is guaranteed to be unique across all invocations that use the same key. +Alternatively, the initialization vector may consist of at least 12 +cryptographically random bytes. For more information on constructing +initialization vectors for AES-GCM, refer to Section 8 of [NIST SP 800-38D][]. #### `aesGcmParams.name` @@ -1923,5 +1929,6 @@ added: v15.0.0 [JSON Web Key]: https://tools.ietf.org/html/rfc7517 [Key usages]: #cryptokeyusages +[NIST SP 800-38D]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf [RFC 4122]: https://www.rfc-editor.org/rfc/rfc4122.txt [Web Crypto API]: https://www.w3.org/TR/WebCryptoAPI/ diff --git a/doc/api/webstreams.md b/doc/api/webstreams.md index cde2addc7d09fa..93effbdf0e4f9d 100644 --- a/doc/api/webstreams.md +++ b/doc/api/webstreams.md @@ -2,25 +2,17 @@ -> Stability: 1 - Experimental - -An implementation of the [WHATWG Streams Standard][]. + -```mjs -import { - ReadableStream, - WritableStream, - TransformStream, -} from 'node:stream/web'; -``` +> Stability: 1 - Experimental. -```cjs -const { - ReadableStream, - WritableStream, - TransformStream, -} = require('stream/web'); -``` +An implementation of the [WHATWG Streams Standard][]. ## Overview @@ -29,7 +21,7 @@ streaming data. It is similar to the Node.js [Streams][] API but emerged later and has become the "standard" API for streaming data across many JavaScript environments. -There are three primary types of objects +There are three primary types of objects: * `ReadableStream` - Represents a source of streaming data. * `WritableStream` - Represents a destination for streaming data. @@ -70,15 +62,15 @@ for await (const value of stream) ```cjs const { ReadableStream -} = require('stream/web'); +} = require('node:stream/web'); const { setInterval: every -} = require('timers/promises'); +} = require('node:timers/promises'); const { performance -} = require('perf_hooks'); +} = require('node:perf_hooks'); const SECOND = 1000; @@ -101,6 +93,10 @@ const stream = new ReadableStream({ #### `new ReadableStream([underlyingSource [, strategy]])` @@ -183,7 +179,7 @@ console.log(await reader.read()); ``` ```cjs -const { ReadableStream } = require('stream/web'); +const { ReadableStream } = require('node:stream/web'); const stream = new ReadableStream(); @@ -255,7 +251,7 @@ for await (const chunk of transformedStream) const { ReadableStream, TransformStream, -} = require('stream/web'); +} = require('node:stream/web'); const stream = new ReadableStream({ start(controller) { @@ -346,7 +342,7 @@ The {ReadableStream} object supports the async iterator protocol using `for await` syntax. ```mjs -import { Buffer } from 'buffer'; +import { Buffer } from 'node:buffer'; const stream = new ReadableStream(getSomeSource()); @@ -387,6 +383,10 @@ port2.postMessage(stream, [stream]); By default, calling `readableStream.getReader()` with no arguments @@ -454,6 +454,10 @@ Releases this reader's lock on the underlying {ReadableStream}. The `ReadableStreamBYOBReader` is an alternative consumer for @@ -573,7 +577,7 @@ available. Do not pass a pooled {Buffer} object instance in to this method. Pooled `Buffer` objects are created using `Buffer.allocUnsafe()`, -or `Buffer.from()`, or are often returned by various `fs` module +or `Buffer.from()`, or are often returned by various `node:fs` module callbacks. These types of `Buffer`s use a shared underlying {ArrayBuffer} object that contains all of the data from all of the pooled `Buffer` instances. When a `Buffer`, {TypedArray}, @@ -701,6 +705,10 @@ Signals an error that causes the {ReadableStream} to error and close. When using `ReadableByteStreamController` in byte-oriented @@ -747,6 +755,10 @@ added: v16.5.0 The `WritableStream` is a destination to which stream data is sent. @@ -863,6 +875,10 @@ port2.postMessage(stream, [stream]); #### `new WritableStreamDefaultWriter(stream)` @@ -950,6 +966,10 @@ Appends a new chunk of data to the {WritableStream}'s queue. The `WritableStreamDefaultController` manage's the {WritableStream}'s @@ -980,6 +1000,10 @@ with currently pending writes canceled. A `TransformStream` consists of a {ReadableStream} and a {WritableStream} that @@ -1082,6 +1106,10 @@ port2.postMessage(stream, [stream]); The `TransformStreamDefaultController` manages the internal state @@ -1132,6 +1160,10 @@ to be abruptly closed with an error. #### `new ByteLengthQueuingStrategy(options)` @@ -1165,6 +1197,10 @@ added: v16.5.0 #### `new CountQueuingStrategy(options)` @@ -1198,6 +1234,10 @@ added: v16.5.0 #### `new TextEncoderStream()` @@ -1238,6 +1278,10 @@ added: v16.6.0 #### `new TextDecoderStream([encoding[, options]])` @@ -1309,6 +1353,10 @@ added: v16.6.0 #### `new CompressionStream(format)` @@ -1339,6 +1387,10 @@ added: v17.0.0 #### `new DecompressionStream(format)` @@ -1393,7 +1445,7 @@ const { buffer, json, text, -} = require('stream/consumers'); +} = require('node:stream/consumers'); ``` #### `streamConsumers.arrayBuffer(stream)` diff --git a/doc/api/worker_threads.md b/doc/api/worker_threads.md index 3e8158470d432f..bbb35aa3ca5410 100644 --- a/doc/api/worker_threads.md +++ b/doc/api/worker_threads.md @@ -6,11 +6,11 @@ -The `worker_threads` module enables the use of threads that execute JavaScript -in parallel. To access it: +The `node:worker_threads` module enables the use of threads that execute +JavaScript in parallel. To access it: ```js -const worker = require('worker_threads'); +const worker = require('node:worker_threads'); ``` Workers (threads) are useful for performing CPU-intensive JavaScript operations. @@ -24,7 +24,7 @@ instances. ```js const { Worker, isMainThread, parentPort, workerData -} = require('worker_threads'); +} = require('node:worker_threads'); if (isMainThread) { module.exports = function parseJSAsync(script) { @@ -88,7 +88,7 @@ const { isMainThread, setEnvironmentData, getEnvironmentData, -} = require('worker_threads'); +} = require('node:worker_threads'); if (isMainThread) { setEnvironmentData('Hello', 'World!'); @@ -109,7 +109,7 @@ added: v10.5.0 Is `true` if this code is not running inside of a [`Worker`][] thread. ```js -const { Worker, isMainThread } = require('worker_threads'); +const { Worker, isMainThread } = require('node:worker_threads'); if (isMainThread) { // This re-loads the current file inside a Worker instance. @@ -139,7 +139,7 @@ For example, Node.js marks the `ArrayBuffer`s it uses for its This operation cannot be undone. ```js -const { MessageChannel, markAsUntransferable } = require('worker_threads'); +const { MessageChannel, markAsUntransferable } = require('node:worker_threads'); const pooledBuffer = new ArrayBuffer(8); const typedArray1 = new Uint8Array(pooledBuffer); @@ -202,7 +202,7 @@ using `worker.postMessage()` are available in this thread using `parentPort.on('message')`. ```js -const { Worker, isMainThread, parentPort } = require('worker_threads'); +const { Worker, isMainThread, parentPort } = require('node:worker_threads'); if (isMainThread) { const worker = new Worker(__filename); @@ -238,7 +238,7 @@ that contains the message payload, corresponding to the oldest message in the `MessagePort`’s queue. ```js -const { MessageChannel, receiveMessageOnPort } = require('worker_threads'); +const { MessageChannel, receiveMessageOnPort } = require('node:worker_threads'); const { port1, port2 } = new MessageChannel(); port1.postMessage({ hello: 'world' }); @@ -284,7 +284,7 @@ constructor, to indicate that the current thread and the Worker thread should share read and write access to the same set of environment variables. ```js -const { Worker, SHARE_ENV } = require('worker_threads'); +const { Worker, SHARE_ENV } = require('node:worker_threads'); new Worker('process.env.SET_IN_WORKER = "foo"', { eval: true, env: SHARE_ENV }) .on('exit', () => { console.log(process.env.SET_IN_WORKER); // Prints 'foo'. @@ -338,7 +338,7 @@ The data is cloned as if using [`postMessage()`][`port.postMessage()`], according to the [HTML structured clone algorithm][]. ```js -const { Worker, isMainThread, workerData } = require('worker_threads'); +const { Worker, isMainThread, workerData } = require('node:worker_threads'); if (isMainThread) { const worker = new Worker(__filename, { workerData: 'Hello, world!' }); @@ -352,7 +352,7 @@ if (isMainThread) { @@ -367,7 +367,7 @@ const { isMainThread, BroadcastChannel, Worker -} = require('worker_threads'); +} = require('node:worker_threads'); const bc = new BroadcastChannel('hello'); @@ -462,7 +462,7 @@ yields an object with `port1` and `port2` properties, which refer to linked [`MessagePort`][] instances. ```js -const { MessageChannel } = require('worker_threads'); +const { MessageChannel } = require('node:worker_threads'); const { port1, port2 } = new MessageChannel(); port1.on('message', (message) => console.log('received', message)); @@ -501,7 +501,7 @@ The `'close'` event is emitted once either side of the channel has been disconnected. ```js -const { MessageChannel } = require('worker_threads'); +const { MessageChannel } = require('node:worker_threads'); const { port1, port2 } = new MessageChannel(); // Prints: @@ -618,7 +618,7 @@ In particular, the significant differences to `JSON` are: * {X509Certificate}s. ```js -const { MessageChannel } = require('worker_threads'); +const { MessageChannel } = require('node:worker_threads'); const { port1, port2 } = new MessageChannel(); port1.on('message', (message) => console.log(message)); @@ -643,7 +643,7 @@ from either thread. They cannot be listed in `transferList`. `transferList`; in that case, the underlying memory is copied rather than moved. ```js -const { MessageChannel } = require('worker_threads'); +const { MessageChannel } = require('node:worker_threads'); const { port1, port2 } = new MessageChannel(); port1.on('message', (message) => console.log(message)); @@ -670,7 +670,7 @@ The message object is cloned immediately, and can be modified after posting without having side effects. For more information on the serialization and deserialization mechanisms -behind this API, see the [serialization API of the `v8` module][v8.serdes]. +behind this API, see the [serialization API of the `node:v8` module][v8.serdes]. #### Considerations when transferring TypedArrays and Buffers @@ -822,8 +822,8 @@ Notable differences inside a Worker environment are: * The [`process.stdin`][], [`process.stdout`][] and [`process.stderr`][] may be redirected by the parent thread. -* The [`require('worker_threads').isMainThread`][] property is set to `false`. -* The [`require('worker_threads').parentPort`][] message port is available. +* The [`require('node:worker_threads').isMainThread`][] property is set to `false`. +* The [`require('node:worker_threads').parentPort`][] message port is available. * [`process.exit()`][] does not stop the whole program, just the single thread, and [`process.abort()`][] is not available. * [`process.chdir()`][] and `process` methods that set group or user ids @@ -844,11 +844,11 @@ Notable differences inside a Worker environment are: Creating `Worker` instances inside of other `Worker`s is possible. -Like [Web Workers][] and the [`cluster` module][], two-way communication can be -achieved through inter-thread message passing. Internally, a `Worker` has a -built-in pair of [`MessagePort`][]s that are already associated with each other -when the `Worker` is created. While the `MessagePort` object on the parent side -is not directly exposed, its functionalities are exposed through +Like [Web Workers][] and the [`node:cluster` module][], two-way communication +can be achieved through inter-thread message passing. Internally, a `Worker` has +a built-in pair of [`MessagePort`][]s that are already associated with each +other when the `Worker` is created. While the `MessagePort` object on the parent +side is not directly exposed, its functionalities are exposed through [`worker.postMessage()`][] and the [`worker.on('message')`][] event on the `Worker` object for the parent thread. @@ -863,10 +863,10 @@ and what kind of JavaScript values can be successfully transported through the thread barrier. ```js -const assert = require('assert'); +const assert = require('node:assert'); const { Worker, MessageChannel, MessagePort, isMainThread, parentPort -} = require('worker_threads'); +} = require('node:worker_threads'); if (isMainThread) { const worker = new Worker(__filename); const subChannel = new MessageChannel(); @@ -957,7 +957,7 @@ changes: * `stderr` {boolean} If this is set to `true`, then `worker.stderr` is not automatically piped through to `process.stderr` in the parent. * `workerData` {any} Any JavaScript value that is cloned and made - available as [`require('worker_threads').workerData`][]. The cloning + available as [`require('node:worker_threads').workerData`][]. The cloning occurs as described in the [HTML structured clone algorithm][], and an error is thrown if the object cannot be cloned (e.g. because it contains `function`s). @@ -1019,7 +1019,7 @@ added: v10.5.0 * `value` {any} The transmitted value The `'message'` event is emitted when the worker thread has invoked -[`require('worker_threads').parentPort.postMessage()`][]. +[`require('node:worker_threads').parentPort.postMessage()`][]. See the [`port.on('message')`][] event for more details. All messages sent from the worker thread are emitted before the @@ -1107,7 +1107,7 @@ lifetime never accumulates any `idle` time, but is still be able to process messages. ```js -const { Worker, isMainThread, parentPort } = require('worker_threads'); +const { Worker, isMainThread, parentPort } = require('node:worker_threads'); if (isMainThread) { const worker = new Worker(__filename); @@ -1141,7 +1141,7 @@ added: v10.5.0 * `transferList` {Object\[]} Send a message to the worker that is received via -[`require('worker_threads').parentPort.on('message')`][]. +[`require('node:worker_threads').parentPort.on('message')`][]. See [`port.postMessage()`][] for more details. ### `worker.ref()` @@ -1241,7 +1241,7 @@ added: v10.5.0 * {integer} An integer identifier for the referenced thread. Inside the worker thread, -it is available as [`require('worker_threads').threadId`][]. +it is available as [`require('node:worker_threads').threadId`][]. This value is unique for each `Worker` instance inside a single process. ### `worker.unref()` @@ -1286,7 +1286,7 @@ if (isMainThread) { const { Worker, isMainThread, -} = require('worker_threads'); +} = require('node:worker_threads'); if (isMainThread) { new Worker(__filename); @@ -1330,11 +1330,11 @@ thread spawned will spawn another until the application crashes. [`WebAssembly.Module`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Module [`Worker constructor options`]: #new-workerfilename-options [`Worker`]: #class-worker -[`cluster` module]: cluster.md [`data:` URL]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs [`fs.close()`]: fs.md#fsclosefd-callback [`fs.open()`]: fs.md#fsopenpath-flags-mode-callback [`markAsUntransferable()`]: #workermarkasuntransferableobject +[`node:cluster` module]: cluster.md [`perf_hooks.performance`]: perf_hooks.md#perf_hooksperformance [`perf_hooks` `eventLoopUtilization()`]: perf_hooks.md#performanceeventlooputilizationutilization1-utilization2 [`port.on('message')`]: #event-message @@ -1349,12 +1349,12 @@ thread spawned will spawn another until the application crashes. [`process.stdin`]: process.md#processstdin [`process.stdout`]: process.md#processstdout [`process.title`]: process.md#processtitle -[`require('worker_threads').isMainThread`]: #workerismainthread -[`require('worker_threads').parentPort.on('message')`]: #event-message -[`require('worker_threads').parentPort.postMessage()`]: #workerpostmessagevalue-transferlist -[`require('worker_threads').parentPort`]: #workerparentport -[`require('worker_threads').threadId`]: #workerthreadid -[`require('worker_threads').workerData`]: #workerworkerdata +[`require('node:worker_threads').isMainThread`]: #workerismainthread +[`require('node:worker_threads').parentPort.on('message')`]: #event-message +[`require('node:worker_threads').parentPort.postMessage()`]: #workerpostmessagevalue-transferlist +[`require('node:worker_threads').parentPort`]: #workerparentport +[`require('node:worker_threads').threadId`]: #workerthreadid +[`require('node:worker_threads').workerData`]: #workerworkerdata [`trace_events`]: tracing.md [`v8.getHeapSnapshot()`]: v8.md#v8getheapsnapshot [`vm`]: vm.md diff --git a/doc/api/zlib.md b/doc/api/zlib.md index 77e19cb4d5cf22..ee54973e5d366a 100644 --- a/doc/api/zlib.md +++ b/doc/api/zlib.md @@ -6,13 +6,13 @@ -The `zlib` module provides compression functionality implemented using Gzip, -Deflate/Inflate, and Brotli. +The `node:zlib` module provides compression functionality implemented using +Gzip, Deflate/Inflate, and Brotli. To access it: ```js -const zlib = require('zlib'); +const zlib = require('node:zlib'); ``` Compression and decompression are built around the Node.js [Streams API][]. @@ -22,12 +22,12 @@ piping the source stream through a `zlib` `Transform` stream into a destination stream: ```js -const { createGzip } = require('zlib'); -const { pipeline } = require('stream'); +const { createGzip } = require('node:zlib'); +const { pipeline } = require('node:stream'); const { createReadStream, createWriteStream -} = require('fs'); +} = require('node:fs'); const gzip = createGzip(); const source = createReadStream('input.txt'); @@ -42,7 +42,7 @@ pipeline(source, gzip, destination, (err) => { // Or, Promisified -const { promisify } = require('util'); +const { promisify } = require('node:util'); const pipe = promisify(pipeline); async function do_gzip(input, output) { @@ -62,7 +62,7 @@ do_gzip('input.txt', 'input.txt.gz') It is also possible to compress or decompress data in a single step: ```js -const { deflate, unzip } = require('zlib'); +const { deflate, unzip } = require('node:zlib'); const input = '.................................'; deflate(input, (err, buffer) => { @@ -84,7 +84,7 @@ unzip(buffer, (err, buffer) => { // Or, Promisified -const { promisify } = require('util'); +const { promisify } = require('node:util'); const do_unzip = promisify(unzip); do_unzip(buffer) @@ -105,7 +105,7 @@ Creating and using a large number of zlib objects simultaneously can cause significant memory fragmentation. ```js -const zlib = require('zlib'); +const zlib = require('node:zlib'); const payload = Buffer.from('This is some data'); @@ -117,14 +117,14 @@ for (let i = 0; i < 30000; ++i) { In the preceding example, 30,000 deflate instances are created concurrently. Because of how some operating systems handle memory allocation and -deallocation, this may lead to to significant memory fragmentation. +deallocation, this may lead to significant memory fragmentation. It is strongly recommended that the results of compression operations be cached to avoid duplication of effort. ## Compressing HTTP requests and responses -The `zlib` module can be used to implement support for the `gzip`, `deflate` +The `node:zlib` module can be used to implement support for the `gzip`, `deflate` and `br` content-encoding mechanisms defined by [HTTP](https://tools.ietf.org/html/rfc7230#section-4.2). @@ -140,10 +140,10 @@ tradeoffs involved in `zlib` usage. ```js // Client request example -const zlib = require('zlib'); -const http = require('http'); -const fs = require('fs'); -const { pipeline } = require('stream'); +const zlib = require('node:zlib'); +const http = require('node:http'); +const fs = require('node:fs'); +const { pipeline } = require('node:stream'); const request = http.get({ host: 'example.com', path: '/', @@ -181,10 +181,10 @@ request.on('response', (response) => { // server example // Running a gzip operation on every request is quite expensive. // It would be much more efficient to cache the compressed buffer. -const zlib = require('zlib'); -const http = require('http'); -const fs = require('fs'); -const { pipeline } = require('stream'); +const zlib = require('node:zlib'); +const http = require('node:http'); +const fs = require('node:fs'); +const { pipeline } = require('node:stream'); http.createServer((request, response) => { const raw = fs.createReadStream('index.html'); @@ -319,9 +319,9 @@ In the following example, `flush()` is used to write a compressed partial HTTP response to the client: ```js -const zlib = require('zlib'); -const http = require('http'); -const { pipeline } = require('stream'); +const zlib = require('node:zlib'); +const http = require('node:http'); +const { pipeline } = require('node:stream'); http.createServer((request, response) => { // For the sake of simplicity, the Accept-Encoding checks are omitted. @@ -365,14 +365,14 @@ added: v0.5.8 ### zlib constants All of the constants defined in `zlib.h` are also defined on -`require('zlib').constants`. In the normal course of operations, it will not be -necessary to use these constants. They are documented so that their presence is -not surprising. This section is taken almost directly from the +`require('node:zlib').constants`. In the normal course of operations, it will +not be necessary to use these constants. They are documented so that their +presence is not surprising. This section is taken almost directly from the [zlib documentation][]. -Previously, the constants were available directly from `require('zlib')`, for -instance `zlib.Z_NO_FLUSH`. Accessing the constants directly from the module is -currently still possible but is deprecated. +Previously, the constants were available directly from `require('node:zlib')`, +for instance `zlib.Z_NO_FLUSH`. Accessing the constants directly from the module +is currently still possible but is deprecated. Allowed flush values. @@ -678,11 +678,11 @@ changes: description: This class was renamed from `Zlib` to `ZlibBase`. --> -Not exported by the `zlib` module. It is documented here because it is the base -class of the compressor/decompressor classes. +Not exported by the `node:zlib` module. It is documented here because it is the +base class of the compressor/decompressor classes. -This class inherits from [`stream.Transform`][], allowing `zlib` objects to be -used in pipes and similar stream operations. +This class inherits from [`stream.Transform`][], allowing `node:zlib` objects to +be used in pipes and similar stream operations. ### `zlib.bytesRead` diff --git a/doc/api_assets/README.md b/doc/api_assets/README.md index 07262bba4ce01a..e2c1d90cd0953f 100644 --- a/doc/api_assets/README.md +++ b/doc/api_assets/README.md @@ -1,5 +1,9 @@ # API Reference Document Assets +## api.js + +The main script for API reference documents. + ## hljs.css The syntax theme for code snippets in API reference documents. diff --git a/doc/api_assets/api.js b/doc/api_assets/api.js new file mode 100644 index 00000000000000..8854182c4c56f0 --- /dev/null +++ b/doc/api_assets/api.js @@ -0,0 +1,161 @@ +'use strict'; + +{ + function setupTheme() { + const kCustomPreference = 'customDarkTheme'; + const userSettings = sessionStorage.getItem(kCustomPreference); + const themeToggleButton = document.getElementById('theme-toggle-btn'); + + if (userSettings === null && window.matchMedia) { + const mq = window.matchMedia('(prefers-color-scheme: dark)'); + + if ('onchange' in mq) { + function mqChangeListener(e) { + document.documentElement.classList.toggle('dark-mode', e.matches); + } + mq.addEventListener('change', mqChangeListener); + if (themeToggleButton) { + themeToggleButton.addEventListener( + 'click', + function() { + mq.removeEventListener('change', mqChangeListener); + }, + { once: true } + ); + } + } + + if (mq.matches) { + document.documentElement.classList.add('dark-mode'); + } + } else if (userSettings === 'true') { + document.documentElement.classList.add('dark-mode'); + } + + if (themeToggleButton) { + themeToggleButton.hidden = false; + themeToggleButton.addEventListener('click', function() { + sessionStorage.setItem( + kCustomPreference, + document.documentElement.classList.toggle('dark-mode') + ); + }); + } + } + + function setupPickers() { + function closeAllPickers() { + for (const picker of pickers) { + picker.parentNode.classList.remove('expanded'); + } + + window.removeEventListener('click', closeAllPickers); + window.removeEventListener('keydown', onKeyDown); + } + + function onKeyDown(e) { + if (e.key === 'Escape') { + closeAllPickers(); + } + } + + const pickers = document.querySelectorAll('.picker-header > a'); + + for (const picker of pickers) { + const parentNode = picker.parentNode; + + picker.addEventListener('click', function(e) { + e.preventDefault(); + + /* + closeAllPickers as window event trigger already closed all the pickers, + if it already closed there is nothing else to do here + */ + if (parentNode.classList.contains('expanded')) { + return; + } + + /* + In the next frame reopen the picker if needed and also setup events + to close pickers if needed. + */ + + requestAnimationFrame(function() { + parentNode.classList.add('expanded'); + window.addEventListener('click', closeAllPickers); + window.addEventListener('keydown', onKeyDown); + }); + }); + } + } + + function setupStickyHeaders() { + const header = document.querySelector('.header'); + let ignoreNextIntersection = false; + + new IntersectionObserver( + function(e) { + const currentStatus = header.classList.contains('is-pinned'); + const newStatus = e[0].intersectionRatio < 1; + + // Same status, do nothing + if (currentStatus === newStatus) { + return; + } else if (ignoreNextIntersection) { + ignoreNextIntersection = false; + return; + } + + /* + To avoid flickering, ignore the next changes event that is triggered + as the visible elements in the header change once we pin it. + + The timer is reset anyway after few milliseconds. + */ + ignoreNextIntersection = true; + setTimeout(function() { + ignoreNextIntersection = false; + }, 50); + + header.classList.toggle('is-pinned', newStatus); + }, + { threshold: [1] } + ).observe(header); + } + + function setupAltDocsLink() { + const linkWrapper = document.getElementById('alt-docs'); + + function updateHashes() { + for (const link of linkWrapper.querySelectorAll('a')) { + link.hash = location.hash; + } + } + + addEventListener('hashchange', updateHashes); + updateHashes(); + } + + function bootstrap() { + // Check if we have JavaScript support. + document.documentElement.classList.add('has-js'); + + // Restore user mode preferences. + setupTheme(); + + // Handle pickers with click/taps rather than hovers. + setupPickers(); + + // Track when the header is in sticky position. + setupStickyHeaders(); + + // Make link to other versions of the doc open to the same hash target (if it exists). + setupAltDocsLink(); + } + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', bootstrap, { once: true }); + } else { + bootstrap(); + } +} diff --git a/doc/api_assets/style.css b/doc/api_assets/style.css index 9ae45a09c84ca8..c567fbed1c23d4 100644 --- a/doc/api_assets/style.css +++ b/doc/api_assets/style.css @@ -40,6 +40,17 @@ --color-text-secondary: var(--green2); } +h4 :target, +h5 :target { + scroll-margin-top: 55px; +} + +@supports not (content-visibility: auto) { + h3 :target { + scroll-margin-top: 55px; + } +} + .dark-mode { --background-color-highlight: var(--black2); --color-critical: var(--red4); @@ -162,7 +173,7 @@ em code { margin-bottom: 1rem; } -#gtoc ul { +#gtoc > ul { list-style: none; margin-left: 0; line-height: 1.5rem; @@ -172,42 +183,77 @@ em code { color: var(--color-critical); } -li.version-picker { +li.picker-header { position: relative; } -li.version-picker:hover > a { +li.picker-header .collapsed-arrow, li.picker-header .expanded-arrow { + width: 1.5ch; + height: 1.5em; +} + +li.picker-header .collapsed-arrow { + display: inline-block; +} + +li.picker-header .expanded-arrow { + display: none; +} + +li.picker-header.expanded .collapsed-arrow, +:root:not(.has-js) li.picker-header:hover .collapsed-arrow { + display: none; +} + +li.picker-header.expanded .expanded-arrow, +:root:not(.has-js) li.picker-header:hover .expanded-arrow { + display: inline-block; +} + +li.picker-header.expanded > a, +:root:not(.has-js) li.picker-header:hover > a { border-radius: 2px 2px 0 0; } -li.version-picker:hover > ol { +li.picker-header.expanded > .picker, +:root:not(.has-js) li.picker-header:hover > .picker { display: block; z-index: 1; } -li.version-picker a span { +li.picker-header a span { font-size: .7rem; } -ol.version-picker { +.picker { background-color: var(--color-fill-app); border: 1px solid var(--color-brand-secondary); border-radius: 0 0 2px 2px; display: none; list-style: none; position: absolute; - right: 0; + left: 0; top: 100%; - width: 100%; + width: max-content; + min-width: min(300px, 75vw); + max-width: 75vw; + max-height: min(600px, 60vh); + overflow-y: auto; } -#gtoc ol.version-picker li { +.picker > ul, .picker > ol { + list-style: none; + margin-left: 0; + line-height: 1.5rem; +} + +.picker li { display: block; border-right: 0; margin-right: 0; } -ol.version-picker li a { +.picker li a { border-radius: 0; display: block; margin: 0; @@ -215,17 +261,32 @@ ol.version-picker li a { padding-left: 1rem; } -ol.version-picker li:last-child a { +.picker li a.active, +.picker li a.active:hover, +.picker li a.active:focus { + font-weight: 700; +} + +.picker li:last-child a { border-bottom-right-radius: 1px; border-bottom-left-radius: 1px; } +.gtoc-picker-header { + display: none; +} + .line { width: calc(100% - 1rem); display: block; padding-bottom: 1px; } +.picker .line { + margin: 0; + width: 100%; +} + .api_stability { color: var(--white) !important; margin: 0 0 1rem; @@ -506,6 +567,41 @@ hr { margin-top: .666rem; } +.toc ul { + margin: 0 +} + +.toc li a::before { + content: "■"; + color: var(--color-text-primary); + padding-right: 1em; + font-size: 0.9em; +} + +.toc li a:hover::before { + color: var(--white); +} + +.toc ul ul a { + padding-left: 1rem; +} + +.toc ul ul ul a { + padding-left: 2rem; +} + +.toc ul ul ul ul a { + padding-left: 3rem; +} + +.toc ul ul ul ul ul a { + padding-left: 4rem; +} + +.toc ul ul ul ul ul ul a { + padding-left: 5rem; +} + #toc .stability_0::after { background-color: var(--red2); color: var(--white); @@ -718,10 +814,50 @@ kbd { } } +.header { + position: sticky; + top: -1px; + z-index: 1; + padding-top: 1rem; + background-color: var(--color-fill-app); +} + +@media not screen, (max-width: 600px) { + .header { + position: relative; + top: 0; + } +} + +@media not screen, (max-height: 1000px) { + :root:not(.has-js) .header { + position: relative; + top: 0; + } +} + +.header .pinned-header { + display: none; + margin-right: 0.4rem; + font-weight: 700; +} + +.header.is-pinned .header-container { + display: none; +} + +.header.is-pinned .pinned-header { + display: inline; +} + +.header.is-pinned #gtoc { + margin: 0; +} + .header-container { display: flex; align-items: center; - margin: 1.5rem 0 1rem; + margin-bottom: 1rem; justify-content: space-between; } @@ -735,7 +871,7 @@ kbd { outline: var(--brand3) dotted 2px; } -@media only screen and (min-width: 577px) { +@media only screen and (min-width: 601px) { #gtoc > ul > li { display: inline; border-right: 1px currentColor solid; @@ -748,6 +884,18 @@ kbd { margin-right: 0; padding-right: 0; } + + .header #gtoc > ul > li.pinned-header { + display: none; + } + + .header.is-pinned #gtoc > ul > li.pinned-header { + display: inline; + } + + #gtoc > ul > li.gtoc-picker-header { + display: none; + } } @media only screen and (max-width: 1024px) { @@ -764,6 +912,10 @@ kbd { #column2 { display: none; } + + #gtoc > ul > li.gtoc-picker-header { + display: inline; + } } .icon { diff --git a/doc/changelogs/CHANGELOG_IOJS.md b/doc/changelogs/CHANGELOG_IOJS.md index 6d240d516c74f7..87838efc613020 100644 --- a/doc/changelogs/CHANGELOG_IOJS.md +++ b/doc/changelogs/CHANGELOG_IOJS.md @@ -62,6 +62,7 @@
18 (Current) 17 (Current) 16 (LTS) 14 (LTS)
-17.6.0
+18.0.0
+
+17.8.0
+17.7.2
+17.7.1
+17.7.0
+17.6.0
17.5.0
17.4.0
17.3.1
@@ -44,7 +53,9 @@ release. 17.0.0
-16.14.0
+16.14.2
+16.14.1
+16.14.0
16.13.2
16.13.1
16.13.0
@@ -69,7 +80,8 @@ release. 16.0.0
-14.19.0
+14.19.1
+14.19.0
14.18.3
14.18.2
14.18.1
@@ -108,7 +120,9 @@ release. 14.0.0
-12.22.10
+12.22.12
+12.22.11
+12.22.10
12.22.9
12.22.8
12.22.7
diff --git a/LICENSE b/LICENSE index 306e57a23decdf..dad0dccc0cc56d 100644 --- a/LICENSE +++ b/LICENSE @@ -109,9 +109,22 @@ The externally maintained libraries used by Node.js are: - ICU, located at deps/icu-small, is licensed as follows: """ - COPYRIGHT AND PERMISSION NOTICE (ICU 58 and later) + UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE - Copyright © 1991-2020 Unicode, Inc. All rights reserved. + See Terms of Use + for definitions of Unicode Inc.’s Data Files and Software. + + NOTICE TO USER: Carefully read the following legal agreement. + BY DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S + DATA FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), + YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE + TERMS AND CONDITIONS OF THIS AGREEMENT. + IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE + THE DATA FILES OR SOFTWARE. + + COPYRIGHT AND PERMISSION NOTICE + + Copyright © 1991-2022 Unicode, Inc. All rights reserved. Distributed under the Terms of Use in https://www.unicode.org/copyright.html. Permission is hereby granted, free of charge, to any person obtaining @@ -143,7 +156,7 @@ The externally maintained libraries used by Node.js are: use or other dealings in these Data Files or Software without prior written authorization of the copyright holder. - --------------------- + ---------------------------------------------------------------------- Third-Party Software Licenses @@ -151,7 +164,9 @@ The externally maintained libraries used by Node.js are: terms for licensed third-party software components included within ICU libraries. - 1. ICU License - ICU 1.8.1 to ICU 57.1 + ---------------------------------------------------------------------- + + ICU License - ICU 1.8.1 to ICU 57.1 COPYRIGHT AND PERMISSION NOTICE @@ -186,7 +201,9 @@ The externally maintained libraries used by Node.js are: All trademarks and registered trademarks mentioned herein are the property of their respective owners. - 2. Chinese/Japanese Word Break Dictionary Data (cjdict.txt) + ---------------------------------------------------------------------- + + Chinese/Japanese Word Break Dictionary Data (cjdict.txt) # The Google Chrome software developed by Google is licensed under # the BSD license. Other software included in this distribution is @@ -390,7 +407,9 @@ The externally maintained libraries used by Node.js are: # # ---------------COPYING.ipadic-----END---------------------------------- - 3. Lao Word Break Dictionary Data (laodict.txt) + ---------------------------------------------------------------------- + + Lao Word Break Dictionary Data (laodict.txt) # Copyright (C) 2016 and later: Unicode, Inc. and others. # License & terms of use: http://www.unicode.org/copyright.html @@ -430,7 +449,9 @@ The externally maintained libraries used by Node.js are: # OF THE POSSIBILITY OF SUCH DAMAGE. # -------------------------------------------------------------------------- - 4. Burmese Word Break Dictionary Data (burmesedict.txt) + ---------------------------------------------------------------------- + + Burmese Word Break Dictionary Data (burmesedict.txt) # Copyright (c) 2014 International Business Machines Corporation # and others. All Rights Reserved. @@ -470,7 +491,9 @@ The externally maintained libraries used by Node.js are: # SUCH DAMAGE. # -------------------------------------------------------------------------- - 5. Time Zone Database + ---------------------------------------------------------------------- + + Time Zone Database ICU uses the public domain data and code derived from Time Zone Database for its time zone support. The ownership of the TZ database @@ -493,7 +516,9 @@ The externally maintained libraries used by Node.js are: # making a contribution to the database or code waives all rights to # future claims in that contribution or in the TZ Database. - 6. Google double-conversion + ---------------------------------------------------------------------- + + Google double-conversion Copyright 2006-2011, the V8 project authors. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -521,6 +546,83 @@ The externally maintained libraries used by Node.js are: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + ---------------------------------------------------------------------- + + File: aclocal.m4 (only for ICU4C) + Section: pkg.m4 - Macros to locate and utilise pkg-config. + + Copyright © 2004 Scott James Remnant . + Copyright © 2012-2015 Dan Nicholson + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. + + As a special exception to the GNU General Public License, if you + distribute this file as part of a program that contains a + configuration script generated by Autoconf, you may include it under + the same distribution terms that you use for the rest of that + program. + + (The condition for the exception is fulfilled because + ICU4C includes a configuration script generated by Autoconf, + namely the `configure` script.) + + ---------------------------------------------------------------------- + + File: config.guess (only for ICU4C) + + This file is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + + As a special exception to the GNU General Public License, if you + distribute this file as part of a program that contains a + configuration script generated by Autoconf, you may include it under + the same distribution terms that you use for the rest of that + program. This Exception is an additional permission under section 7 + of the GNU General Public License, version 3 ("GPLv3"). + + (The condition for the exception is fulfilled because + ICU4C includes a configuration script generated by Autoconf, + namely the `configure` script.) + + ---------------------------------------------------------------------- + + File: install-sh (only for ICU4C) + + Copyright 1991 by the Massachusetts Institute of Technology + + Permission to use, copy, modify, distribute, and sell this software and its + documentation for any purpose is hereby granted without fee, provided that + the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation, and that the name of M.I.T. not be used in advertising or + publicity pertaining to distribution of the software without specific, + written prior permission. M.I.T. makes no representations about the + suitability of this software for any purpose. It is provided "as is" + without express or implied warranty. """ - libuv, located at deps/uv, is licensed as follows: diff --git a/Makefile b/Makefile index 8b1b1f16014cd9..a6549a8474c215 100644 --- a/Makefile +++ b/Makefile @@ -679,7 +679,7 @@ ifneq ("","$(wildcard deps/v8/tools/run-tests.py)") # Related CI job: node-test-commit-v8-linux test-v8: v8 ## Runs the V8 test suite on deps/v8. export PATH="$(NO_BIN_OVERRIDE_PATH)" && \ - deps/v8/tools/run-tests.py --gn --arch=$(V8_ARCH) $(V8_TEST_OPTIONS) \ + $(PYTHON) deps/v8/tools/run-tests.py --gn --arch=$(V8_ARCH) $(V8_TEST_OPTIONS) \ mjsunit cctest debugger inspector message preparser \ $(TAP_V8) $(info Testing hash seed) @@ -687,13 +687,13 @@ test-v8: v8 ## Runs the V8 test suite on deps/v8. test-v8-intl: v8 export PATH="$(NO_BIN_OVERRIDE_PATH)" && \ - deps/v8/tools/run-tests.py --gn --arch=$(V8_ARCH) \ + $(PYTHON) deps/v8/tools/run-tests.py --gn --arch=$(V8_ARCH) \ intl \ $(TAP_V8_INTL) test-v8-benchmarks: v8 export PATH="$(NO_BIN_OVERRIDE_PATH)" && \ - deps/v8/tools/run-tests.py --gn --arch=$(V8_ARCH) \ + $(PYTHON) deps/v8/tools/run-tests.py --gn --arch=$(V8_ARCH) \ benchmarks \ $(TAP_V8_BENCHMARKS) @@ -1119,13 +1119,15 @@ pkg: $(PKG) .PHONY: corepack-update corepack-update: - rm -rf /tmp/node-corepack-clone - git clone 'https://github.com/nodejs/corepack.git' /tmp/node-corepack-clone - cd /tmp/node-corepack-clone && yarn pack - rm -rf deps/corepack && mkdir -p deps/corepack - cd deps/corepack && tar xf /tmp/node-corepack-clone/package.tgz --strip-components=1 + mkdir -p /tmp/node-corepack + curl -qLo /tmp/node-corepack/package.tgz "$$(npm view corepack dist.tarball)" + + rm -rf deps/corepack && mkdir deps/corepack + cd deps/corepack && tar xf /tmp/node-corepack/package.tgz --strip-components=1 chmod +x deps/corepack/shims/* + node deps/corepack/dist/corepack.js --version + .PHONY: pkg-upload # Note: this is strictly for release builds on release machines only. pkg-upload: pkg diff --git a/README.md b/README.md index 35175d85d02443..b9d35c1afec6f6 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,4 @@ - - -

- - Node.js - -

+# Node.js Node.js is an open-source, cross-platform, JavaScript runtime environment. @@ -19,7 +9,7 @@ The Node.js project uses an [open governance model](./GOVERNANCE.md). The **This project has a [Code of Conduct][].** -# Table of contents +## Table of contents * [Support](#support) * [Release types](#release-types) @@ -61,7 +51,7 @@ Looking for help? Check out the * **Nightly**: Code from the Current branch built every 24-hours when there are changes. Use with caution. -Current and LTS releases follow [Semantic Versioning](https://semver.org). A +Current and LTS releases follow [semantic versioning](https://semver.org). A member of the Release Team [signs](#release-keys) each Current and LTS release. For more information, see the [Release README](https://github.com/nodejs/Release#readme). @@ -123,7 +113,7 @@ import the keys: $ gpg --keyserver hkps://keys.openpgp.org --recv-keys DD8F2338BAE7501E3DD5AC78C273792F7D83545D ``` -See the bottom of this README for a full script to import active release keys. +See [Release keys](#release-keys) for a script to import active release keys. Next, download the `SHASUMS256.txt.sig` for the release: @@ -190,8 +180,6 @@ For information about the governance of the Node.js project, see **Matteo Collina** <> (he/him) * [mhdawson](https://github.com/mhdawson) - **Michael Dawson** <> (he/him) -* [mmarchini](https://github.com/mmarchini) - - **Mary Marchini** <> (she/her) * [MylesBorins](https://github.com/MylesBorins) - **Myles Borins** <> (he/him) * [RaisinTen](https://github.com/RaisinTen) - @@ -237,6 +225,8 @@ For information about the governance of the Node.js project, see **Isaac Z. Schlueter** <> * [joshgav](https://github.com/joshgav) - **Josh Gavant** <> +* [mmarchini](https://github.com/mmarchini) - + **Mary Marchini** <> (she/her) * [mscdex](https://github.com/mscdex) - **Brian White** <> * [nebrius](https://github.com/nebrius) - @@ -272,8 +262,6 @@ For information about the governance of the Node.js project, see **Anna Henningsen** <> (she/her) * [aduh95](https://github.com/aduh95) - **Antoine du Hamel** <> (he/him) -* [ak239](https://github.com/ak239) - - **Aleksei Koziatinskii** <> * [antsmartian](https://github.com/antsmartian) - **Anto Aravinth** <> (he/him) * [apapirovski](https://github.com/apapirovski) - @@ -296,8 +284,6 @@ For information about the governance of the Node.js project, see **Tierney Cyren** <> (they/he) * [bnoordhuis](https://github.com/bnoordhuis) - **Ben Noordhuis** <> -* [boneskull](https://github.com/boneskull) - - **Christopher Hiller** <> (he/him) * [BridgeAR](https://github.com/BridgeAR) - **Ruben Bridgewater** <> (he/him) * [bzoz](https://github.com/bzoz) - @@ -342,8 +328,6 @@ For information about the governance of the Node.js project, see **Guy Bedford** <> (he/him) * [HarshithaKP](https://github.com/HarshithaKP) - **Harshitha K P** <> (she/her) -* [hashseed](https://github.com/hashseed) - - **Yang Guo** <> (he/him) * [himself65](https://github.com/himself65) - **Zeyu Yang** <> (he/him) * [hiroppy](https://github.com/hiroppy) - @@ -354,6 +338,8 @@ For information about the governance of the Node.js project, see **Fedor Indutny** <> * [JacksonTian](https://github.com/JacksonTian) - **Jackson Tian** <> +* [JakobJingleheimer](https://github.com/JakobJingleheimer) - + **Jacob Smith** <> (he/him) * [jasnell](https://github.com/jasnell) - **James M Snell** <> (he/him) * [jkrems](https://github.com/jkrems) - @@ -366,6 +352,8 @@ For information about the governance of the Node.js project, see **Juan José Arboleda** <> (he/him) * [JungMinu](https://github.com/JungMinu) - **Minwoo Jung** <> (he/him) +* [kuriyosh](https://github.com/kuriyosh) - + **Yoshiki Kurihara** <> (he/him) * [legendecas](https://github.com/legendecas) - **Chengzhong Wu** <> (he/him) * [Leko](https://github.com/Leko) - @@ -382,6 +370,8 @@ For information about the governance of the Node.js project, see **Akhil Marsonya** <> (he/him) * [mcollina](https://github.com/mcollina) - **Matteo Collina** <> (he/him) +* [meixg](https://github.com/meixg) - + **Xuguang Mei** <> (he/him) * [Mesteery](https://github.com/Mesteery) - **Mestery** <> (he/him) * [mhdawson](https://github.com/mhdawson) - @@ -390,8 +380,6 @@ For information about the governance of the Node.js project, see **Milad Fa** <> (he/him) * [mildsunrise](https://github.com/mildsunrise) - **Alba Mendez** <> (she/her) -* [mmarchini](https://github.com/mmarchini) - - **Mary Marchini** <> (she/her) * [mscdex](https://github.com/mscdex) - **Brian White** <> * [MylesBorins](https://github.com/MylesBorins) - @@ -406,6 +394,8 @@ For information about the governance of the Node.js project, see **Andrey Pechkurov** <> (he/him) * [Qard](https://github.com/Qard) - **Stephen Belanger** <> (he/him) +* [RafaelGSS](https://github.com/RafaelGSS) - + **Rafael Gonzaga** <> (he/him) * [RaisinTen](https://github.com/RaisinTen) - **Darshan Sen** <> (he/him) * [rexagod](https://github.com/rexagod) - @@ -426,6 +416,8 @@ For information about the governance of the Node.js project, see **Santiago Gimeno** <> * [shisama](https://github.com/shisama) - **Masashi Hirano** <> (he/him) +* [ShogunPanda](https://github.com/ShogunPanda) - + **Paolo Insogna** <> (he/him) * [srl295](https://github.com/srl295) - **Steven R Loomis** <> * [starkwang](https://github.com/starkwang) - @@ -466,6 +458,8 @@ For information about the governance of the Node.js project, see ### Collaborator emeriti +* [ak239](https://github.com/ak239) - + **Aleksei Koziatinskii** <> * [andrasq](https://github.com/andrasq) - **Andras** <> * [AnnaMag](https://github.com/AnnaMag) - @@ -476,6 +470,8 @@ For information about the governance of the Node.js project, see **Alexey Orlenko** <> (he/him) * [bmeurer](https://github.com/bmeurer) - **Benedikt Meurer** <> +* [boneskull](https://github.com/boneskull) - + **Christopher Hiller** <> (he/him) * [brendanashworth](https://github.com/brendanashworth) - **Brendan Ashworth** <> * [calvinmetcalf](https://github.com/calvinmetcalf) - @@ -508,6 +504,8 @@ For information about the governance of the Node.js project, see **Gibson Fahnestock** <> (he/him) * [glentiki](https://github.com/glentiki) - **Glen Keane** <> (he/him) +* [hashseed](https://github.com/hashseed) - + **Yang Guo** <> (he/him) * [iarna](https://github.com/iarna) - **Rebecca Turner** <> * [imran-iq](https://github.com/imran-iq) - @@ -554,6 +552,8 @@ For information about the governance of the Node.js project, see **Mikeal Rogers** <> * [misterdjules](https://github.com/misterdjules) - **Julien Gilli** <> +* [mmarchini](https://github.com/mmarchini) - + **Mary Marchini** <> (she/her) * [monsanto](https://github.com/monsanto) - **Christopher Monsanto** <> * [MoonBall](https://github.com/MoonBall) - @@ -651,7 +651,7 @@ maintaining the Node.js project. * [marsonya](https://github.com/marsonya) - **Akhil Marsonya** <> (he/him) * [meixg](https://github.com/meixg) - - **Xuguang Mei** <> (he/him) + **Xuguang Mei** <> (he/him) * [Mesteery](https://github.com/Mesteery) - **Mestery** <> (he/him) * [PoojaDurgad](https://github.com/PoojaDurgad) - @@ -667,6 +667,8 @@ Primary GPG keys for Node.js Releasers (some Releasers sign with subkeys): * **Beth Griggs** <> `4ED778F539E3634C779C87C6D7062848A1AB005C` +* **Bryan English** <> + `141F07595B7B3FFE74309A937405533BE57C7D57` * **Colin Ihrig** <> `94AE36675C464D64BAFA68DD7434390BDBE9B9C5` * **Danielle Adams** <> @@ -693,6 +695,7 @@ to sign releases): ```bash gpg --keyserver hkps://keys.openpgp.org --recv-keys 4ED778F539E3634C779C87C6D7062848A1AB005C +gpg --keyserver hkps://keys.openpgp.org --recv-keys 141F07595B7B3FFE74309A937405533BE57C7D57 gpg --keyserver hkps://keys.openpgp.org --recv-keys 94AE36675C464D64BAFA68DD7434390BDBE9B9C5 gpg --keyserver hkps://keys.openpgp.org --recv-keys 74F12602B6F1C4E913FAA37AD3A89613643B6201 gpg --keyserver hkps://keys.openpgp.org --recv-keys 71DCFD284A79C3B38668286BC97EC7A07EDE3FC1 @@ -705,8 +708,8 @@ gpg --keyserver hkps://keys.openpgp.org --recv-keys 108F52B48DB57BB0CC439B2997B0 gpg --keyserver hkps://keys.openpgp.org --recv-keys B9E2F5981AA6E0CD28160D9FF13993A75599653C ``` -See the section above on [Verifying binaries](#verifying-binaries) for how to -use these keys to verify a downloaded file. +See [Verifying binaries](#verifying-binaries) for how to use these keys to +verify a downloaded file.
diff --git a/SECURITY.md b/SECURITY.md index 8e5e3c4fe80815..b22301a1f1d556 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -22,14 +22,7 @@ the HackerOne platform. See for further details. ## Reporting a bug in a third party module Security bugs in third party modules should be reported to their respective -maintainers and should also be coordinated through the Node.js Ecosystem -Security Team via [HackerOne](https://hackerone.com/nodejs-ecosystem). - -Details regarding this process can be found in the -[Security Working Group repository](https://github.com/nodejs/security-wg/blob/HEAD/processes/third_party_vuln_process.md). - -Thank you for improving the security of Node.js and its ecosystem. Your efforts -and responsible disclosure are greatly appreciated and will be acknowledged. +maintainers. ## Disclosure policy diff --git a/benchmark/.eslintrc.yaml b/benchmark/.eslintrc.yaml index 6871299adece7b..aa014eec4e3627 100644 --- a/benchmark/.eslintrc.yaml +++ b/benchmark/.eslintrc.yaml @@ -5,5 +5,4 @@ env: es6: true rules: - no-var: error prefer-arrow-callback: error diff --git a/benchmark/crypto/webcrypto-digest.js b/benchmark/crypto/webcrypto-digest.js index 2d95f868f66f7c..4acd82878dacf2 100644 --- a/benchmark/crypto/webcrypto-digest.js +++ b/benchmark/crypto/webcrypto-digest.js @@ -3,11 +3,9 @@ const common = require('../common.js'); const { createHash, - webcrypto: { - subtle, - getRandomValues - } + webcrypto, } = require('crypto'); +const { subtle } = webcrypto; const bench = common.createBenchmark(main, { sync: ['createHash', 'subtle'], @@ -50,7 +48,7 @@ function measureSubtle(n, data, method) { } function main({ n, sync, data, method }) { - data = getRandomValues(Buffer.alloc(data)); + data = webcrypto.getRandomValues(Buffer.alloc(data)); switch (sync) { case 'createHash': return measureLegacy(n, data, method); case 'subtle': return measureSubtle(n, data, method); diff --git a/benchmark/fixtures/require-cachable.js b/benchmark/fixtures/require-cachable.js index 4afda3cda47675..85e3a81f4e9fb0 100644 --- a/benchmark/fixtures/require-cachable.js +++ b/benchmark/fixtures/require-cachable.js @@ -6,5 +6,5 @@ const { } = internalBinding('native_module'); for (const key of canBeRequired) { - require(key); + require(`node:${key}`); } diff --git a/benchmark/http/chunked.js b/benchmark/http/chunked.js index 9ae7bb7495f29a..ec86d0a1f65295 100644 --- a/benchmark/http/chunked.js +++ b/benchmark/http/chunked.js @@ -32,10 +32,11 @@ function main({ len, n, c, duration }) { send(n); }); - server.listen(common.PORT, () => { + server.listen(0, () => { bench.http({ connections: c, - duration + duration, + port: server.address().port, }, () => { server.close(); }); diff --git a/benchmark/http/client-request-body.js b/benchmark/http/client-request-body.js index 6e2323b3f07e8a..3d673d36b7bc63 100644 --- a/benchmark/http/client-request-body.js +++ b/benchmark/http/client-request-body.js @@ -32,7 +32,6 @@ function main({ dur, len, type, method }) { headers: { 'Connection': 'keep-alive', 'Transfer-Encoding': 'chunked' }, agent: new http.Agent({ maxSockets: 1 }), host: '127.0.0.1', - port: common.PORT, path: '/', method: 'POST' }; @@ -40,16 +39,17 @@ function main({ dur, len, type, method }) { const server = http.createServer((req, res) => { res.end(); }); - server.listen(options.port, options.host, () => { + server.listen(0, options.host, () => { setTimeout(done, dur * 1000); bench.start(); - pummel(); + pummel(server.address().port); }); - function pummel() { + function pummel(port) { + options.port = port; const req = http.request(options, (res) => { nreqs++; - pummel(); // Line up next request. + pummel(port); // Line up next request. res.resume(); }); if (method === 'write') { diff --git a/benchmark/http/end-vs-write-end.js b/benchmark/http/end-vs-write-end.js index 60174ef3adf4f2..9f128bf8c1499e 100644 --- a/benchmark/http/end-vs-write-end.js +++ b/benchmark/http/end-vs-write-end.js @@ -48,10 +48,11 @@ function main({ len, type, method, c, duration }) { fn(res); }); - server.listen(common.PORT, () => { + server.listen(0, () => { bench.http({ connections: c, - duration + duration, + port: server.address().port, }, () => { server.close(); }); diff --git a/benchmark/http/headers.js b/benchmark/http/headers.js index a3d2b7810be676..b405868eb0ea2b 100644 --- a/benchmark/http/headers.js +++ b/benchmark/http/headers.js @@ -27,11 +27,12 @@ function main({ len, n, duration }) { res.writeHead(200, headers); res.end(); }); - server.listen(common.PORT, () => { + server.listen(0, () => { bench.http({ path: '/', connections: 10, - duration + duration, + port: server.address().port, }, () => { server.close(); }); diff --git a/benchmark/http/incoming_headers.js b/benchmark/http/incoming_headers.js index 983bd5632fcb7d..7501d2cb92d456 100644 --- a/benchmark/http/incoming_headers.js +++ b/benchmark/http/incoming_headers.js @@ -14,7 +14,7 @@ function main({ connections, headers, w, duration }) { res.end(); }); - server.listen(common.PORT, () => { + server.listen(0, () => { const headers = { 'Content-Type': 'text/plain', 'Accept': 'text/plain', @@ -34,7 +34,8 @@ function main({ connections, headers, w, duration }) { path: '/', connections, headers, - duration + duration, + port: server.address().port, }, () => { server.close(); }); diff --git a/benchmark/http/set-header.js b/benchmark/http/set-header.js index 48e0163a6ced10..47681ebd1b1405 100644 --- a/benchmark/http/set-header.js +++ b/benchmark/http/set-header.js @@ -1,6 +1,5 @@ 'use strict'; const common = require('../common.js'); -const PORT = common.PORT; const bench = common.createBenchmark(main, { res: ['normal', 'setHeader', 'setHeaderWH'], @@ -17,16 +16,16 @@ const c = 50; // setHeader: statusCode = status, setHeader(...) x2 // setHeaderWH: setHeader(...), writeHead(status, ...) function main({ res, duration }) { - process.env.PORT = PORT; const server = require('../fixtures/simple-http-server.js') - .listen(PORT) + .listen(0) .on('listening', () => { const path = `/${type}/${len}/${chunks}/${res}/${chunkedEnc}`; bench.http({ path: path, connections: c, - duration + duration, + port: server.address().port, }, () => { server.close(); }); diff --git a/benchmark/http/simple.js b/benchmark/http/simple.js index 095b15ca4465fb..31e12ca7c27438 100644 --- a/benchmark/http/simple.js +++ b/benchmark/http/simple.js @@ -13,14 +13,15 @@ const bench = common.createBenchmark(main, { function main({ type, len, chunks, c, chunkedEnc, duration }) { const server = require('../fixtures/simple-http-server.js') - .listen(common.PORT) + .listen(0) .on('listening', () => { const path = `/${type}/${len}/${chunks}/normal/${chunkedEnc}`; bench.http({ path, connections: c, - duration + duration, + port: server.address().port, }, () => { server.close(); }); diff --git a/benchmark/http/upgrade.js b/benchmark/http/upgrade.js index 8d365fe46df24e..c4f5cd342284c3 100644 --- a/benchmark/http/upgrade.js +++ b/benchmark/http/upgrade.js @@ -20,7 +20,7 @@ const resData = 'HTTP/1.1 101 Web Socket Protocol Handshake\r\n' + function main({ n }) { const server = require('../fixtures/simple-http-server.js') - .listen(common.PORT) + .listen(0) .on('listening', () => { bench.start(); doBench(server.address(), n, () => { diff --git a/common.gypi b/common.gypi index 2027a4e1050430..ecb5dd907f2b75 100644 --- a/common.gypi +++ b/common.gypi @@ -36,7 +36,7 @@ # Reset this number to 0 on major V8 upgrades. # Increment by one for each non-official patch applied to deps/v8. - 'v8_embedder_string': '-node.13', + 'v8_embedder_string': '-node.8', ##### V8 defaults for Node.js ##### @@ -280,7 +280,11 @@ '-std:c++17' ], 'BufferSecurityCheck': 'true', - 'DebugInformationFormat': 1, # /Z7 embed info in .obj files + 'target_conditions': [ + ['_toolset=="target"', { + 'DebugInformationFormat': 1 # /Z7 embed info in .obj files + }], + ], 'ExceptionHandling': 0, # /EHsc 'MultiProcessorCompilation': 'true', 'StringPooling': 'true', # pool string literals @@ -497,7 +501,7 @@ 'GCC_ENABLE_CPP_RTTI': 'NO', # -fno-rtti 'GCC_ENABLE_PASCAL_STRINGS': 'NO', # No -mpascal-strings 'PREBINDING': 'NO', # No -Wl,-prebind - 'MACOSX_DEPLOYMENT_TARGET': '10.13', # -mmacosx-version-min=10.13 + 'MACOSX_DEPLOYMENT_TARGET': '10.15', # -mmacosx-version-min=10.15 'USE_HEADERMAP': 'NO', 'OTHER_CFLAGS': [ '-fno-strict-aliasing', diff --git a/configure.py b/configure.py index 8f74784cc0cfad..699f09d195718f 100755 --- a/configure.py +++ b/configure.py @@ -782,12 +782,26 @@ help='Enable V8 transparent hugepage support. This feature is only '+ 'available on Linux platform.') +parser.add_argument('--v8-enable-short-builtin-calls', + action='store_true', + dest='v8_enable_short_builtin_calls', + default=None, + help='Enable V8 short builtin calls support. This feature is enabled '+ + 'on x86_64 platform by default.') + parser.add_argument('--node-builtin-modules-path', action='store', dest='node_builtin_modules_path', default=False, help='node will load builtin modules from disk instead of from binary') +parser.add_argument('--node-snapshot-main', + action='store', + dest='node_snapshot_main', + default=None, + help='Run a file when building the embedded snapshot. Currently ' + + 'experimental.') + # Create compile_commands.json in out/Debug and out/Release. parser.add_argument('-C', action='store_true', @@ -1115,6 +1129,7 @@ def host_arch_win(): 'x86' : 'ia32', 'arm' : 'arm', 'mips' : 'mips', + 'ARM64' : 'arm64' } return matchup.get(arch, 'ia32') @@ -1216,6 +1231,18 @@ def configure_node(o): o['variables']['want_separate_host_toolset'] = int(cross_compiling) + if options.node_snapshot_main is not None: + if options.shared: + # This should be possible to fix, but we will need to refactor the + # libnode target to avoid building it twice. + error('--node-snapshot-main is incompatible with --shared') + if options.without_node_snapshot: + error('--node-snapshot-main is incompatible with ' + + '--without-node-snapshot') + if cross_compiling: + error('--node-snapshot-main is incompatible with cross compilation') + o['variables']['node_snapshot_main'] = options.node_snapshot_main + if options.without_node_snapshot or options.node_builtin_modules_path: o['variables']['node_use_node_snapshot'] = 'false' else: @@ -1419,6 +1446,7 @@ def configure_library(lib, output, pkgname=None): def configure_v8(o): o['variables']['v8_enable_webassembly'] = 1 + o['variables']['v8_enable_javascript_promise_hooks'] = 1 o['variables']['v8_enable_lite_mode'] = 1 if options.v8_lite_mode else 0 o['variables']['v8_enable_gdbjit'] = 1 if options.gdb else 0 o['variables']['v8_no_strict_aliasing'] = 1 # Work around compiler bugs. @@ -1430,6 +1458,7 @@ def configure_v8(o): o['variables']['v8_use_siphash'] = 0 if options.without_siphash else 1 o['variables']['v8_enable_pointer_compression'] = 1 if options.enable_pointer_compression else 0 o['variables']['v8_enable_31bit_smis_on_64bit_arch'] = 1 if options.enable_pointer_compression else 0 + o['variables']['v8_enable_shared_ro_heap'] = 0 if options.enable_pointer_compression else 1 o['variables']['v8_trace_maps'] = 1 if options.trace_maps else 0 o['variables']['node_use_v8_platform'] = b(not options.without_v8_platform) o['variables']['node_use_bundled_v8'] = b(not options.without_bundled_v8) @@ -1444,6 +1473,8 @@ def configure_v8(o): if flavor != 'linux' and options.v8_enable_hugepage: raise Exception('--v8-enable-hugepage is supported only on linux.') o['variables']['v8_enable_hugepage'] = 1 if options.v8_enable_hugepage else 0 + if options.v8_enable_short_builtin_calls or o['variables']['target_arch'] == 'x64': + o['variables']['v8_enable_short_builtin_calls'] = 1 def configure_openssl(o): variables = o['variables'] @@ -1604,7 +1635,7 @@ def icu_download(path): # write an empty file to start with write(icu_config_name, do_not_edit + - pprint.pformat(icu_config, indent=2) + '\n') + pprint.pformat(icu_config, indent=2, width=1024) + '\n') # always set icu_small, node.gyp depends on it being defined. o['variables']['icu_small'] = b(False) @@ -1856,7 +1887,7 @@ def icu_download(path): # write updated icu_config.gypi with a bunch of paths write(icu_config_name, do_not_edit + - pprint.pformat(icu_config, indent=2) + '\n') + pprint.pformat(icu_config, indent=2, width=1024) + '\n') return # end of configure_intl def configure_inspector(o): @@ -1985,7 +2016,7 @@ def make_bin_override(): print_verbose(output) write('config.gypi', do_not_edit + - pprint.pformat(output, indent=2) + '\n') + pprint.pformat(output, indent=2, width=1024) + '\n') write('config.status', '#!/bin/sh\nset -x\nexec ./configure ' + ' '.join([pipes.quote(arg) for arg in original_argv]) + '\n') diff --git a/deps/acorn/acorn/CHANGELOG.md b/deps/acorn/acorn/CHANGELOG.md index 278fa50c9d83b1..d19b5f1bad0b93 100644 --- a/deps/acorn/acorn/CHANGELOG.md +++ b/deps/acorn/acorn/CHANGELOG.md @@ -1,3 +1,17 @@ +## 8.7.0 (2021-12-27) + +### New features + +Support quoted export names. + +Upgrade to Unicode 14. + +Add support for Unicode 13 properties in regular expressions. + +### Bug fixes + +Use a loop to find line breaks, because the existing regexp search would overrun the end of the searched range and waste a lot of time in minified code. + ## 8.6.0 (2021-11-18) ### Bug fixes diff --git a/deps/acorn/acorn/dist/acorn.js b/deps/acorn/acorn/dist/acorn.js index 5d9b521ac320bd..2d279e9fa8417d 100644 --- a/deps/acorn/acorn/dist/acorn.js +++ b/deps/acorn/acorn/dist/acorn.js @@ -33,8 +33,8 @@ // are only applied when a character is found to actually have a // code point above 128. // Generated by `bin/generate-identifier-regex.js`. - var nonASCIIidentifierStartChars = "\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u037f\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u052f\u0531-\u0556\u0559\u0560-\u0588\u05d0-\u05ea\u05ef-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u0860-\u086a\u08a0-\u08b4\u08b6-\u08c7\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u09fc\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0af9\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d\u0c58-\u0c5a\u0c60\u0c61\u0c80\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d04-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d54-\u0d56\u0d5f-\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e86-\u0e8a\u0e8c-\u0ea3\u0ea5\u0ea7-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f5\u13f8-\u13fd\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f8\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1878\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191e\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1c80-\u1c88\u1c90-\u1cba\u1cbd-\u1cbf\u1ce9-\u1cec\u1cee-\u1cf3\u1cf5\u1cf6\u1cfa\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2118-\u211d\u2124\u2126\u2128\u212a-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309b-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312f\u3131-\u318e\u31a0-\u31bf\u31f0-\u31ff\u3400-\u4dbf\u4e00-\u9ffc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua69d\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua7bf\ua7c2-\ua7ca\ua7f5-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua8fd\ua8fe\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\ua9e0-\ua9e4\ua9e6-\ua9ef\ua9fa-\ua9fe\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa7e-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a\uab5c-\uab69\uab70-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc"; - var nonASCIIidentifierChars = "\u200c\u200d\xb7\u0300-\u036f\u0387\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u0669\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7\u06e8\u06ea-\u06ed\u06f0-\u06f9\u0711\u0730-\u074a\u07a6-\u07b0\u07c0-\u07c9\u07eb-\u07f3\u07fd\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u08d3-\u08e1\u08e3-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09cb-\u09cd\u09d7\u09e2\u09e3\u09e6-\u09ef\u09fe\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2\u0ae3\u0ae6-\u0aef\u0afa-\u0aff\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b55-\u0b57\u0b62\u0b63\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c00-\u0c04\u0c3e-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0c66-\u0c6f\u0c81-\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0ce6-\u0cef\u0d00-\u0d03\u0d3b\u0d3c\u0d3e-\u0d44\u0d46-\u0d48\u0d4a-\u0d4d\u0d57\u0d62\u0d63\u0d66-\u0d6f\u0d81-\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0de6-\u0def\u0df2\u0df3\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0e50-\u0e59\u0eb1\u0eb4-\u0ebc\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e\u0f3f\u0f71-\u0f84\u0f86\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102b-\u103e\u1040-\u1049\u1056-\u1059\u105e-\u1060\u1062-\u1064\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u1369-\u1371\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b4-\u17d3\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u18a9\u1920-\u192b\u1930-\u193b\u1946-\u194f\u19d0-\u19da\u1a17-\u1a1b\u1a55-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1ab0-\u1abd\u1abf\u1ac0\u1b00-\u1b04\u1b34-\u1b44\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1b82\u1ba1-\u1bad\u1bb0-\u1bb9\u1be6-\u1bf3\u1c24-\u1c37\u1c40-\u1c49\u1c50-\u1c59\u1cd0-\u1cd2\u1cd4-\u1ce8\u1ced\u1cf4\u1cf7-\u1cf9\u1dc0-\u1df9\u1dfb-\u1dff\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua620-\ua629\ua66f\ua674-\ua67d\ua69e\ua69f\ua6f0\ua6f1\ua802\ua806\ua80b\ua823-\ua827\ua82c\ua880\ua881\ua8b4-\ua8c5\ua8d0-\ua8d9\ua8e0-\ua8f1\ua8ff-\ua909\ua926-\ua92d\ua947-\ua953\ua980-\ua983\ua9b3-\ua9c0\ua9d0-\ua9d9\ua9e5\ua9f0-\ua9f9\uaa29-\uaa36\uaa43\uaa4c\uaa4d\uaa50-\uaa59\uaa7b-\uaa7d\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uaaeb-\uaaef\uaaf5\uaaf6\uabe3-\uabea\uabec\uabed\uabf0-\uabf9\ufb1e\ufe00-\ufe0f\ufe20-\ufe2f\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f"; + var nonASCIIidentifierStartChars = "\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u037f\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u052f\u0531-\u0556\u0559\u0560-\u0588\u05d0-\u05ea\u05ef-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u0860-\u086a\u0870-\u0887\u0889-\u088e\u08a0-\u08c9\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u09fc\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0af9\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d\u0c58-\u0c5a\u0c5d\u0c60\u0c61\u0c80\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cdd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d04-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d54-\u0d56\u0d5f-\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e86-\u0e8a\u0e8c-\u0ea3\u0ea5\u0ea7-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f5\u13f8-\u13fd\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f8\u1700-\u1711\u171f-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1878\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191e\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4c\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1c80-\u1c88\u1c90-\u1cba\u1cbd-\u1cbf\u1ce9-\u1cec\u1cee-\u1cf3\u1cf5\u1cf6\u1cfa\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2118-\u211d\u2124\u2126\u2128\u212a-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309b-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312f\u3131-\u318e\u31a0-\u31bf\u31f0-\u31ff\u3400-\u4dbf\u4e00-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua69d\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua7ca\ua7d0\ua7d1\ua7d3\ua7d5-\ua7d9\ua7f2-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua8fd\ua8fe\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\ua9e0-\ua9e4\ua9e6-\ua9ef\ua9fa-\ua9fe\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa7e-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a\uab5c-\uab69\uab70-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc"; + var nonASCIIidentifierChars = "\u200c\u200d\xb7\u0300-\u036f\u0387\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u0669\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7\u06e8\u06ea-\u06ed\u06f0-\u06f9\u0711\u0730-\u074a\u07a6-\u07b0\u07c0-\u07c9\u07eb-\u07f3\u07fd\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u0898-\u089f\u08ca-\u08e1\u08e3-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09cb-\u09cd\u09d7\u09e2\u09e3\u09e6-\u09ef\u09fe\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2\u0ae3\u0ae6-\u0aef\u0afa-\u0aff\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b55-\u0b57\u0b62\u0b63\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c00-\u0c04\u0c3c\u0c3e-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0c66-\u0c6f\u0c81-\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0ce6-\u0cef\u0d00-\u0d03\u0d3b\u0d3c\u0d3e-\u0d44\u0d46-\u0d48\u0d4a-\u0d4d\u0d57\u0d62\u0d63\u0d66-\u0d6f\u0d81-\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0de6-\u0def\u0df2\u0df3\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0e50-\u0e59\u0eb1\u0eb4-\u0ebc\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e\u0f3f\u0f71-\u0f84\u0f86\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102b-\u103e\u1040-\u1049\u1056-\u1059\u105e-\u1060\u1062-\u1064\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u1369-\u1371\u1712-\u1715\u1732-\u1734\u1752\u1753\u1772\u1773\u17b4-\u17d3\u17dd\u17e0-\u17e9\u180b-\u180d\u180f-\u1819\u18a9\u1920-\u192b\u1930-\u193b\u1946-\u194f\u19d0-\u19da\u1a17-\u1a1b\u1a55-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1ab0-\u1abd\u1abf-\u1ace\u1b00-\u1b04\u1b34-\u1b44\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1b82\u1ba1-\u1bad\u1bb0-\u1bb9\u1be6-\u1bf3\u1c24-\u1c37\u1c40-\u1c49\u1c50-\u1c59\u1cd0-\u1cd2\u1cd4-\u1ce8\u1ced\u1cf4\u1cf7-\u1cf9\u1dc0-\u1dff\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua620-\ua629\ua66f\ua674-\ua67d\ua69e\ua69f\ua6f0\ua6f1\ua802\ua806\ua80b\ua823-\ua827\ua82c\ua880\ua881\ua8b4-\ua8c5\ua8d0-\ua8d9\ua8e0-\ua8f1\ua8ff-\ua909\ua926-\ua92d\ua947-\ua953\ua980-\ua983\ua9b3-\ua9c0\ua9d0-\ua9d9\ua9e5\ua9f0-\ua9f9\uaa29-\uaa36\uaa43\uaa4c\uaa4d\uaa50-\uaa59\uaa7b-\uaa7d\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uaaeb-\uaaef\uaaf5\uaaf6\uabe3-\uabea\uabec\uabed\uabf0-\uabf9\ufb1e\ufe00-\ufe0f\ufe20-\ufe2f\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f"; var nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]"); var nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]"); @@ -48,10 +48,10 @@ // generated by bin/generate-identifier-regex.js // eslint-disable-next-line comma-spacing - var astralIdentifierStartCodes = [0,11,2,25,2,18,2,1,2,14,3,13,35,122,70,52,268,28,4,48,48,31,14,29,6,37,11,29,3,35,5,7,2,4,43,157,19,35,5,35,5,39,9,51,157,310,10,21,11,7,153,5,3,0,2,43,2,1,4,0,3,22,11,22,10,30,66,18,2,1,11,21,11,25,71,55,7,1,65,0,16,3,2,2,2,28,43,28,4,28,36,7,2,27,28,53,11,21,11,18,14,17,111,72,56,50,14,50,14,35,349,41,7,1,79,28,11,0,9,21,107,20,28,22,13,52,76,44,33,24,27,35,30,0,3,0,9,34,4,0,13,47,15,3,22,0,2,0,36,17,2,24,85,6,2,0,2,3,2,14,2,9,8,46,39,7,3,1,3,21,2,6,2,1,2,4,4,0,19,0,13,4,159,52,19,3,21,2,31,47,21,1,2,0,185,46,42,3,37,47,21,0,60,42,14,0,72,26,230,43,117,63,32,7,3,0,3,7,2,1,2,23,16,0,2,0,95,7,3,38,17,0,2,0,29,0,11,39,8,0,22,0,12,45,20,0,35,56,264,8,2,36,18,0,50,29,113,6,2,1,2,37,22,0,26,5,2,1,2,31,15,0,328,18,190,0,80,921,103,110,18,195,2749,1070,4050,582,8634,568,8,30,114,29,19,47,17,3,32,20,6,18,689,63,129,74,6,0,67,12,65,1,2,0,29,6135,9,1237,43,8,8952,286,50,2,18,3,9,395,2309,106,6,12,4,8,8,9,5991,84,2,70,2,1,3,0,3,1,3,3,2,11,2,0,2,6,2,64,2,3,3,7,2,6,2,27,2,3,2,4,2,0,4,6,2,339,3,24,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,7,2357,44,11,6,17,0,370,43,1301,196,60,67,8,0,1205,3,2,26,2,1,2,0,3,0,2,9,2,3,2,0,2,0,7,0,5,0,2,0,2,0,2,2,2,1,2,0,3,0,2,0,2,0,2,0,2,0,2,1,2,0,3,3,2,6,2,3,2,3,2,0,2,9,2,16,6,2,2,4,2,16,4421,42717,35,4148,12,221,3,5761,15,7472,3104,541,1507,4938]; + var astralIdentifierStartCodes = [0,11,2,25,2,18,2,1,2,14,3,13,35,122,70,52,268,28,4,48,48,31,14,29,6,37,11,29,3,35,5,7,2,4,43,157,19,35,5,35,5,39,9,51,13,10,2,14,2,6,2,1,2,10,2,14,2,6,2,1,68,310,10,21,11,7,25,5,2,41,2,8,70,5,3,0,2,43,2,1,4,0,3,22,11,22,10,30,66,18,2,1,11,21,11,25,71,55,7,1,65,0,16,3,2,2,2,28,43,28,4,28,36,7,2,27,28,53,11,21,11,18,14,17,111,72,56,50,14,50,14,35,349,41,7,1,79,28,11,0,9,21,43,17,47,20,28,22,13,52,58,1,3,0,14,44,33,24,27,35,30,0,3,0,9,34,4,0,13,47,15,3,22,0,2,0,36,17,2,24,85,6,2,0,2,3,2,14,2,9,8,46,39,7,3,1,3,21,2,6,2,1,2,4,4,0,19,0,13,4,159,52,19,3,21,2,31,47,21,1,2,0,185,46,42,3,37,47,21,0,60,42,14,0,72,26,38,6,186,43,117,63,32,7,3,0,3,7,2,1,2,23,16,0,2,0,95,7,3,38,17,0,2,0,29,0,11,39,8,0,22,0,12,45,20,0,19,72,264,8,2,36,18,0,50,29,113,6,2,1,2,37,22,0,26,5,2,1,2,31,15,0,328,18,190,0,80,921,103,110,18,195,2637,96,16,1070,4050,582,8634,568,8,30,18,78,18,29,19,47,17,3,32,20,6,18,689,63,129,74,6,0,67,12,65,1,2,0,29,6135,9,1237,43,8,8936,3,2,6,2,1,2,290,46,2,18,3,9,395,2309,106,6,12,4,8,8,9,5991,84,2,70,2,1,3,0,3,1,3,3,2,11,2,0,2,6,2,64,2,3,3,7,2,6,2,27,2,3,2,4,2,0,4,6,2,339,3,24,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,7,1845,30,482,44,11,6,17,0,322,29,19,43,1269,6,2,3,2,1,2,14,2,196,60,67,8,0,1205,3,2,26,2,1,2,0,3,0,2,9,2,3,2,0,2,0,7,0,5,0,2,0,2,0,2,2,2,1,2,0,3,0,2,0,2,0,2,0,2,0,2,1,2,0,3,3,2,6,2,3,2,3,2,0,2,9,2,16,6,2,2,4,2,16,4421,42719,33,4152,8,221,3,5761,15,7472,3104,541,1507,4938]; // eslint-disable-next-line comma-spacing - var astralIdentifierCodes = [509,0,227,0,150,4,294,9,1368,2,2,1,6,3,41,2,5,0,166,1,574,3,9,9,370,1,154,10,176,2,54,14,32,9,16,3,46,10,54,9,7,2,37,13,2,9,6,1,45,0,13,2,49,13,9,3,2,11,83,11,7,0,161,11,6,9,7,3,56,1,2,6,3,1,3,2,10,0,11,1,3,6,4,4,193,17,10,9,5,0,82,19,13,9,214,6,3,8,28,1,83,16,16,9,82,12,9,9,84,14,5,9,243,14,166,9,71,5,2,1,3,3,2,0,2,1,13,9,120,6,3,6,4,0,29,9,41,6,2,3,9,0,10,10,47,15,406,7,2,7,17,9,57,21,2,13,123,5,4,0,2,1,2,6,2,0,9,9,49,4,2,1,2,4,9,9,330,3,19306,9,135,4,60,6,26,9,1014,0,2,54,8,3,82,0,12,1,19628,1,5319,4,4,5,9,7,3,6,31,3,149,2,1418,49,513,54,5,49,9,0,15,0,23,4,2,14,1361,6,2,16,3,6,2,1,2,4,262,6,10,9,419,13,1495,6,110,6,6,9,4759,9,787719,239]; + var astralIdentifierCodes = [509,0,227,0,150,4,294,9,1368,2,2,1,6,3,41,2,5,0,166,1,574,3,9,9,370,1,154,10,50,3,123,2,54,14,32,10,3,1,11,3,46,10,8,0,46,9,7,2,37,13,2,9,6,1,45,0,13,2,49,13,9,3,2,11,83,11,7,0,161,11,6,9,7,3,56,1,2,6,3,1,3,2,10,0,11,1,3,6,4,4,193,17,10,9,5,0,82,19,13,9,214,6,3,8,28,1,83,16,16,9,82,12,9,9,84,14,5,9,243,14,166,9,71,5,2,1,3,3,2,0,2,1,13,9,120,6,3,6,4,0,29,9,41,6,2,3,9,0,10,10,47,15,406,7,2,7,17,9,57,21,2,13,123,5,4,0,2,1,2,6,2,0,9,9,49,4,2,1,2,4,9,9,330,3,19306,9,87,9,39,4,60,6,26,9,1014,0,2,54,8,3,82,0,12,1,19628,1,4706,45,3,22,543,4,4,5,9,7,3,6,31,3,149,2,1418,49,513,54,5,49,9,0,15,0,23,4,2,14,1361,6,2,16,3,6,2,1,2,4,262,6,10,9,357,0,62,13,1495,6,110,6,6,9,4759,9,787719,239]; // This has a complexity linear to the value of the code. The // assumption is that looking up astral identifier characters is @@ -256,6 +256,17 @@ return code === 10 || code === 13 || code === 0x2028 || code === 0x2029 } + function nextLineBreak(code, from, end) { + if ( end === void 0 ) end = code.length; + + for (var i = from; i < end; i++) { + var next = code.charCodeAt(i); + if (isNewLine(next)) + { return i < end - 1 && next === 13 && code.charCodeAt(i + 1) === 10 ? i + 2 : i + 1 } + } + return -1 + } + var nonASCIIwhitespace = /[\u1680\u2000-\u200a\u202f\u205f\u3000\ufeff]/; var skipWhiteSpace = /(?:\s|\/\/.*|\/\*[^]*?\*\/)*/g; @@ -264,11 +275,9 @@ var hasOwnProperty = ref.hasOwnProperty; var toString = ref.toString; - // Checks if an object has a property. - - function has(obj, propName) { - return hasOwnProperty.call(obj, propName) - } + var hasOwn = Object.hasOwn || (function (obj, propName) { return ( + hasOwnProperty.call(obj, propName) + ); }); var isArray = Array.isArray || (function (obj) { return ( toString.call(obj) === "[object Array]" @@ -278,6 +287,8 @@ return new RegExp("^(?:" + words.replace(/ /g, "|") + ")$") } + var loneSurrogate = /(?:[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])/; + // These are used when `options.locations` is on, for the // `startLoc` and `endLoc` properties. @@ -304,14 +315,10 @@ function getLineInfo(input, offset) { for (var line = 1, cur = 0;;) { - lineBreakG.lastIndex = cur; - var match = lineBreakG.exec(input); - if (match && match.index < offset) { - ++line; - cur = match.index + match[0].length; - } else { - return new Position(line, offset - cur) - } + var nextBreak = nextLineBreak(input, cur, offset); + if (nextBreak < 0) { return new Position(line, offset - cur) } + ++line; + cur = nextBreak; } } @@ -417,7 +424,7 @@ var options = {}; for (var opt in defaultOptions) - { options[opt] = opts && has(opts, opt) ? opts[opt] : defaultOptions[opt]; } + { options[opt] = opts && hasOwn(opts, opt) ? opts[opt] : defaultOptions[opt]; } if (options.ecmaVersion === "latest") { options.ecmaVersion = 1e8; @@ -1583,7 +1590,7 @@ var parent = len === 0 ? null : this.privateNameStack[len - 1]; for (var i = 0; i < used.length; ++i) { var id = used[i]; - if (!has(declared, id.name)) { + if (!hasOwn(declared, id.name)) { if (parent) { parent.used.push(id); } else { @@ -1636,7 +1643,7 @@ if (this.eat(types$1.star)) { if (this.options.ecmaVersion >= 11) { if (this.eatContextual("as")) { - node.exported = this.parseIdent(true); + node.exported = this.parseModuleExportName(); this.checkExport(exports, node.exported.name, this.lastTokStart); } else { node.exported = null; @@ -1688,6 +1695,10 @@ this.checkUnreserved(spec.local); // check if export is defined this.checkLocalExport(spec.local); + + if (spec.local.type === "Literal") { + this.raise(spec.local.start, "A string literal cannot be used as an exported binding without `from`."); + } } node.source = null; @@ -1699,7 +1710,7 @@ pp$8.checkExport = function(exports, name, pos) { if (!exports) { return } - if (has(exports, name)) + if (hasOwn(exports, name)) { this.raiseRecoverable(pos, "Duplicate export '" + name + "'"); } exports[name] = true; }; @@ -1763,9 +1774,13 @@ } else { first = false; } var node = this.startNode(); - node.local = this.parseIdent(true); - node.exported = this.eatContextual("as") ? this.parseIdent(true) : node.local; - this.checkExport(exports, node.exported.name, node.exported.start); + node.local = this.parseModuleExportName(); + node.exported = this.eatContextual("as") ? this.parseModuleExportName() : node.local; + this.checkExport( + exports, + node.exported[node.exported.type === "Identifier" ? "name" : "value"], + node.exported.start + ); nodes.push(this.finishNode(node, "ExportSpecifier")); } return nodes @@ -1817,7 +1832,7 @@ } else { first = false; } var node$2 = this.startNode(); - node$2.imported = this.parseIdent(true); + node$2.imported = this.parseModuleExportName(); if (this.eatContextual("as")) { node$2.local = this.parseIdent(); } else { @@ -1830,6 +1845,17 @@ return nodes }; + pp$8.parseModuleExportName = function() { + if (this.options.ecmaVersion >= 13 && this.type === types$1.string) { + var stringLiteral = this.parseLiteral(this.value); + if (loneSurrogate.test(stringLiteral.value)) { + this.raise(stringLiteral.start, "An export name cannot include a lone surrogate."); + } + return stringLiteral + } + return this.parseIdent(true) + }; + // Set `ExpressionStatement#directive` property for directive prologues. pp$8.adaptDirectivePrologue = function(statements) { for (var i = 0; i < statements.length && this.isDirectiveCandidate(statements[i]); ++i) { @@ -2103,7 +2129,7 @@ if (bindingType === BIND_LEXICAL && expr.name === "let") { this.raiseRecoverable(expr.start, "let is disallowed as a lexically bound name"); } if (checkClashes) { - if (has(checkClashes, expr.name)) + if (hasOwn(checkClashes, expr.name)) { this.raiseRecoverable(expr.start, "Argument name clash"); } checkClashes[expr.name] = true; } @@ -3598,26 +3624,30 @@ var ecma10BinaryProperties = ecma9BinaryProperties + " Extended_Pictographic"; var ecma11BinaryProperties = ecma10BinaryProperties; var ecma12BinaryProperties = ecma11BinaryProperties + " EBase EComp EMod EPres ExtPict"; + var ecma13BinaryProperties = ecma12BinaryProperties; var unicodeBinaryProperties = { 9: ecma9BinaryProperties, 10: ecma10BinaryProperties, 11: ecma11BinaryProperties, - 12: ecma12BinaryProperties + 12: ecma12BinaryProperties, + 13: ecma13BinaryProperties }; // #table-unicode-general-category-values var unicodeGeneralCategoryValues = "Cased_Letter LC Close_Punctuation Pe Connector_Punctuation Pc Control Cc cntrl Currency_Symbol Sc Dash_Punctuation Pd Decimal_Number Nd digit Enclosing_Mark Me Final_Punctuation Pf Format Cf Initial_Punctuation Pi Letter L Letter_Number Nl Line_Separator Zl Lowercase_Letter Ll Mark M Combining_Mark Math_Symbol Sm Modifier_Letter Lm Modifier_Symbol Sk Nonspacing_Mark Mn Number N Open_Punctuation Ps Other C Other_Letter Lo Other_Number No Other_Punctuation Po Other_Symbol So Paragraph_Separator Zp Private_Use Co Punctuation P punct Separator Z Space_Separator Zs Spacing_Mark Mc Surrogate Cs Symbol S Titlecase_Letter Lt Unassigned Cn Uppercase_Letter Lu"; // #table-unicode-script-values - var ecma9ScriptValues = "Adlam Adlm Ahom Ahom Anatolian_Hieroglyphs Hluw Arabic Arab Armenian Armn Avestan Avst Balinese Bali Bamum Bamu Bassa_Vah Bass Batak Batk Bengali Beng Bhaiksuki Bhks Bopomofo Bopo Brahmi Brah Braille Brai Buginese Bugi Buhid Buhd Canadian_Aboriginal Cans Carian Cari Caucasian_Albanian Aghb Chakma Cakm Cham Cham Cherokee Cher Common Zyyy Coptic Copt Qaac Cuneiform Xsux Cypriot Cprt Cyrillic Cyrl Deseret Dsrt Devanagari Deva Duployan Dupl Egyptian_Hieroglyphs Egyp Elbasan Elba Ethiopic Ethi Georgian Geor Glagolitic Glag Gothic Goth Grantha Gran Greek Grek Gujarati Gujr Gurmukhi Guru Han Hani Hangul Hang Hanunoo Hano Hatran Hatr Hebrew Hebr Hiragana Hira Imperial_Aramaic Armi Inherited Zinh Qaai Inscriptional_Pahlavi Phli Inscriptional_Parthian Prti Javanese Java Kaithi Kthi Kannada Knda Katakana Kana Kayah_Li Kali Kharoshthi Khar Khmer Khmr Khojki Khoj Khudawadi Sind Lao Laoo Latin Latn Lepcha Lepc Limbu Limb Linear_A Lina Linear_B Linb Lisu Lisu Lycian Lyci Lydian Lydi Mahajani Mahj Malayalam Mlym Mandaic Mand Manichaean Mani Marchen Marc Masaram_Gondi Gonm Meetei_Mayek Mtei Mende_Kikakui Mend Meroitic_Cursive Merc Meroitic_Hieroglyphs Mero Miao Plrd Modi Modi Mongolian Mong Mro Mroo Multani Mult Myanmar Mymr Nabataean Nbat New_Tai_Lue Talu Newa Newa Nko Nkoo Nushu Nshu Ogham Ogam Ol_Chiki Olck Old_Hungarian Hung Old_Italic Ital Old_North_Arabian Narb Old_Permic Perm Old_Persian Xpeo Old_South_Arabian Sarb Old_Turkic Orkh Oriya Orya Osage Osge Osmanya Osma Pahawh_Hmong Hmng Palmyrene Palm Pau_Cin_Hau Pauc Phags_Pa Phag Phoenician Phnx Psalter_Pahlavi Phlp Rejang Rjng Runic Runr Samaritan Samr Saurashtra Saur Sharada Shrd Shavian Shaw Siddham Sidd SignWriting Sgnw Sinhala Sinh Sora_Sompeng Sora Soyombo Soyo Sundanese Sund Syloti_Nagri Sylo Syriac Syrc Tagalog Tglg Tagbanwa Tagb Tai_Le Tale Tai_Tham Lana Tai_Viet Tavt Takri Takr Tamil Taml Tangut Tang Telugu Telu Thaana Thaa Thai Thai Tibetan Tibt Tifinagh Tfng Tirhuta Tirh Ugaritic Ugar Vai Vaii Warang_Citi Wara Yi Yiii Zanabazar_Square Zanb"; + var ecma9ScriptValues = "Adlam Adlm Ahom Anatolian_Hieroglyphs Hluw Arabic Arab Armenian Armn Avestan Avst Balinese Bali Bamum Bamu Bassa_Vah Bass Batak Batk Bengali Beng Bhaiksuki Bhks Bopomofo Bopo Brahmi Brah Braille Brai Buginese Bugi Buhid Buhd Canadian_Aboriginal Cans Carian Cari Caucasian_Albanian Aghb Chakma Cakm Cham Cham Cherokee Cher Common Zyyy Coptic Copt Qaac Cuneiform Xsux Cypriot Cprt Cyrillic Cyrl Deseret Dsrt Devanagari Deva Duployan Dupl Egyptian_Hieroglyphs Egyp Elbasan Elba Ethiopic Ethi Georgian Geor Glagolitic Glag Gothic Goth Grantha Gran Greek Grek Gujarati Gujr Gurmukhi Guru Han Hani Hangul Hang Hanunoo Hano Hatran Hatr Hebrew Hebr Hiragana Hira Imperial_Aramaic Armi Inherited Zinh Qaai Inscriptional_Pahlavi Phli Inscriptional_Parthian Prti Javanese Java Kaithi Kthi Kannada Knda Katakana Kana Kayah_Li Kali Kharoshthi Khar Khmer Khmr Khojki Khoj Khudawadi Sind Lao Laoo Latin Latn Lepcha Lepc Limbu Limb Linear_A Lina Linear_B Linb Lisu Lisu Lycian Lyci Lydian Lydi Mahajani Mahj Malayalam Mlym Mandaic Mand Manichaean Mani Marchen Marc Masaram_Gondi Gonm Meetei_Mayek Mtei Mende_Kikakui Mend Meroitic_Cursive Merc Meroitic_Hieroglyphs Mero Miao Plrd Modi Mongolian Mong Mro Mroo Multani Mult Myanmar Mymr Nabataean Nbat New_Tai_Lue Talu Newa Newa Nko Nkoo Nushu Nshu Ogham Ogam Ol_Chiki Olck Old_Hungarian Hung Old_Italic Ital Old_North_Arabian Narb Old_Permic Perm Old_Persian Xpeo Old_South_Arabian Sarb Old_Turkic Orkh Oriya Orya Osage Osge Osmanya Osma Pahawh_Hmong Hmng Palmyrene Palm Pau_Cin_Hau Pauc Phags_Pa Phag Phoenician Phnx Psalter_Pahlavi Phlp Rejang Rjng Runic Runr Samaritan Samr Saurashtra Saur Sharada Shrd Shavian Shaw Siddham Sidd SignWriting Sgnw Sinhala Sinh Sora_Sompeng Sora Soyombo Soyo Sundanese Sund Syloti_Nagri Sylo Syriac Syrc Tagalog Tglg Tagbanwa Tagb Tai_Le Tale Tai_Tham Lana Tai_Viet Tavt Takri Takr Tamil Taml Tangut Tang Telugu Telu Thaana Thaa Thai Thai Tibetan Tibt Tifinagh Tfng Tirhuta Tirh Ugaritic Ugar Vai Vaii Warang_Citi Wara Yi Yiii Zanabazar_Square Zanb"; var ecma10ScriptValues = ecma9ScriptValues + " Dogra Dogr Gunjala_Gondi Gong Hanifi_Rohingya Rohg Makasar Maka Medefaidrin Medf Old_Sogdian Sogo Sogdian Sogd"; var ecma11ScriptValues = ecma10ScriptValues + " Elymaic Elym Nandinagari Nand Nyiakeng_Puachue_Hmong Hmnp Wancho Wcho"; var ecma12ScriptValues = ecma11ScriptValues + " Chorasmian Chrs Diak Dives_Akuru Khitan_Small_Script Kits Yezi Yezidi"; + var ecma13ScriptValues = ecma12ScriptValues + " Cypro_Minoan Cpmn Old_Uyghur Ougr Tangsa Tnsa Toto Vithkuqi Vith"; var unicodeScriptValues = { 9: ecma9ScriptValues, 10: ecma10ScriptValues, 11: ecma11ScriptValues, - 12: ecma12ScriptValues + 12: ecma12ScriptValues, + 13: ecma13ScriptValues }; var data = {}; @@ -3635,17 +3665,19 @@ d.nonBinary.sc = d.nonBinary.Script; d.nonBinary.scx = d.nonBinary.Script_Extensions; } - buildUnicodeData(9); - buildUnicodeData(10); - buildUnicodeData(11); - buildUnicodeData(12); + + for (var i = 0, list = [9, 10, 11, 12, 13]; i < list.length; i += 1) { + var ecmaVersion = list[i]; + + buildUnicodeData(ecmaVersion); + } var pp$1 = Parser.prototype; var RegExpValidationState = function RegExpValidationState(parser) { this.parser = parser; this.validFlags = "gim" + (parser.options.ecmaVersion >= 6 ? "uy" : "") + (parser.options.ecmaVersion >= 9 ? "s" : "") + (parser.options.ecmaVersion >= 13 ? "d" : ""); - this.unicodeProperties = data[parser.options.ecmaVersion >= 12 ? 12 : parser.options.ecmaVersion]; + this.unicodeProperties = data[parser.options.ecmaVersion >= 13 ? 13 : parser.options.ecmaVersion]; this.source = ""; this.flags = ""; this.start = 0; @@ -4444,7 +4476,7 @@ return false }; pp$1.regexp_validateUnicodePropertyNameAndValue = function(state, name, value) { - if (!has(state.unicodeProperties.nonBinary, name)) + if (!hasOwn(state.unicodeProperties.nonBinary, name)) { state.raise("Invalid property name"); } if (!state.unicodeProperties.nonBinary[name].test(value)) { state.raise("Invalid property value"); } @@ -4796,11 +4828,9 @@ if (end === -1) { this.raise(this.pos - 2, "Unterminated comment"); } this.pos = end + 2; if (this.options.locations) { - lineBreakG.lastIndex = start; - var match; - while ((match = lineBreakG.exec(this.input)) && match.index < this.pos) { + for (var nextBreak = (void 0), pos = start; (nextBreak = nextLineBreak(this.input, pos, this.pos)) > -1;) { ++this.curLine; - this.lineStart = match.index + match[0].length; + pos = this.lineStart = nextBreak; } } if (this.options.onComment) @@ -5511,7 +5541,7 @@ // Acorn is a tiny, fast JavaScript parser written in JavaScript. - var version = "8.6.0"; + var version = "8.7.0"; Parser.acorn = { Parser: Parser, diff --git a/deps/acorn/acorn/dist/acorn.mjs b/deps/acorn/acorn/dist/acorn.mjs index df5b26e5dd0f74..e99e9b30e1b635 100644 --- a/deps/acorn/acorn/dist/acorn.mjs +++ b/deps/acorn/acorn/dist/acorn.mjs @@ -27,8 +27,8 @@ var keywordRelationalOperator = /^in(stanceof)?$/; // are only applied when a character is found to actually have a // code point above 128. // Generated by `bin/generate-identifier-regex.js`. -var nonASCIIidentifierStartChars = "\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u037f\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u052f\u0531-\u0556\u0559\u0560-\u0588\u05d0-\u05ea\u05ef-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u0860-\u086a\u08a0-\u08b4\u08b6-\u08c7\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u09fc\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0af9\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d\u0c58-\u0c5a\u0c60\u0c61\u0c80\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d04-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d54-\u0d56\u0d5f-\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e86-\u0e8a\u0e8c-\u0ea3\u0ea5\u0ea7-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f5\u13f8-\u13fd\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f8\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1878\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191e\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1c80-\u1c88\u1c90-\u1cba\u1cbd-\u1cbf\u1ce9-\u1cec\u1cee-\u1cf3\u1cf5\u1cf6\u1cfa\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2118-\u211d\u2124\u2126\u2128\u212a-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309b-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312f\u3131-\u318e\u31a0-\u31bf\u31f0-\u31ff\u3400-\u4dbf\u4e00-\u9ffc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua69d\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua7bf\ua7c2-\ua7ca\ua7f5-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua8fd\ua8fe\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\ua9e0-\ua9e4\ua9e6-\ua9ef\ua9fa-\ua9fe\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa7e-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a\uab5c-\uab69\uab70-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc"; -var nonASCIIidentifierChars = "\u200c\u200d\xb7\u0300-\u036f\u0387\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u0669\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7\u06e8\u06ea-\u06ed\u06f0-\u06f9\u0711\u0730-\u074a\u07a6-\u07b0\u07c0-\u07c9\u07eb-\u07f3\u07fd\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u08d3-\u08e1\u08e3-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09cb-\u09cd\u09d7\u09e2\u09e3\u09e6-\u09ef\u09fe\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2\u0ae3\u0ae6-\u0aef\u0afa-\u0aff\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b55-\u0b57\u0b62\u0b63\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c00-\u0c04\u0c3e-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0c66-\u0c6f\u0c81-\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0ce6-\u0cef\u0d00-\u0d03\u0d3b\u0d3c\u0d3e-\u0d44\u0d46-\u0d48\u0d4a-\u0d4d\u0d57\u0d62\u0d63\u0d66-\u0d6f\u0d81-\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0de6-\u0def\u0df2\u0df3\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0e50-\u0e59\u0eb1\u0eb4-\u0ebc\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e\u0f3f\u0f71-\u0f84\u0f86\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102b-\u103e\u1040-\u1049\u1056-\u1059\u105e-\u1060\u1062-\u1064\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u1369-\u1371\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b4-\u17d3\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u18a9\u1920-\u192b\u1930-\u193b\u1946-\u194f\u19d0-\u19da\u1a17-\u1a1b\u1a55-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1ab0-\u1abd\u1abf\u1ac0\u1b00-\u1b04\u1b34-\u1b44\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1b82\u1ba1-\u1bad\u1bb0-\u1bb9\u1be6-\u1bf3\u1c24-\u1c37\u1c40-\u1c49\u1c50-\u1c59\u1cd0-\u1cd2\u1cd4-\u1ce8\u1ced\u1cf4\u1cf7-\u1cf9\u1dc0-\u1df9\u1dfb-\u1dff\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua620-\ua629\ua66f\ua674-\ua67d\ua69e\ua69f\ua6f0\ua6f1\ua802\ua806\ua80b\ua823-\ua827\ua82c\ua880\ua881\ua8b4-\ua8c5\ua8d0-\ua8d9\ua8e0-\ua8f1\ua8ff-\ua909\ua926-\ua92d\ua947-\ua953\ua980-\ua983\ua9b3-\ua9c0\ua9d0-\ua9d9\ua9e5\ua9f0-\ua9f9\uaa29-\uaa36\uaa43\uaa4c\uaa4d\uaa50-\uaa59\uaa7b-\uaa7d\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uaaeb-\uaaef\uaaf5\uaaf6\uabe3-\uabea\uabec\uabed\uabf0-\uabf9\ufb1e\ufe00-\ufe0f\ufe20-\ufe2f\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f"; +var nonASCIIidentifierStartChars = "\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u037f\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u052f\u0531-\u0556\u0559\u0560-\u0588\u05d0-\u05ea\u05ef-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u0860-\u086a\u0870-\u0887\u0889-\u088e\u08a0-\u08c9\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u09fc\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0af9\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d\u0c58-\u0c5a\u0c5d\u0c60\u0c61\u0c80\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cdd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d04-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d54-\u0d56\u0d5f-\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e86-\u0e8a\u0e8c-\u0ea3\u0ea5\u0ea7-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f5\u13f8-\u13fd\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f8\u1700-\u1711\u171f-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1878\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191e\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4c\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1c80-\u1c88\u1c90-\u1cba\u1cbd-\u1cbf\u1ce9-\u1cec\u1cee-\u1cf3\u1cf5\u1cf6\u1cfa\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2118-\u211d\u2124\u2126\u2128\u212a-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309b-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312f\u3131-\u318e\u31a0-\u31bf\u31f0-\u31ff\u3400-\u4dbf\u4e00-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua69d\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua7ca\ua7d0\ua7d1\ua7d3\ua7d5-\ua7d9\ua7f2-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua8fd\ua8fe\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\ua9e0-\ua9e4\ua9e6-\ua9ef\ua9fa-\ua9fe\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa7e-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a\uab5c-\uab69\uab70-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc"; +var nonASCIIidentifierChars = "\u200c\u200d\xb7\u0300-\u036f\u0387\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u0669\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7\u06e8\u06ea-\u06ed\u06f0-\u06f9\u0711\u0730-\u074a\u07a6-\u07b0\u07c0-\u07c9\u07eb-\u07f3\u07fd\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u0898-\u089f\u08ca-\u08e1\u08e3-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09cb-\u09cd\u09d7\u09e2\u09e3\u09e6-\u09ef\u09fe\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2\u0ae3\u0ae6-\u0aef\u0afa-\u0aff\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b55-\u0b57\u0b62\u0b63\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c00-\u0c04\u0c3c\u0c3e-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0c66-\u0c6f\u0c81-\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0ce6-\u0cef\u0d00-\u0d03\u0d3b\u0d3c\u0d3e-\u0d44\u0d46-\u0d48\u0d4a-\u0d4d\u0d57\u0d62\u0d63\u0d66-\u0d6f\u0d81-\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0de6-\u0def\u0df2\u0df3\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0e50-\u0e59\u0eb1\u0eb4-\u0ebc\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e\u0f3f\u0f71-\u0f84\u0f86\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102b-\u103e\u1040-\u1049\u1056-\u1059\u105e-\u1060\u1062-\u1064\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u1369-\u1371\u1712-\u1715\u1732-\u1734\u1752\u1753\u1772\u1773\u17b4-\u17d3\u17dd\u17e0-\u17e9\u180b-\u180d\u180f-\u1819\u18a9\u1920-\u192b\u1930-\u193b\u1946-\u194f\u19d0-\u19da\u1a17-\u1a1b\u1a55-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1ab0-\u1abd\u1abf-\u1ace\u1b00-\u1b04\u1b34-\u1b44\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1b82\u1ba1-\u1bad\u1bb0-\u1bb9\u1be6-\u1bf3\u1c24-\u1c37\u1c40-\u1c49\u1c50-\u1c59\u1cd0-\u1cd2\u1cd4-\u1ce8\u1ced\u1cf4\u1cf7-\u1cf9\u1dc0-\u1dff\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua620-\ua629\ua66f\ua674-\ua67d\ua69e\ua69f\ua6f0\ua6f1\ua802\ua806\ua80b\ua823-\ua827\ua82c\ua880\ua881\ua8b4-\ua8c5\ua8d0-\ua8d9\ua8e0-\ua8f1\ua8ff-\ua909\ua926-\ua92d\ua947-\ua953\ua980-\ua983\ua9b3-\ua9c0\ua9d0-\ua9d9\ua9e5\ua9f0-\ua9f9\uaa29-\uaa36\uaa43\uaa4c\uaa4d\uaa50-\uaa59\uaa7b-\uaa7d\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uaaeb-\uaaef\uaaf5\uaaf6\uabe3-\uabea\uabec\uabed\uabf0-\uabf9\ufb1e\ufe00-\ufe0f\ufe20-\ufe2f\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f"; var nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]"); var nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]"); @@ -42,10 +42,10 @@ nonASCIIidentifierStartChars = nonASCIIidentifierChars = null; // generated by bin/generate-identifier-regex.js // eslint-disable-next-line comma-spacing -var astralIdentifierStartCodes = [0,11,2,25,2,18,2,1,2,14,3,13,35,122,70,52,268,28,4,48,48,31,14,29,6,37,11,29,3,35,5,7,2,4,43,157,19,35,5,35,5,39,9,51,157,310,10,21,11,7,153,5,3,0,2,43,2,1,4,0,3,22,11,22,10,30,66,18,2,1,11,21,11,25,71,55,7,1,65,0,16,3,2,2,2,28,43,28,4,28,36,7,2,27,28,53,11,21,11,18,14,17,111,72,56,50,14,50,14,35,349,41,7,1,79,28,11,0,9,21,107,20,28,22,13,52,76,44,33,24,27,35,30,0,3,0,9,34,4,0,13,47,15,3,22,0,2,0,36,17,2,24,85,6,2,0,2,3,2,14,2,9,8,46,39,7,3,1,3,21,2,6,2,1,2,4,4,0,19,0,13,4,159,52,19,3,21,2,31,47,21,1,2,0,185,46,42,3,37,47,21,0,60,42,14,0,72,26,230,43,117,63,32,7,3,0,3,7,2,1,2,23,16,0,2,0,95,7,3,38,17,0,2,0,29,0,11,39,8,0,22,0,12,45,20,0,35,56,264,8,2,36,18,0,50,29,113,6,2,1,2,37,22,0,26,5,2,1,2,31,15,0,328,18,190,0,80,921,103,110,18,195,2749,1070,4050,582,8634,568,8,30,114,29,19,47,17,3,32,20,6,18,689,63,129,74,6,0,67,12,65,1,2,0,29,6135,9,1237,43,8,8952,286,50,2,18,3,9,395,2309,106,6,12,4,8,8,9,5991,84,2,70,2,1,3,0,3,1,3,3,2,11,2,0,2,6,2,64,2,3,3,7,2,6,2,27,2,3,2,4,2,0,4,6,2,339,3,24,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,7,2357,44,11,6,17,0,370,43,1301,196,60,67,8,0,1205,3,2,26,2,1,2,0,3,0,2,9,2,3,2,0,2,0,7,0,5,0,2,0,2,0,2,2,2,1,2,0,3,0,2,0,2,0,2,0,2,0,2,1,2,0,3,3,2,6,2,3,2,3,2,0,2,9,2,16,6,2,2,4,2,16,4421,42717,35,4148,12,221,3,5761,15,7472,3104,541,1507,4938]; +var astralIdentifierStartCodes = [0,11,2,25,2,18,2,1,2,14,3,13,35,122,70,52,268,28,4,48,48,31,14,29,6,37,11,29,3,35,5,7,2,4,43,157,19,35,5,35,5,39,9,51,13,10,2,14,2,6,2,1,2,10,2,14,2,6,2,1,68,310,10,21,11,7,25,5,2,41,2,8,70,5,3,0,2,43,2,1,4,0,3,22,11,22,10,30,66,18,2,1,11,21,11,25,71,55,7,1,65,0,16,3,2,2,2,28,43,28,4,28,36,7,2,27,28,53,11,21,11,18,14,17,111,72,56,50,14,50,14,35,349,41,7,1,79,28,11,0,9,21,43,17,47,20,28,22,13,52,58,1,3,0,14,44,33,24,27,35,30,0,3,0,9,34,4,0,13,47,15,3,22,0,2,0,36,17,2,24,85,6,2,0,2,3,2,14,2,9,8,46,39,7,3,1,3,21,2,6,2,1,2,4,4,0,19,0,13,4,159,52,19,3,21,2,31,47,21,1,2,0,185,46,42,3,37,47,21,0,60,42,14,0,72,26,38,6,186,43,117,63,32,7,3,0,3,7,2,1,2,23,16,0,2,0,95,7,3,38,17,0,2,0,29,0,11,39,8,0,22,0,12,45,20,0,19,72,264,8,2,36,18,0,50,29,113,6,2,1,2,37,22,0,26,5,2,1,2,31,15,0,328,18,190,0,80,921,103,110,18,195,2637,96,16,1070,4050,582,8634,568,8,30,18,78,18,29,19,47,17,3,32,20,6,18,689,63,129,74,6,0,67,12,65,1,2,0,29,6135,9,1237,43,8,8936,3,2,6,2,1,2,290,46,2,18,3,9,395,2309,106,6,12,4,8,8,9,5991,84,2,70,2,1,3,0,3,1,3,3,2,11,2,0,2,6,2,64,2,3,3,7,2,6,2,27,2,3,2,4,2,0,4,6,2,339,3,24,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,7,1845,30,482,44,11,6,17,0,322,29,19,43,1269,6,2,3,2,1,2,14,2,196,60,67,8,0,1205,3,2,26,2,1,2,0,3,0,2,9,2,3,2,0,2,0,7,0,5,0,2,0,2,0,2,2,2,1,2,0,3,0,2,0,2,0,2,0,2,0,2,1,2,0,3,3,2,6,2,3,2,3,2,0,2,9,2,16,6,2,2,4,2,16,4421,42719,33,4152,8,221,3,5761,15,7472,3104,541,1507,4938]; // eslint-disable-next-line comma-spacing -var astralIdentifierCodes = [509,0,227,0,150,4,294,9,1368,2,2,1,6,3,41,2,5,0,166,1,574,3,9,9,370,1,154,10,176,2,54,14,32,9,16,3,46,10,54,9,7,2,37,13,2,9,6,1,45,0,13,2,49,13,9,3,2,11,83,11,7,0,161,11,6,9,7,3,56,1,2,6,3,1,3,2,10,0,11,1,3,6,4,4,193,17,10,9,5,0,82,19,13,9,214,6,3,8,28,1,83,16,16,9,82,12,9,9,84,14,5,9,243,14,166,9,71,5,2,1,3,3,2,0,2,1,13,9,120,6,3,6,4,0,29,9,41,6,2,3,9,0,10,10,47,15,406,7,2,7,17,9,57,21,2,13,123,5,4,0,2,1,2,6,2,0,9,9,49,4,2,1,2,4,9,9,330,3,19306,9,135,4,60,6,26,9,1014,0,2,54,8,3,82,0,12,1,19628,1,5319,4,4,5,9,7,3,6,31,3,149,2,1418,49,513,54,5,49,9,0,15,0,23,4,2,14,1361,6,2,16,3,6,2,1,2,4,262,6,10,9,419,13,1495,6,110,6,6,9,4759,9,787719,239]; +var astralIdentifierCodes = [509,0,227,0,150,4,294,9,1368,2,2,1,6,3,41,2,5,0,166,1,574,3,9,9,370,1,154,10,50,3,123,2,54,14,32,10,3,1,11,3,46,10,8,0,46,9,7,2,37,13,2,9,6,1,45,0,13,2,49,13,9,3,2,11,83,11,7,0,161,11,6,9,7,3,56,1,2,6,3,1,3,2,10,0,11,1,3,6,4,4,193,17,10,9,5,0,82,19,13,9,214,6,3,8,28,1,83,16,16,9,82,12,9,9,84,14,5,9,243,14,166,9,71,5,2,1,3,3,2,0,2,1,13,9,120,6,3,6,4,0,29,9,41,6,2,3,9,0,10,10,47,15,406,7,2,7,17,9,57,21,2,13,123,5,4,0,2,1,2,6,2,0,9,9,49,4,2,1,2,4,9,9,330,3,19306,9,87,9,39,4,60,6,26,9,1014,0,2,54,8,3,82,0,12,1,19628,1,4706,45,3,22,543,4,4,5,9,7,3,6,31,3,149,2,1418,49,513,54,5,49,9,0,15,0,23,4,2,14,1361,6,2,16,3,6,2,1,2,4,262,6,10,9,357,0,62,13,1495,6,110,6,6,9,4759,9,787719,239]; // This has a complexity linear to the value of the code. The // assumption is that looking up astral identifier characters is @@ -250,6 +250,17 @@ function isNewLine(code) { return code === 10 || code === 13 || code === 0x2028 || code === 0x2029 } +function nextLineBreak(code, from, end) { + if ( end === void 0 ) end = code.length; + + for (var i = from; i < end; i++) { + var next = code.charCodeAt(i); + if (isNewLine(next)) + { return i < end - 1 && next === 13 && code.charCodeAt(i + 1) === 10 ? i + 2 : i + 1 } + } + return -1 +} + var nonASCIIwhitespace = /[\u1680\u2000-\u200a\u202f\u205f\u3000\ufeff]/; var skipWhiteSpace = /(?:\s|\/\/.*|\/\*[^]*?\*\/)*/g; @@ -258,11 +269,9 @@ var ref = Object.prototype; var hasOwnProperty = ref.hasOwnProperty; var toString = ref.toString; -// Checks if an object has a property. - -function has(obj, propName) { - return hasOwnProperty.call(obj, propName) -} +var hasOwn = Object.hasOwn || (function (obj, propName) { return ( + hasOwnProperty.call(obj, propName) +); }); var isArray = Array.isArray || (function (obj) { return ( toString.call(obj) === "[object Array]" @@ -272,6 +281,8 @@ function wordsRegexp(words) { return new RegExp("^(?:" + words.replace(/ /g, "|") + ")$") } +var loneSurrogate = /(?:[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])/; + // These are used when `options.locations` is on, for the // `startLoc` and `endLoc` properties. @@ -298,14 +309,10 @@ var SourceLocation = function SourceLocation(p, start, end) { function getLineInfo(input, offset) { for (var line = 1, cur = 0;;) { - lineBreakG.lastIndex = cur; - var match = lineBreakG.exec(input); - if (match && match.index < offset) { - ++line; - cur = match.index + match[0].length; - } else { - return new Position(line, offset - cur) - } + var nextBreak = nextLineBreak(input, cur, offset); + if (nextBreak < 0) { return new Position(line, offset - cur) } + ++line; + cur = nextBreak; } } @@ -411,7 +418,7 @@ function getOptions(opts) { var options = {}; for (var opt in defaultOptions) - { options[opt] = opts && has(opts, opt) ? opts[opt] : defaultOptions[opt]; } + { options[opt] = opts && hasOwn(opts, opt) ? opts[opt] : defaultOptions[opt]; } if (options.ecmaVersion === "latest") { options.ecmaVersion = 1e8; @@ -1577,7 +1584,7 @@ pp$8.exitClassBody = function() { var parent = len === 0 ? null : this.privateNameStack[len - 1]; for (var i = 0; i < used.length; ++i) { var id = used[i]; - if (!has(declared, id.name)) { + if (!hasOwn(declared, id.name)) { if (parent) { parent.used.push(id); } else { @@ -1630,7 +1637,7 @@ pp$8.parseExport = function(node, exports) { if (this.eat(types$1.star)) { if (this.options.ecmaVersion >= 11) { if (this.eatContextual("as")) { - node.exported = this.parseIdent(true); + node.exported = this.parseModuleExportName(); this.checkExport(exports, node.exported.name, this.lastTokStart); } else { node.exported = null; @@ -1682,6 +1689,10 @@ pp$8.parseExport = function(node, exports) { this.checkUnreserved(spec.local); // check if export is defined this.checkLocalExport(spec.local); + + if (spec.local.type === "Literal") { + this.raise(spec.local.start, "A string literal cannot be used as an exported binding without `from`."); + } } node.source = null; @@ -1693,7 +1704,7 @@ pp$8.parseExport = function(node, exports) { pp$8.checkExport = function(exports, name, pos) { if (!exports) { return } - if (has(exports, name)) + if (hasOwn(exports, name)) { this.raiseRecoverable(pos, "Duplicate export '" + name + "'"); } exports[name] = true; }; @@ -1757,9 +1768,13 @@ pp$8.parseExportSpecifiers = function(exports) { } else { first = false; } var node = this.startNode(); - node.local = this.parseIdent(true); - node.exported = this.eatContextual("as") ? this.parseIdent(true) : node.local; - this.checkExport(exports, node.exported.name, node.exported.start); + node.local = this.parseModuleExportName(); + node.exported = this.eatContextual("as") ? this.parseModuleExportName() : node.local; + this.checkExport( + exports, + node.exported[node.exported.type === "Identifier" ? "name" : "value"], + node.exported.start + ); nodes.push(this.finishNode(node, "ExportSpecifier")); } return nodes @@ -1811,7 +1826,7 @@ pp$8.parseImportSpecifiers = function() { } else { first = false; } var node$2 = this.startNode(); - node$2.imported = this.parseIdent(true); + node$2.imported = this.parseModuleExportName(); if (this.eatContextual("as")) { node$2.local = this.parseIdent(); } else { @@ -1824,6 +1839,17 @@ pp$8.parseImportSpecifiers = function() { return nodes }; +pp$8.parseModuleExportName = function() { + if (this.options.ecmaVersion >= 13 && this.type === types$1.string) { + var stringLiteral = this.parseLiteral(this.value); + if (loneSurrogate.test(stringLiteral.value)) { + this.raise(stringLiteral.start, "An export name cannot include a lone surrogate."); + } + return stringLiteral + } + return this.parseIdent(true) +}; + // Set `ExpressionStatement#directive` property for directive prologues. pp$8.adaptDirectivePrologue = function(statements) { for (var i = 0; i < statements.length && this.isDirectiveCandidate(statements[i]); ++i) { @@ -2097,7 +2123,7 @@ pp$7.checkLValSimple = function(expr, bindingType, checkClashes) { if (bindingType === BIND_LEXICAL && expr.name === "let") { this.raiseRecoverable(expr.start, "let is disallowed as a lexically bound name"); } if (checkClashes) { - if (has(checkClashes, expr.name)) + if (hasOwn(checkClashes, expr.name)) { this.raiseRecoverable(expr.start, "Argument name clash"); } checkClashes[expr.name] = true; } @@ -3592,26 +3618,30 @@ var ecma9BinaryProperties = "ASCII ASCII_Hex_Digit AHex Alphabetic Alpha Any Ass var ecma10BinaryProperties = ecma9BinaryProperties + " Extended_Pictographic"; var ecma11BinaryProperties = ecma10BinaryProperties; var ecma12BinaryProperties = ecma11BinaryProperties + " EBase EComp EMod EPres ExtPict"; +var ecma13BinaryProperties = ecma12BinaryProperties; var unicodeBinaryProperties = { 9: ecma9BinaryProperties, 10: ecma10BinaryProperties, 11: ecma11BinaryProperties, - 12: ecma12BinaryProperties + 12: ecma12BinaryProperties, + 13: ecma13BinaryProperties }; // #table-unicode-general-category-values var unicodeGeneralCategoryValues = "Cased_Letter LC Close_Punctuation Pe Connector_Punctuation Pc Control Cc cntrl Currency_Symbol Sc Dash_Punctuation Pd Decimal_Number Nd digit Enclosing_Mark Me Final_Punctuation Pf Format Cf Initial_Punctuation Pi Letter L Letter_Number Nl Line_Separator Zl Lowercase_Letter Ll Mark M Combining_Mark Math_Symbol Sm Modifier_Letter Lm Modifier_Symbol Sk Nonspacing_Mark Mn Number N Open_Punctuation Ps Other C Other_Letter Lo Other_Number No Other_Punctuation Po Other_Symbol So Paragraph_Separator Zp Private_Use Co Punctuation P punct Separator Z Space_Separator Zs Spacing_Mark Mc Surrogate Cs Symbol S Titlecase_Letter Lt Unassigned Cn Uppercase_Letter Lu"; // #table-unicode-script-values -var ecma9ScriptValues = "Adlam Adlm Ahom Ahom Anatolian_Hieroglyphs Hluw Arabic Arab Armenian Armn Avestan Avst Balinese Bali Bamum Bamu Bassa_Vah Bass Batak Batk Bengali Beng Bhaiksuki Bhks Bopomofo Bopo Brahmi Brah Braille Brai Buginese Bugi Buhid Buhd Canadian_Aboriginal Cans Carian Cari Caucasian_Albanian Aghb Chakma Cakm Cham Cham Cherokee Cher Common Zyyy Coptic Copt Qaac Cuneiform Xsux Cypriot Cprt Cyrillic Cyrl Deseret Dsrt Devanagari Deva Duployan Dupl Egyptian_Hieroglyphs Egyp Elbasan Elba Ethiopic Ethi Georgian Geor Glagolitic Glag Gothic Goth Grantha Gran Greek Grek Gujarati Gujr Gurmukhi Guru Han Hani Hangul Hang Hanunoo Hano Hatran Hatr Hebrew Hebr Hiragana Hira Imperial_Aramaic Armi Inherited Zinh Qaai Inscriptional_Pahlavi Phli Inscriptional_Parthian Prti Javanese Java Kaithi Kthi Kannada Knda Katakana Kana Kayah_Li Kali Kharoshthi Khar Khmer Khmr Khojki Khoj Khudawadi Sind Lao Laoo Latin Latn Lepcha Lepc Limbu Limb Linear_A Lina Linear_B Linb Lisu Lisu Lycian Lyci Lydian Lydi Mahajani Mahj Malayalam Mlym Mandaic Mand Manichaean Mani Marchen Marc Masaram_Gondi Gonm Meetei_Mayek Mtei Mende_Kikakui Mend Meroitic_Cursive Merc Meroitic_Hieroglyphs Mero Miao Plrd Modi Modi Mongolian Mong Mro Mroo Multani Mult Myanmar Mymr Nabataean Nbat New_Tai_Lue Talu Newa Newa Nko Nkoo Nushu Nshu Ogham Ogam Ol_Chiki Olck Old_Hungarian Hung Old_Italic Ital Old_North_Arabian Narb Old_Permic Perm Old_Persian Xpeo Old_South_Arabian Sarb Old_Turkic Orkh Oriya Orya Osage Osge Osmanya Osma Pahawh_Hmong Hmng Palmyrene Palm Pau_Cin_Hau Pauc Phags_Pa Phag Phoenician Phnx Psalter_Pahlavi Phlp Rejang Rjng Runic Runr Samaritan Samr Saurashtra Saur Sharada Shrd Shavian Shaw Siddham Sidd SignWriting Sgnw Sinhala Sinh Sora_Sompeng Sora Soyombo Soyo Sundanese Sund Syloti_Nagri Sylo Syriac Syrc Tagalog Tglg Tagbanwa Tagb Tai_Le Tale Tai_Tham Lana Tai_Viet Tavt Takri Takr Tamil Taml Tangut Tang Telugu Telu Thaana Thaa Thai Thai Tibetan Tibt Tifinagh Tfng Tirhuta Tirh Ugaritic Ugar Vai Vaii Warang_Citi Wara Yi Yiii Zanabazar_Square Zanb"; +var ecma9ScriptValues = "Adlam Adlm Ahom Anatolian_Hieroglyphs Hluw Arabic Arab Armenian Armn Avestan Avst Balinese Bali Bamum Bamu Bassa_Vah Bass Batak Batk Bengali Beng Bhaiksuki Bhks Bopomofo Bopo Brahmi Brah Braille Brai Buginese Bugi Buhid Buhd Canadian_Aboriginal Cans Carian Cari Caucasian_Albanian Aghb Chakma Cakm Cham Cham Cherokee Cher Common Zyyy Coptic Copt Qaac Cuneiform Xsux Cypriot Cprt Cyrillic Cyrl Deseret Dsrt Devanagari Deva Duployan Dupl Egyptian_Hieroglyphs Egyp Elbasan Elba Ethiopic Ethi Georgian Geor Glagolitic Glag Gothic Goth Grantha Gran Greek Grek Gujarati Gujr Gurmukhi Guru Han Hani Hangul Hang Hanunoo Hano Hatran Hatr Hebrew Hebr Hiragana Hira Imperial_Aramaic Armi Inherited Zinh Qaai Inscriptional_Pahlavi Phli Inscriptional_Parthian Prti Javanese Java Kaithi Kthi Kannada Knda Katakana Kana Kayah_Li Kali Kharoshthi Khar Khmer Khmr Khojki Khoj Khudawadi Sind Lao Laoo Latin Latn Lepcha Lepc Limbu Limb Linear_A Lina Linear_B Linb Lisu Lisu Lycian Lyci Lydian Lydi Mahajani Mahj Malayalam Mlym Mandaic Mand Manichaean Mani Marchen Marc Masaram_Gondi Gonm Meetei_Mayek Mtei Mende_Kikakui Mend Meroitic_Cursive Merc Meroitic_Hieroglyphs Mero Miao Plrd Modi Mongolian Mong Mro Mroo Multani Mult Myanmar Mymr Nabataean Nbat New_Tai_Lue Talu Newa Newa Nko Nkoo Nushu Nshu Ogham Ogam Ol_Chiki Olck Old_Hungarian Hung Old_Italic Ital Old_North_Arabian Narb Old_Permic Perm Old_Persian Xpeo Old_South_Arabian Sarb Old_Turkic Orkh Oriya Orya Osage Osge Osmanya Osma Pahawh_Hmong Hmng Palmyrene Palm Pau_Cin_Hau Pauc Phags_Pa Phag Phoenician Phnx Psalter_Pahlavi Phlp Rejang Rjng Runic Runr Samaritan Samr Saurashtra Saur Sharada Shrd Shavian Shaw Siddham Sidd SignWriting Sgnw Sinhala Sinh Sora_Sompeng Sora Soyombo Soyo Sundanese Sund Syloti_Nagri Sylo Syriac Syrc Tagalog Tglg Tagbanwa Tagb Tai_Le Tale Tai_Tham Lana Tai_Viet Tavt Takri Takr Tamil Taml Tangut Tang Telugu Telu Thaana Thaa Thai Thai Tibetan Tibt Tifinagh Tfng Tirhuta Tirh Ugaritic Ugar Vai Vaii Warang_Citi Wara Yi Yiii Zanabazar_Square Zanb"; var ecma10ScriptValues = ecma9ScriptValues + " Dogra Dogr Gunjala_Gondi Gong Hanifi_Rohingya Rohg Makasar Maka Medefaidrin Medf Old_Sogdian Sogo Sogdian Sogd"; var ecma11ScriptValues = ecma10ScriptValues + " Elymaic Elym Nandinagari Nand Nyiakeng_Puachue_Hmong Hmnp Wancho Wcho"; var ecma12ScriptValues = ecma11ScriptValues + " Chorasmian Chrs Diak Dives_Akuru Khitan_Small_Script Kits Yezi Yezidi"; +var ecma13ScriptValues = ecma12ScriptValues + " Cypro_Minoan Cpmn Old_Uyghur Ougr Tangsa Tnsa Toto Vithkuqi Vith"; var unicodeScriptValues = { 9: ecma9ScriptValues, 10: ecma10ScriptValues, 11: ecma11ScriptValues, - 12: ecma12ScriptValues + 12: ecma12ScriptValues, + 13: ecma13ScriptValues }; var data = {}; @@ -3629,17 +3659,19 @@ function buildUnicodeData(ecmaVersion) { d.nonBinary.sc = d.nonBinary.Script; d.nonBinary.scx = d.nonBinary.Script_Extensions; } -buildUnicodeData(9); -buildUnicodeData(10); -buildUnicodeData(11); -buildUnicodeData(12); + +for (var i = 0, list = [9, 10, 11, 12, 13]; i < list.length; i += 1) { + var ecmaVersion = list[i]; + + buildUnicodeData(ecmaVersion); +} var pp$1 = Parser.prototype; var RegExpValidationState = function RegExpValidationState(parser) { this.parser = parser; this.validFlags = "gim" + (parser.options.ecmaVersion >= 6 ? "uy" : "") + (parser.options.ecmaVersion >= 9 ? "s" : "") + (parser.options.ecmaVersion >= 13 ? "d" : ""); - this.unicodeProperties = data[parser.options.ecmaVersion >= 12 ? 12 : parser.options.ecmaVersion]; + this.unicodeProperties = data[parser.options.ecmaVersion >= 13 ? 13 : parser.options.ecmaVersion]; this.source = ""; this.flags = ""; this.start = 0; @@ -4438,7 +4470,7 @@ pp$1.regexp_eatUnicodePropertyValueExpression = function(state) { return false }; pp$1.regexp_validateUnicodePropertyNameAndValue = function(state, name, value) { - if (!has(state.unicodeProperties.nonBinary, name)) + if (!hasOwn(state.unicodeProperties.nonBinary, name)) { state.raise("Invalid property name"); } if (!state.unicodeProperties.nonBinary[name].test(value)) { state.raise("Invalid property value"); } @@ -4790,11 +4822,9 @@ pp.skipBlockComment = function() { if (end === -1) { this.raise(this.pos - 2, "Unterminated comment"); } this.pos = end + 2; if (this.options.locations) { - lineBreakG.lastIndex = start; - var match; - while ((match = lineBreakG.exec(this.input)) && match.index < this.pos) { + for (var nextBreak = (void 0), pos = start; (nextBreak = nextLineBreak(this.input, pos, this.pos)) > -1;) { ++this.curLine; - this.lineStart = match.index + match[0].length; + pos = this.lineStart = nextBreak; } } if (this.options.onComment) @@ -5505,7 +5535,7 @@ pp.readWord = function() { // Acorn is a tiny, fast JavaScript parser written in JavaScript. -var version = "8.6.0"; +var version = "8.7.0"; Parser.acorn = { Parser: Parser, diff --git a/deps/acorn/acorn/dist/bin.js b/deps/acorn/acorn/dist/bin.js index 675cab9ac89cae..8e645009450b64 100644 --- a/deps/acorn/acorn/dist/bin.js +++ b/deps/acorn/acorn/dist/bin.js @@ -30,7 +30,7 @@ var options = {}; function help(status) { var print = (status === 0) ? console.log : console.error; print("usage: " + path.basename(process.argv[1]) + " [--ecma3|--ecma5|--ecma6|--ecma7|--ecma8|--ecma9|...|--ecma2015|--ecma2016|--ecma2017|--ecma2018|...]"); - print(" [--tokenize] [--locations] [---allow-hash-bang] [--allow-await-outside-function] [--compact] [--silent] [--module] [--help] [--] [...]"); + print(" [--tokenize] [--locations] [--allow-hash-bang] [--allow-await-outside-function] [--compact] [--silent] [--module] [--help] [--] [...]"); process.exit(status); } diff --git a/deps/acorn/acorn/package.json b/deps/acorn/acorn/package.json index e242a235e00c84..8e2edc65cff81c 100644 --- a/deps/acorn/acorn/package.json +++ b/deps/acorn/acorn/package.json @@ -16,7 +16,7 @@ ], "./package.json": "./package.json" }, - "version": "8.6.0", + "version": "8.7.0", "engines": {"node": ">=0.4.0"}, "maintainers": [ { @@ -40,7 +40,7 @@ }, "license": "MIT", "scripts": { - "prepare": "cd ..; npm run build:main && npm run build:bin" + "prepare": "cd ..; npm run build:main" }, "bin": {"acorn": "./bin/acorn"} } diff --git a/deps/cares/src/lib/ares_expand_name.c b/deps/cares/src/lib/ares_expand_name.c index fcd88a2a42eb42..6c7a35a715bf47 100644 --- a/deps/cares/src/lib/ares_expand_name.c +++ b/deps/cares/src/lib/ares_expand_name.c @@ -64,6 +64,8 @@ static int ares__isprint(int ch) * - underscores which are used in SRV records. * - Forward slashes such as are used for classless in-addr.arpa * delegation (CNAMEs) + * - Asterisks may be used for wildcard domains in CNAMEs as seen in the + * real world. * While RFC 2181 section 11 does state not to do validation, * that applies to servers, not clients. Vulnerabilities have been * reported when this validation is not performed. Security is more @@ -71,7 +73,7 @@ static int ares__isprint(int ch) * anyhow). */ static int is_hostnamech(int ch) { - /* [A-Za-z0-9-._/] + /* [A-Za-z0-9-*._/] * Don't use isalnum() as it is locale-specific */ if (ch >= 'A' && ch <= 'Z') @@ -80,7 +82,7 @@ static int is_hostnamech(int ch) return 1; if (ch >= '0' && ch <= '9') return 1; - if (ch == '-' || ch == '.' || ch == '_' || ch == '/') + if (ch == '-' || ch == '.' || ch == '_' || ch == '/' || ch == '*') return 1; return 0; diff --git a/deps/icu-small/LICENSE b/deps/icu-small/LICENSE index 970ae074cbf555..80b587723a67f7 100644 --- a/deps/icu-small/LICENSE +++ b/deps/icu-small/LICENSE @@ -1,6 +1,19 @@ -COPYRIGHT AND PERMISSION NOTICE (ICU 58 and later) +UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE -Copyright © 1991-2020 Unicode, Inc. All rights reserved. +See Terms of Use +for definitions of Unicode Inc.’s Data Files and Software. + +NOTICE TO USER: Carefully read the following legal agreement. +BY DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S +DATA FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), +YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. +IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE +THE DATA FILES OR SOFTWARE. + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 1991-2022 Unicode, Inc. All rights reserved. Distributed under the Terms of Use in https://www.unicode.org/copyright.html. Permission is hereby granted, free of charge, to any person obtaining @@ -32,7 +45,7 @@ shall not be used in advertising or otherwise to promote the sale, use or other dealings in these Data Files or Software without prior written authorization of the copyright holder. ---------------------- +---------------------------------------------------------------------- Third-Party Software Licenses @@ -40,7 +53,9 @@ This section contains third-party software notices and/or additional terms for licensed third-party software components included within ICU libraries. -1. ICU License - ICU 1.8.1 to ICU 57.1 +---------------------------------------------------------------------- + +ICU License - ICU 1.8.1 to ICU 57.1 COPYRIGHT AND PERMISSION NOTICE @@ -75,7 +90,9 @@ of the copyright holder. All trademarks and registered trademarks mentioned herein are the property of their respective owners. -2. Chinese/Japanese Word Break Dictionary Data (cjdict.txt) +---------------------------------------------------------------------- + +Chinese/Japanese Word Break Dictionary Data (cjdict.txt) # The Google Chrome software developed by Google is licensed under # the BSD license. Other software included in this distribution is @@ -279,7 +296,9 @@ property of their respective owners. # # ---------------COPYING.ipadic-----END---------------------------------- -3. Lao Word Break Dictionary Data (laodict.txt) +---------------------------------------------------------------------- + +Lao Word Break Dictionary Data (laodict.txt) # Copyright (C) 2016 and later: Unicode, Inc. and others. # License & terms of use: http://www.unicode.org/copyright.html @@ -319,7 +338,9 @@ property of their respective owners. # OF THE POSSIBILITY OF SUCH DAMAGE. # -------------------------------------------------------------------------- -4. Burmese Word Break Dictionary Data (burmesedict.txt) +---------------------------------------------------------------------- + +Burmese Word Break Dictionary Data (burmesedict.txt) # Copyright (c) 2014 International Business Machines Corporation # and others. All Rights Reserved. @@ -359,7 +380,9 @@ property of their respective owners. # SUCH DAMAGE. # -------------------------------------------------------------------------- -5. Time Zone Database +---------------------------------------------------------------------- + +Time Zone Database ICU uses the public domain data and code derived from Time Zone Database for its time zone support. The ownership of the TZ database @@ -382,7 +405,9 @@ Database section 7. # making a contribution to the database or code waives all rights to # future claims in that contribution or in the TZ Database. -6. Google double-conversion +---------------------------------------------------------------------- + +Google double-conversion Copyright 2006-2011, the V8 project authors. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -410,3 +435,85 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- + +File: aclocal.m4 (only for ICU4C) +Section: pkg.m4 - Macros to locate and utilise pkg-config. + + +Copyright © 2004 Scott James Remnant . +Copyright © 2012-2015 Dan Nicholson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +02111-1307, USA. + +As a special exception to the GNU General Public License, if you +distribute this file as part of a program that contains a +configuration script generated by Autoconf, you may include it under +the same distribution terms that you use for the rest of that +program. + + +(The condition for the exception is fulfilled because +ICU4C includes a configuration script generated by Autoconf, +namely the `configure` script.) + +---------------------------------------------------------------------- + +File: config.guess (only for ICU4C) + + +This file is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see . + +As a special exception to the GNU General Public License, if you +distribute this file as part of a program that contains a +configuration script generated by Autoconf, you may include it under +the same distribution terms that you use for the rest of that +program. This Exception is an additional permission under section 7 +of the GNU General Public License, version 3 ("GPLv3"). + + +(The condition for the exception is fulfilled because +ICU4C includes a configuration script generated by Autoconf, +namely the `configure` script.) + +---------------------------------------------------------------------- + +File: install-sh (only for ICU4C) + + +Copyright 1991 by the Massachusetts Institute of Technology + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of M.I.T. not be used in advertising or +publicity pertaining to distribution of the software without specific, +written prior permission. M.I.T. makes no representations about the +suitability of this software for any purpose. It is provided "as is" +without express or implied warranty. diff --git a/deps/icu-small/README-FULL-ICU.txt b/deps/icu-small/README-FULL-ICU.txt index a6afcf4952945e..0c33485768901c 100644 --- a/deps/icu-small/README-FULL-ICU.txt +++ b/deps/icu-small/README-FULL-ICU.txt @@ -1,8 +1,8 @@ ICU sources - auto generated by shrink-icu-src.py This directory contains the ICU subset used by --with-intl=full-icu -It is a strict subset of ICU 70 source files with the following exception(s): -* deps/icu-small/source/data/in/icudt70l.dat.bz2 : compressed data file +It is a strict subset of ICU 71 source files with the following exception(s): +* deps/icu-small/source/data/in/icudt71l.dat.bz2 : compressed data file To rebuild this directory, see ../../tools/icu/README.md diff --git a/deps/icu-small/source/common/BUILD b/deps/icu-small/source/common/BUILD.bazel similarity index 100% rename from deps/icu-small/source/common/BUILD rename to deps/icu-small/source/common/BUILD.bazel diff --git a/deps/icu-small/source/common/brkeng.cpp b/deps/icu-small/source/common/brkeng.cpp index 52e9c53621dca2..dc9fb99bf1972d 100644 --- a/deps/icu-small/source/common/brkeng.cpp +++ b/deps/icu-small/source/common/brkeng.cpp @@ -79,6 +79,7 @@ UnhandledEngine::findBreaks( UText *text, int32_t /* startPos */, int32_t endPos, UVector32 &/*foundBreaks*/, + UBool /* isPhraseBreaking */, UErrorCode &status) const { if (U_FAILURE(status)) return 0; UChar32 c = utext_current32(text); diff --git a/deps/icu-small/source/common/brkeng.h b/deps/icu-small/source/common/brkeng.h index 6843f1cc953511..127ba59e186f23 100644 --- a/deps/icu-small/source/common/brkeng.h +++ b/deps/icu-small/source/common/brkeng.h @@ -75,6 +75,7 @@ class LanguageBreakEngine : public UMemory { int32_t startPos, int32_t endPos, UVector32 &foundBreaks, + UBool isPhraseBreaking, UErrorCode &status) const = 0; }; @@ -194,6 +195,7 @@ class UnhandledEngine : public LanguageBreakEngine { int32_t startPos, int32_t endPos, UVector32 &foundBreaks, + UBool isPhraseBreaking, UErrorCode &status) const override; /** diff --git a/deps/icu-small/source/common/brkiter.cpp b/deps/icu-small/source/common/brkiter.cpp index 8b228acf2c384c..8a1915880ee229 100644 --- a/deps/icu-small/source/common/brkiter.cpp +++ b/deps/icu-small/source/common/brkiter.cpp @@ -30,6 +30,7 @@ #include "unicode/ures.h" #include "unicode/ustring.h" #include "unicode/filteredbrk.h" +#include "bytesinkutil.h" #include "ucln_cmn.h" #include "cstring.h" #include "umutex.h" @@ -115,7 +116,7 @@ BreakIterator::buildInstance(const Locale& loc, const char *type, UErrorCode &st } // Create a RuleBasedBreakIterator - result = new RuleBasedBreakIterator(file, status); + result = new RuleBasedBreakIterator(file, uprv_strstr(type, "phrase") != NULL, status); // If there is a result, set the valid locale and actual locale, and the kind if (U_SUCCESS(status) && result != NULL) { @@ -408,7 +409,6 @@ BreakIterator::makeInstance(const Locale& loc, int32_t kind, UErrorCode& status) if (U_FAILURE(status)) { return NULL; } - char lbType[kKeyValueLenMax]; BreakIterator *result = NULL; switch (kind) { @@ -428,18 +428,29 @@ BreakIterator::makeInstance(const Locale& loc, int32_t kind, UErrorCode& status) break; case UBRK_LINE: { + char lb_lw[kKeyValueLenMax]; UTRACE_ENTRY(UTRACE_UBRK_CREATE_LINE); - uprv_strcpy(lbType, "line"); - char lbKeyValue[kKeyValueLenMax] = {0}; + uprv_strcpy(lb_lw, "line"); UErrorCode kvStatus = U_ZERO_ERROR; - int32_t kLen = loc.getKeywordValue("lb", lbKeyValue, kKeyValueLenMax, kvStatus); - if (U_SUCCESS(kvStatus) && kLen > 0 && (uprv_strcmp(lbKeyValue,"strict")==0 || uprv_strcmp(lbKeyValue,"normal")==0 || uprv_strcmp(lbKeyValue,"loose")==0)) { - uprv_strcat(lbType, "_"); - uprv_strcat(lbType, lbKeyValue); + CharString value; + CharStringByteSink valueSink(&value); + loc.getKeywordValue("lb", valueSink, kvStatus); + if (U_SUCCESS(kvStatus) && (value == "strict" || value == "normal" || value == "loose")) { + uprv_strcat(lb_lw, "_"); + uprv_strcat(lb_lw, value.data()); } - result = BreakIterator::buildInstance(loc, lbType, status); + // lw=phrase is only supported in Japanese. + if (uprv_strcmp(loc.getLanguage(), "ja") == 0) { + value.clear(); + loc.getKeywordValue("lw", valueSink, kvStatus); + if (U_SUCCESS(kvStatus) && value == "phrase") { + uprv_strcat(lb_lw, "_"); + uprv_strcat(lb_lw, value.data()); + } + } + result = BreakIterator::buildInstance(loc, lb_lw, status); - UTRACE_DATA1(UTRACE_INFO, "lb=%s", lbKeyValue); + UTRACE_DATA1(UTRACE_INFO, "lb_lw=%s", lb_lw); UTRACE_EXIT_STATUS(status); } break; diff --git a/deps/icu-small/source/common/dictbe.cpp b/deps/icu-small/source/common/dictbe.cpp index 4d158e3226db28..4fdbdf2760f1c6 100644 --- a/deps/icu-small/source/common/dictbe.cpp +++ b/deps/icu-small/source/common/dictbe.cpp @@ -17,7 +17,10 @@ #include "dictbe.h" #include "unicode/uniset.h" #include "unicode/chariter.h" +#include "unicode/resbund.h" #include "unicode/ubrk.h" +#include "unicode/usetiter.h" +#include "ubrkimpl.h" #include "utracimp.h" #include "uvectr32.h" #include "uvector.h" @@ -48,6 +51,7 @@ DictionaryBreakEngine::findBreaks( UText *text, int32_t startPos, int32_t endPos, UVector32 &foundBreaks, + UBool isPhraseBreaking, UErrorCode& status) const { if (U_FAILURE(status)) return 0; (void)startPos; // TODO: remove this param? @@ -68,7 +72,7 @@ DictionaryBreakEngine::findBreaks( UText *text, } rangeStart = start; rangeEnd = current; - result = divideUpDictionaryRange(text, rangeStart, rangeEnd, foundBreaks, status); + result = divideUpDictionaryRange(text, rangeStart, rangeEnd, foundBreaks, isPhraseBreaking, status); utext_setNativeIndex(text, current); return result; @@ -199,13 +203,13 @@ ThaiBreakEngine::ThaiBreakEngine(DictionaryMatcher *adoptDictionary, UErrorCode { UTRACE_ENTRY(UTRACE_UBRK_CREATE_BREAK_ENGINE); UTRACE_DATA1(UTRACE_INFO, "dictbe=%s", "Thai"); - fThaiWordSet.applyPattern(UNICODE_STRING_SIMPLE("[[:Thai:]&[:LineBreak=SA:]]"), status); + UnicodeSet thaiWordSet(UnicodeString(u"[[:Thai:]&[:LineBreak=SA:]]"), status); if (U_SUCCESS(status)) { - setCharacters(fThaiWordSet); + setCharacters(thaiWordSet); } - fMarkSet.applyPattern(UNICODE_STRING_SIMPLE("[[:Thai:]&[:LineBreak=SA:]&[:M:]]"), status); + fMarkSet.applyPattern(UnicodeString(u"[[:Thai:]&[:LineBreak=SA:]&[:M:]]"), status); fMarkSet.add(0x0020); - fEndWordSet = fThaiWordSet; + fEndWordSet = thaiWordSet; fEndWordSet.remove(0x0E31); // MAI HAN-AKAT fEndWordSet.remove(0x0E40, 0x0E44); // SARA E through SARA AI MAIMALAI fBeginWordSet.add(0x0E01, 0x0E2E); // KO KAI through HO NOKHUK @@ -230,6 +234,7 @@ ThaiBreakEngine::divideUpDictionaryRange( UText *text, int32_t rangeStart, int32_t rangeEnd, UVector32 &foundBreaks, + UBool /* isPhraseBreaking */, UErrorCode& status) const { if (U_FAILURE(status)) return 0; utext_setNativeIndex(text, rangeStart); @@ -441,13 +446,13 @@ LaoBreakEngine::LaoBreakEngine(DictionaryMatcher *adoptDictionary, UErrorCode &s { UTRACE_ENTRY(UTRACE_UBRK_CREATE_BREAK_ENGINE); UTRACE_DATA1(UTRACE_INFO, "dictbe=%s", "Laoo"); - fLaoWordSet.applyPattern(UNICODE_STRING_SIMPLE("[[:Laoo:]&[:LineBreak=SA:]]"), status); + UnicodeSet laoWordSet(UnicodeString(u"[[:Laoo:]&[:LineBreak=SA:]]"), status); if (U_SUCCESS(status)) { - setCharacters(fLaoWordSet); + setCharacters(laoWordSet); } - fMarkSet.applyPattern(UNICODE_STRING_SIMPLE("[[:Laoo:]&[:LineBreak=SA:]&[:M:]]"), status); + fMarkSet.applyPattern(UnicodeString(u"[[:Laoo:]&[:LineBreak=SA:]&[:M:]]"), status); fMarkSet.add(0x0020); - fEndWordSet = fLaoWordSet; + fEndWordSet = laoWordSet; fEndWordSet.remove(0x0EC0, 0x0EC4); // prefix vowels fBeginWordSet.add(0x0E81, 0x0EAE); // basic consonants (including holes for corresponding Thai characters) fBeginWordSet.add(0x0EDC, 0x0EDD); // digraph consonants (no Thai equivalent) @@ -469,6 +474,7 @@ LaoBreakEngine::divideUpDictionaryRange( UText *text, int32_t rangeStart, int32_t rangeEnd, UVector32 &foundBreaks, + UBool /* isPhraseBreaking */, UErrorCode& status) const { if (U_FAILURE(status)) return 0; if ((rangeEnd - rangeStart) < LAO_MIN_WORD_SPAN) { @@ -637,14 +643,13 @@ BurmeseBreakEngine::BurmeseBreakEngine(DictionaryMatcher *adoptDictionary, UErro { UTRACE_ENTRY(UTRACE_UBRK_CREATE_BREAK_ENGINE); UTRACE_DATA1(UTRACE_INFO, "dictbe=%s", "Mymr"); - fBurmeseWordSet.applyPattern(UNICODE_STRING_SIMPLE("[[:Mymr:]&[:LineBreak=SA:]]"), status); + fBeginWordSet.add(0x1000, 0x102A); // basic consonants and independent vowels + fEndWordSet.applyPattern(UnicodeString(u"[[:Mymr:]&[:LineBreak=SA:]]"), status); + fMarkSet.applyPattern(UnicodeString(u"[[:Mymr:]&[:LineBreak=SA:]&[:M:]]"), status); + fMarkSet.add(0x0020); if (U_SUCCESS(status)) { - setCharacters(fBurmeseWordSet); + setCharacters(fEndWordSet); } - fMarkSet.applyPattern(UNICODE_STRING_SIMPLE("[[:Mymr:]&[:LineBreak=SA:]&[:M:]]"), status); - fMarkSet.add(0x0020); - fEndWordSet = fBurmeseWordSet; - fBeginWordSet.add(0x1000, 0x102A); // basic consonants and independent vowels // Compact for caching. fMarkSet.compact(); @@ -662,6 +667,7 @@ BurmeseBreakEngine::divideUpDictionaryRange( UText *text, int32_t rangeStart, int32_t rangeEnd, UVector32 &foundBreaks, + UBool /* isPhraseBreaking */, UErrorCode& status ) const { if (U_FAILURE(status)) return 0; if ((rangeEnd - rangeStart) < BURMESE_MIN_WORD_SPAN) { @@ -830,13 +836,13 @@ KhmerBreakEngine::KhmerBreakEngine(DictionaryMatcher *adoptDictionary, UErrorCod { UTRACE_ENTRY(UTRACE_UBRK_CREATE_BREAK_ENGINE); UTRACE_DATA1(UTRACE_INFO, "dictbe=%s", "Khmr"); - fKhmerWordSet.applyPattern(UNICODE_STRING_SIMPLE("[[:Khmr:]&[:LineBreak=SA:]]"), status); + UnicodeSet khmerWordSet(UnicodeString(u"[[:Khmr:]&[:LineBreak=SA:]]"), status); if (U_SUCCESS(status)) { - setCharacters(fKhmerWordSet); + setCharacters(khmerWordSet); } - fMarkSet.applyPattern(UNICODE_STRING_SIMPLE("[[:Khmr:]&[:LineBreak=SA:]&[:M:]]"), status); + fMarkSet.applyPattern(UnicodeString(u"[[:Khmr:]&[:LineBreak=SA:]&[:M:]]"), status); fMarkSet.add(0x0020); - fEndWordSet = fKhmerWordSet; + fEndWordSet = khmerWordSet; fBeginWordSet.add(0x1780, 0x17B3); //fBeginWordSet.add(0x17A3, 0x17A4); // deprecated vowels //fEndWordSet.remove(0x17A5, 0x17A9); // Khmer independent vowels that can't end a word @@ -867,6 +873,7 @@ KhmerBreakEngine::divideUpDictionaryRange( UText *text, int32_t rangeStart, int32_t rangeEnd, UVector32 &foundBreaks, + UBool /* isPhraseBreaking */, UErrorCode& status ) const { if (U_FAILURE(status)) return 0; if ((rangeEnd - rangeStart) < KHMER_MIN_WORD_SPAN) { @@ -1050,25 +1057,27 @@ CjkBreakEngine::CjkBreakEngine(DictionaryMatcher *adoptDictionary, LanguageType : DictionaryBreakEngine(), fDictionary(adoptDictionary) { UTRACE_ENTRY(UTRACE_UBRK_CREATE_BREAK_ENGINE); UTRACE_DATA1(UTRACE_INFO, "dictbe=%s", "Hani"); - // Korean dictionary only includes Hangul syllables - fHangulWordSet.applyPattern(UNICODE_STRING_SIMPLE("[\\uac00-\\ud7a3]"), status); - fHanWordSet.applyPattern(UNICODE_STRING_SIMPLE("[:Han:]"), status); - fKatakanaWordSet.applyPattern(UNICODE_STRING_SIMPLE("[[:Katakana:]\\uff9e\\uff9f]"), status); - fHiraganaWordSet.applyPattern(UNICODE_STRING_SIMPLE("[:Hiragana:]"), status); nfkcNorm2 = Normalizer2::getNFKCInstance(status); - - if (U_SUCCESS(status)) { - // handle Korean and Japanese/Chinese using different dictionaries - if (type == kKorean) { + // Korean dictionary only includes Hangul syllables + fHangulWordSet.applyPattern(UnicodeString(u"[\\uac00-\\ud7a3]"), status); + fHangulWordSet.compact(); + // Digits, open puncutation and Alphabetic characters. + fDigitOrOpenPunctuationOrAlphabetSet.applyPattern( + UnicodeString(u"[[:Nd:][:Pi:][:Ps:][:Alphabetic:]]"), status); + fDigitOrOpenPunctuationOrAlphabetSet.compact(); + fClosePunctuationSet.applyPattern(UnicodeString(u"[[:Pc:][:Pd:][:Pe:][:Pf:][:Po:]]"), status); + fClosePunctuationSet.compact(); + + // handle Korean and Japanese/Chinese using different dictionaries + if (type == kKorean) { + if (U_SUCCESS(status)) { setCharacters(fHangulWordSet); - } else { //Chinese and Japanese - UnicodeSet cjSet; - cjSet.addAll(fHanWordSet); - cjSet.addAll(fKatakanaWordSet); - cjSet.addAll(fHiraganaWordSet); - cjSet.add(0xFF70); // HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK - cjSet.add(0x30FC); // KATAKANA-HIRAGANA PROLONGED SOUND MARK + } + } else { //Chinese and Japanese + UnicodeSet cjSet(UnicodeString(u"[[:Han:][:Hiragana:][:Katakana:]\\u30fc\\uff70\\uff9e\\uff9f]"), status); + if (U_SUCCESS(status)) { setCharacters(cjSet); + initJapanesePhraseParameter(status); } } UTRACE_EXIT_STATUS(status); @@ -1096,14 +1105,12 @@ static inline bool isKatakana(UChar32 value) { (value >= 0xFF66 && value <= 0xFF9f); } - // Function for accessing internal utext flags. // Replicates an internal UText function. static inline int32_t utext_i32_flag(int32_t bitIndex) { return (int32_t)1 << bitIndex; } - /* * @param text A UText representing the text @@ -1117,6 +1124,7 @@ CjkBreakEngine::divideUpDictionaryRange( UText *inText, int32_t rangeStart, int32_t rangeEnd, UVector32 &foundBreaks, + UBool isPhraseBreaking, UErrorCode& status) const { if (U_FAILURE(status)) return 0; if (rangeStart >= rangeEnd) { @@ -1347,6 +1355,31 @@ CjkBreakEngine::divideUpDictionaryRange( UText *inText, if ((uint32_t)bestSnlp.elementAti(numCodePts) == kuint32max) { t_boundary.addElement(numCodePts, status); numBreaks++; + } else if (isPhraseBreaking) { + t_boundary.addElement(numCodePts, status); + if(U_SUCCESS(status)) { + numBreaks++; + int32_t prevIdx = numCodePts; + + int32_t codeUnitIdx = -1; + int32_t prevCodeUnitIdx = -1; + int32_t length = -1; + for (int32_t i = prev.elementAti(numCodePts); i > 0; i = prev.elementAti(i)) { + codeUnitIdx = inString.moveIndex32(0, i); + prevCodeUnitIdx = inString.moveIndex32(0, prevIdx); + // Calculate the length by using the code unit. + length = prevCodeUnitIdx - codeUnitIdx; + prevIdx = i; + // Keep the breakpoint if the pattern is not in the fSkipSet and continuous Katakana + // characters don't occur. + if (!fSkipSet.containsKey(inString.tempSubString(codeUnitIdx, length)) + && (!isKatakana(inString.char32At(inString.moveIndex32(codeUnitIdx, -1))) + || !isKatakana(inString.char32At(codeUnitIdx)))) { + t_boundary.addElement(i, status); + numBreaks++; + } + } + } } else { for (int32_t i = numCodePts; i > 0; i = prev.elementAti(i)) { t_boundary.addElement(i, status); @@ -1367,7 +1400,8 @@ CjkBreakEngine::divideUpDictionaryRange( UText *inText, // while reversing t_boundary and pushing values to foundBreaks. int32_t prevCPPos = -1; int32_t prevUTextPos = -1; - for (int32_t i = numBreaks-1; i >= 0; i--) { + int32_t correctedNumBreaks = 0; + for (int32_t i = numBreaks - 1; i >= 0; i--) { int32_t cpPos = t_boundary.elementAti(i); U_ASSERT(cpPos > prevCPPos); int32_t utextPos = inputMap.isValid() ? inputMap->elementAti(cpPos) : cpPos + rangeStart; @@ -1375,7 +1409,15 @@ CjkBreakEngine::divideUpDictionaryRange( UText *inText, if (utextPos > prevUTextPos) { // Boundaries are added to foundBreaks output in ascending order. U_ASSERT(foundBreaks.size() == 0 || foundBreaks.peeki() < utextPos); - foundBreaks.push(utextPos, status); + // In phrase breaking, there has to be a breakpoint between Cj character and close + // punctuation. + // E.g.[携帯電話]正しい選択 -> [携帯▁電話]▁正しい▁選択 -> breakpoint between ] and 正 + if (utextPos != rangeStart + || (isPhraseBreaking && utextPos > 0 + && fClosePunctuationSet.contains(utext_char32At(inText, utextPos - 1)))) { + foundBreaks.push(utextPos, status); + correctedNumBreaks++; + } } else { // Normalization expanded the input text, the dictionary found a boundary // within the expansion, giving two boundaries with the same index in the @@ -1387,9 +1429,52 @@ CjkBreakEngine::divideUpDictionaryRange( UText *inText, } (void)prevCPPos; // suppress compiler warnings about unused variable + UChar32 nextChar = utext_char32At(inText, rangeEnd); + if (!foundBreaks.isEmpty() && foundBreaks.peeki() == rangeEnd) { + // In phrase breaking, there has to be a breakpoint between Cj character and + // the number/open punctuation. + // E.g. る文字「そうだ、京都」->る▁文字▁「そうだ、▁京都」-> breakpoint between 字 and「 + // E.g. 乗車率90%程度だろうか -> 乗車▁率▁90%▁程度だろうか -> breakpoint between 率 and 9 + // E.g. しかもロゴがUnicode! -> しかも▁ロゴが▁Unicode!-> breakpoint between が and U + if (isPhraseBreaking) { + if (!fDigitOrOpenPunctuationOrAlphabetSet.contains(nextChar)) { + foundBreaks.popi(); + correctedNumBreaks--; + } + } else { + foundBreaks.popi(); + correctedNumBreaks--; + } + } + // inString goes out of scope // inputMap goes out of scope - return numBreaks; + return correctedNumBreaks; +} + +void CjkBreakEngine::initJapanesePhraseParameter(UErrorCode& error) { + loadJapaneseExtensions(error); + loadHiragana(error); +} + +void CjkBreakEngine::loadJapaneseExtensions(UErrorCode& error) { + const char* tag = "extensions"; + ResourceBundle ja(U_ICUDATA_BRKITR, "ja", error); + if (U_SUCCESS(error)) { + ResourceBundle bundle = ja.get(tag, error); + while (U_SUCCESS(error) && bundle.hasNext()) { + fSkipSet.puti(bundle.getNextString(error), 1, error); + } + } +} + +void CjkBreakEngine::loadHiragana(UErrorCode& error) { + UnicodeSet hiraganaWordSet(UnicodeString(u"[:Hiragana:]"), error); + hiraganaWordSet.compact(); + UnicodeSetIterator iterator(hiraganaWordSet); + while (iterator.next()) { + fSkipSet.puti(UnicodeString(iterator.getCodepoint()), 1, error); + } } #endif diff --git a/deps/icu-small/source/common/dictbe.h b/deps/icu-small/source/common/dictbe.h index 4e70ed38171e44..ca1a3c28b7be80 100644 --- a/deps/icu-small/source/common/dictbe.h +++ b/deps/icu-small/source/common/dictbe.h @@ -15,6 +15,7 @@ #include "unicode/utext.h" #include "brkeng.h" +#include "hash.h" #include "uvectr32.h" U_NAMESPACE_BEGIN @@ -80,6 +81,7 @@ class DictionaryBreakEngine : public LanguageBreakEngine { int32_t startPos, int32_t endPos, UVector32 &foundBreaks, + UBool isPhraseBreaking, UErrorCode& status ) const override; protected: @@ -105,6 +107,7 @@ class DictionaryBreakEngine : public LanguageBreakEngine { int32_t rangeStart, int32_t rangeEnd, UVector32 &foundBreaks, + UBool isPhraseBreaking, UErrorCode& status) const = 0; }; @@ -127,7 +130,6 @@ class ThaiBreakEngine : public DictionaryBreakEngine { * @internal */ - UnicodeSet fThaiWordSet; UnicodeSet fEndWordSet; UnicodeSet fBeginWordSet; UnicodeSet fSuffixSet; @@ -164,6 +166,7 @@ class ThaiBreakEngine : public DictionaryBreakEngine { int32_t rangeStart, int32_t rangeEnd, UVector32 &foundBreaks, + UBool isPhraseBreaking, UErrorCode& status) const override; }; @@ -186,7 +189,6 @@ class LaoBreakEngine : public DictionaryBreakEngine { * @internal */ - UnicodeSet fLaoWordSet; UnicodeSet fEndWordSet; UnicodeSet fBeginWordSet; UnicodeSet fMarkSet; @@ -222,6 +224,7 @@ class LaoBreakEngine : public DictionaryBreakEngine { int32_t rangeStart, int32_t rangeEnd, UVector32 &foundBreaks, + UBool isPhraseBreaking, UErrorCode& status) const override; }; @@ -244,7 +247,6 @@ class BurmeseBreakEngine : public DictionaryBreakEngine { * @internal */ - UnicodeSet fBurmeseWordSet; UnicodeSet fEndWordSet; UnicodeSet fBeginWordSet; UnicodeSet fMarkSet; @@ -280,6 +282,7 @@ class BurmeseBreakEngine : public DictionaryBreakEngine { int32_t rangeStart, int32_t rangeEnd, UVector32 &foundBreaks, + UBool isPhraseBreaking, UErrorCode& status) const override; }; @@ -302,7 +305,6 @@ class KhmerBreakEngine : public DictionaryBreakEngine { * @internal */ - UnicodeSet fKhmerWordSet; UnicodeSet fEndWordSet; UnicodeSet fBeginWordSet; UnicodeSet fMarkSet; @@ -338,6 +340,7 @@ class KhmerBreakEngine : public DictionaryBreakEngine { int32_t rangeStart, int32_t rangeEnd, UVector32 &foundBreaks, + UBool isPhraseBreaking, UErrorCode& status) const override; }; @@ -366,13 +369,22 @@ class CjkBreakEngine : public DictionaryBreakEngine { * @internal */ UnicodeSet fHangulWordSet; - UnicodeSet fHanWordSet; - UnicodeSet fKatakanaWordSet; - UnicodeSet fHiraganaWordSet; + UnicodeSet fDigitOrOpenPunctuationOrAlphabetSet; + UnicodeSet fClosePunctuationSet; DictionaryMatcher *fDictionary; const Normalizer2 *nfkcNorm2; + private: + // Load Japanese extensions. + void loadJapaneseExtensions(UErrorCode& error); + // Load Japanese Hiragana. + void loadHiragana(UErrorCode& error); + // Initialize fSkipSet by loading Japanese Hiragana and extensions. + void initJapanesePhraseParameter(UErrorCode& error); + + Hashtable fSkipSet; + public: /** @@ -404,6 +416,7 @@ class CjkBreakEngine : public DictionaryBreakEngine { int32_t rangeStart, int32_t rangeEnd, UVector32 &foundBreaks, + UBool isPhraseBreaking, UErrorCode& status) const override; }; diff --git a/deps/icu-small/source/common/localematcher.cpp b/deps/icu-small/source/common/localematcher.cpp index 3d178dfbaf1732..2cad708d99f0d2 100644 --- a/deps/icu-small/source/common/localematcher.cpp +++ b/deps/icu-small/source/common/localematcher.cpp @@ -168,12 +168,9 @@ void LocaleMatcher::Builder::clearSupportedLocales() { bool LocaleMatcher::Builder::ensureSupportedLocaleVector() { if (U_FAILURE(errorCode_)) { return false; } if (supportedLocales_ != nullptr) { return true; } - supportedLocales_ = new UVector(uprv_deleteUObject, nullptr, errorCode_); + LocalPointer lpSupportedLocales(new UVector(uprv_deleteUObject, nullptr, errorCode_), errorCode_); if (U_FAILURE(errorCode_)) { return false; } - if (supportedLocales_ == nullptr) { - errorCode_ = U_MEMORY_ALLOCATION_ERROR; - return false; - } + supportedLocales_ = lpSupportedLocales.orphan(); return true; } @@ -187,9 +184,8 @@ LocaleMatcher::Builder &LocaleMatcher::Builder::setSupportedLocalesFromListStrin for (int32_t i = 0; i < length; ++i) { Locale *locale = list.orphanLocaleAt(i); if (locale == nullptr) { continue; } - supportedLocales_->addElementX(locale, errorCode_); + supportedLocales_->adoptElement(locale, errorCode_); if (U_FAILURE(errorCode_)) { - delete locale; break; } } @@ -197,35 +193,21 @@ LocaleMatcher::Builder &LocaleMatcher::Builder::setSupportedLocalesFromListStrin } LocaleMatcher::Builder &LocaleMatcher::Builder::setSupportedLocales(Locale::Iterator &locales) { - if (U_FAILURE(errorCode_)) { return *this; } - clearSupportedLocales(); - if (!ensureSupportedLocaleVector()) { return *this; } - while (locales.hasNext()) { - const Locale &locale = locales.next(); - Locale *clone = locale.clone(); - if (clone == nullptr) { - errorCode_ = U_MEMORY_ALLOCATION_ERROR; - break; - } - supportedLocales_->addElementX(clone, errorCode_); - if (U_FAILURE(errorCode_)) { - delete clone; - break; + if (ensureSupportedLocaleVector()) { + clearSupportedLocales(); + while (locales.hasNext() && U_SUCCESS(errorCode_)) { + const Locale &locale = locales.next(); + LocalPointer clone (locale.clone(), errorCode_); + supportedLocales_->adoptElement(clone.orphan(), errorCode_); } } return *this; } LocaleMatcher::Builder &LocaleMatcher::Builder::addSupportedLocale(const Locale &locale) { - if (!ensureSupportedLocaleVector()) { return *this; } - Locale *clone = locale.clone(); - if (clone == nullptr) { - errorCode_ = U_MEMORY_ALLOCATION_ERROR; - return *this; - } - supportedLocales_->addElementX(clone, errorCode_); - if (U_FAILURE(errorCode_)) { - delete clone; + if (ensureSupportedLocaleVector()) { + LocalPointer clone(locale.clone(), errorCode_); + supportedLocales_->adoptElement(clone.orphan(), errorCode_); } return *this; } diff --git a/deps/icu-small/source/common/locid.cpp b/deps/icu-small/source/common/locid.cpp index e8859c7048b110..73bb8d8aec1c70 100644 --- a/deps/icu-small/source/common/locid.cpp +++ b/deps/icu-small/source/common/locid.cpp @@ -1204,14 +1204,11 @@ AliasReplacer::parseLanguageReplacement( // We have multiple field so we have to allocate and parse CharString* str = new CharString( replacement, (int32_t)uprv_strlen(replacement), status); + LocalPointer lpStr(str, status); + toBeFreed.adoptElement(lpStr.orphan(), status); if (U_FAILURE(status)) { return; } - if (str == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - toBeFreed.addElementX(str, status); char* data = str->data(); replacedLanguage = (const char*) data; char* endOfField = uprv_strchr(data, '_'); @@ -1420,12 +1417,9 @@ AliasReplacer::replaceTerritory(UVector& toBeFreed, UErrorCode& status) (int32_t)(firstSpace - replacement), status), status); } if (U_FAILURE(status)) { return false; } - if (item.isNull()) { - status = U_MEMORY_ALLOCATION_ERROR; - return false; - } replacedRegion = item->data(); - toBeFreed.addElementX(item.orphan(), status); + toBeFreed.adoptElement(item.orphan(), status); + if (U_FAILURE(status)) { return false; } } U_ASSERT(!same(region, replacedRegion)); region = replacedRegion; @@ -1659,10 +1653,10 @@ AliasReplacer::replace(const Locale& locale, CharString& out, UErrorCode& status while ((end = uprv_strchr(start, SEP_CHAR)) != nullptr && U_SUCCESS(status)) { *end = NULL_CHAR; // null terminate inside variantsBuff - variants.addElementX(start, status); + variants.addElement(start, status); start = end + 1; } - variants.addElementX(start, status); + variants.addElement(start, status); } if (U_FAILURE(status)) { return false; } diff --git a/deps/icu-small/source/common/lstmbe.cpp b/deps/icu-small/source/common/lstmbe.cpp index 3793abceb3fb1c..f6114cdfe25e19 100644 --- a/deps/icu-small/source/common/lstmbe.cpp +++ b/deps/icu-small/source/common/lstmbe.cpp @@ -1,8 +1,8 @@ // © 2021 and later: Unicode, Inc. and others. // License & terms of use: http://www.unicode.org/copyright.html +#include #include -#include #include "unicode/utypes.h" @@ -639,6 +639,7 @@ LSTMBreakEngine::divideUpDictionaryRange( UText *text, int32_t startPos, int32_t endPos, UVector32 &foundBreaks, + UBool /* isPhraseBreaking */, UErrorCode& status) const { if (U_FAILURE(status)) return 0; int32_t beginFoundBreakSize = foundBreaks.size(); diff --git a/deps/icu-small/source/common/lstmbe.h b/deps/icu-small/source/common/lstmbe.h index c3f7ecf81540dd..ffdf805eca265d 100644 --- a/deps/icu-small/source/common/lstmbe.h +++ b/deps/icu-small/source/common/lstmbe.h @@ -62,6 +62,7 @@ class LSTMBreakEngine : public DictionaryBreakEngine { int32_t rangeStart, int32_t rangeEnd, UVector32 &foundBreaks, + UBool isPhraseBreaking, UErrorCode& status) const override; private: const LSTMData* fData; diff --git a/deps/icu-small/source/common/normalizer2impl.cpp b/deps/icu-small/source/common/normalizer2impl.cpp index 5bfd49e8cb9e2a..e6bd75e7173889 100644 --- a/deps/icu-small/source/common/normalizer2impl.cpp +++ b/deps/icu-small/source/common/normalizer2impl.cpp @@ -2496,15 +2496,18 @@ void CanonIterData::addToStartSet(UChar32 origin, UChar32 decompLead, UErrorCode // origin is not the first character, or it is U+0000. UnicodeSet *set; if((canonValue&CANON_HAS_SET)==0) { - set=new UnicodeSet; - if(set==NULL) { - errorCode=U_MEMORY_ALLOCATION_ERROR; + LocalPointer lpSet(new UnicodeSet, errorCode); + set=lpSet.getAlias(); + if(U_FAILURE(errorCode)) { return; } UChar32 firstOrigin=(UChar32)(canonValue&CANON_VALUE_MASK); canonValue=(canonValue&~CANON_VALUE_MASK)|CANON_HAS_SET|(uint32_t)canonStartSets.size(); umutablecptrie_set(mutableTrie, decompLead, canonValue, &errorCode); - canonStartSets.addElementX(set, errorCode); + canonStartSets.adoptElement(lpSet.orphan(), errorCode); + if (U_FAILURE(errorCode)) { + return; + } if(firstOrigin!=0) { set->add(firstOrigin); } diff --git a/deps/icu-small/source/common/rbbi.cpp b/deps/icu-small/source/common/rbbi.cpp index f65177f232334d..cae8d154b30802 100644 --- a/deps/icu-small/source/common/rbbi.cpp +++ b/deps/icu-small/source/common/rbbi.cpp @@ -82,6 +82,19 @@ RuleBasedBreakIterator::RuleBasedBreakIterator(RBBIDataHeader* data, UErrorCode } } +//------------------------------------------------------------------------------- +// +// Constructor from a UDataMemory handle to precompiled break rules +// stored in an ICU data file. This construcotr is private API, +// only for internal use. +// +//------------------------------------------------------------------------------- +RuleBasedBreakIterator::RuleBasedBreakIterator(UDataMemory* udm, UBool isPhraseBreaking, + UErrorCode &status) : RuleBasedBreakIterator(udm, status) +{ + fIsPhraseBreaking = isPhraseBreaking; +} + // // Construct from precompiled binary rules (tables). This constructor is public API, // taking the rules as a (const uint8_t *) to match the type produced by getBinaryRules(). @@ -322,6 +335,7 @@ void RuleBasedBreakIterator::init(UErrorCode &status) { fBreakCache = nullptr; fDictionaryCache = nullptr; fLookAheadMatches = nullptr; + fIsPhraseBreaking = false; // Note: IBM xlC is unable to assign or initialize member fText from UTEXT_INITIALIZER. // fText = UTEXT_INITIALIZER; diff --git a/deps/icu-small/source/common/rbbi_cache.cpp b/deps/icu-small/source/common/rbbi_cache.cpp index 6bfe3feca495f6..26d82df7811838 100644 --- a/deps/icu-small/source/common/rbbi_cache.cpp +++ b/deps/icu-small/source/common/rbbi_cache.cpp @@ -163,7 +163,7 @@ void RuleBasedBreakIterator::DictionaryCache::populateDictionary(int32_t startPo // Ask the language object if there are any breaks. It will add them to the cache and // leave the text pointer on the other side of its range, ready to search for the next one. if (lbe != NULL) { - foundBreakCount += lbe->findBreaks(text, rangeStart, rangeEnd, fBreaks, status); + foundBreakCount += lbe->findBreaks(text, rangeStart, rangeEnd, fBreaks, fBI->fIsPhraseBreaking, status); } // Reload the loop variables for the next go-round diff --git a/deps/icu-small/source/common/serv.cpp b/deps/icu-small/source/common/serv.cpp index 0c54a4dce99225..c26dbca1a9c244 100644 --- a/deps/icu-small/source/common/serv.cpp +++ b/deps/icu-small/source/common/serv.cpp @@ -625,10 +625,7 @@ ICUService::getVisibleIDs(UVector& result, const UnicodeString* matchID, UErrorC } } - LocalPointer idClone(new UnicodeString(*id), status); - if (U_SUCCESS(status) && idClone->isBogus()) { - status = U_MEMORY_ALLOCATION_ERROR; - } + LocalPointer idClone(id->clone(), status); result.adoptElement(idClone.orphan(), status); } delete fallbackKey; diff --git a/deps/icu-small/source/common/servls.cpp b/deps/icu-small/source/common/servls.cpp index 7108afd4a5282b..98f0a8a12b0006 100644 --- a/deps/icu-small/source/common/servls.cpp +++ b/deps/icu-small/source/common/servls.cpp @@ -179,7 +179,8 @@ class ServiceEnumeration : public StringEnumeration { length = other._ids.size(); for(i = 0; i < length; ++i) { - _ids.addElementX(((UnicodeString *)other._ids.elementAt(i))->clone(), status); + LocalPointer clonedId(((UnicodeString *)other._ids.elementAt(i))->clone(), status); + _ids.adoptElement(clonedId.orphan(), status); } if(U_SUCCESS(status)) { diff --git a/deps/icu-small/source/common/servnotf.cpp b/deps/icu-small/source/common/servnotf.cpp index 342e0d9f24d2a7..d9fb38875202df 100644 --- a/deps/icu-small/source/common/servnotf.cpp +++ b/deps/icu-small/source/common/servnotf.cpp @@ -49,7 +49,11 @@ ICUNotifier::addListener(const EventListener* l, UErrorCode& status) if (acceptsListener(*l)) { Mutex lmx(¬ifyLock); if (listeners == NULL) { - listeners = new UVector(5, status); + LocalPointer lpListeners(new UVector(5, status), status); + if (U_FAILURE(status)) { + return; + } + listeners = lpListeners.orphan(); } else { for (int i = 0, e = listeners->size(); i < e; ++i) { const EventListener* el = (const EventListener*)(listeners->elementAt(i)); @@ -59,7 +63,7 @@ ICUNotifier::addListener(const EventListener* l, UErrorCode& status) } } - listeners->addElementX((void*)l, status); // cast away const + listeners->addElement((void*)l, status); // cast away const } #ifdef NOTIFIER_DEBUG else { @@ -102,13 +106,11 @@ ICUNotifier::removeListener(const EventListener *l, UErrorCode& status) void ICUNotifier::notifyChanged(void) { + Mutex lmx(¬ifyLock); if (listeners != NULL) { - Mutex lmx(¬ifyLock); - if (listeners != NULL) { - for (int i = 0, e = listeners->size(); i < e; ++i) { - EventListener* el = (EventListener*)listeners->elementAt(i); - notifyListener(*el); - } + for (int i = 0, e = listeners->size(); i < e; ++i) { + EventListener* el = (EventListener*)listeners->elementAt(i); + notifyListener(*el); } } } diff --git a/deps/icu-small/source/common/ubrk.cpp b/deps/icu-small/source/common/ubrk.cpp index bb5bdd1b5012fb..f4e064961f3968 100644 --- a/deps/icu-small/source/common/ubrk.cpp +++ b/deps/icu-small/source/common/ubrk.cpp @@ -168,7 +168,7 @@ ubrk_safeClone( BreakIterator *newBI = ((BreakIterator *)bi)->clone(); if (newBI == NULL) { *status = U_MEMORY_ALLOCATION_ERROR; - } else { + } else if (pBufferSize != NULL) { *status = U_SAFECLONE_ALLOCATED_WARNING; } return (UBreakIterator *)newBI; @@ -176,15 +176,7 @@ ubrk_safeClone( U_CAPI UBreakIterator * U_EXPORT2 ubrk_clone(const UBreakIterator *bi, UErrorCode *status) { - if (U_FAILURE(*status)) { - return nullptr; - } - BreakIterator *newBI = ((BreakIterator *)bi)->clone(); - if (newBI == nullptr) { - *status = U_MEMORY_ALLOCATION_ERROR; - return nullptr; - } - return (UBreakIterator *)newBI; + return ubrk_safeClone(bi, nullptr, nullptr, status); } diff --git a/deps/icu-small/source/common/ucase.cpp b/deps/icu-small/source/common/ucase.cpp index 4aa856507aafb1..388c86b1bba791 100644 --- a/deps/icu-small/source/common/ucase.cpp +++ b/deps/icu-small/source/common/ucase.cpp @@ -22,27 +22,14 @@ #include "unicode/utypes.h" #include "unicode/unistr.h" #include "unicode/uset.h" -#include "unicode/udata.h" /* UDataInfo */ #include "unicode/utf16.h" -#include "ucmndata.h" /* DataHeader */ -#include "udatamem.h" -#include "umutex.h" -#include "uassert.h" #include "cmemory.h" -#include "utrie2.h" +#include "uassert.h" #include "ucase.h" +#include "umutex.h" +#include "utrie2.h" -struct UCaseProps { - UDataMemory *mem; - const int32_t *indexes; - const uint16_t *exceptions; - const uint16_t *unfold; - - UTrie2 trie; - uint8_t formatVersion[4]; -}; - -/* ucase_props_data.h is machine-generated by gencase --csource */ +/* ucase_props_data.h is machine-generated by genprops/casepropsbuilder.cpp */ #define INCLUDED_FROM_UCASE_CPP #include "ucase_props_data.h" @@ -77,6 +64,13 @@ ucase_addPropertyStarts(const USetAdder *sa, UErrorCode *pErrorCode) { /* data access primitives --------------------------------------------------- */ +U_CAPI const struct UCaseProps * U_EXPORT2 +ucase_getSingleton(int32_t *pExceptionsLength, int32_t *pUnfoldLength) { + *pExceptionsLength = UPRV_LENGTHOF(ucase_props_exceptions); + *pUnfoldLength = UPRV_LENGTHOF(ucase_props_unfold); + return &ucase_props_singleton; +} + U_CFUNC const UTrie2 * U_EXPORT2 ucase_getTrie() { return &ucase_props_singleton.trie; @@ -690,7 +684,7 @@ ucase_isCaseSensitive(UChar32 c) { * - The general category of C is * Nonspacing Mark (Mn), or Enclosing Mark (Me), or Format Control (Cf), or * Letter Modifier (Lm), or Symbol Modifier (Sk) - * - C is one of the following characters + * - C is one of the following characters * U+0027 APOSTROPHE * U+00AD SOFT HYPHEN (SHY) * U+2019 RIGHT SINGLE QUOTATION MARK @@ -1064,6 +1058,8 @@ ucase_toFullLower(UChar32 c, // The sign of the result has meaning, input must be non-negative so that it can be returned as is. U_ASSERT(c >= 0); UChar32 result=c; + // Reset the output pointer in case it was uninitialized. + *pString=nullptr; uint16_t props=UTRIE2_GET16(&ucase_props_singleton.trie, c); if(!UCASE_HAS_EXCEPTION(props)) { if(UCASE_IS_UPPER_OR_TITLE(props)) { @@ -1148,7 +1144,6 @@ ucase_toFullLower(UChar32 c, 0307; ; 0307; 0307; tr After_I; # COMBINING DOT ABOVE 0307; ; 0307; 0307; az After_I; # COMBINING DOT ABOVE */ - *pString=nullptr; return 0; /* remove the dot (continue without output) */ } else if(loc==UCASE_LOC_TURKISH && c==0x49 && !isFollowedByDotAbove(iter, context)) { /* @@ -1215,6 +1210,8 @@ toUpperOrTitle(UChar32 c, // The sign of the result has meaning, input must be non-negative so that it can be returned as is. U_ASSERT(c >= 0); UChar32 result=c; + // Reset the output pointer in case it was uninitialized. + *pString=nullptr; uint16_t props=UTRIE2_GET16(&ucase_props_singleton.trie, c); if(!UCASE_HAS_EXCEPTION(props)) { if(UCASE_GET_TYPE(props)==UCASE_LOWER) { @@ -1252,7 +1249,6 @@ toUpperOrTitle(UChar32 c, 0307; 0307; ; ; lt After_Soft_Dotted; # COMBINING DOT ABOVE */ - *pString=nullptr; return 0; /* remove the dot (continue without output) */ } else if(c==0x0587) { // See ICU-13416: @@ -1449,6 +1445,8 @@ ucase_toFullFolding(UChar32 c, // The sign of the result has meaning, input must be non-negative so that it can be returned as is. U_ASSERT(c >= 0); UChar32 result=c; + // Reset the output pointer in case it was uninitialized. + *pString=nullptr; uint16_t props=UTRIE2_GET16(&ucase_props_singleton.trie, c); if(!UCASE_HAS_EXCEPTION(props)) { if(UCASE_IS_UPPER_OR_TITLE(props)) { @@ -1542,7 +1540,7 @@ U_CAPI UChar32 U_EXPORT2 u_tolower(UChar32 c) { return ucase_tolower(c); } - + /* Transforms the Unicode character to its upper case equivalent.*/ U_CAPI UChar32 U_EXPORT2 u_toupper(UChar32 c) { diff --git a/deps/icu-small/source/common/ucase.h b/deps/icu-small/source/common/ucase.h index a018f82b81b229..7bf57fd370631b 100644 --- a/deps/icu-small/source/common/ucase.h +++ b/deps/icu-small/source/common/ucase.h @@ -312,6 +312,21 @@ UCaseMapFull(UChar32 c, U_CDECL_END +/* for icuexportdata -------------------------------------------------------- */ + +struct UCaseProps { + void *mem; // TODO: was unused, and type UDataMemory -- remove + const int32_t *indexes; + const uint16_t *exceptions; + const uint16_t *unfold; + + UTrie2 trie; + uint8_t formatVersion[4]; +}; + +U_CAPI const struct UCaseProps * U_EXPORT2 +ucase_getSingleton(int32_t *pExceptionsLength, int32_t *pUnfoldLength); + /* file definitions --------------------------------------------------------- */ #define UCASE_DATA_NAME "ucase" diff --git a/deps/icu-small/source/common/ucasemap.cpp b/deps/icu-small/source/common/ucasemap.cpp index ed72bda828fc1c..95b55d56a02c47 100644 --- a/deps/icu-small/source/common/ucasemap.cpp +++ b/deps/icu-small/source/common/ucasemap.cpp @@ -112,8 +112,7 @@ ucasemap_setLocale(UCaseMap *csm, const char *locale, UErrorCode *pErrorCode) { if(length==sizeof(csm->locale)) { *pErrorCode=U_BUFFER_OVERFLOW_ERROR; } - if(U_SUCCESS(*pErrorCode)) { - csm->caseLocale=UCASE_LOC_UNKNOWN; + if(U_SUCCESS(*pErrorCode)) { csm->caseLocale = ucase_getCaseLocale(csm->locale); } else { csm->locale[0]=0; @@ -420,6 +419,97 @@ void toUpper(int32_t caseLocale, uint32_t options, #if !UCONFIG_NO_BREAK_ITERATION +namespace { + +constexpr uint8_t ACUTE_BYTE0 = u8"\u0301"[0]; + +constexpr uint8_t ACUTE_BYTE1 = u8"\u0301"[1]; + +/** + * Input: c is a letter I with or without acute accent. + * start is the index in src after c, and is less than segmentLimit. + * If a plain i/I is followed by a plain j/J, + * or an i/I with acute (precomposed or decomposed) is followed by a j/J with acute, + * then we output accordingly. + * + * @return the src index after the titlecased sequence, or the start index if no Dutch IJ + */ +int32_t maybeTitleDutchIJ(const uint8_t *src, UChar32 c, int32_t start, int32_t segmentLimit, + ByteSink &sink, uint32_t options, icu::Edits *edits, UErrorCode &errorCode) { + U_ASSERT(start < segmentLimit); + + int32_t index = start; + bool withAcute = false; + + // If the conditions are met, then the following variables tell us what to output. + int32_t unchanged1 = 0; // code units before the j, or the whole sequence (0..3) + bool doTitleJ = false; // true if the j needs to be titlecased + int32_t unchanged2 = 0; // after the j (0 or 1) + + // next character after the first letter + UChar32 c2; + c2 = src[index++]; + + // Is the first letter an i/I with accent? + if (c == u'I') { + if (c2 == ACUTE_BYTE0 && index < segmentLimit && src[index++] == ACUTE_BYTE1) { + withAcute = true; + unchanged1 = 2; // ACUTE is 2 code units in UTF-8 + if (index == segmentLimit) { return start; } + c2 = src[index++]; + } + } else { // Í + withAcute = true; + } + + // Is the next character a j/J? + if (c2 == u'j') { + doTitleJ = true; + } else if (c2 == u'J') { + ++unchanged1; + } else { + return start; + } + + // A plain i/I must be followed by a plain j/J. + // An i/I with acute must be followed by a j/J with acute. + if (withAcute) { + if ((index + 1) >= segmentLimit || src[index++] != ACUTE_BYTE0 || src[index++] != ACUTE_BYTE1) { + return start; + } + if (doTitleJ) { + unchanged2 = 2; // ACUTE is 2 code units in UTF-8 + } else { + unchanged1 = unchanged1 + 2; // ACUTE is 2 code units in UTF-8 + } + } + + // There must not be another combining mark. + if (index < segmentLimit) { + int32_t cp; + int32_t i = index; + U8_NEXT(src, i, segmentLimit, cp); + uint32_t typeMask = U_GET_GC_MASK(cp); + if ((typeMask & U_GC_M_MASK) != 0) { + return start; + } + } + + // Output the rest of the Dutch IJ. + ByteSinkUtil::appendUnchanged(src + start, unchanged1, sink, options, edits, errorCode); + start += unchanged1; + if (doTitleJ) { + ByteSinkUtil::appendCodePoint(1, u'J', sink, edits); + ++start; + } + ByteSinkUtil::appendUnchanged(src + start, unchanged2, sink, options, edits, errorCode); + + U_ASSERT(start + unchanged2 == index); + return index; +} + +} // namespace + U_CFUNC void U_CALLCONV ucasemap_internalUTF8ToTitle( int32_t caseLocale, uint32_t options, BreakIterator *iter, @@ -504,19 +594,14 @@ ucasemap_internalUTF8ToTitle( } /* Special case Dutch IJ titlecasing */ - if (titleStart+1 < index && - caseLocale == UCASE_LOC_DUTCH && - (src[titleStart] == 0x0049 || src[titleStart] == 0x0069)) { - if (src[titleStart+1] == 0x006A) { - ByteSinkUtil::appendCodePoint(1, 0x004A, sink, edits); - titleLimit++; - } else if (src[titleStart+1] == 0x004A) { - // Keep the capital J from getting lowercased. - if (!ByteSinkUtil::appendUnchanged(src+titleStart+1, 1, - sink, options, edits, errorCode)) { - return; - } - titleLimit++; + if (titleLimit < index && + caseLocale == UCASE_LOC_DUTCH) { + if (c < 0) { + c = ~c; + } + + if (c == u'I' || c == u'Í') { + titleLimit = maybeTitleDutchIJ(src, c, titleLimit, index, sink, options, edits, errorCode); } } diff --git a/deps/icu-small/source/common/ucnv.cpp b/deps/icu-small/source/common/ucnv.cpp index 5dcf35e043850e..019bcb6a79cd90 100644 --- a/deps/icu-small/source/common/ucnv.cpp +++ b/deps/icu-small/source/common/ucnv.cpp @@ -252,7 +252,10 @@ ucnv_safeClone(const UConverter* cnv, void *stackBuffer, int32_t *pBufferSize, U UTRACE_EXIT_STATUS(*status); return NULL; } - *status = U_SAFECLONE_ALLOCATED_WARNING; + // If pBufferSize was NULL as the input, pBufferSize is set to &stackBufferSize in this function. + if (pBufferSize != &stackBufferSize) { + *status = U_SAFECLONE_ALLOCATED_WARNING; + } /* record the fact that memory was allocated */ *pBufferSize = bufferSizeNeeded; @@ -317,7 +320,11 @@ ucnv_safeClone(const UConverter* cnv, void *stackBuffer, int32_t *pBufferSize, U return localConverter; } - +U_CAPI UConverter* U_EXPORT2 +ucnv_clone(const UConverter* cnv, UErrorCode *status) +{ + return ucnv_safeClone(cnv, nullptr, nullptr, status); +} /*Decreases the reference counter in the shared immutable section of the object *and frees the mutable part*/ diff --git a/deps/icu-small/source/common/ucurr.cpp b/deps/icu-small/source/common/ucurr.cpp index 67aab4e8ffec2f..6e489e0563d416 100644 --- a/deps/icu-small/source/common/ucurr.cpp +++ b/deps/icu-small/source/common/ucurr.cpp @@ -254,7 +254,7 @@ currSymbolsEquiv_cleanup(void) } /** - * Deleter for OlsonToMetaMappingEntry + * Deleter for IsoCodeEntry */ static void U_CALLCONV deleteIsoCodeEntry(void *obj) { diff --git a/deps/icu-small/source/common/uloc.cpp b/deps/icu-small/source/common/uloc.cpp index c8a3f1ff731340..99c6a0af39dbae 100644 --- a/deps/icu-small/source/common/uloc.cpp +++ b/deps/icu-small/source/common/uloc.cpp @@ -186,10 +186,10 @@ NULL }; static const char* const DEPRECATED_LANGUAGES[]={ - "in", "iw", "ji", "jw", NULL, NULL + "in", "iw", "ji", "jw", "mo", NULL, NULL }; static const char* const REPLACEMENT_LANGUAGES[]={ - "id", "he", "yi", "jv", NULL, NULL + "id", "he", "yi", "jv", "ro", NULL, NULL }; /** @@ -444,7 +444,7 @@ static const char * const COUNTRIES_3[] = { /* "VA", "VC", "VE", "VG", "VI", "VN", "VU", "WF", */ "VAT", "VCT", "VEN", "VGB", "VIR", "VNM", "VUT", "WLF", /* "WS", "XK", "YE", "YT", "ZA", "ZM", "ZW", */ - "WSM", "XXK", "YEM", "MYT", "ZAF", "ZMB", "ZWE", + "WSM", "XKK", "YEM", "MYT", "ZAF", "ZMB", "ZWE", NULL, /* "AN", "BU", "CS", "FX", "RO", "SU", "TP", "YD", "YU", "ZR" */ "ANT", "BUR", "SCG", "FXX", "ROM", "SUN", "TMP", "YMD", "YUG", "ZAR", diff --git a/deps/icu-small/source/common/unicode/localematcher.h b/deps/icu-small/source/common/unicode/localematcher.h index 252bb7fdc20753..0f7e04a3afdcf2 100644 --- a/deps/icu-small/source/common/unicode/localematcher.h +++ b/deps/icu-small/source/common/unicode/localematcher.h @@ -461,13 +461,13 @@ class U_COMMON_API LocaleMatcher : public UMemory { * Option for whether to include or ignore one-way (fallback) match data. * By default, they are included. * - * @param direction the match direction to set. + * @param matchDirection the match direction to set. * @return this Builder object * @stable ICU 67 */ - Builder &setDirection(ULocMatchDirection direction) { + Builder &setDirection(ULocMatchDirection matchDirection) { if (U_SUCCESS(errorCode_)) { - direction_ = direction; + direction_ = matchDirection; } return *this; } diff --git a/deps/icu-small/source/common/unicode/rbbi.h b/deps/icu-small/source/common/unicode/rbbi.h index 0ce93819f54cbf..0bad0d3897cc48 100644 --- a/deps/icu-small/source/common/unicode/rbbi.h +++ b/deps/icu-small/source/common/unicode/rbbi.h @@ -147,6 +147,11 @@ class U_COMMON_API RuleBasedBreakIterator /*U_FINAL*/ : public BreakIterator { */ int32_t *fLookAheadMatches; + /** + * A flag to indicate if phrase based breaking is enabled. + */ + UBool fIsPhraseBreaking; + //======================================================================= // constructors //======================================================================= @@ -163,6 +168,21 @@ class U_COMMON_API RuleBasedBreakIterator /*U_FINAL*/ : public BreakIterator { */ RuleBasedBreakIterator(RBBIDataHeader* data, UErrorCode &status); + /** + * This constructor uses the udata interface to create a BreakIterator + * whose internal tables live in a memory-mapped file. "image" is an + * ICU UDataMemory handle for the pre-compiled break iterator tables. + * @param image handle to the memory image for the break iterator data. + * Ownership of the UDataMemory handle passes to the Break Iterator, + * which will be responsible for closing it when it is no longer needed. + * @param status Information on any errors encountered. + * @param isPhraseBreaking true if phrase based breaking is required, otherwise false. + * @see udata_open + * @see #getBinaryRules + * @internal (private) + */ + RuleBasedBreakIterator(UDataMemory* image, UBool isPhraseBreaking, UErrorCode &status); + /** @internal */ friend class RBBIRuleBuilder; /** @internal */ diff --git a/deps/icu-small/source/common/unicode/ubrk.h b/deps/icu-small/source/common/unicode/ubrk.h index c603f7c13f359a..2b3dc7aa576803 100644 --- a/deps/icu-small/source/common/unicode/ubrk.h +++ b/deps/icu-small/source/common/unicode/ubrk.h @@ -312,11 +312,12 @@ ubrk_openBinaryRules(const uint8_t *binaryRules, int32_t rulesLength, * If *pBufferSize is not enough for a stack-based safe clone, * new memory will be allocated. * @param status to indicate whether the operation went on smoothly or there were errors - * An informational status value, U_SAFECLONE_ALLOCATED_ERROR, is used if any allocations were necessary. + * An informational status value, U_SAFECLONE_ALLOCATED_ERROR, is used + * if pBufferSize != NULL and any allocations were necessary * @return pointer to the new clone * @deprecated ICU 69 Use ubrk_clone() instead. */ -U_CAPI UBreakIterator * U_EXPORT2 +U_DEPRECATED UBreakIterator * U_EXPORT2 ubrk_safeClone( const UBreakIterator *bi, void *stackBuffer, @@ -325,21 +326,17 @@ ubrk_safeClone( #endif /* U_HIDE_DEPRECATED_API */ -#ifndef U_HIDE_DRAFT_API - /** * Thread safe cloning operation. * @param bi iterator to be cloned * @param status to indicate whether the operation went on smoothly or there were errors * @return pointer to the new clone - * @draft ICU 69 + * @stable ICU 69 */ U_CAPI UBreakIterator * U_EXPORT2 ubrk_clone(const UBreakIterator *bi, UErrorCode *status); -#endif // U_HIDE_DRAFT_API - #ifndef U_HIDE_DEPRECATED_API /** diff --git a/deps/icu-small/source/common/unicode/ucnv.h b/deps/icu-small/source/common/unicode/ucnv.h index 2687c984d43b1d..20c173b662832d 100644 --- a/deps/icu-small/source/common/unicode/ucnv.h +++ b/deps/icu-small/source/common/unicode/ucnv.h @@ -477,7 +477,7 @@ ucnv_openCCSID(int32_t codepage, * *

The name will NOT be looked up in the alias mechanism, nor will the converter be * stored in the converter cache or the alias table. The only way to open further converters - * is call this function multiple times, or use the ucnv_safeClone() function to clone a + * is call this function multiple times, or use the ucnv_clone() function to clone a * 'primary' converter.

* *

A future version of ICU may add alias table lookups and/or caching @@ -493,13 +493,27 @@ ucnv_openCCSID(int32_t codepage, * @return the created Unicode converter object, or NULL if an error occurred * @see udata_open * @see ucnv_open - * @see ucnv_safeClone + * @see ucnv_clone * @see ucnv_close * @stable ICU 2.2 */ U_CAPI UConverter* U_EXPORT2 ucnv_openPackage(const char *packageName, const char *converterName, UErrorCode *err); +/** + * Thread safe converter cloning operation. + * + * You must ucnv_close() the clone. + * + * @param cnv converter to be cloned + * @param status to indicate whether the operation went on smoothly or there were errors + * @return pointer to the new clone + * @stable ICU 71 + */ +U_CAPI UConverter* U_EXPORT2 ucnv_clone(const UConverter *cnv, UErrorCode *status); + +#ifndef U_HIDE_DEPRECATED_API + /** * Thread safe converter cloning operation. * For most efficient operation, pass in a stackBuffer (and a *pBufferSize) @@ -532,21 +546,19 @@ ucnv_openPackage(const char *packageName, const char *converterName, UErrorCode * pointer to size of allocated space. * @param status to indicate whether the operation went on smoothly or there were errors * An informational status value, U_SAFECLONE_ALLOCATED_WARNING, - * is used if any allocations were necessary. + * is used if pBufferSize != NULL and any allocations were necessary * However, it is better to check if *pBufferSize grew for checking for * allocations because warning codes can be overridden by subsequent * function calls. * @return pointer to the new clone - * @stable ICU 2.0 + * @deprecated ICU 71 Use ucnv_clone() instead. */ -U_CAPI UConverter * U_EXPORT2 +U_DEPRECATED UConverter * U_EXPORT2 ucnv_safeClone(const UConverter *cnv, void *stackBuffer, int32_t *pBufferSize, UErrorCode *status); -#ifndef U_HIDE_DEPRECATED_API - /** * \def U_CNV_SAFECLONE_BUFFERSIZE * Definition of a buffer size that is designed to be large enough for diff --git a/deps/icu-small/source/common/unicode/uniset.h b/deps/icu-small/source/common/unicode/uniset.h index 730337a3535ea8..310c7c8d2011cd 100644 --- a/deps/icu-small/source/common/unicode/uniset.h +++ b/deps/icu-small/source/common/unicode/uniset.h @@ -1229,7 +1229,6 @@ class U_COMMON_API UnicodeSet U_FINAL : public UnicodeFilter { */ UnicodeSet& retain(UChar32 c); -#ifndef U_HIDE_DRAFT_API /** * Retains only the specified string from this set if it is present. * Upon return this set will be empty if it did not contain s, or @@ -1238,10 +1237,9 @@ class U_COMMON_API UnicodeSet U_FINAL : public UnicodeFilter { * * @param s the source string * @return this object, for chaining - * @draft ICU 69 + * @stable ICU 69 */ UnicodeSet& retain(const UnicodeString &s); -#endif // U_HIDE_DRAFT_API /** * Removes the specified range from this set if it is present. diff --git a/deps/icu-small/source/common/unicode/urename.h b/deps/icu-small/source/common/unicode/urename.h index 4605f632ea8c91..d9f9b8f336c4cf 100644 --- a/deps/icu-small/source/common/unicode/urename.h +++ b/deps/icu-small/source/common/unicode/urename.h @@ -567,6 +567,7 @@ #define ucase_addStringCaseClosure U_ICU_ENTRY_POINT_RENAME(ucase_addStringCaseClosure) #define ucase_fold U_ICU_ENTRY_POINT_RENAME(ucase_fold) #define ucase_getCaseLocale U_ICU_ENTRY_POINT_RENAME(ucase_getCaseLocale) +#define ucase_getSingleton U_ICU_ENTRY_POINT_RENAME(ucase_getSingleton) #define ucase_getTrie U_ICU_ENTRY_POINT_RENAME(ucase_getTrie) #define ucase_getType U_ICU_ENTRY_POINT_RENAME(ucase_getType) #define ucase_getTypeOrIgnorable U_ICU_ENTRY_POINT_RENAME(ucase_getTypeOrIgnorable) @@ -630,6 +631,7 @@ #define ucnv_cbFromUWriteUChars U_ICU_ENTRY_POINT_RENAME(ucnv_cbFromUWriteUChars) #define ucnv_cbToUWriteSub U_ICU_ENTRY_POINT_RENAME(ucnv_cbToUWriteSub) #define ucnv_cbToUWriteUChars U_ICU_ENTRY_POINT_RENAME(ucnv_cbToUWriteUChars) +#define ucnv_clone U_ICU_ENTRY_POINT_RENAME(ucnv_clone) #define ucnv_close U_ICU_ENTRY_POINT_RENAME(ucnv_close) #define ucnv_compareNames U_ICU_ENTRY_POINT_RENAME(ucnv_compareNames) #define ucnv_convert U_ICU_ENTRY_POINT_RENAME(ucnv_convert) @@ -725,6 +727,7 @@ #define ucnvsel_selectForString U_ICU_ENTRY_POINT_RENAME(ucnvsel_selectForString) #define ucnvsel_selectForUTF8 U_ICU_ENTRY_POINT_RENAME(ucnvsel_selectForUTF8) #define ucnvsel_serialize U_ICU_ENTRY_POINT_RENAME(ucnvsel_serialize) +#define ucol_clone U_ICU_ENTRY_POINT_RENAME(ucol_clone) #define ucol_cloneBinary U_ICU_ENTRY_POINT_RENAME(ucol_cloneBinary) #define ucol_close U_ICU_ENTRY_POINT_RENAME(ucol_close) #define ucol_closeElements U_ICU_ENTRY_POINT_RENAME(ucol_closeElements) @@ -904,6 +907,7 @@ #define udatpg_getBestPattern U_ICU_ENTRY_POINT_RENAME(udatpg_getBestPattern) #define udatpg_getBestPatternWithOptions U_ICU_ENTRY_POINT_RENAME(udatpg_getBestPatternWithOptions) #define udatpg_getDateTimeFormat U_ICU_ENTRY_POINT_RENAME(udatpg_getDateTimeFormat) +#define udatpg_getDateTimeFormatForStyle U_ICU_ENTRY_POINT_RENAME(udatpg_getDateTimeFormatForStyle) #define udatpg_getDecimal U_ICU_ENTRY_POINT_RENAME(udatpg_getDecimal) #define udatpg_getDefaultHourCycle U_ICU_ENTRY_POINT_RENAME(udatpg_getDefaultHourCycle) #define udatpg_getFieldDisplayName U_ICU_ENTRY_POINT_RENAME(udatpg_getFieldDisplayName) @@ -918,6 +922,7 @@ #define udatpg_setAppendItemFormat U_ICU_ENTRY_POINT_RENAME(udatpg_setAppendItemFormat) #define udatpg_setAppendItemName U_ICU_ENTRY_POINT_RENAME(udatpg_setAppendItemName) #define udatpg_setDateTimeFormat U_ICU_ENTRY_POINT_RENAME(udatpg_setDateTimeFormat) +#define udatpg_setDateTimeFormatForStyle U_ICU_ENTRY_POINT_RENAME(udatpg_setDateTimeFormatForStyle) #define udatpg_setDecimal U_ICU_ENTRY_POINT_RENAME(udatpg_setDecimal) #define udict_swap U_ICU_ENTRY_POINT_RENAME(udict_swap) #define udtitvfmt_close U_ICU_ENTRY_POINT_RENAME(udtitvfmt_close) diff --git a/deps/icu-small/source/common/unicode/uset.h b/deps/icu-small/source/common/unicode/uset.h index 2ef352ef563b02..33332f2d362441 100644 --- a/deps/icu-small/source/common/unicode/uset.h +++ b/deps/icu-small/source/common/unicode/uset.h @@ -628,7 +628,6 @@ uset_removeRange(USet* set, UChar32 start, UChar32 end); U_CAPI void U_EXPORT2 uset_removeString(USet* set, const UChar* str, int32_t strLen); -#ifndef U_HIDE_DRAFT_API /** * Removes EACH of the characters in this string. Note: "ch" == {"c", "h"} * A frozen set will not be modified. @@ -636,11 +635,10 @@ uset_removeString(USet* set, const UChar* str, int32_t strLen); * @param set the object to be modified * @param str the string * @param length the length of the string, or -1 if NUL-terminated - * @draft ICU 69 + * @stable ICU 69 */ U_CAPI void U_EXPORT2 uset_removeAllCodePoints(USet *set, const UChar *str, int32_t length); -#endif // U_HIDE_DRAFT_API /** * Removes from this set all of its elements that are contained in the @@ -671,7 +669,6 @@ uset_removeAll(USet* set, const USet* removeSet); U_CAPI void U_EXPORT2 uset_retain(USet* set, UChar32 start, UChar32 end); -#ifndef U_HIDE_DRAFT_API /** * Retains only the specified string from this set if it is present. * Upon return this set will be empty if it did not contain s, or @@ -681,7 +678,7 @@ uset_retain(USet* set, UChar32 start, UChar32 end); * @param set the object to be modified * @param str the string * @param length the length of the string, or -1 if NUL-terminated - * @draft ICU 69 + * @stable ICU 69 */ U_CAPI void U_EXPORT2 uset_retainString(USet *set, const UChar *str, int32_t length); @@ -693,11 +690,10 @@ uset_retainString(USet *set, const UChar *str, int32_t length); * @param set the object to be modified * @param str the string * @param length the length of the string, or -1 if NUL-terminated - * @draft ICU 69 + * @stable ICU 69 */ U_CAPI void U_EXPORT2 uset_retainAllCodePoints(USet *set, const UChar *str, int32_t length); -#endif // U_HIDE_DRAFT_API /** * Retains only the elements in this set that are contained in the @@ -741,7 +737,6 @@ uset_compact(USet* set); U_CAPI void U_EXPORT2 uset_complement(USet* set); -#ifndef U_HIDE_DRAFT_API /** * Complements the specified range in this set. Any character in * the range will be removed if it is in this set, or will be @@ -753,7 +748,7 @@ uset_complement(USet* set); * @param set the object to be modified * @param start first character, inclusive, of range * @param end last character, inclusive, of range - * @draft ICU 69 + * @stable ICU 69 */ U_CAPI void U_EXPORT2 uset_complementRange(USet *set, UChar32 start, UChar32 end); @@ -766,7 +761,7 @@ uset_complementRange(USet *set, UChar32 start, UChar32 end); * @param set the object to be modified * @param str the string * @param length the length of the string, or -1 if NUL-terminated - * @draft ICU 69 + * @stable ICU 69 */ U_CAPI void U_EXPORT2 uset_complementString(USet *set, const UChar *str, int32_t length); @@ -778,11 +773,10 @@ uset_complementString(USet *set, const UChar *str, int32_t length); * @param set the object to be modified * @param str the string * @param length the length of the string, or -1 if NUL-terminated - * @draft ICU 69 + * @stable ICU 69 */ U_CAPI void U_EXPORT2 uset_complementAllCodePoints(USet *set, const UChar *str, int32_t length); -#endif // U_HIDE_DRAFT_API /** * Complements in this set all elements contained in the specified diff --git a/deps/icu-small/source/common/unicode/uvernum.h b/deps/icu-small/source/common/unicode/uvernum.h index 42e8865d7e330b..2706e0b0606429 100644 --- a/deps/icu-small/source/common/unicode/uvernum.h +++ b/deps/icu-small/source/common/unicode/uvernum.h @@ -60,7 +60,7 @@ * This value will change in the subsequent releases of ICU * @stable ICU 2.4 */ -#define U_ICU_VERSION_MAJOR_NUM 70 +#define U_ICU_VERSION_MAJOR_NUM 71 /** The current ICU minor version as an integer. * This value will change in the subsequent releases of ICU @@ -86,7 +86,7 @@ * This value will change in the subsequent releases of ICU * @stable ICU 2.6 */ -#define U_ICU_VERSION_SUFFIX _70 +#define U_ICU_VERSION_SUFFIX _71 /** * \def U_DEF2_ICU_ENTRY_POINT_RENAME @@ -139,7 +139,7 @@ * This value will change in the subsequent releases of ICU * @stable ICU 2.4 */ -#define U_ICU_VERSION "70.1" +#define U_ICU_VERSION "71.1" /** * The current ICU library major version number as a string, for library name suffixes. @@ -152,13 +152,13 @@ * * @stable ICU 2.6 */ -#define U_ICU_VERSION_SHORT "70" +#define U_ICU_VERSION_SHORT "71" #ifndef U_HIDE_INTERNAL_API /** Data version in ICU4C. * @internal ICU 4.4 Internal Use Only **/ -#define U_ICU_DATA_VERSION "70.1" +#define U_ICU_DATA_VERSION "71.1" #endif /* U_HIDE_INTERNAL_API */ /*=========================================================================== diff --git a/deps/icu-small/source/common/unistr.cpp b/deps/icu-small/source/common/unistr.cpp index 077b4d6ef20811..c18665928d851a 100644 --- a/deps/icu-small/source/common/unistr.cpp +++ b/deps/icu-small/source/common/unistr.cpp @@ -334,7 +334,8 @@ Replaceable::clone() const { // UnicodeString overrides clone() with a real implementation UnicodeString * UnicodeString::clone() const { - return new UnicodeString(*this); + LocalPointer clonedString(new UnicodeString(*this)); + return clonedString.isValid() && !clonedString->isBogus() ? clonedString.orphan() : nullptr; } //======================================== @@ -1976,7 +1977,12 @@ The vector deleting destructor is already a part of UObject, but defining it here makes sure that it is included with this object file. This makes sure that static library dependencies are kept to a minimum. */ +#if defined(__clang__) || U_GCC_MAJOR_MINOR >= 1100 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" static void uprv_UnicodeStringDummy(void) { delete [] (new UnicodeString[2]); } +#pragma GCC diagnostic pop +#endif #endif diff --git a/deps/icu-small/source/common/ustrcase.cpp b/deps/icu-small/source/common/ustrcase.cpp index 36b19e75f2d7ae..43910ea520984e 100644 --- a/deps/icu-small/source/common/ustrcase.cpp +++ b/deps/icu-small/source/common/ustrcase.cpp @@ -36,6 +36,12 @@ #include "ustr_imp.h" #include "uassert.h" +/** + * Code point for COMBINING ACUTE ACCENT + * @internal + */ +#define ACUTE u'\u0301' + U_NAMESPACE_BEGIN namespace { @@ -396,6 +402,94 @@ U_NAMESPACE_USE #if !UCONFIG_NO_BREAK_ITERATION +namespace { + +/** + * Input: c is a letter I with or without acute accent. + * start is the index in src after c, and is less than segmentLimit. + * If a plain i/I is followed by a plain j/J, + * or an i/I with acute (precomposed or decomposed) is followed by a j/J with acute, + * then we output accordingly. + * + * @return the src index after the titlecased sequence, or the start index if no Dutch IJ + */ +int32_t maybeTitleDutchIJ(const UChar *src, UChar32 c, int32_t start, int32_t segmentLimit, + UChar *dest, int32_t &destIndex, int32_t destCapacity, uint32_t options, + icu::Edits *edits) { + U_ASSERT(start < segmentLimit); + + int32_t index = start; + bool withAcute = false; + + // If the conditions are met, then the following variables tell us what to output. + int32_t unchanged1 = 0; // code units before the j, or the whole sequence (0..3) + bool doTitleJ = false; // true if the j needs to be titlecased + int32_t unchanged2 = 0; // after the j (0 or 1) + + // next character after the first letter + UChar c2 = src[index++]; + + // Is the first letter an i/I with accent? + if (c == u'I') { + if (c2 == ACUTE) { + withAcute = true; + unchanged1 = 1; + if (index == segmentLimit) { return start; } + c2 = src[index++]; + } + } else { // Í + withAcute = true; + } + + // Is the next character a j/J? + if (c2 == u'j') { + doTitleJ = true; + } else if (c2 == u'J') { + ++unchanged1; + } else { + return start; + } + + // A plain i/I must be followed by a plain j/J. + // An i/I with acute must be followed by a j/J with acute. + if (withAcute) { + if (index == segmentLimit || src[index++] != ACUTE) { return start; } + if (doTitleJ) { + unchanged2 = 1; + } else { + ++unchanged1; + } + } + + // There must not be another combining mark. + if (index < segmentLimit) { + int32_t cp; + int32_t i = index; + U16_NEXT(src, i, segmentLimit, cp); + uint32_t typeMask = U_GET_GC_MASK(cp); + if ((typeMask & U_GC_M_MASK) != 0) { + return start; + } + } + + // Output the rest of the Dutch IJ. + destIndex = appendUnchanged(dest, destIndex, destCapacity, src + start, unchanged1, options, edits); + start += unchanged1; + if (doTitleJ) { + destIndex = appendUChar(dest, destIndex, destCapacity, u'J'); + if (edits != nullptr) { + edits->addReplace(1, 1); + } + ++start; + } + destIndex = appendUnchanged(dest, destIndex, destCapacity, src + start, unchanged2, options, edits); + + U_ASSERT(start + unchanged2 == index); + return index; +} + +} // namespace + U_CFUNC int32_t U_CALLCONV ustrcase_internalToTitle(int32_t caseLocale, uint32_t options, BreakIterator *iter, UChar *dest, int32_t destCapacity, @@ -412,14 +506,14 @@ ustrcase_internalToTitle(int32_t caseLocale, uint32_t options, BreakIterator *it csc.limit=srcLength; int32_t destIndex=0; int32_t prev=0; - UBool isFirstIndex=TRUE; + bool isFirstIndex=true; /* titlecasing loop */ while(prevfirst(); } else { index=iter->next(); @@ -446,7 +540,7 @@ ustrcase_internalToTitle(int32_t caseLocale, uint32_t options, BreakIterator *it // Stop with titleStartaddReplace(1, 1); - } - titleLimit++; - } else if (src[titleStart+1] == 0x004A) { - // Keep the capital J from getting lowercased. - destIndex=appendUnchanged(dest, destIndex, destCapacity, - src+titleStart+1, 1, options, edits); - if(destIndex<0) { - errorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - titleLimit++; + caseLocale == UCASE_LOC_DUTCH) { + if (c < 0) { + c = ~c; + } + + if (c == u'I' || c == u'Í') { + titleLimit = maybeTitleDutchIJ(src, c, titleStart + 1, index, + dest, destIndex, destCapacity, options, + edits); } } diff --git a/deps/icu-small/source/common/uvector.cpp b/deps/icu-small/source/common/uvector.cpp index 4da8b864e1be34..844463921efd9a 100644 --- a/deps/icu-small/source/common/uvector.cpp +++ b/deps/icu-small/source/common/uvector.cpp @@ -99,14 +99,6 @@ bool UVector::operator==(const UVector& other) const { return true; } -// TODO: delete this function once all call sites have been migrated to the -// new addElement(). -void UVector::addElementX(void* obj, UErrorCode &status) { - if (ensureCapacityX(count + 1, status)) { - elements[count++].pointer = obj; - } -} - void UVector::addElement(void* obj, UErrorCode &status) { U_ASSERT(deleter == nullptr); if (ensureCapacity(count + 1, status)) { @@ -331,38 +323,6 @@ int32_t UVector::indexOf(UElement key, int32_t startIndex, int8_t hint) const { return -1; } -UBool UVector::ensureCapacityX(int32_t minimumCapacity, UErrorCode &status) { - if (minimumCapacity < 0) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return FALSE; - } - if (capacity < minimumCapacity) { - if (capacity > (INT32_MAX - 1) / 2) { // integer overflow check - status = U_ILLEGAL_ARGUMENT_ERROR; - return FALSE; - } - int32_t newCap = capacity * 2; - if (newCap < minimumCapacity) { - newCap = minimumCapacity; - } - if (newCap > (int32_t)(INT32_MAX / sizeof(UElement))) { // integer overflow check - // We keep the original memory contents on bad minimumCapacity. - status = U_ILLEGAL_ARGUMENT_ERROR; - return FALSE; - } - UElement* newElems = (UElement *)uprv_realloc(elements, sizeof(UElement)*newCap); - if (newElems == nullptr) { - // We keep the original contents on the memory failure on realloc or bad minimumCapacity. - status = U_MEMORY_ALLOCATION_ERROR; - return FALSE; - } - elements = newElems; - capacity = newCap; - } - return TRUE; -} - - UBool UVector::ensureCapacity(int32_t minimumCapacity, UErrorCode &status) { if (U_FAILURE(status)) { return false; @@ -370,7 +330,7 @@ UBool UVector::ensureCapacity(int32_t minimumCapacity, UErrorCode &status) { if (minimumCapacity < 0) { status = U_ILLEGAL_ARGUMENT_ERROR; return false; - } + } if (capacity < minimumCapacity) { if (capacity > (INT32_MAX - 1) / 2) { // integer overflow check status = U_ILLEGAL_ARGUMENT_ERROR; @@ -396,6 +356,7 @@ UBool UVector::ensureCapacity(int32_t minimumCapacity, UErrorCode &status) { } return true; } + /** * Change the size of this vector as follows: If newSize is smaller, * then truncate the array, possibly deleting held elements for i >= diff --git a/deps/icu-small/source/common/uvector.h b/deps/icu-small/source/common/uvector.h index f61fcc2be60fb1..1eb7d136e7ab82 100644 --- a/deps/icu-small/source/common/uvector.h +++ b/deps/icu-small/source/common/uvector.h @@ -123,12 +123,6 @@ class U_COMMON_API UVector : public UObject { // java.util.Vector API //------------------------------------------------------------ - /* - * Old version of addElement, with non-standard error handling. - * Will be removed once all uses have been switched to the new addElement(). - */ - void addElementX(void* obj, UErrorCode &status); - /** * Add an element at the end of the vector. * For use only with vectors that do not adopt their elements, which is to say, @@ -197,12 +191,6 @@ class U_COMMON_API UVector : public UObject { inline UBool isEmpty(void) const {return count == 0;} - /* - * Old version of ensureCapacity, with non-standard error handling. - * Will be removed once all uses have been switched to the new ensureCapacity(). - */ - UBool ensureCapacityX(int32_t minimumCapacity, UErrorCode &status); - UBool ensureCapacity(int32_t minimumCapacity, UErrorCode &status); /** diff --git a/deps/icu-small/source/common/uvectr32.cpp b/deps/icu-small/source/common/uvectr32.cpp index a77ecb689fdaad..2b4d0b8a75a365 100644 --- a/deps/icu-small/source/common/uvectr32.cpp +++ b/deps/icu-small/source/common/uvectr32.cpp @@ -83,7 +83,7 @@ void UVector32::assign(const UVector32& other, UErrorCode &ec) { } -bool UVector32::operator==(const UVector32& other) { +bool UVector32::operator==(const UVector32& other) const { int32_t i; if (count != other.count) return false; for (i=0; isetDeleter(uprv_deleteUObject); // underflow bucket - Bucket *bucket = new Bucket(getUnderflowLabel(), emptyString_, U_ALPHAINDEX_UNDERFLOW); - if (bucket == NULL) { - errorCode = U_MEMORY_ALLOCATION_ERROR; + LocalPointer bucket(new Bucket(getUnderflowLabel(), emptyString_, U_ALPHAINDEX_UNDERFLOW), errorCode); + if (U_FAILURE(errorCode)) { return NULL; } - bucketList->addElementX(bucket, errorCode); + bucketList->adoptElement(bucket.orphan(), errorCode); if (U_FAILURE(errorCode)) { return NULL; } UnicodeString temp; @@ -481,28 +480,24 @@ BucketList *AlphabeticIndex::createBucketList(UErrorCode &errorCode) const { if (skippedScript && bucketList->size() > 1) { // We are skipping one or more scripts, // and we are not just getting out of the underflow label. - bucket = new Bucket(getInflowLabel(), inflowBoundary, U_ALPHAINDEX_INFLOW); - if (bucket == NULL) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - bucketList->addElementX(bucket, errorCode); + bucket.adoptInsteadAndCheckErrorCode( + new Bucket(getInflowLabel(), inflowBoundary, U_ALPHAINDEX_INFLOW), errorCode); + bucketList->adoptElement(bucket.orphan(), errorCode); + if (U_FAILURE(errorCode)) { return nullptr; } } } // Add a bucket with the current label. - bucket = new Bucket(fixLabel(current, temp), current, U_ALPHAINDEX_NORMAL); - if (bucket == NULL) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - bucketList->addElementX(bucket, errorCode); + bucket.adoptInsteadAndCheckErrorCode( + new Bucket(fixLabel(current, temp), current, U_ALPHAINDEX_NORMAL), errorCode); + bucketList->adoptElement(bucket.orphan(), errorCode); + if (U_FAILURE(errorCode)) { return nullptr; } // Remember ASCII and Pinyin buckets for Pinyin redirects. UChar c; if (current.length() == 1 && 0x41 <= (c = current.charAt(0)) && c <= 0x5A) { // A-Z - asciiBuckets[c - 0x41] = bucket; + asciiBuckets[c - 0x41] = (Bucket *)bucketList->lastElement(); } else if (current.length() == BASE_LENGTH + 1 && current.startsWith(BASE, BASE_LENGTH) && 0x41 <= (c = current.charAt(BASE_LENGTH)) && c <= 0x5A) { - pinyinBuckets[c - 0x41] = bucket; + pinyinBuckets[c - 0x41] = (Bucket *)bucketList->lastElement(); hasPinyin = TRUE; } // Check for multiple primary weights. @@ -526,15 +521,16 @@ BucketList *AlphabeticIndex::createBucketList(UErrorCode &errorCode) const { // to the previous single-character bucket. // For example, after ... Q R S Sch we add Sch\uFFFF->S // and after ... Q R S Sch Sch\uFFFF St we add St\uFFFF->S. - bucket = new Bucket(emptyString_, + bucket.adoptInsteadAndCheckErrorCode(new Bucket(emptyString_, UnicodeString(current).append((UChar)0xFFFF), - U_ALPHAINDEX_NORMAL); - if (bucket == NULL) { - errorCode = U_MEMORY_ALLOCATION_ERROR; + U_ALPHAINDEX_NORMAL), + errorCode); + if (U_FAILURE(errorCode)) { return NULL; } bucket->displayBucket_ = singleBucket; - bucketList->addElementX(bucket, errorCode); + bucketList->adoptElement(bucket.orphan(), errorCode); + if (U_FAILURE(errorCode)) { return nullptr; } hasInvisibleBuckets = TRUE; break; } @@ -553,12 +549,10 @@ BucketList *AlphabeticIndex::createBucketList(UErrorCode &errorCode) const { return bl; } // overflow bucket - bucket = new Bucket(getOverflowLabel(), *scriptUpperBoundary, U_ALPHAINDEX_OVERFLOW); - if (bucket == NULL) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - bucketList->addElementX(bucket, errorCode); // final + bucket.adoptInsteadAndCheckErrorCode( + new Bucket(getOverflowLabel(), *scriptUpperBoundary, U_ALPHAINDEX_OVERFLOW), errorCode); + bucketList->adoptElement(bucket.orphan(), errorCode); // final + if (U_FAILURE(errorCode)) { return nullptr; } if (hasPinyin) { // Redirect Pinyin buckets. @@ -589,7 +583,7 @@ BucketList *AlphabeticIndex::createBucketList(UErrorCode &errorCode) const { int32_t i = bucketList->size() - 1; Bucket *nextBucket = getBucket(*bucketList, i); while (--i > 0) { - bucket = getBucket(*bucketList, i); + Bucket *bucket = getBucket(*bucketList, i); if (bucket->displayBucket_ != NULL) { continue; // skip invisible buckets } @@ -609,9 +603,9 @@ BucketList *AlphabeticIndex::createBucketList(UErrorCode &errorCode) const { // Do not call publicBucketList->setDeleter(): // This vector shares its objects with the bucketList. for (int32_t j = 0; j < bucketList->size(); ++j) { - bucket = getBucket(*bucketList, j); + Bucket *bucket = getBucket(*bucketList, j); if (bucket->displayBucket_ == NULL) { - publicBucketList->addElementX(bucket, errorCode); + publicBucketList->addElement(bucket, errorCode); } } if (U_FAILURE(errorCode)) { return NULL; } @@ -679,13 +673,13 @@ void AlphabeticIndex::initBuckets(UErrorCode &errorCode) { bucket = bucket->displayBucket_; } if (bucket->records_ == NULL) { - bucket->records_ = new UVector(errorCode); - if (bucket->records_ == NULL) { - errorCode = U_MEMORY_ALLOCATION_ERROR; + LocalPointer records(new UVector(errorCode), errorCode); + if (U_FAILURE(errorCode)) { return; } + bucket->records_ = records.orphan(); } - bucket->records_->addElementX(r, errorCode); + bucket->records_->addElement(r, errorCode); } } @@ -1011,12 +1005,11 @@ UVector *AlphabeticIndex::firstStringsInScript(UErrorCode &status) { // and the one for unassigned implicit weights (Cn). continue; } - UnicodeString *s = new UnicodeString(boundary); - if (s == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; + LocalPointer s(new UnicodeString(boundary), status); + dest->adoptElement(s.orphan(), status); + if (U_FAILURE(status)) { + return nullptr; } - dest->addElementX(s, status); } return dest.orphan(); } @@ -1067,19 +1060,18 @@ AlphabeticIndex & AlphabeticIndex::addRecord(const UnicodeString &name, const vo return *this; } if (inputList_ == NULL) { - inputList_ = new UVector(status); - if (inputList_ == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; + LocalPointer inputList(new UVector(status), status); + if (U_FAILURE(status)) { return *this; } + inputList_ = inputList.orphan(); inputList_->setDeleter(alphaIndex_deleteRecord); } - Record *r = new Record(name, data); - if (r == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; + LocalPointer r(new Record(name, data), status); + inputList_->adoptElement(r.orphan(), status); + if (U_FAILURE(status)) { return *this; } - inputList_->addElementX(r, status); clearBuckets(); //std::string ss; //std::string ss2; diff --git a/deps/icu-small/source/i18n/collationdatabuilder.cpp b/deps/icu-small/source/i18n/collationdatabuilder.cpp index 25050aa777e681..b10de993c279a3 100644 --- a/deps/icu-small/source/i18n/collationdatabuilder.cpp +++ b/deps/icu-small/source/i18n/collationdatabuilder.cpp @@ -522,12 +522,11 @@ CollationDataBuilder::addConditionalCE32(const UnicodeString &context, uint32_t errorCode = U_BUFFER_OVERFLOW_ERROR; return -1; } - ConditionalCE32 *cond = new ConditionalCE32(context, ce32); - if(cond == NULL) { - errorCode = U_MEMORY_ALLOCATION_ERROR; + LocalPointer cond(new ConditionalCE32(context, ce32), errorCode); + conditionalCE32s.adoptElement(cond.orphan(), errorCode); + if(U_FAILURE(errorCode)) { return -1; } - conditionalCE32s.addElementX(cond, errorCode); return index; } diff --git a/deps/icu-small/source/i18n/csdetect.cpp b/deps/icu-small/source/i18n/csdetect.cpp index 84f0776542d896..d866eb66286811 100644 --- a/deps/icu-small/source/i18n/csdetect.cpp +++ b/deps/icu-small/source/i18n/csdetect.cpp @@ -270,6 +270,11 @@ const CharsetMatch * const *CharsetDetector::detectAll(int32_t &maxMatchesFound, maxMatchesFound = resultCount; + if (maxMatchesFound == 0) { + status = U_INVALID_CHAR_FOUND; + return NULL; + } + return resultArray; } diff --git a/deps/icu-small/source/i18n/double-conversion-ieee.h b/deps/icu-small/source/i18n/double-conversion-ieee.h index 31c35867de5610..2940acb16981e8 100644 --- a/deps/icu-small/source/i18n/double-conversion-ieee.h +++ b/deps/icu-small/source/i18n/double-conversion-ieee.h @@ -164,11 +164,19 @@ class Double { } bool IsQuietNan() const { +#if (defined(__mips__) && !defined(__mips_nan2008)) || defined(__hppa__) + return IsNan() && ((AsUint64() & kQuietNanBit) == 0); +#else return IsNan() && ((AsUint64() & kQuietNanBit) != 0); +#endif } bool IsSignalingNan() const { +#if (defined(__mips__) && !defined(__mips_nan2008)) || defined(__hppa__) + return IsNan() && ((AsUint64() & kQuietNanBit) != 0); +#else return IsNan() && ((AsUint64() & kQuietNanBit) == 0); +#endif } @@ -250,7 +258,12 @@ class Double { private: static const int kDenormalExponent = -kExponentBias + 1; static const uint64_t kInfinity = DOUBLE_CONVERSION_UINT64_2PART_C(0x7FF00000, 00000000); +#if (defined(__mips__) && !defined(__mips_nan2008)) || defined(__hppa__) + static const uint64_t kNaN = DOUBLE_CONVERSION_UINT64_2PART_C(0x7FF7FFFF, FFFFFFFF); +#else static const uint64_t kNaN = DOUBLE_CONVERSION_UINT64_2PART_C(0x7FF80000, 00000000); +#endif + const uint64_t d64_; @@ -350,11 +363,19 @@ class Single { } bool IsQuietNan() const { +#if (defined(__mips__) && !defined(__mips_nan2008)) || defined(__hppa__) + return IsNan() && ((AsUint32() & kQuietNanBit) == 0); +#else return IsNan() && ((AsUint32() & kQuietNanBit) != 0); +#endif } bool IsSignalingNan() const { +#if (defined(__mips__) && !defined(__mips_nan2008)) || defined(__hppa__) + return IsNan() && ((AsUint32() & kQuietNanBit) != 0); +#else return IsNan() && ((AsUint32() & kQuietNanBit) == 0); +#endif } @@ -424,7 +445,11 @@ class Single { static const int kDenormalExponent = -kExponentBias + 1; static const int kMaxExponent = 0xFF - kExponentBias; static const uint32_t kInfinity = 0x7F800000; +#if (defined(__mips__) && !defined(__mips_nan2008)) || defined(__hppa__) + static const uint32_t kNaN = 0x7FBFFFFF; +#else static const uint32_t kNaN = 0x7FC00000; +#endif const uint32_t d32_; diff --git a/deps/icu-small/source/i18n/double-conversion-utils.h b/deps/icu-small/source/i18n/double-conversion-utils.h index 7f23e0a8250d2b..11c92717c10d72 100644 --- a/deps/icu-small/source/i18n/double-conversion-utils.h +++ b/deps/icu-small/source/i18n/double-conversion-utils.h @@ -37,6 +37,9 @@ #ifndef DOUBLE_CONVERSION_UTILS_H_ #define DOUBLE_CONVERSION_UTILS_H_ +// Use DOUBLE_CONVERSION_NON_PREFIXED_MACROS to get unprefixed macros as was +// the case in double-conversion releases prior to 3.1.6 + #include #include @@ -46,9 +49,17 @@ #define DOUBLE_CONVERSION_ASSERT(condition) \ U_ASSERT(condition) #endif +#if defined(DOUBLE_CONVERSION_NON_PREFIXED_MACROS) && !defined(ASSERT) +#define ASSERT DOUBLE_CONVERSION_ASSERT +#endif + #ifndef DOUBLE_CONVERSION_UNIMPLEMENTED #define DOUBLE_CONVERSION_UNIMPLEMENTED() (abort()) #endif +#if defined(DOUBLE_CONVERSION_NON_PREFIXED_MACROS) && !defined(UNIMPLEMENTED) +#define UNIMPLEMENTED DOUBLE_CONVERSION_UNIMPLEMENTED +#endif + #ifndef DOUBLE_CONVERSION_NO_RETURN #ifdef _MSC_VER #define DOUBLE_CONVERSION_NO_RETURN __declspec(noreturn) @@ -56,6 +67,10 @@ #define DOUBLE_CONVERSION_NO_RETURN __attribute__((noreturn)) #endif #endif +#if defined(DOUBLE_CONVERSION_NON_PREFIXED_MACROS) && !defined(NO_RETURN) +#define NO_RETURN DOUBLE_CONVERSION_NO_RETURN +#endif + #ifndef DOUBLE_CONVERSION_UNREACHABLE #ifdef _MSC_VER void DOUBLE_CONVERSION_NO_RETURN abort_noreturn(); @@ -65,6 +80,9 @@ inline void abort_noreturn() { abort(); } #define DOUBLE_CONVERSION_UNREACHABLE() (abort()) #endif #endif +#if defined(DOUBLE_CONVERSION_NON_PREFIXED_MACROS) && !defined(UNREACHABLE) +#define UNREACHABLE DOUBLE_CONVERSION_UNREACHABLE +#endif // Not all compilers support __has_attribute and combining a check for both // ifdef and __has_attribute on the same preprocessor line isn't portable. @@ -81,12 +99,18 @@ inline void abort_noreturn() { abort(); } #define DOUBLE_CONVERSION_UNUSED #endif #endif +#if defined(DOUBLE_CONVERSION_NON_PREFIXED_MACROS) && !defined(UNUSED) +#define UNUSED DOUBLE_CONVERSION_UNUSED +#endif #if DOUBLE_CONVERSION_HAS_ATTRIBUTE(uninitialized) #define DOUBLE_CONVERSION_STACK_UNINITIALIZED __attribute__((uninitialized)) #else #define DOUBLE_CONVERSION_STACK_UNINITIALIZED #endif +#if defined(DOUBLE_CONVERSION_NON_PREFIXED_MACROS) && !defined(STACK_UNINITIALIZED) +#define STACK_UNINITIALIZED DOUBLE_CONVERSION_STACK_UNINITIALIZED +#endif // Double operations detection based on target architecture. // Linux uses a 80bit wide floating point stack on x86. This induces double @@ -127,7 +151,7 @@ int main(int argc, char** argv) { defined(_MIPS_ARCH_MIPS32R2) || defined(__ARMEB__) ||\ defined(__AARCH64EL__) || defined(__aarch64__) || defined(__AARCH64EB__) || \ defined(__riscv) || defined(__e2k__) || \ - defined(__or1k__) || defined(__arc__) || \ + defined(__or1k__) || defined(__arc__) || defined(__ARC64__) || \ defined(__microblaze__) || defined(__XTENSA__) || \ defined(__EMSCRIPTEN__) || defined(__wasm32__) #define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1 @@ -144,6 +168,9 @@ int main(int argc, char** argv) { #else #error Target architecture was not detected as supported by Double-Conversion. #endif +#if defined(DOUBLE_CONVERSION_NON_PREFIXED_MACROS) && !defined(CORRECT_DOUBLE_OPERATIONS) +#define CORRECT_DOUBLE_OPERATIONS DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS +#endif #if defined(_WIN32) && !defined(__MINGW32__) @@ -169,7 +196,9 @@ typedef uint16_t uc16; // Usage: instead of writing 0x1234567890123456 // write DOUBLE_CONVERSION_UINT64_2PART_C(0x12345678,90123456); #define DOUBLE_CONVERSION_UINT64_2PART_C(a, b) (((static_cast(a) << 32) + 0x##b##u)) - +#if defined(DOUBLE_CONVERSION_NON_PREFIXED_MACROS) && !defined(UINT64_2PART_C) +#define UINT64_2PART_C DOUBLE_CONVERSION_UINT64_2PART_C +#endif // The expression DOUBLE_CONVERSION_ARRAY_SIZE(a) is a compile-time constant of type // size_t which represents the number of elements of the given @@ -180,6 +209,9 @@ typedef uint16_t uc16; ((sizeof(a) / sizeof(*(a))) / \ static_cast(!(sizeof(a) % sizeof(*(a))))) #endif +#if defined(DOUBLE_CONVERSION_NON_PREFIXED_MACROS) && !defined(ARRAY_SIZE) +#define ARRAY_SIZE DOUBLE_CONVERSION_ARRAY_SIZE +#endif // A macro to disallow the evil copy constructor and operator= functions // This should be used in the private: declarations for a class @@ -188,6 +220,9 @@ typedef uint16_t uc16; TypeName(const TypeName&); \ void operator=(const TypeName&) #endif +#if defined(DOUBLE_CONVERSION_NON_PREFIXED_MACROS) && !defined(DC_DISALLOW_COPY_AND_ASSIGN) +#define DC_DISALLOW_COPY_AND_ASSIGN DOUBLE_CONVERSION_DISALLOW_COPY_AND_ASSIGN +#endif // A macro to disallow all the implicit constructors, namely the // default constructor, copy constructor and operator= functions. @@ -200,6 +235,9 @@ typedef uint16_t uc16; TypeName(); \ DOUBLE_CONVERSION_DISALLOW_COPY_AND_ASSIGN(TypeName) #endif +#if defined(DOUBLE_CONVERSION_NON_PREFIXED_MACROS) && !defined(DC_DISALLOW_IMPLICIT_CONSTRUCTORS) +#define DC_DISALLOW_IMPLICIT_CONSTRUCTORS DOUBLE_CONVERSION_DISALLOW_IMPLICIT_CONSTRUCTORS +#endif // ICU PATCH: Wrap in ICU namespace U_NAMESPACE_BEGIN diff --git a/deps/icu-small/source/i18n/dtfmtsym.cpp b/deps/icu-small/source/i18n/dtfmtsym.cpp index ab5f2b612c1f0e..134b919f06ea59 100644 --- a/deps/icu-small/source/i18n/dtfmtsym.cpp +++ b/deps/icu-small/source/i18n/dtfmtsym.cpp @@ -1574,26 +1574,20 @@ struct CalendarDataSink : public ResourceSink { errorCode); if (U_FAILURE(errorCode)) { return; } } - LocalPointer aliasRelativePathCopy(new UnicodeString(aliasRelativePath), errorCode); - resourcesToVisitNext->addElementX(aliasRelativePathCopy.getAlias(), errorCode); + LocalPointer aliasRelativePathCopy(aliasRelativePath.clone(), errorCode); + resourcesToVisitNext->adoptElement(aliasRelativePathCopy.orphan(), errorCode); if (U_FAILURE(errorCode)) { return; } - // Only release ownership after resourcesToVisitNext takes it (no error happened): - aliasRelativePathCopy.orphan(); continue; } else if (aliasType == SAME_CALENDAR) { // Register same-calendar alias if (arrays.get(aliasRelativePath) == NULL && maps.get(aliasRelativePath) == NULL) { - LocalPointer aliasRelativePathCopy(new UnicodeString(aliasRelativePath), errorCode); - aliasPathPairs.addElementX(aliasRelativePathCopy.getAlias(), errorCode); + LocalPointer aliasRelativePathCopy(aliasRelativePath.clone(), errorCode); + aliasPathPairs.adoptElement(aliasRelativePathCopy.orphan(), errorCode); if (U_FAILURE(errorCode)) { return; } - // Only release ownership after aliasPathPairs takes it (no error happened): - aliasRelativePathCopy.orphan(); - LocalPointer keyUStringCopy(new UnicodeString(keyUString), errorCode); - aliasPathPairs.addElementX(keyUStringCopy.getAlias(), errorCode); + LocalPointer keyUStringCopy(keyUString.clone(), errorCode); + aliasPathPairs.adoptElement(keyUStringCopy.orphan(), errorCode); if (U_FAILURE(errorCode)) { return; } - // Only release ownership after aliasPathPairs takes it (no error happened): - keyUStringCopy.orphan(); } continue; } @@ -1760,16 +1754,12 @@ struct CalendarDataSink : public ResourceSink { if (U_FAILURE(errorCode)) { return; } if (aliasType == SAME_CALENDAR) { // Store the alias path and the current path on aliasPathPairs - LocalPointer aliasRelativePathCopy(new UnicodeString(aliasRelativePath), errorCode); - aliasPathPairs.addElementX(aliasRelativePathCopy.getAlias(), errorCode); + LocalPointer aliasRelativePathCopy(aliasRelativePath.clone(), errorCode); + aliasPathPairs.adoptElement(aliasRelativePathCopy.orphan(), errorCode); if (U_FAILURE(errorCode)) { return; } - // Only release ownership after aliasPathPairs takes it (no error happened): - aliasRelativePathCopy.orphan(); - LocalPointer pathCopy(new UnicodeString(path), errorCode); - aliasPathPairs.addElementX(pathCopy.getAlias(), errorCode); + LocalPointer pathCopy(path.clone(), errorCode); + aliasPathPairs.adoptElement(pathCopy.orphan(), errorCode); if (U_FAILURE(errorCode)) { return; } - // Only release ownership after aliasPathPairs takes it (no error happened): - pathCopy.orphan(); // Drop the latest key on the path and continue path.retainBetween(0, pathLength); diff --git a/deps/icu-small/source/i18n/dtitvfmt.cpp b/deps/icu-small/source/i18n/dtitvfmt.cpp index 298fb62be0cc22..d51ddcd5c70356 100644 --- a/deps/icu-small/source/i18n/dtitvfmt.cpp +++ b/deps/icu-small/source/i18n/dtitvfmt.cpp @@ -965,16 +965,22 @@ DateIntervalFormat::normalizeHourMetacharacters(const UnicodeString& skeleton) c UnicodeString result = skeleton; UChar hourMetachar = u'\0'; + UChar dayPeriodChar = u'\0'; int32_t metacharStart = 0; int32_t metacharCount = 0; for (int32_t i = 0; i < result.length(); i++) { UChar c = result[i]; - if (c == LOW_J || c == CAP_J || c == CAP_C) { + if (c == LOW_J || c == CAP_J || c == CAP_C || c == LOW_H || c == CAP_H || c == LOW_K || c == CAP_K) { if (hourMetachar == u'\0') { hourMetachar = c; metacharStart = i; } ++metacharCount; + } else if (c == LOW_A || c == LOW_B || c == CAP_B) { + if (dayPeriodChar == u'\0') { + dayPeriodChar = c; + } + ++metacharCount; } else { if (hourMetachar != u'\0') { break; @@ -985,7 +991,6 @@ DateIntervalFormat::normalizeHourMetacharacters(const UnicodeString& skeleton) c if (hourMetachar != u'\0') { UErrorCode err = U_ZERO_ERROR; UChar hourChar = CAP_H; - UChar dayPeriodChar = LOW_A; UnicodeString convertedPattern = DateFormat::getBestPattern(fLocale, UnicodeString(hourMetachar), err); if (U_SUCCESS(err)) { @@ -1012,6 +1017,8 @@ DateIntervalFormat::normalizeHourMetacharacters(const UnicodeString& skeleton) c dayPeriodChar = LOW_B; } else if (convertedPattern.indexOf(CAP_B) != -1) { dayPeriodChar = CAP_B; + } else if (dayPeriodChar == u'\0') { + dayPeriodChar = LOW_A; } } diff --git a/deps/icu-small/source/i18n/dtptngen.cpp b/deps/icu-small/source/i18n/dtptngen.cpp index 6aee1750f90617..e781c6e26f5fcb 100644 --- a/deps/icu-small/source/i18n/dtptngen.cpp +++ b/deps/icu-small/source/i18n/dtptngen.cpp @@ -273,8 +273,6 @@ static const char* const CLDR_FIELD_WIDTH[] = { // [UDATPG_WIDTH_COUNT] "", "-short", "-narrow" }; -// TODO(ticket:13619): remove when definition uncommented in dtptngen.h. -static const int32_t UDATPG_WIDTH_COUNT = UDATPG_NARROW + 1; static constexpr UDateTimePGDisplayWidth UDATPG_WIDTH_APPENDITEM = UDATPG_WIDE; static constexpr int32_t UDATPG_FIELD_KEY_MAX = 24; // max length of CLDR field tag (type + width) @@ -393,10 +391,13 @@ DateTimePatternGenerator::operator=(const DateTimePatternGenerator& other) { *fp = *(other.fp); dtMatcher->copyFrom(other.dtMatcher->skeleton); *distanceInfo = *(other.distanceInfo); - dateTimeFormat = other.dateTimeFormat; + for (int32_t style = UDAT_FULL; style <= UDAT_SHORT; style++) { + dateTimeFormat[style] = other.dateTimeFormat[style]; + } decimal = other.decimal; - // NUL-terminate for the C API. - dateTimeFormat.getTerminatedBuffer(); + for (int32_t style = UDAT_FULL; style <= UDAT_SHORT; style++) { + dateTimeFormat[style].getTerminatedBuffer(); // NUL-terminate for the C API. + } decimal.getTerminatedBuffer(); delete skipMatcher; if ( other.skipMatcher == nullptr ) { @@ -430,7 +431,12 @@ DateTimePatternGenerator::operator==(const DateTimePatternGenerator& other) cons return true; } if ((pLocale==other.pLocale) && (patternMap->equals(*other.patternMap)) && - (dateTimeFormat==other.dateTimeFormat) && (decimal==other.decimal)) { + (decimal==other.decimal)) { + for (int32_t style = UDAT_FULL; style <= UDAT_SHORT; style++) { + if (dateTimeFormat[style] != other.dateTimeFormat[style]) { + return false; + } + } for ( int32_t i=0 ; igetSkeletonPtr(); + UDateFormatStyle style = UDAT_SHORT; + int32_t monthFieldLen = reqSkeleton->baseOriginal.getFieldLength(UDATPG_MONTH_FIELD); + if (monthFieldLen == 4) { + if (reqSkeleton->baseOriginal.getFieldLength(UDATPG_WEEKDAY_FIELD) > 0) { + style = UDAT_FULL; + } else { + style = UDAT_LONG; + } + } else if (monthFieldLen == 3) { + style = UDAT_MEDIUM; + } + // and now use it to compose date and time + dtFormat=getDateTimeFormat(style, status); SimpleFormatter(dtFormat, 2, 2, status).format(timePattern, datePattern, resultPattern, status); return resultPattern; } @@ -1335,14 +1355,45 @@ DateTimePatternGenerator::addCanonicalItems(UErrorCode& status) { void DateTimePatternGenerator::setDateTimeFormat(const UnicodeString& dtFormat) { - dateTimeFormat = dtFormat; - // NUL-terminate for the C API. - dateTimeFormat.getTerminatedBuffer(); + UErrorCode status = U_ZERO_ERROR; + for (int32_t style = UDAT_FULL; style <= UDAT_SHORT; style++) { + setDateTimeFormat((UDateFormatStyle)style, dtFormat, status); + } } const UnicodeString& DateTimePatternGenerator::getDateTimeFormat() const { - return dateTimeFormat; + UErrorCode status = U_ZERO_ERROR; + return getDateTimeFormat(UDAT_MEDIUM, status); +} + +void +DateTimePatternGenerator::setDateTimeFormat(UDateFormatStyle style, const UnicodeString& dtFormat, UErrorCode& status) { + if (U_FAILURE(status)) { + return; + } + if (style < UDAT_FULL || style > UDAT_SHORT) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + dateTimeFormat[style] = dtFormat; + // Note for the following: getTerminatedBuffer() can re-allocate the UnicodeString + // buffer so we do this here before clients request a const ref to the UnicodeString + // or its buffer. + dateTimeFormat[style].getTerminatedBuffer(); // NUL-terminate for the C API. +} + +const UnicodeString& +DateTimePatternGenerator::getDateTimeFormat(UDateFormatStyle style, UErrorCode& status) const { + static const UnicodeString emptyString = UNICODE_STRING_SIMPLE(""); + if (U_FAILURE(status)) { + return emptyString; + } + if (style < UDAT_FULL || style > UDAT_SHORT) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return emptyString; + } + return dateTimeFormat[style]; } void @@ -1378,13 +1429,15 @@ DateTimePatternGenerator::setDateTimeFromCalendar(const Locale& locale, UErrorCo } if (U_FAILURE(status)) { return; } - if (ures_getSize(dateTimePatterns.getAlias()) <= DateFormat::kDateTime) + if (ures_getSize(dateTimePatterns.getAlias()) <= DateFormat::kDateTimeOffset + DateFormat::kShort) { status = U_INVALID_FORMAT_ERROR; return; } - resStr = ures_getStringByIndex(dateTimePatterns.getAlias(), (int32_t)DateFormat::kDateTime, &resStrLen, &status); - setDateTimeFormat(UnicodeString(TRUE, resStr, resStrLen)); + for (int32_t style = UDAT_FULL; style <= UDAT_SHORT; style++) { + resStr = ures_getStringByIndex(dateTimePatterns.getAlias(), (int32_t)DateFormat::kDateTimeOffset + style, &resStrLen, &status); + setDateTimeFormat((UDateFormatStyle)style, UnicodeString(TRUE, resStr, resStrLen), status); + } } void @@ -2788,16 +2841,17 @@ DTSkeletonEnumeration::DTSkeletonEnumeration(PatternMap& patternMap, dtStrEnum t break; } if ( !isCanonicalItem(s) ) { - LocalPointer newElem(new UnicodeString(s), status); + LocalPointer newElem(s.clone(), status); if (U_FAILURE(status)) { return; } - fSkeletons->addElementX(newElem.getAlias(), status); + fSkeletons->addElement(newElem.getAlias(), status); if (U_FAILURE(status)) { fSkeletons.adoptInstead(nullptr); return; } - newElem.orphan(); // fSkeletons vector now owns the UnicodeString. + newElem.orphan(); // fSkeletons vector now owns the UnicodeString (although it + // does not use a deleter function to manage the ownership). } curElem = curElem->next.getAlias(); } @@ -2865,12 +2919,13 @@ DTRedundantEnumeration::add(const UnicodeString& pattern, UErrorCode& status) { if (U_FAILURE(status)) { return; } - fPatterns->addElementX(newElem.getAlias(), status); + fPatterns->addElement(newElem.getAlias(), status); if (U_FAILURE(status)) { fPatterns.adoptInstead(nullptr); return; } - newElem.orphan(); // fPatterns now owns the string. + newElem.orphan(); // fPatterns now owns the string, although a UVector + // deleter function is not used to manage that ownership. } const UnicodeString* diff --git a/deps/icu-small/source/i18n/formattedval_sbimpl.cpp b/deps/icu-small/source/i18n/formattedval_sbimpl.cpp index 70ffacac4b7416..72197cdd8c7abf 100644 --- a/deps/icu-small/source/i18n/formattedval_sbimpl.cpp +++ b/deps/icu-small/source/i18n/formattedval_sbimpl.cpp @@ -230,6 +230,11 @@ bool FormattedValueStringBuilderImpl::nextPositionImpl(ConstrainedFieldPosition& if (si + 1 < spanIndicesCount) { nextSpanStart = spanIndices[si + 1].start; } + if (length == 0) { + // ICU-21871: Don't return fields on empty spans + i--; + continue; + } if (cfpos.matchesField(spanCategory, spanValue)) { fieldStart = i - fString.fZero; int32_t end = fieldStart + length; diff --git a/deps/icu-small/source/i18n/measunit_extra.cpp b/deps/icu-small/source/i18n/measunit_extra.cpp index 8281119007949a..2df9edee96a8a6 100644 --- a/deps/icu-small/source/i18n/measunit_extra.cpp +++ b/deps/icu-small/source/i18n/measunit_extra.cpp @@ -615,7 +615,7 @@ class Parser { // Set to true when we've seen a "-per-" or a "per-", after which all units // are in the denominator. Until we find an "-and-", at which point the - // identifier is invalid pending TODO(CLDR-13700). + // identifier is invalid pending TODO(CLDR-13701). bool fAfterPer = false; Parser() : fSource(""), fTrie(u"") {} @@ -669,7 +669,7 @@ class Parser { * dimensionality. * * Returns an error if we parse both compound units and "-and-", since mixed - * compound units are not yet supported - TODO(CLDR-13700). + * compound units are not yet supported - TODO(CLDR-13701). * * @param result Will be overwritten by the result, if status shows success. * @param sawAnd If an "-and-" was parsed prior to finding the "single diff --git a/deps/icu-small/source/i18n/msgfmt.cpp b/deps/icu-small/source/i18n/msgfmt.cpp index b8cb2e2ca560fe..13a5a0895160fb 100644 --- a/deps/icu-small/source/i18n/msgfmt.cpp +++ b/deps/icu-small/source/i18n/msgfmt.cpp @@ -854,19 +854,21 @@ StringEnumeration* MessageFormat::getFormatNames(UErrorCode& status) { if (U_FAILURE(status)) return NULL; - UVector *fFormatNames = new UVector(status); + LocalPointer formatNames(new UVector(status), status); if (U_FAILURE(status)) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; + return nullptr; } - fFormatNames->setDeleter(uprv_deleteUObject); + formatNames->setDeleter(uprv_deleteUObject); for (int32_t partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { - fFormatNames->addElementX(new UnicodeString(getArgName(partIndex + 1)), status); + LocalPointer name(getArgName(partIndex + 1).clone(), status); + formatNames->adoptElement(name.orphan(), status); + if (U_FAILURE(status)) return nullptr; } - StringEnumeration* nameEnumerator = new FormatNameEnumeration(fFormatNames, status); - return nameEnumerator; + LocalPointer nameEnumerator( + new FormatNameEnumeration(std::move(formatNames), status), status); + return U_SUCCESS(status) ? nameEnumerator.orphan() : nullptr; } // ------------------------------------- @@ -1912,9 +1914,9 @@ void MessageFormat::DummyFormat::parseObject(const UnicodeString&, } -FormatNameEnumeration::FormatNameEnumeration(UVector *fNameList, UErrorCode& /*status*/) { +FormatNameEnumeration::FormatNameEnumeration(LocalPointer nameList, UErrorCode& /*status*/) { pos=0; - fFormatNames = fNameList; + fFormatNames = std::move(nameList); } const UnicodeString* @@ -1936,7 +1938,6 @@ FormatNameEnumeration::count(UErrorCode& /*status*/) const { } FormatNameEnumeration::~FormatNameEnumeration() { - delete fFormatNames; } MessageFormat::PluralSelectorProvider::PluralSelectorProvider(const MessageFormat &mf, UPluralType t) diff --git a/deps/icu-small/source/i18n/msgfmt_impl.h b/deps/icu-small/source/i18n/msgfmt_impl.h index 57988389132a67..80cb8a691eb1dd 100644 --- a/deps/icu-small/source/i18n/msgfmt_impl.h +++ b/deps/icu-small/source/i18n/msgfmt_impl.h @@ -26,7 +26,7 @@ U_NAMESPACE_BEGIN class FormatNameEnumeration : public StringEnumeration { public: - FormatNameEnumeration(UVector *fFormatNames, UErrorCode& status); + FormatNameEnumeration(LocalPointer fFormatNames, UErrorCode& status); virtual ~FormatNameEnumeration(); static UClassID U_EXPORT2 getStaticClassID(void); virtual UClassID getDynamicClassID(void) const override; @@ -35,7 +35,7 @@ class FormatNameEnumeration : public StringEnumeration { virtual int32_t count(UErrorCode& status) const override; private: int32_t pos; - UVector *fFormatNames; + LocalPointer fFormatNames; }; U_NAMESPACE_END diff --git a/deps/icu-small/source/i18n/number_affixutils.cpp b/deps/icu-small/source/i18n/number_affixutils.cpp index f9c154c885cd8a..5f5ff4c3a63422 100644 --- a/deps/icu-small/source/i18n/number_affixutils.cpp +++ b/deps/icu-small/source/i18n/number_affixutils.cpp @@ -135,8 +135,7 @@ Field AffixUtils::getFieldForType(AffixPatternType type) { case TYPE_PLUS_SIGN: return {UFIELD_CATEGORY_NUMBER, UNUM_SIGN_FIELD}; case TYPE_APPROXIMATELY_SIGN: - // TODO: Introduce a new field for the approximately sign? - return {UFIELD_CATEGORY_NUMBER, UNUM_SIGN_FIELD}; + return {UFIELD_CATEGORY_NUMBER, UNUM_APPROXIMATELY_SIGN_FIELD}; case TYPE_PERCENT: return {UFIELD_CATEGORY_NUMBER, UNUM_PERCENT_FIELD}; case TYPE_PERMILLE: diff --git a/deps/icu-small/source/i18n/number_compact.cpp b/deps/icu-small/source/i18n/number_compact.cpp index 62692f444dff07..60cd7bedf667b0 100644 --- a/deps/icu-small/source/i18n/number_compact.cpp +++ b/deps/icu-small/source/i18n/number_compact.cpp @@ -157,8 +157,8 @@ void CompactData::getUniquePatterns(UVector &output, UErrorCode &status) const { } // The string was not found; add it to the UVector. - // ANDY: This requires a const_cast. Why? - output.addElementX(const_cast(pattern), status); + // Note: must cast off const from pattern to store it in a UVector, which expects (void *) + output.addElement(const_cast(pattern), status); continue_outer: continue; diff --git a/deps/icu-small/source/i18n/number_decimalquantity.cpp b/deps/icu-small/source/i18n/number_decimalquantity.cpp index 6a2847b1c18f19..b40e1276c350fe 100644 --- a/deps/icu-small/source/i18n/number_decimalquantity.cpp +++ b/deps/icu-small/source/i18n/number_decimalquantity.cpp @@ -181,20 +181,22 @@ uint64_t DecimalQuantity::getPositionFingerprint() const { return fingerprint; } -void DecimalQuantity::roundToIncrement(double roundingIncrement, RoundingMode roundingMode, - UErrorCode& status) { +void DecimalQuantity::roundToIncrement( + uint64_t increment, + digits_t magnitude, + RoundingMode roundingMode, + UErrorCode& status) { // Do not call this method with an increment having only a 1 or a 5 digit! // Use a more efficient call to either roundToMagnitude() or roundToNickel(). // Check a few popular rounding increments; a more thorough check is in Java. - U_ASSERT(roundingIncrement != 0.01); - U_ASSERT(roundingIncrement != 0.05); - U_ASSERT(roundingIncrement != 0.1); - U_ASSERT(roundingIncrement != 0.5); - U_ASSERT(roundingIncrement != 1); - U_ASSERT(roundingIncrement != 5); + U_ASSERT(increment != 1); + U_ASSERT(increment != 5); + DecimalQuantity incrementDQ; + incrementDQ.setToLong(increment); + incrementDQ.adjustMagnitude(magnitude); DecNum incrementDN; - incrementDN.setTo(roundingIncrement, status); + incrementDQ.toDecNum(incrementDN, status); if (U_FAILURE(status)) { return; } // Divide this DecimalQuantity by the increment, round, then multiply back. @@ -254,6 +256,12 @@ bool DecimalQuantity::adjustMagnitude(int32_t delta) { return false; } +int32_t DecimalQuantity::adjustToZeroScale() { + int32_t retval = scale; + scale = 0; + return retval; +} + double DecimalQuantity::getPluralOperand(PluralOperand operand) const { // If this assertion fails, you need to call roundToInfinity() or some other rounding method. // See the comment at the top of this file explaining the "isApproximate" field. @@ -549,6 +557,65 @@ void DecimalQuantity::_setToDecNum(const DecNum& decnum, UErrorCode& status) { } } +DecimalQuantity DecimalQuantity::fromExponentString(UnicodeString num, UErrorCode& status) { + if (num.indexOf(u'e') >= 0 || num.indexOf(u'c') >= 0 + || num.indexOf(u'E') >= 0 || num.indexOf(u'C') >= 0) { + int32_t ePos = num.lastIndexOf('e'); + if (ePos < 0) { + ePos = num.lastIndexOf('c'); + } + if (ePos < 0) { + ePos = num.lastIndexOf('E'); + } + if (ePos < 0) { + ePos = num.lastIndexOf('C'); + } + int32_t expNumPos = ePos + 1; + UnicodeString exponentStr = num.tempSubString(expNumPos, num.length() - expNumPos); + + // parse exponentStr into exponent, but note that parseAsciiInteger doesn't handle the minus sign + bool isExpStrNeg = num[expNumPos] == u'-'; + int32_t exponentParsePos = isExpStrNeg ? 1 : 0; + int32_t exponent = ICU_Utility::parseAsciiInteger(exponentStr, exponentParsePos); + exponent = isExpStrNeg ? -exponent : exponent; + + // Compute the decNumber representation + UnicodeString fractionStr = num.tempSubString(0, ePos); + CharString fracCharStr = CharString(); + fracCharStr.appendInvariantChars(fractionStr, status); + DecNum decnum; + decnum.setTo(fracCharStr.toStringPiece(), status); + + // Clear and set this DecimalQuantity instance + DecimalQuantity dq; + dq.setToDecNum(decnum, status); + int32_t numFracDigit = getVisibleFractionCount(fractionStr); + dq.setMinFraction(numFracDigit); + dq.adjustExponent(exponent); + + return dq; + } else { + DecimalQuantity dq; + int numFracDigit = getVisibleFractionCount(num); + + CharString numCharStr = CharString(); + numCharStr.appendInvariantChars(num, status); + dq.setToDecNumber(numCharStr.toStringPiece(), status); + + dq.setMinFraction(numFracDigit); + return dq; + } +} + +int32_t DecimalQuantity::getVisibleFractionCount(UnicodeString value) { + int decimalPos = value.indexOf('.') + 1; + if (decimalPos == 0) { + return 0; + } else { + return value.length() - decimalPos; + } +} + int64_t DecimalQuantity::toLong(bool truncateIfOverflow) const { // NOTE: Call sites should be guarded by fitsInLong(), like this: // if (dq.fitsInLong()) { /* use dq.toLong() */ } else { /* use some fallback */ } @@ -948,6 +1015,44 @@ UnicodeString DecimalQuantity::toPlainString() const { return sb; } + +UnicodeString DecimalQuantity::toExponentString() const { + U_ASSERT(!isApproximate); + UnicodeString sb; + if (isNegative()) { + sb.append(u'-'); + } + + int32_t upper = scale + precision - 1; + int32_t lower = scale; + if (upper < lReqPos - 1) { + upper = lReqPos - 1; + } + if (lower > rReqPos) { + lower = rReqPos; + } + int32_t p = upper; + if (p < 0) { + sb.append(u'0'); + } + for (; p >= 0; p--) { + sb.append(u'0' + getDigitPos(p - scale)); + } + if (lower < 0) { + sb.append(u'.'); + } + for(; p >= lower; p--) { + sb.append(u'0' + getDigitPos(p - scale)); + } + + if (exponent != 0) { + sb.append(u'c'); + ICU_Utility::appendNumber(sb, exponent); + } + + return sb; +} + UnicodeString DecimalQuantity::toScientificString() const { U_ASSERT(!isApproximate); UnicodeString result; diff --git a/deps/icu-small/source/i18n/number_decimalquantity.h b/deps/icu-small/source/i18n/number_decimalquantity.h index 107c09a96a53d2..862addf5d6cd90 100644 --- a/deps/icu-small/source/i18n/number_decimalquantity.h +++ b/deps/icu-small/source/i18n/number_decimalquantity.h @@ -81,11 +81,15 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory { * *

If rounding to a power of ten, use the more efficient {@link #roundToMagnitude} instead. * - * @param roundingIncrement The increment to which to round. + * @param increment The increment to which to round. + * @param magnitude The power of 10 to which to round. * @param roundingMode The {@link RoundingMode} to use if rounding is necessary. */ - void roundToIncrement(double roundingIncrement, RoundingMode roundingMode, - UErrorCode& status); + void roundToIncrement( + uint64_t increment, + digits_t magnitude, + RoundingMode roundingMode, + UErrorCode& status); /** Removes all fraction digits. */ void truncate(); @@ -140,6 +144,13 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory { */ bool adjustMagnitude(int32_t delta); + /** + * Scales the number such that the least significant nonzero digit is at magnitude 0. + * + * @return The previous magnitude of the least significant digit. + */ + int32_t adjustToZeroScale(); + /** * @return The power of ten corresponding to the most significant nonzero digit. * The number must not be zero. @@ -234,6 +245,9 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory { /** Internal method if the caller already has a DecNum. */ DecimalQuantity &setToDecNum(const DecNum& n, UErrorCode& status); + /** Returns a DecimalQuantity after parsing the input string. */ + static DecimalQuantity fromExponentString(UnicodeString n, UErrorCode& status); + /** * Appends a digit, optionally with one or more leading zeros, to the end of the value represented * by this DecimalQuantity. @@ -315,6 +329,10 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory { /** Returns the string without exponential notation. Slightly slower than toScientificString(). */ UnicodeString toPlainString() const; + /** Returns the string using ASCII digits and using exponential notation for non-zero + exponents, following the UTS 35 specification for plural rule samples. */ + UnicodeString toExponentString() const; + /** Visible for testing */ inline bool isUsingBytes() { return usingBytes; } @@ -518,6 +536,8 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory { void _setToDecNum(const DecNum& dn, UErrorCode& status); + static int32_t getVisibleFractionCount(UnicodeString value); + void convertToAccurateDouble(); /** Ensure that a byte array of at least 40 digits is allocated. */ diff --git a/deps/icu-small/source/i18n/number_longnames.cpp b/deps/icu-small/source/i18n/number_longnames.cpp index 5a4cf6321c8a40..b4e96504dede98 100644 --- a/deps/icu-small/source/i18n/number_longnames.cpp +++ b/deps/icu-small/source/i18n/number_longnames.cpp @@ -431,13 +431,33 @@ void getMeasureData(const Locale &locale, subKey.append(unit.getType(), status); subKey.append("/", status); + // Check if unitSubType is an alias or not. + LocalUResourceBundlePointer aliasBundle(ures_open(U_ICUDATA_ALIAS, "metadata", &status)); + + UErrorCode aliasStatus = status; + StackUResourceBundle aliasFillIn; + CharString aliasKey; + aliasKey.append("alias/unit/", aliasStatus); + aliasKey.append(unit.getSubtype(), aliasStatus); + aliasKey.append("/replacement", aliasStatus); + ures_getByKeyWithFallback(aliasBundle.getAlias(), aliasKey.data(), aliasFillIn.getAlias(), + &aliasStatus); + CharString unitSubType; + if (!U_FAILURE(aliasStatus)) { + // This means the subType is an alias. Then, replace unitSubType with the replacement. + auto replacement = ures_getUnicodeString(aliasFillIn.getAlias(), &status); + unitSubType.appendInvariantChars(replacement, status); + } else { + unitSubType.append(unit.getSubtype(), status); + } + // Map duration-year-person, duration-week-person, etc. to duration-year, duration-week, ... // TODO(ICU-20400): Get duration-*-person data properly with aliases. - int32_t subtypeLen = static_cast(uprv_strlen(unit.getSubtype())); - if (subtypeLen > 7 && uprv_strcmp(unit.getSubtype() + subtypeLen - 7, "-person") == 0) { - subKey.append({unit.getSubtype(), subtypeLen - 7}, status); + int32_t subtypeLen = static_cast(uprv_strlen(unitSubType.data())); + if (subtypeLen > 7 && uprv_strcmp(unitSubType.data() + subtypeLen - 7, "-person") == 0) { + subKey.append({unitSubType.data(), subtypeLen - 7}, status); } else { - subKey.append({unit.getSubtype(), subtypeLen}, status); + subKey.append({unitSubType.data(), subtypeLen}, status); } if (width != UNUM_UNIT_WIDTH_FULL_NAME) { diff --git a/deps/icu-small/source/i18n/number_mapper.cpp b/deps/icu-small/source/i18n/number_mapper.cpp index 2d4d47a094d999..350c431dfdd079 100644 --- a/deps/icu-small/source/i18n/number_mapper.cpp +++ b/deps/icu-small/source/i18n/number_mapper.cpp @@ -134,7 +134,8 @@ MacroProps NumberPropertyMapper::oldToNew(const DecimalFormatProperties& propert if (PatternStringUtils::ignoreRoundingIncrement(roundingIncrement, maxFrac)) { precision = Precision::constructFraction(minFrac, maxFrac); } else { - precision = Precision::constructIncrement(roundingIncrement, minFrac); + // Convert the double increment to an integer increment + precision = Precision::increment(roundingIncrement).withMinFraction(minFrac); } } else if (explicitMinMaxSig) { minSig = minSig < 1 ? 1 : minSig > kMaxIntFracSig ? kMaxIntFracSig : minSig; @@ -293,9 +294,14 @@ MacroProps NumberPropertyMapper::oldToNew(const DecimalFormatProperties& propert } else if (rounding_.fType == Precision::PrecisionType::RND_INCREMENT || rounding_.fType == Precision::PrecisionType::RND_INCREMENT_ONE || rounding_.fType == Precision::PrecisionType::RND_INCREMENT_FIVE) { - increment_ = rounding_.fUnion.increment.fIncrement; minFrac_ = rounding_.fUnion.increment.fMinFrac; + // If incrementRounding is used, maxFrac is set equal to minFrac maxFrac_ = rounding_.fUnion.increment.fMinFrac; + // Convert the integer increment to a double + DecimalQuantity dq; + dq.setToLong(rounding_.fUnion.increment.fIncrement); + dq.adjustMagnitude(rounding_.fUnion.increment.fIncrementMagnitude); + increment_ = dq.toDouble(); } else if (rounding_.fType == Precision::PrecisionType::RND_SIGNIFICANT) { minSig_ = rounding_.fUnion.fracSig.fMinSig; maxSig_ = rounding_.fUnion.fracSig.fMaxSig; diff --git a/deps/icu-small/source/i18n/number_output.cpp b/deps/icu-small/source/i18n/number_output.cpp index 2c2c25eaedb427..78006da8c42f0a 100644 --- a/deps/icu-small/source/i18n/number_output.cpp +++ b/deps/icu-small/source/i18n/number_output.cpp @@ -39,6 +39,49 @@ MeasureUnit FormattedNumber::getOutputUnit(UErrorCode& status) const { return fData->outputUnit; } +NounClass FormattedNumber::getNounClass(UErrorCode &status) const { + UPRV_FORMATTED_VALUE_METHOD_GUARD(NounClass::OTHER); + const char *nounClass = fData->gender; + + // if it is not exist, return `OTHER` + if (uprv_strcmp(nounClass, "") == 0) { + return NounClass::OTHER; + } + + if (uprv_strcmp(nounClass, "neuter") == 0) { + return NounClass::NEUTER; + } + + if (uprv_strcmp(nounClass, "feminine") == 0) { + return NounClass::FEMININE; + } + + if (uprv_strcmp(nounClass, "masculine") == 0) { + return NounClass::MASCULINE; + } + + if (uprv_strcmp(nounClass, "animate") == 0) { + return NounClass::ANIMATE; + } + + if (uprv_strcmp(nounClass, "inanimate") == 0) { + return NounClass::INANIMATE; + } + + if (uprv_strcmp(nounClass, "personal") == 0) { + return NounClass::PERSONAL; + } + + if (uprv_strcmp(nounClass, "common") == 0) { + return NounClass::COMMON; + } + + // In case there is no matching, this means there are noun classes + // that are not supported yet. + status = U_INTERNAL_PROGRAM_ERROR; + return NounClass::OTHER; +} + const char *FormattedNumber::getGender(UErrorCode &status) const { UPRV_FORMATTED_VALUE_METHOD_GUARD("") return fData->gender; diff --git a/deps/icu-small/source/i18n/number_patternstring.cpp b/deps/icu-small/source/i18n/number_patternstring.cpp index e819d39e96769d..2738895d8ad03f 100644 --- a/deps/icu-small/source/i18n/number_patternstring.cpp +++ b/deps/icu-small/source/i18n/number_patternstring.cpp @@ -750,7 +750,7 @@ UnicodeString PatternStringUtils::propertiesToPatternString(const DecimalFormatP int32_t groupingLength = grouping1 + grouping2 + 1; // Figure out the digits we need to put in the pattern. - double roundingInterval = properties.roundingIncrement; + double increment = properties.roundingIncrement; UnicodeString digitsString; int32_t digitsStringScale = 0; if (maxSig != uprv_min(dosMax, -1)) { @@ -761,14 +761,14 @@ UnicodeString PatternStringUtils::propertiesToPatternString(const DecimalFormatP while (digitsString.length() < maxSig) { digitsString.append(u'#'); } - } else if (roundingInterval != 0.0 && !ignoreRoundingIncrement(roundingInterval,maxFrac)) { - // Rounding Interval. - digitsStringScale = -roundingutils::doubleFractionLength(roundingInterval, nullptr); - // TODO: Check for DoS here? + } else if (increment != 0.0 && !ignoreRoundingIncrement(increment,maxFrac)) { + // Rounding Increment. DecimalQuantity incrementQuantity; - incrementQuantity.setToDouble(roundingInterval); + incrementQuantity.setToDouble(increment); + incrementQuantity.roundToInfinity(); + digitsStringScale = incrementQuantity.getLowerDisplayMagnitude(); incrementQuantity.adjustMagnitude(-digitsStringScale); - incrementQuantity.roundToMagnitude(0, kDefaultMode, status); + incrementQuantity.setMinInteger(minInt - digitsStringScale); UnicodeString str = incrementQuantity.toPlainString(); if (str.charAt(0) == u'-') { // TODO: Unsupported operation exception or fail silently? diff --git a/deps/icu-small/source/i18n/number_rounding.cpp b/deps/icu-small/source/i18n/number_rounding.cpp index 877df63c8f68ef..a9b3f16c050d94 100644 --- a/deps/icu-small/source/i18n/number_rounding.cpp +++ b/deps/icu-small/source/i18n/number_rounding.cpp @@ -36,27 +36,24 @@ void number::impl::parseIncrementOption(const StringSegment &segment, // Utilize DecimalQuantity/decNumber to parse this for us. DecimalQuantity dq; UErrorCode localStatus = U_ZERO_ERROR; - DecNum decnum; - decnum.setTo({buffer.data(), buffer.length()}, localStatus); - dq.setToDecNum(decnum, localStatus); - if (U_FAILURE(localStatus) || decnum.isSpecial()) { + dq.setToDecNumber({buffer.data(), buffer.length()}, localStatus); + if (U_FAILURE(localStatus) || dq.isNaN() || dq.isInfinite()) { // throw new SkeletonSyntaxException("Invalid rounding increment", segment, e); status = U_NUMBER_SKELETON_SYNTAX_ERROR; return; } - double increment = dq.toDouble(); - - // We also need to figure out how many digits. Do a brute force string operation. - int decimalOffset = 0; - while (decimalOffset < segment.length() && segment.charAt(decimalOffset) != '.') { - decimalOffset++; - } - if (decimalOffset == segment.length()) { - outPrecision = Precision::increment(increment); - } else { - int32_t fractionLength = segment.length() - decimalOffset - 1; - outPrecision = Precision::increment(increment).withMinFraction(fractionLength); + // Now we break apart the number into a mantissa and exponent (magnitude). + int32_t magnitude = dq.adjustToZeroScale(); + // setToDecNumber drops trailing zeros, so we search for the '.' manually. + for (int32_t i=0; i(length - point); -} - - Precision Precision::unlimited() { return Precision(RND_NONE, {}); } @@ -204,7 +173,19 @@ Precision Precision::trailingZeroDisplay(UNumberTrailingZeroDisplay trailingZero IncrementPrecision Precision::increment(double roundingIncrement) { if (roundingIncrement > 0.0) { - return constructIncrement(roundingIncrement, 0); + DecimalQuantity dq; + dq.setToDouble(roundingIncrement); + dq.roundToInfinity(); + int32_t magnitude = dq.adjustToZeroScale(); + return constructIncrement(dq.toLong(), magnitude); + } else { + return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; + } +} + +IncrementPrecision Precision::incrementExact(uint64_t mantissa, int16_t magnitude) { + if (mantissa > 0.0) { + return constructIncrement(mantissa, magnitude); } else { return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; } @@ -226,7 +207,8 @@ Precision FractionPrecision::withSignificantDigits( *this, minSignificantDigits, maxSignificantDigits, - priority); + priority, + false); } else { return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; } @@ -239,7 +221,8 @@ Precision FractionPrecision::withMinDigits(int32_t minSignificantDigits) const { *this, 1, minSignificantDigits, - UNUM_ROUNDING_PRIORITY_RELAXED); + UNUM_ROUNDING_PRIORITY_RELAXED, + true); } else { return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; } @@ -251,7 +234,8 @@ Precision FractionPrecision::withMaxDigits(int32_t maxSignificantDigits) const { return constructFractionSignificant(*this, 1, maxSignificantDigits, - UNUM_ROUNDING_PRIORITY_STRICT); + UNUM_ROUNDING_PRIORITY_STRICT, + true); } else { return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; } @@ -266,8 +250,8 @@ Precision Precision::withCurrency(const CurrencyUnit ¤cy, UErrorCode &stat int32_t minMaxFrac = ucurr_getDefaultFractionDigitsForUsage( isoCode, fUnion.currencyUsage, &status); Precision retval = (increment != 0.0) - ? static_cast(constructIncrement(increment, minMaxFrac)) - : static_cast(constructFraction(minMaxFrac, minMaxFrac)); + ? Precision::increment(increment) + : static_cast(Precision::fixedFraction(minMaxFrac)); retval.fTrailingZeroDisplay = fTrailingZeroDisplay; return retval; } @@ -285,7 +269,9 @@ Precision CurrencyPrecision::withCurrency(const CurrencyUnit ¤cy) const { Precision IncrementPrecision::withMinFraction(int32_t minFrac) const { if (fType == RND_ERROR) { return *this; } // no-op in error state if (minFrac >= 0 && minFrac <= kMaxIntFracSig) { - return constructIncrement(fUnion.increment.fIncrement, minFrac); + IncrementPrecision copy = *this; + copy.fUnion.increment.fMinFrac = minFrac; + return copy; } else { return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; } @@ -318,35 +304,34 @@ Precision::constructFractionSignificant( const FractionPrecision &base, int32_t minSig, int32_t maxSig, - UNumberRoundingPriority priority) { + UNumberRoundingPriority priority, + bool retain) { FractionSignificantSettings settings = base.fUnion.fracSig; settings.fMinSig = static_cast(minSig); settings.fMaxSig = static_cast(maxSig); settings.fPriority = priority; + settings.fRetain = retain; PrecisionUnion union_; union_.fracSig = settings; return {RND_FRACTION_SIGNIFICANT, union_}; } -IncrementPrecision Precision::constructIncrement(double increment, int32_t minFrac) { +IncrementPrecision Precision::constructIncrement(uint64_t increment, digits_t magnitude) { IncrementSettings settings; // Note: For number formatting, fIncrement is used for RND_INCREMENT but not // RND_INCREMENT_ONE or RND_INCREMENT_FIVE. However, fIncrement is used in all // three when constructing a skeleton. settings.fIncrement = increment; - settings.fMinFrac = static_cast(minFrac); - // One of the few pre-computed quantities: - // Note: it is possible for minFrac to be more than maxFrac... (misleading) - int8_t singleDigit; - settings.fMaxFrac = roundingutils::doubleFractionLength(increment, &singleDigit); + settings.fIncrementMagnitude = magnitude; + settings.fMinFrac = magnitude > 0 ? 0 : -magnitude; PrecisionUnion union_; union_.increment = settings; - if (singleDigit == 1) { + if (increment == 1) { // NOTE: In C++, we must return the correct value type with the correct union. // It would be invalid to return a RND_FRACTION here because the methods on the // IncrementPrecision type assume that the union is backed by increment data. return {RND_INCREMENT_ONE, union_}; - } else if (singleDigit == 5) { + } else if (increment == 5) { return {RND_INCREMENT_FIVE, union_}; } else { return {RND_INCREMENT, union_}; @@ -457,6 +442,23 @@ void RoundingImpl::apply(impl::DecimalQuantity &value, UErrorCode& status) const break; case Precision::RND_FRACTION_SIGNIFICANT: { + // From ECMA-402: + /* + Let sResult be ToRawPrecision(...). + Let fResult be ToRawFixed(...). + If intlObj.[[RoundingType]] is morePrecision, then + If sResult.[[RoundingMagnitude]] ≤ fResult.[[RoundingMagnitude]], then + Let result be sResult. + Else, + Let result be fResult. + Else, + Assert: intlObj.[[RoundingType]] is lessPrecision. + If sResult.[[RoundingMagnitude]] ≤ fResult.[[RoundingMagnitude]], then + Let result be fResult. + Else, + Let result be sResult. + */ + int32_t roundingMag1 = getRoundingMagnitudeFraction(fPrecision.fUnion.fracSig.fMaxFrac); int32_t roundingMag2 = getRoundingMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMaxSig); int32_t roundingMag; @@ -465,11 +467,35 @@ void RoundingImpl::apply(impl::DecimalQuantity &value, UErrorCode& status) const } else { roundingMag = uprv_max(roundingMag1, roundingMag2); } - value.roundToMagnitude(roundingMag, fRoundingMode, status); + if (!value.isZeroish()) { + int32_t upperMag = value.getMagnitude(); + value.roundToMagnitude(roundingMag, fRoundingMode, status); + if (!value.isZeroish() && value.getMagnitude() != upperMag && roundingMag1 == roundingMag2) { + // roundingMag2 needs to be the magnitude after rounding + roundingMag2 += 1; + } + } int32_t displayMag1 = getDisplayMagnitudeFraction(fPrecision.fUnion.fracSig.fMinFrac); int32_t displayMag2 = getDisplayMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMinSig); - int32_t displayMag = uprv_min(displayMag1, displayMag2); + int32_t displayMag; + if (fPrecision.fUnion.fracSig.fRetain) { + // withMinDigits + withMaxDigits + displayMag = uprv_min(displayMag1, displayMag2); + } else if (fPrecision.fUnion.fracSig.fPriority == UNUM_ROUNDING_PRIORITY_RELAXED) { + if (roundingMag2 <= roundingMag1) { + displayMag = displayMag2; + } else { + displayMag = displayMag1; + } + } else { + U_ASSERT(fPrecision.fUnion.fracSig.fPriority == UNUM_ROUNDING_PRIORITY_STRICT); + if (roundingMag2 <= roundingMag1) { + displayMag = displayMag1; + } else { + displayMag = displayMag2; + } + } resolvedMinFraction = uprv_max(0, -displayMag); break; @@ -478,6 +504,7 @@ void RoundingImpl::apply(impl::DecimalQuantity &value, UErrorCode& status) const case Precision::RND_INCREMENT: value.roundToIncrement( fPrecision.fUnion.increment.fIncrement, + fPrecision.fUnion.increment.fIncrementMagnitude, fRoundingMode, status); resolvedMinFraction = fPrecision.fUnion.increment.fMinFrac; @@ -485,7 +512,7 @@ void RoundingImpl::apply(impl::DecimalQuantity &value, UErrorCode& status) const case Precision::RND_INCREMENT_ONE: value.roundToMagnitude( - -fPrecision.fUnion.increment.fMaxFrac, + fPrecision.fUnion.increment.fIncrementMagnitude, fRoundingMode, status); resolvedMinFraction = fPrecision.fUnion.increment.fMinFrac; @@ -493,7 +520,7 @@ void RoundingImpl::apply(impl::DecimalQuantity &value, UErrorCode& status) const case Precision::RND_INCREMENT_FIVE: value.roundToNickel( - -fPrecision.fUnion.increment.fMaxFrac, + fPrecision.fUnion.increment.fIncrementMagnitude, fRoundingMode, status); resolvedMinFraction = fPrecision.fUnion.increment.fMinFrac; diff --git a/deps/icu-small/source/i18n/number_roundingutils.h b/deps/icu-small/source/i18n/number_roundingutils.h index 06fadd29fd544e..66571272545854 100644 --- a/deps/icu-small/source/i18n/number_roundingutils.h +++ b/deps/icu-small/source/i18n/number_roundingutils.h @@ -174,15 +174,6 @@ inline bool roundsAtMidpoint(int roundingMode) { } } -/** - * Computes the number of fraction digits in a double. Used for computing maxFrac for an increment. - * Calls into the DoubleToStringConverter library to do so. - * - * @param singleDigit An output parameter; set to a number if that is the - * only digit in the double, or -1 if there is more than one digit. - */ -digits_t doubleFractionLength(double input, int8_t* singleDigit); - } // namespace roundingutils diff --git a/deps/icu-small/source/i18n/number_skeletons.cpp b/deps/icu-small/source/i18n/number_skeletons.cpp index de70c5cedff3ca..c51831b6823809 100644 --- a/deps/icu-small/source/i18n/number_skeletons.cpp +++ b/deps/icu-small/source/i18n/number_skeletons.cpp @@ -1344,8 +1344,9 @@ bool blueprint_helpers::parseFracSigOption(const StringSegment& segment, MacroPr // @, @@, @@@ maxSig = minSig; } - UNumberRoundingPriority priority; + auto& oldPrecision = static_cast(macros.precision); if (offset < segment.length()) { + UNumberRoundingPriority priority; if (maxSig == -1) { // The wildcard character is not allowed with the priority annotation status = U_NUMBER_SKELETON_SYNTAX_ERROR; @@ -1367,22 +1368,19 @@ bool blueprint_helpers::parseFracSigOption(const StringSegment& segment, MacroPr status = U_NUMBER_SKELETON_SYNTAX_ERROR; return false; } + macros.precision = oldPrecision.withSignificantDigits(minSig, maxSig, priority); } else if (maxSig == -1) { // withMinDigits - maxSig = minSig; - minSig = 1; - priority = UNUM_ROUNDING_PRIORITY_RELAXED; + macros.precision = oldPrecision.withMinDigits(minSig); } else if (minSig == 1) { // withMaxDigits - priority = UNUM_ROUNDING_PRIORITY_STRICT; + macros.precision = oldPrecision.withMaxDigits(maxSig); } else { // Digits options with both min and max sig require the priority option status = U_NUMBER_SKELETON_SYNTAX_ERROR; return false; } - auto& oldPrecision = static_cast(macros.precision); - macros.precision = oldPrecision.withSignificantDigits(minSig, maxSig, priority); return true; } @@ -1399,12 +1397,16 @@ void blueprint_helpers::parseIncrementOption(const StringSegment &segment, Macro number::impl::parseIncrementOption(segment, macros.precision, status); } -void blueprint_helpers::generateIncrementOption(double increment, int32_t minFrac, UnicodeString& sb, - UErrorCode&) { +void blueprint_helpers::generateIncrementOption( + uint32_t increment, + digits_t incrementMagnitude, + int32_t minFrac, + UnicodeString& sb, + UErrorCode&) { // Utilize DecimalQuantity/double_conversion to format this for us. DecimalQuantity dq; - dq.setToDouble(increment); - dq.roundToInfinity(); + dq.setToLong(increment); + dq.adjustMagnitude(incrementMagnitude); dq.setMinFraction(minFrac); sb.append(dq.toPlainString()); } @@ -1617,11 +1619,21 @@ bool GeneratorHelpers::precision(const MacroProps& macros, UnicodeString& sb, UE const Precision::FractionSignificantSettings& impl = macros.precision.fUnion.fracSig; blueprint_helpers::generateFractionStem(impl.fMinFrac, impl.fMaxFrac, sb, status); sb.append(u'/'); - blueprint_helpers::generateDigitsStem(impl.fMinSig, impl.fMaxSig, sb, status); - if (impl.fPriority == UNUM_ROUNDING_PRIORITY_RELAXED) { - sb.append(u'r'); + if (impl.fRetain) { + if (impl.fPriority == UNUM_ROUNDING_PRIORITY_RELAXED) { + // withMinDigits + blueprint_helpers::generateDigitsStem(impl.fMaxSig, -1, sb, status); + } else { + // withMaxDigits + blueprint_helpers::generateDigitsStem(1, impl.fMaxSig, sb, status); + } } else { - sb.append(u's'); + blueprint_helpers::generateDigitsStem(impl.fMinSig, impl.fMaxSig, sb, status); + if (impl.fPriority == UNUM_ROUNDING_PRIORITY_RELAXED) { + sb.append(u'r'); + } else { + sb.append(u's'); + } } } else if (macros.precision.fType == Precision::RND_INCREMENT || macros.precision.fType == Precision::RND_INCREMENT_ONE @@ -1630,6 +1642,7 @@ bool GeneratorHelpers::precision(const MacroProps& macros, UnicodeString& sb, UE sb.append(u"precision-increment/", -1); blueprint_helpers::generateIncrementOption( impl.fIncrement, + impl.fIncrementMagnitude, impl.fMinFrac, sb, status); diff --git a/deps/icu-small/source/i18n/number_skeletons.h b/deps/icu-small/source/i18n/number_skeletons.h index be41f1b3237a94..27f69cd48c39e9 100644 --- a/deps/icu-small/source/i18n/number_skeletons.h +++ b/deps/icu-small/source/i18n/number_skeletons.h @@ -286,7 +286,7 @@ bool parseTrailingZeroOption(const StringSegment& segment, MacroProps& macros, U void parseIncrementOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status); void -generateIncrementOption(double increment, int32_t minFrac, UnicodeString& sb, UErrorCode& status); +generateIncrementOption(uint32_t increment, digits_t incrementMagnitude, int32_t minFrac, UnicodeString& sb, UErrorCode& status); void parseIntegerWidthOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status); diff --git a/deps/icu-small/source/i18n/numsys.cpp b/deps/icu-small/source/i18n/numsys.cpp index 44aaf8e2a5f987..934149039c52d7 100644 --- a/deps/icu-small/source/i18n/numsys.cpp +++ b/deps/icu-small/source/i18n/numsys.cpp @@ -313,12 +313,7 @@ U_CFUNC void initNumsysNames(UErrorCode &status) { } const char *nsName = ures_getKey(nsCurrent.getAlias()); LocalPointer newElem(new UnicodeString(nsName, -1, US_INV), status); - if (U_SUCCESS(status)) { - numsysNames->addElementX(newElem.getAlias(), status); - if (U_SUCCESS(status)) { - newElem.orphan(); // on success, the numsysNames vector owns newElem. - } - } + numsysNames->adoptElement(newElem.orphan(), status); } ures_close(numberingSystemsInfo); diff --git a/deps/icu-small/source/i18n/plurrule.cpp b/deps/icu-small/source/i18n/plurrule.cpp index d1918c4698138b..7d1037f8bdd9e0 100644 --- a/deps/icu-small/source/i18n/plurrule.cpp +++ b/deps/icu-small/source/i18n/plurrule.cpp @@ -1548,14 +1548,9 @@ PluralKeywordEnumeration::PluralKeywordEnumeration(RuleChain *header, UErrorCode UBool addKeywordOther = TRUE; RuleChain *node = header; while (node != nullptr) { - auto newElem = new UnicodeString(node->fKeyword); - if (newElem == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - fKeywordNames.addElementX(newElem, status); + LocalPointer newElem(node->fKeyword.clone(), status); + fKeywordNames.adoptElement(newElem.orphan(), status); if (U_FAILURE(status)) { - delete newElem; return; } if (0 == node->fKeyword.compare(PLURAL_KEYWORD_OTHER, 5)) { @@ -1565,14 +1560,9 @@ PluralKeywordEnumeration::PluralKeywordEnumeration(RuleChain *header, UErrorCode } if (addKeywordOther) { - auto newElem = new UnicodeString(PLURAL_KEYWORD_OTHER); - if (newElem == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - fKeywordNames.addElementX(newElem, status); + LocalPointer newElem(new UnicodeString(PLURAL_KEYWORD_OTHER), status); + fKeywordNames.adoptElement(newElem.orphan(), status); if (U_FAILURE(status)) { - delete newElem; return; } } @@ -1628,7 +1618,7 @@ FixedDecimal::FixedDecimal(double n, int32_t v, int64_t f, int32_t e) { init(n, v, f, e); // check values. TODO make into unit test. // - // long visiblePower = (int) Math.pow(10, v); + // long visiblePower = (int) Math.pow(10.0, v); // if (decimalDigits > visiblePower) { // throw new IllegalArgumentException(); // } @@ -1881,7 +1871,7 @@ void FixedDecimal::adjustForMinFractionDigits(int32_t minFractionDigits) { double FixedDecimal::getPluralOperand(PluralOperand operand) const { switch(operand) { - case PLURAL_OPERAND_N: return (exponent == 0 ? source : source * pow(10, exponent)); + case PLURAL_OPERAND_N: return (exponent == 0 ? source : source * pow(10.0, exponent)); case PLURAL_OPERAND_I: return (double) longValue(); case PLURAL_OPERAND_F: return static_cast(decimalDigits); case PLURAL_OPERAND_T: return static_cast(decimalDigitsWithoutTrailingZeros); @@ -1932,14 +1922,14 @@ UnicodeString FixedDecimal::toString() const { } double FixedDecimal::doubleValue() const { - return (isNegative ? -source : source) * pow(10, exponent); + return (isNegative ? -source : source) * pow(10.0, exponent); } int64_t FixedDecimal::longValue() const { if (exponent == 0) { return intValue; } else { - return (long) (pow(10, exponent) * intValue); + return (long) (pow(10.0, exponent) * intValue); } } diff --git a/deps/icu-small/source/i18n/rbt_set.cpp b/deps/icu-small/source/i18n/rbt_set.cpp index abc4413c2c6f61..6835c03a698b96 100644 --- a/deps/icu-small/source/i18n/rbt_set.cpp +++ b/deps/icu-small/source/i18n/rbt_set.cpp @@ -163,16 +163,13 @@ U_NAMESPACE_BEGIN /** * Construct a new empty rule set. */ -TransliterationRuleSet::TransliterationRuleSet(UErrorCode& status) : UMemory() { - ruleVector = new UVector(&_deleteRule, NULL, status); +TransliterationRuleSet::TransliterationRuleSet(UErrorCode& status) : + UMemory(), ruleVector(nullptr), rules(nullptr), index {}, maxContextLength(0) { + LocalPointer lpRuleVector(new UVector(_deleteRule, nullptr, status), status); if (U_FAILURE(status)) { return; } - if (ruleVector == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - } - rules = NULL; - maxContextLength = 0; + ruleVector = lpRuleVector.orphan(); } /** @@ -180,27 +177,24 @@ TransliterationRuleSet::TransliterationRuleSet(UErrorCode& status) : UMemory() { */ TransliterationRuleSet::TransliterationRuleSet(const TransliterationRuleSet& other) : UMemory(other), - ruleVector(0), - rules(0), + ruleVector(nullptr), + rules(nullptr), maxContextLength(other.maxContextLength) { int32_t i, len; uprv_memcpy(index, other.index, sizeof(index)); UErrorCode status = U_ZERO_ERROR; - ruleVector = new UVector(&_deleteRule, NULL, status); - if (other.ruleVector != 0 && ruleVector != 0 && U_SUCCESS(status)) { + LocalPointer lpRuleVector(new UVector(_deleteRule, nullptr, status), status); + if (U_FAILURE(status)) { + return; + } + ruleVector = lpRuleVector.orphan(); + if (other.ruleVector != nullptr && U_SUCCESS(status)) { len = other.ruleVector->size(); for (i=0; ielementAt(i)); - // Null pointer test - if (tempTranslitRule == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - break; - } - ruleVector->addElementX(tempTranslitRule, status); - if (U_FAILURE(status)) { - break; - } + LocalPointer tempTranslitRule( + new TransliterationRule(*(TransliterationRule*)other.ruleVector->elementAt(i)), status); + ruleVector->adoptElement(tempTranslitRule.orphan(), status); } } if (other.rules != 0 && U_SUCCESS(status)) { @@ -247,11 +241,11 @@ int32_t TransliterationRuleSet::getMaximumContextLength(void) const { */ void TransliterationRuleSet::addRule(TransliterationRule* adoptedRule, UErrorCode& status) { + LocalPointer lpAdoptedRule(adoptedRule); + ruleVector->adoptElement(lpAdoptedRule.orphan(), status); if (U_FAILURE(status)) { - delete adoptedRule; return; } - ruleVector->addElementX(adoptedRule, status); int32_t len; if ((len = adoptedRule->getContextLength()) > maxContextLength) { @@ -316,7 +310,7 @@ void TransliterationRuleSet::freeze(UParseError& parseError,UErrorCode& status) for (j=0; j= 0) { if (indexValue[j] == x) { - v.addElementX(ruleVector->elementAt(j), status); + v.addElement(ruleVector->elementAt(j), status); } } else { // If the indexValue is < 0, then the first key character is @@ -325,13 +319,16 @@ void TransliterationRuleSet::freeze(UParseError& parseError,UErrorCode& status) // rarely, so we seldom treat this code path. TransliterationRule* r = (TransliterationRule*) ruleVector->elementAt(j); if (r->matchesIndexValue((uint8_t)x)) { - v.addElementX(r, status); + v.addElement(r, status); } } } } uprv_free(indexValue); index[256] = v.size(); + if (U_FAILURE(status)) { + return; + } /* Freeze things into an array. */ diff --git a/deps/icu-small/source/i18n/region.cpp b/deps/icu-small/source/i18n/region.cpp index 2e013708bb88e3..277a22fd091cfb 100644 --- a/deps/icu-small/source/i18n/region.cpp +++ b/deps/icu-small/source/i18n/region.cpp @@ -39,11 +39,6 @@ U_CDECL_BEGIN -static void U_CALLCONV -deleteRegion(void *obj) { - delete (icu::Region *)obj; -} - /** * Cleanup callback func */ @@ -90,7 +85,8 @@ void U_CALLCONV Region::loadRegionData(UErrorCode &status) { LocalPointer continents(new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status), status); LocalPointer groupings(new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status), status); - allRegions = new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status); + LocalPointer lpAllRegions(new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status), status); + allRegions = lpAllRegions.orphan(); LocalUResourceBundlePointer metadata(ures_openDirect(NULL,"metadata",&status)); LocalUResourceBundlePointer metadataAlias(ures_getByKey(metadata.getAlias(),"alias",NULL,&status)); @@ -109,16 +105,17 @@ void U_CALLCONV Region::loadRegionData(UErrorCode &status) { LocalUResourceBundlePointer worldContainment(ures_getByKey(territoryContainment.getAlias(),"001",NULL,&status)); LocalUResourceBundlePointer groupingContainment(ures_getByKey(territoryContainment.getAlias(),"grouping",NULL,&status)); + ucln_i18n_registerCleanup(UCLN_I18N_REGION, region_cleanup); if (U_FAILURE(status)) { return; } // now, initialize - uhash_setValueDeleter(newRegionIDMap.getAlias(), deleteRegion); // regionIDMap owns objs - uhash_setKeyDeleter(newRegionAliases.getAlias(), uprv_deleteUObject); // regionAliases owns the string keys + uhash_setValueDeleter(newRegionIDMap.getAlias(), uprv_deleteUObject); // regionIDMap owns objs + uhash_setKeyDeleter(newRegionAliases.getAlias(), uprv_deleteUObject); // regionAliases owns the string keys - while ( ures_hasNext(regionRegular.getAlias()) ) { + while (U_SUCCESS(status) && ures_hasNext(regionRegular.getAlias())) { UnicodeString regionName = ures_getNextUnicodeString(regionRegular.getAlias(),NULL,&status); int32_t rangeMarkerLocation = regionName.indexOf(RANGE_MARKER); UChar buf[6]; @@ -126,18 +123,18 @@ void U_CALLCONV Region::loadRegionData(UErrorCode &status) { if ( rangeMarkerLocation > 0 ) { UChar endRange = regionName.charAt(rangeMarkerLocation+1); buf[rangeMarkerLocation] = 0; - while ( buf[rangeMarkerLocation-1] <= endRange ) { + while (U_SUCCESS(status) && buf[rangeMarkerLocation-1] <= endRange) { LocalPointer newRegion(new UnicodeString(buf), status); - allRegions->addElementX(newRegion.orphan(),status); + allRegions->adoptElement(newRegion.orphan(), status); buf[rangeMarkerLocation-1]++; } } else { LocalPointer newRegion(new UnicodeString(regionName), status); - allRegions->addElementX(newRegion.orphan(),status); + allRegions->adoptElement(newRegion.orphan(), status); } } - while ( ures_hasNext(regionMacro.getAlias()) ) { + while (U_SUCCESS(status) && ures_hasNext(regionMacro.getAlias())) { UnicodeString regionName = ures_getNextUnicodeString(regionMacro.getAlias(),NULL,&status); int32_t rangeMarkerLocation = regionName.indexOf(RANGE_MARKER); UChar buf[6]; @@ -145,25 +142,29 @@ void U_CALLCONV Region::loadRegionData(UErrorCode &status) { if ( rangeMarkerLocation > 0 ) { UChar endRange = regionName.charAt(rangeMarkerLocation+1); buf[rangeMarkerLocation] = 0; - while ( buf[rangeMarkerLocation-1] <= endRange ) { + while ( buf[rangeMarkerLocation-1] <= endRange && U_SUCCESS(status)) { LocalPointer newRegion(new UnicodeString(buf), status); - allRegions->addElementX(newRegion.orphan(),status); + allRegions->adoptElement(newRegion.orphan(),status); buf[rangeMarkerLocation-1]++; } } else { LocalPointer newRegion(new UnicodeString(regionName), status); - allRegions->addElementX(newRegion.orphan(),status); + allRegions->adoptElement(newRegion.orphan(),status); } } - while ( ures_hasNext(regionUnknown.getAlias()) ) { - LocalPointer regionName (new UnicodeString(ures_getNextUnicodeString(regionUnknown.getAlias(),NULL,&status),status)); - allRegions->addElementX(regionName.orphan(),status); + while (U_SUCCESS(status) && ures_hasNext(regionUnknown.getAlias())) { + LocalPointer regionName ( + new UnicodeString(ures_getNextUnicodeString(regionUnknown.getAlias(), nullptr, &status), status)); + allRegions->adoptElement(regionName.orphan(),status); } - while ( ures_hasNext(worldContainment.getAlias()) ) { + while (U_SUCCESS(status) && ures_hasNext(worldContainment.getAlias())) { UnicodeString *continentName = new UnicodeString(ures_getNextUnicodeString(worldContainment.getAlias(),NULL,&status)); - continents->addElementX(continentName,status); + continents->adoptElement(continentName,status); + } + if (U_FAILURE(status)) { + return; } for ( int32_t i = 0 ; i < allRegions->size() ; i++ ) { @@ -191,22 +192,32 @@ void U_CALLCONV Region::loadRegionData(UErrorCode &status) { } UResourceBundle *groupingBundle = nullptr; - while ( ures_hasNext(groupingContainment.getAlias()) ) { + while (U_SUCCESS(status) && ures_hasNext(groupingContainment.getAlias())) { groupingBundle = ures_getNextResource(groupingContainment.getAlias(), groupingBundle, &status); if (U_FAILURE(status)) { break; } UnicodeString *groupingName = new UnicodeString(ures_getKey(groupingBundle), -1, US_INV); - groupings->addElementX(groupingName,status); - Region *grouping = (Region *) uhash_get(newRegionIDMap.getAlias(),groupingName); + LocalPointer lpGroupingName(groupingName, status); + groupings->adoptElement(lpGroupingName.orphan(), status); + if (U_FAILURE(status)) { + break; + } + Region *grouping = (Region *) uhash_get(newRegionIDMap.getAlias(), groupingName); if (grouping != NULL) { - for (int32_t i = 0; i < ures_getSize(groupingBundle); i++) { + for (int32_t i = 0; i < ures_getSize(groupingBundle) && U_SUCCESS(status); i++) { UnicodeString child = ures_getUnicodeStringByIndex(groupingBundle, i, &status); if (U_SUCCESS(status)) { if (grouping->containedRegions == NULL) { - grouping->containedRegions = new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status); + LocalPointer lpContainedRegions( + new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status), status); + grouping->containedRegions = lpContainedRegions.orphan(); + if (U_FAILURE(status)) { + break; + } } - grouping->containedRegions->addElementX(new UnicodeString(child), status); + LocalPointer lpChildCopy(new UnicodeString(child), status); + grouping->containedRegions->adoptElement(lpChildCopy.orphan(), status); } } } @@ -214,7 +225,7 @@ void U_CALLCONV Region::loadRegionData(UErrorCode &status) { ures_close(groupingBundle); // Process the territory aliases - while ( ures_hasNext(territoryAlias.getAlias()) ) { + while (U_SUCCESS(status) && ures_hasNext(territoryAlias.getAlias())) { LocalUResourceBundlePointer res(ures_getNextResource(territoryAlias.getAlias(),NULL,&status)); const char *aliasFrom = ures_getKey(res.getAlias()); LocalPointer aliasFromStr(new UnicodeString(aliasFrom, -1, US_INV), status); @@ -259,7 +270,7 @@ void U_CALLCONV Region::loadRegionData(UErrorCode &status) { } UnicodeString currentRegion; //currentRegion.remove(); TODO: was already 0 length? - for (int32_t i = 0 ; i < aliasTo.length() ; i++ ) { + for (int32_t i = 0 ; i < aliasTo.length() && U_SUCCESS(status); i++ ) { if ( aliasTo.charAt(i) != 0x0020 ) { currentRegion.append(aliasTo.charAt(i)); } @@ -267,7 +278,7 @@ void U_CALLCONV Region::loadRegionData(UErrorCode &status) { Region *target = (Region *)uhash_get(newRegionIDMap.getAlias(),(void *)¤tRegion); if (target) { LocalPointer preferredValue(new UnicodeString(target->idStr), status); - aliasFromRegion->preferredValues->addElementX((void *)preferredValue.orphan(),status); // may add null if err + aliasFromRegion->preferredValues->adoptElement(preferredValue.orphan(),status); // may add null if err } currentRegion.remove(); } @@ -276,9 +287,9 @@ void U_CALLCONV Region::loadRegionData(UErrorCode &status) { } // Process the code mappings - This will allow us to assign numeric codes to most of the territories. - while ( ures_hasNext(codeMappings.getAlias()) ) { + while (U_SUCCESS(status) && ures_hasNext(codeMappings.getAlias())) { UResourceBundle *mapping = ures_getNextResource(codeMappings.getAlias(),NULL,&status); - if ( ures_getType(mapping) == URES_ARRAY && ures_getSize(mapping) == 3) { + if (U_SUCCESS(status) && ures_getType(mapping) == URES_ARRAY && ures_getSize(mapping) == 3) { UnicodeString codeMappingID = ures_getUnicodeStringByIndex(mapping,0,&status); UnicodeString codeMappingNumber = ures_getUnicodeStringByIndex(mapping,1,&status); UnicodeString codeMapping3Letter = ures_getUnicodeStringByIndex(mapping,2,&status); @@ -356,15 +367,23 @@ void U_CALLCONV Region::loadRegionData(UErrorCode &status) { // Add the child region to the set of regions contained by the parent if (parentRegion->containedRegions == NULL) { - parentRegion->containedRegions = new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status); + LocalPointer lpContainedRegions( + new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status), status); + parentRegion->containedRegions = lpContainedRegions.orphan(); + if (U_FAILURE(status)) { + return; + } } LocalPointer childStr(new UnicodeString(), status); - if( U_FAILURE(status) ) { + if (U_FAILURE(status)) { return; // error out } childStr->fastCopyFrom(childRegion->idStr); - parentRegion->containedRegions->addElementX((void *)childStr.orphan(),status); + parentRegion->containedRegions->adoptElement(childStr.orphan(),status); + if (U_FAILURE(status)) { + return; + } // Set the parent region to be the containing region of the child. // Regions of type GROUPING can't be set as the parent, since another region @@ -388,10 +407,9 @@ void U_CALLCONV Region::loadRegionData(UErrorCode &status) { if( U_FAILURE(status) ) { return; // error out } - availableRegions[ar->fType]->addElementX((void *)arString.orphan(),status); + availableRegions[ar->fType]->adoptElement(arString.orphan(), status); } - ucln_i18n_registerCleanup(UCLN_I18N_REGION, region_cleanup); // copy hashtables numericCodeMap = newNumericCodeMap.orphan(); regionIDMap = newRegionIDMap.orphan(); @@ -402,6 +420,7 @@ void Region::cleanupRegionData() { for (int32_t i = 0 ; i < URGN_LIMIT ; i++ ) { if ( availableRegions[i] ) { delete availableRegions[i]; + availableRegions[i] = nullptr; } } @@ -417,7 +436,6 @@ void Region::cleanupRegionData() { uhash_close(regionIDMap); } if (allRegions) { - allRegions->removeAllElements(); // Don't need the temporary list anymore. delete allRegions; allRegions = NULL; } @@ -615,33 +633,30 @@ Region::getContainedRegions(UErrorCode &status) const { StringEnumeration* Region::getContainedRegions( URegionType type, UErrorCode &status ) const { umtx_initOnce(gRegionDataInitOnce, &loadRegionData, status); // returns immediately if U_FAILURE(status) + + UVector result(nullptr, uhash_compareChars, status); + LocalPointer cr(getContainedRegions(status), status); if (U_FAILURE(status)) { - return NULL; + return nullptr; } - UVector *result = new UVector(NULL, uhash_compareChars, status); - - StringEnumeration *cr = getContainedRegions(status); - - for ( int32_t i = 0 ; i < cr->count(status) ; i++ ) { - const char *regionId = cr->next(NULL,status); - const Region *r = Region::getInstance(regionId,status); + const char *regionId; + while((regionId = cr->next(nullptr, status)) != nullptr && U_SUCCESS(status)) { + const Region *r = Region::getInstance(regionId, status); if ( r->getType() == type) { - result->addElementX((void *)&r->idStr,status); + result.addElement(const_cast(&r->idStr), status); } else { - StringEnumeration *children = r->getContainedRegions(type, status); - for ( int32_t j = 0 ; j < children->count(status) ; j++ ) { - const char *id2 = children->next(NULL,status); + LocalPointer children(r->getContainedRegions(type, status)); + const char *id2; + while(U_SUCCESS(status) && ((id2 = children->next(nullptr, status)) != nullptr)) { const Region *r2 = Region::getInstance(id2,status); - result->addElementX((void *)&r2->idStr,status); + result.addElement(const_cast(&r2->idStr), status); } - delete children; } } - delete cr; - StringEnumeration* resultEnumeration = new RegionNameEnumeration(result,status); - delete result; - return resultEnumeration; + LocalPointer resultEnumeration( + new RegionNameEnumeration(&result, status), status); + return U_SUCCESS(status) ? resultEnumeration.orphan() : nullptr; } /** @@ -706,18 +721,21 @@ Region::getType() const { return fType; } -RegionNameEnumeration::RegionNameEnumeration(UVector *fNameList, UErrorCode& status) { - pos=0; - if (fNameList && U_SUCCESS(status)) { - fRegionNames = new UVector(uprv_deleteUObject, uhash_compareUnicodeString, fNameList->size(),status); - for ( int32_t i = 0 ; i < fNameList->size() ; i++ ) { - UnicodeString* this_region_name = (UnicodeString *)fNameList->elementAt(i); - UnicodeString* new_region_name = new UnicodeString(*this_region_name); - fRegionNames->addElementX((void *)new_region_name,status); +RegionNameEnumeration::RegionNameEnumeration(UVector *nameList, UErrorCode& status) : + pos(0), fRegionNames(nullptr) { + // TODO: https://unicode-org.atlassian.net/browse/ICU-21829 + // Is all of the copying going on here really necessary? + if (nameList && U_SUCCESS(status)) { + LocalPointer regionNames( + new UVector(uprv_deleteUObject, uhash_compareUnicodeString, nameList->size(), status), status); + for ( int32_t i = 0 ; U_SUCCESS(status) && i < nameList->size() ; i++ ) { + UnicodeString* this_region_name = (UnicodeString *)nameList->elementAt(i); + LocalPointer new_region_name(new UnicodeString(*this_region_name), status); + regionNames->adoptElement(new_region_name.orphan(), status); + } + if (U_SUCCESS(status)) { + fRegionNames = regionNames.orphan(); } - } - else { - fRegionNames = NULL; } } diff --git a/deps/icu-small/source/i18n/region_impl.h b/deps/icu-small/source/i18n/region_impl.h index 62acaa4511b49f..b6a281393f8911 100644 --- a/deps/icu-small/source/i18n/region_impl.h +++ b/deps/icu-small/source/i18n/region_impl.h @@ -26,7 +26,11 @@ U_NAMESPACE_BEGIN class RegionNameEnumeration : public StringEnumeration { public: - RegionNameEnumeration(UVector *fNameList, UErrorCode& status); + /** + * Construct an string enumeration over the supplied name list. + * Makes a copy of the supplied input name list; does not retain a reference to the original. + */ + RegionNameEnumeration(UVector *nameList, UErrorCode& status); virtual ~RegionNameEnumeration(); static UClassID U_EXPORT2 getStaticClassID(void); virtual UClassID getDynamicClassID(void) const override; diff --git a/deps/icu-small/source/i18n/smpdtfmt.cpp b/deps/icu-small/source/i18n/smpdtfmt.cpp index 91748d82f9fd64..c1e943a0949da2 100644 --- a/deps/icu-small/source/i18n/smpdtfmt.cpp +++ b/deps/icu-small/source/i18n/smpdtfmt.cpp @@ -3792,6 +3792,9 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC src = &text; } parseInt(*src, number, pos, allowNegative,currentNumberFormat); + if (!isLenient() && pos.getIndex() < start + count) { + return -start; + } if (pos.getIndex() != parseStart) { int32_t val = number.getLong(); diff --git a/deps/icu-small/source/i18n/tmutfmt.cpp b/deps/icu-small/source/i18n/tmutfmt.cpp index 057bb634ebbb24..f0335a81f50fa4 100644 --- a/deps/icu-small/source/i18n/tmutfmt.cpp +++ b/deps/icu-small/source/i18n/tmutfmt.cpp @@ -320,14 +320,14 @@ void TimeUnitFormat::setup(UErrorCode& err) { initDataMembers(err); - UVector pluralCounts(0, uhash_compareUnicodeString, 6, err); + UVector pluralCounts(nullptr, uhash_compareUnicodeString, 6, err); LocalPointer keywords(getPluralRules().getKeywords(err), err); if (U_FAILURE(err)) { return; } UnicodeString* pluralCount; while ((pluralCount = const_cast(keywords->snext(err))) != NULL) { - pluralCounts.addElementX(pluralCount, err); + pluralCounts.addElement(pluralCount, err); } readFromCurrentLocale(UTMUTFMT_FULL_STYLE, gUnitsTag, pluralCounts, err); checkConsistency(UTMUTFMT_FULL_STYLE, gUnitsTag, err); diff --git a/deps/icu-small/source/i18n/tzfmt.cpp b/deps/icu-small/source/i18n/tzfmt.cpp index ef3cfad80ce1d6..9d046c30c8f07b 100644 --- a/deps/icu-small/source/i18n/tzfmt.cpp +++ b/deps/icu-small/source/i18n/tzfmt.cpp @@ -2459,7 +2459,7 @@ TimeZoneFormat::parseOffsetPattern(const UnicodeString& pattern, OffsetFields re if (itemType != GMTOffsetField::TEXT) { if (GMTOffsetField::isValid(itemType, itemLength)) { GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, static_cast(itemLength), status); - result->addElementX(fld, status); + result->adoptElement(fld, status); if (U_FAILURE(status)) { break; } @@ -2485,7 +2485,7 @@ TimeZoneFormat::parseOffsetPattern(const UnicodeString& pattern, OffsetFields re if (itemType == GMTOffsetField::TEXT) { if (text.length() > 0) { GMTOffsetField* textfld = GMTOffsetField::createText(text, status); - result->addElementX(textfld, status); + result->adoptElement(textfld, status); if (U_FAILURE(status)) { break; } @@ -2494,7 +2494,7 @@ TimeZoneFormat::parseOffsetPattern(const UnicodeString& pattern, OffsetFields re } else { if (GMTOffsetField::isValid(itemType, itemLength)) { GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, static_cast(itemLength), status); - result->addElementX(fld, status); + result->adoptElement(fld, status); if (U_FAILURE(status)) { break; } @@ -2512,7 +2512,7 @@ TimeZoneFormat::parseOffsetPattern(const UnicodeString& pattern, OffsetFields re if (itemType != GMTOffsetField::TEXT) { if (GMTOffsetField::isValid(itemType, itemLength)) { GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, static_cast(itemLength), status); - result->addElementX(fld, status); + result->adoptElement(fld, status); if (U_FAILURE(status)) { break; } @@ -2532,12 +2532,12 @@ TimeZoneFormat::parseOffsetPattern(const UnicodeString& pattern, OffsetFields re if (itemType == GMTOffsetField::TEXT) { if (text.length() > 0) { GMTOffsetField* tfld = GMTOffsetField::createText(text, status); - result->addElementX(tfld, status); + result->adoptElement(tfld, status); } } else { if (GMTOffsetField::isValid(itemType, itemLength)) { GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, static_cast(itemLength), status); - result->addElementX(fld, status); + result->adoptElement(fld, status); } else { status = U_ILLEGAL_ARGUMENT_ERROR; } diff --git a/deps/icu-small/source/i18n/tzgnames.cpp b/deps/icu-small/source/i18n/tzgnames.cpp index ed5f42d7bc1d6d..d5ee45ced78db4 100644 --- a/deps/icu-small/source/i18n/tzgnames.cpp +++ b/deps/icu-small/source/i18n/tzgnames.cpp @@ -229,30 +229,27 @@ GNameSearchHandler::handleMatch(int32_t matchLength, const CharacterNode *node, if ((nameinfo->type & fTypes) != 0) { // matches a requested type if (fResults == NULL) { - fResults = new UVector(uprv_free, NULL, status); - if (fResults == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; + LocalPointer lpResults(new UVector(uprv_free, NULL, status), status); + if (U_FAILURE(status)) { + return false; } + fResults = lpResults.orphan(); } - if (U_SUCCESS(status)) { - U_ASSERT(fResults != NULL); - GMatchInfo *gmatch = (GMatchInfo *)uprv_malloc(sizeof(GMatchInfo)); - if (gmatch == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - } else { - // add the match to the vector - gmatch->gnameInfo = nameinfo; - gmatch->matchLength = matchLength; - gmatch->timeType = UTZFMT_TIME_TYPE_UNKNOWN; - fResults->addElementX(gmatch, status); - if (U_FAILURE(status)) { - uprv_free(gmatch); - } else { - if (matchLength > fMaxMatchLen) { - fMaxMatchLen = matchLength; - } - } - } + GMatchInfo *gmatch = (GMatchInfo *)uprv_malloc(sizeof(GMatchInfo)); + if (gmatch == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return false; + } + // add the match to the vector + gmatch->gnameInfo = nameinfo; + gmatch->matchLength = matchLength; + gmatch->timeType = UTZFMT_TIME_TYPE_UNKNOWN; + fResults->adoptElement(gmatch, status); + if (U_FAILURE(status)) { + return false; + } + if (matchLength > fMaxMatchLen) { + fMaxMatchLen = matchLength; } } } diff --git a/deps/icu-small/source/i18n/tznames.cpp b/deps/icu-small/source/i18n/tznames.cpp index 5c504d01cb6342..781f1cc161f9c5 100644 --- a/deps/icu-small/source/i18n/tznames.cpp +++ b/deps/icu-small/source/i18n/tznames.cpp @@ -414,15 +414,12 @@ TimeZoneNames::MatchInfoCollection::addZone(UTimeZoneNameType nameType, int32_t if (U_FAILURE(status)) { return; } - MatchInfo* matchInfo = new MatchInfo(nameType, matchLength, &tzID, NULL); - if (matchInfo == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - matches(status)->addElementX(matchInfo, status); + LocalPointer matchInfo(new MatchInfo(nameType, matchLength, &tzID, NULL), status); + UVector *matchesVec = matches(status); if (U_FAILURE(status)) { - delete matchInfo; + return; } + matchesVec->adoptElement(matchInfo.orphan(), status); } void @@ -431,15 +428,12 @@ TimeZoneNames::MatchInfoCollection::addMetaZone(UTimeZoneNameType nameType, int3 if (U_FAILURE(status)) { return; } - MatchInfo* matchInfo = new MatchInfo(nameType, matchLength, NULL, &mzID); - if (matchInfo == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - matches(status)->addElementX(matchInfo, status); + LocalPointer matchInfo(new MatchInfo(nameType, matchLength, NULL, &mzID), status); + UVector *matchesVec = matches(status); if (U_FAILURE(status)) { - delete matchInfo; + return; } + matchesVec->adoptElement(matchInfo.orphan(), status); } int32_t diff --git a/deps/icu-small/source/i18n/tznames_impl.cpp b/deps/icu-small/source/i18n/tznames_impl.cpp index d450b7456489bf..69991dfef4b5c0 100644 --- a/deps/icu-small/source/i18n/tznames_impl.cpp +++ b/deps/icu-small/source/i18n/tznames_impl.cpp @@ -148,19 +148,29 @@ CharacterNode::addValue(void *value, UObjectDeleter *valueDeleter, UErrorCode &s if (!fHasValuesVector) { // There is only one value so far, and not in a vector yet. // Create a vector and add the old value. - UVector *values = new UVector(valueDeleter, NULL, DEFAULT_CHARACTERNODE_CAPACITY, status); + LocalPointer values( + new UVector(valueDeleter, NULL, DEFAULT_CHARACTERNODE_CAPACITY, status), status); if (U_FAILURE(status)) { if (valueDeleter) { valueDeleter(value); } return; } - values->addElementX(fValues, status); - fValues = values; + if (values->hasDeleter()) { + values->adoptElement(fValues, status); + } else { + values->addElement(fValues, status); + } + fValues = values.orphan(); fHasValuesVector = TRUE; } // Add the new value. - ((UVector *)fValues)->addElementX(value, status); + UVector *values = (UVector *)fValues; + if (values->hasDeleter()) { + values->adoptElement(value, status); + } else { + values->addElement(value, status); + } } } @@ -219,10 +229,8 @@ void TextTrieMap::put(const UChar *key, void *value, UErrorCode &status) { fIsEmpty = FALSE; if (fLazyContents == NULL) { - fLazyContents = new UVector(status); - if (fLazyContents == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - } + LocalPointer lpLazyContents(new UVector(status), status); + fLazyContents = lpLazyContents.orphan(); } if (U_FAILURE(status)) { if (fValueDeleter) { @@ -233,7 +241,7 @@ TextTrieMap::put(const UChar *key, void *value, UErrorCode &status) { U_ASSERT(fLazyContents != NULL); UChar *s = const_cast(key); - fLazyContents->addElementX(s, status); + fLazyContents->addElement(s, status); if (U_FAILURE(status)) { if (fValueDeleter) { fValueDeleter((void*) key); @@ -241,7 +249,7 @@ TextTrieMap::put(const UChar *key, void *value, UErrorCode &status) { return; } - fLazyContents->addElementX(value, status); + fLazyContents->addElement(value, status); } void @@ -854,7 +862,7 @@ class MetaZoneIDsEnumeration : public StringEnumeration { public: MetaZoneIDsEnumeration(); MetaZoneIDsEnumeration(const UVector& mzIDs); - MetaZoneIDsEnumeration(UVector* mzIDs); + MetaZoneIDsEnumeration(LocalPointer mzIDs); virtual ~MetaZoneIDsEnumeration(); static UClassID U_EXPORT2 getStaticClassID(void); virtual UClassID getDynamicClassID(void) const override; @@ -865,7 +873,7 @@ class MetaZoneIDsEnumeration : public StringEnumeration { int32_t fLen; int32_t fPos; const UVector* fMetaZoneIDs; - UVector *fLocalVector; + LocalPointer fLocalVector; }; UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MetaZoneIDsEnumeration) @@ -879,8 +887,9 @@ MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(const UVector& mzIDs) fLen = fMetaZoneIDs->size(); } -MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(UVector *mzIDs) -: fLen(0), fPos(0), fMetaZoneIDs(mzIDs), fLocalVector(mzIDs) { +MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(LocalPointer mzIDs) +: fLen(0), fPos(0), fMetaZoneIDs(nullptr), fLocalVector(std::move(mzIDs)) { + fMetaZoneIDs = fLocalVector.getAlias(); if (fMetaZoneIDs) { fLen = fMetaZoneIDs->size(); } @@ -906,9 +915,6 @@ MetaZoneIDsEnumeration::count(UErrorCode& /*status*/) const { } MetaZoneIDsEnumeration::~MetaZoneIDsEnumeration() { - if (fLocalVector) { - delete fLocalVector; - } } @@ -1153,28 +1159,23 @@ TimeZoneNamesImpl::_getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCod return new MetaZoneIDsEnumeration(); } - MetaZoneIDsEnumeration *senum = NULL; - UVector* mzIDs = new UVector(NULL, uhash_compareUChars, status); - if (mzIDs == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - } + LocalPointer senum; + LocalPointer mzIDs(new UVector(NULL, uhash_compareUChars, status), status); if (U_SUCCESS(status)) { - U_ASSERT(mzIDs != NULL); + U_ASSERT(mzIDs.isValid()); for (int32_t i = 0; U_SUCCESS(status) && i < mappings->size(); i++) { OlsonToMetaMappingEntry *map = (OlsonToMetaMappingEntry *)mappings->elementAt(i); const UChar *mzID = map->mzid; if (!mzIDs->contains((void *)mzID)) { - mzIDs->addElementX((void *)mzID, status); + mzIDs->addElement((void *)mzID, status); } } if (U_SUCCESS(status)) { - senum = new MetaZoneIDsEnumeration(mzIDs); - } else { - delete mzIDs; + senum.adoptInsteadAndCheckErrorCode(new MetaZoneIDsEnumeration(std::move(mzIDs)), status); } } - return senum; + return U_SUCCESS(status) ? senum.orphan() : nullptr; } UnicodeString& diff --git a/deps/icu-small/source/i18n/ucol.cpp b/deps/icu-small/source/i18n/ucol.cpp index f59333ede3c890..8e1df8d5577beb 100644 --- a/deps/icu-small/source/i18n/ucol.cpp +++ b/deps/icu-small/source/i18n/ucol.cpp @@ -96,12 +96,18 @@ ucol_safeClone(const UCollator *coll, void * /*stackBuffer*/, int32_t * pBufferS if (newColl == NULL) { *status = U_MEMORY_ALLOCATION_ERROR; return nullptr; - } else { + } else if (pBufferSize != NULL) { *status = U_SAFECLONE_ALLOCATED_WARNING; } return newColl->toUCollator(); } +U_CAPI UCollator* U_EXPORT2 +ucol_clone(const UCollator *coll, UErrorCode *status) +{ + return ucol_safeClone(coll, nullptr, nullptr, status); +} + U_CAPI void U_EXPORT2 ucol_close(UCollator *coll) { diff --git a/deps/icu-small/source/i18n/udatpg.cpp b/deps/icu-small/source/i18n/udatpg.cpp index 332636a93889f1..9e61a12076803a 100644 --- a/deps/icu-small/source/i18n/udatpg.cpp +++ b/deps/icu-small/source/i18n/udatpg.cpp @@ -210,12 +210,47 @@ udatpg_setDateTimeFormat(const UDateTimePatternGenerator *dtpg, U_CAPI const UChar * U_EXPORT2 udatpg_getDateTimeFormat(const UDateTimePatternGenerator *dtpg, int32_t *pLength) { - const UnicodeString &result=((const DateTimePatternGenerator *)dtpg)->getDateTimeFormat(); - if(pLength!=NULL) { + UErrorCode status = U_ZERO_ERROR; + return udatpg_getDateTimeFormatForStyle(dtpg, UDAT_MEDIUM, pLength, &status); +} + +U_CAPI void U_EXPORT2 +udatpg_setDateTimeFormatForStyle(UDateTimePatternGenerator *udtpg, + UDateFormatStyle style, + const UChar *dateTimeFormat, int32_t length, + UErrorCode *pErrorCode) { + if (U_FAILURE(*pErrorCode)) { + return; + } else if (dateTimeFormat==nullptr) { + *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + DateTimePatternGenerator *dtpg = reinterpret_cast(udtpg); + UnicodeString dtFormatString((UBool)(length<0), dateTimeFormat, length); + dtpg->setDateTimeFormat(style, dtFormatString, *pErrorCode); +} + +U_CAPI const UChar* U_EXPORT2 +udatpg_getDateTimeFormatForStyle(const UDateTimePatternGenerator *udtpg, + UDateFormatStyle style, int32_t *pLength, + UErrorCode *pErrorCode) { + static const UChar emptyString[] = { (UChar)0 }; + if (U_FAILURE(*pErrorCode)) { + if (pLength !=nullptr) { + *pLength = 0; + } + return emptyString; + } + const DateTimePatternGenerator *dtpg = reinterpret_cast(udtpg); + const UnicodeString &result = dtpg->getDateTimeFormat(style, *pErrorCode); + if (pLength != nullptr) { *pLength=result.length(); } + // Note: The UnicodeString for the dateTimeFormat string in the DateTimePatternGenerator + // was NUL-terminated what it was set, to avoid doing it here which could re-allocate + // the buffe and affect and cont references to the string or its buffer. return result.getBuffer(); -} + } U_CAPI void U_EXPORT2 udatpg_setDecimal(UDateTimePatternGenerator *dtpg, diff --git a/deps/icu-small/source/i18n/unicode/basictz.h b/deps/icu-small/source/i18n/unicode/basictz.h index 250ea309279aa7..d9f85e45eeff26 100644 --- a/deps/icu-small/source/i18n/unicode/basictz.h +++ b/deps/icu-small/source/i18n/unicode/basictz.h @@ -152,17 +152,15 @@ class U_I18N_API BasicTimeZone: public TimeZone { virtual void getSimpleRulesNear(UDate date, InitialTimeZoneRule*& initial, AnnualTimeZoneRule*& std, AnnualTimeZoneRule*& dst, UErrorCode& status) const; -#ifndef U_FORCE_HIDE_DRAFT_API /** * Get time zone offsets from local wall time. - * @draft ICU 69 + * @stable ICU 69 */ virtual void getOffsetFromLocal( UDate date, UTimeZoneLocalOption nonExistingTimeOpt, UTimeZoneLocalOption duplicatedTimeOpt, int32_t& rawOffset, int32_t& dstOffset, UErrorCode& status) const; -#endif /* U_FORCE_HIDE_DRAFT_API */ #ifndef U_HIDE_INTERNAL_API /** diff --git a/deps/icu-small/source/i18n/unicode/dtptngen.h b/deps/icu-small/source/i18n/unicode/dtptngen.h index 250a0e089fed6b..6be0e2a571c7bb 100644 --- a/deps/icu-small/source/i18n/unicode/dtptngen.h +++ b/deps/icu-small/source/i18n/unicode/dtptngen.h @@ -311,6 +311,11 @@ class U_I18N_API DateTimePatternGenerator : public UObject { * for those two skeletons, so the result is put together with this pattern, * resulting in "d-MMM h:mm". * + * There are four DateTimeFormats in a DateTimePatternGenerator object, + * corresponding to date styles UDAT_FULL..UDAT_SHORT. This method sets + * all of them to the specified pattern. To set them individually, see + * setDateTimeFormat(UDateFormatStyle style, ...). + * * @param dateTimeFormat * message format pattern, here {1} will be replaced by the date * pattern and {0} will be replaced by the time pattern. @@ -320,11 +325,66 @@ class U_I18N_API DateTimePatternGenerator : public UObject { /** * Getter corresponding to setDateTimeFormat. + * + * There are four DateTimeFormats in a DateTimePatternGenerator object, + * corresponding to date styles UDAT_FULL..UDAT_SHORT. This method gets + * the style for UDAT_MEDIUM (the default). To get them individually, see + * getDateTimeFormat(UDateFormatStyle style). + * * @return DateTimeFormat. * @stable ICU 3.8 */ const UnicodeString& getDateTimeFormat() const; +#if !UCONFIG_NO_FORMATTING +#ifndef U_HIDE_DRAFT_API + /** + * dateTimeFormats are message patterns used to compose combinations of date + * and time patterns. There are four length styles, corresponding to the + * inferred style of the date pattern; these are UDateFormatStyle values: + * - UDAT_FULL (for date pattern with weekday and long month), else + * - UDAT_LONG (for a date pattern with long month), else + * - UDAT_MEDIUM (for a date pattern with abbreviated month), else + * - UDAT_SHORT (for any other date pattern). + * For details on dateTimeFormats, see + * https://www.unicode.org/reports/tr35/tr35-dates.html#dateTimeFormats. + * The default pattern in the root locale for all styles is "{1} {0}". + * + * @param style + * one of DateFormat.FULL..DateFormat.SHORT. Error if out of range. + * @param dateTimeFormat + * the new dateTimeFormat to set for the the specified style + * @param status + * in/out parameter; if no failure status is already set, + * it will be set according to result of the function (e.g. + * U_ILLEGAL_ARGUMENT_ERROR for style out of range). + * @draft ICU 71 + */ + void setDateTimeFormat(UDateFormatStyle style, const UnicodeString& dateTimeFormat, + UErrorCode& status); + + /** + * Getter corresponding to setDateTimeFormat. + * + * @param style + * one of UDAT_FULL..UDAT_SHORT. Error if out of range. + * @param status + * in/out parameter; if no failure status is already set, + * it will be set according to result of the function (e.g. + * U_ILLEGAL_ARGUMENT_ERROR for style out of range). + * @return + * the current dateTimeFormat for the the specified style, or + * empty string in case of error. The UnicodeString reference, + * or the contents of the string, may no longer be valid if + * setDateTimeFormat is called, or the DateTimePatternGenerator + * object is deleted. + * @draft ICU 71 + */ + const UnicodeString& getDateTimeFormat(UDateFormatStyle style, + UErrorCode& status) const; +#endif /* U_HIDE_DRAFT_API */ +#endif /* #if !UCONFIG_NO_FORMATTING */ + /** * Return the best pattern matching the input skeleton. It is guaranteed to * have all of the fields in the skeleton. @@ -545,8 +605,7 @@ class U_I18N_API DateTimePatternGenerator : public UObject { */ DateTimePatternGenerator& operator=(const DateTimePatternGenerator& other); - // TODO(ticket:13619): re-enable when UDATPG_NARROW no longer in draft mode. - // static const int32_t UDATPG_WIDTH_COUNT = UDATPG_NARROW + 1; + static const int32_t UDATPG_WIDTH_COUNT = UDATPG_NARROW + 1; Locale pLocale; // pattern locale FormatParser *fp; @@ -554,9 +613,8 @@ class U_I18N_API DateTimePatternGenerator : public UObject { DistanceInfo *distanceInfo; PatternMap *patternMap; UnicodeString appendItemFormats[UDATPG_FIELD_COUNT]; - // TODO(ticket:13619): [3] -> UDATPG_WIDTH_COUNT - UnicodeString fieldDisplayNames[UDATPG_FIELD_COUNT][3]; - UnicodeString dateTimeFormat; + UnicodeString fieldDisplayNames[UDATPG_FIELD_COUNT][UDATPG_WIDTH_COUNT]; + UnicodeString dateTimeFormat[4]; UnicodeString decimal; DateTimeMatcher *skipMatcher; Hashtable *fAvailableFormatKeyHash; diff --git a/deps/icu-small/source/i18n/unicode/measunit.h b/deps/icu-small/source/i18n/unicode/measunit.h index 61da62e71f2271..b7e8e1676a41dc 100644 --- a/deps/icu-small/source/i18n/unicode/measunit.h +++ b/deps/icu-small/source/i18n/unicode/measunit.h @@ -77,14 +77,13 @@ enum UMeasureUnitComplexity { }; -#ifndef U_HIDE_DRAFT_API /** * Enumeration for SI and binary prefixes, e.g. "kilo-", "nano-", "mebi-". * * Enum values should be treated as opaque: use umeas_getPrefixPower() and * umeas_getPrefixBase() to find their corresponding values. * - * @draft ICU 69 + * @stable ICU 69 * @see umeas_getPrefixBase * @see umeas_getPrefixPower */ @@ -96,14 +95,14 @@ typedef enum UMeasurePrefix { * implementation detail and should not be relied upon: use * umeas_getPrefixPower() to obtain meaningful values. * - * @draft ICU 69 + * @stable ICU 69 */ UMEASURE_PREFIX_ONE = 30 + 0, /** * SI prefix: yotta, 10^24. * - * @draft ICU 69 + * @stable ICU 69 */ UMEASURE_PREFIX_YOTTA = UMEASURE_PREFIX_ONE + 24, @@ -119,133 +118,133 @@ typedef enum UMeasurePrefix { /** * SI prefix: zetta, 10^21. * - * @draft ICU 69 + * @stable ICU 69 */ UMEASURE_PREFIX_ZETTA = UMEASURE_PREFIX_ONE + 21, /** * SI prefix: exa, 10^18. * - * @draft ICU 69 + * @stable ICU 69 */ UMEASURE_PREFIX_EXA = UMEASURE_PREFIX_ONE + 18, /** * SI prefix: peta, 10^15. * - * @draft ICU 69 + * @stable ICU 69 */ UMEASURE_PREFIX_PETA = UMEASURE_PREFIX_ONE + 15, /** * SI prefix: tera, 10^12. * - * @draft ICU 69 + * @stable ICU 69 */ UMEASURE_PREFIX_TERA = UMEASURE_PREFIX_ONE + 12, /** * SI prefix: giga, 10^9. * - * @draft ICU 69 + * @stable ICU 69 */ UMEASURE_PREFIX_GIGA = UMEASURE_PREFIX_ONE + 9, /** * SI prefix: mega, 10^6. * - * @draft ICU 69 + * @stable ICU 69 */ UMEASURE_PREFIX_MEGA = UMEASURE_PREFIX_ONE + 6, /** * SI prefix: kilo, 10^3. * - * @draft ICU 69 + * @stable ICU 69 */ UMEASURE_PREFIX_KILO = UMEASURE_PREFIX_ONE + 3, /** * SI prefix: hecto, 10^2. * - * @draft ICU 69 + * @stable ICU 69 */ UMEASURE_PREFIX_HECTO = UMEASURE_PREFIX_ONE + 2, /** * SI prefix: deka, 10^1. * - * @draft ICU 69 + * @stable ICU 69 */ UMEASURE_PREFIX_DEKA = UMEASURE_PREFIX_ONE + 1, /** * SI prefix: deci, 10^-1. * - * @draft ICU 69 + * @stable ICU 69 */ UMEASURE_PREFIX_DECI = UMEASURE_PREFIX_ONE + -1, /** * SI prefix: centi, 10^-2. * - * @draft ICU 69 + * @stable ICU 69 */ UMEASURE_PREFIX_CENTI = UMEASURE_PREFIX_ONE + -2, /** * SI prefix: milli, 10^-3. * - * @draft ICU 69 + * @stable ICU 69 */ UMEASURE_PREFIX_MILLI = UMEASURE_PREFIX_ONE + -3, /** * SI prefix: micro, 10^-6. * - * @draft ICU 69 + * @stable ICU 69 */ UMEASURE_PREFIX_MICRO = UMEASURE_PREFIX_ONE + -6, /** * SI prefix: nano, 10^-9. * - * @draft ICU 69 + * @stable ICU 69 */ UMEASURE_PREFIX_NANO = UMEASURE_PREFIX_ONE + -9, /** * SI prefix: pico, 10^-12. * - * @draft ICU 69 + * @stable ICU 69 */ UMEASURE_PREFIX_PICO = UMEASURE_PREFIX_ONE + -12, /** * SI prefix: femto, 10^-15. * - * @draft ICU 69 + * @stable ICU 69 */ UMEASURE_PREFIX_FEMTO = UMEASURE_PREFIX_ONE + -15, /** * SI prefix: atto, 10^-18. * - * @draft ICU 69 + * @stable ICU 69 */ UMEASURE_PREFIX_ATTO = UMEASURE_PREFIX_ONE + -18, /** * SI prefix: zepto, 10^-21. * - * @draft ICU 69 + * @stable ICU 69 */ UMEASURE_PREFIX_ZEPTO = UMEASURE_PREFIX_ONE + -21, /** * SI prefix: yocto, 10^-24. * - * @draft ICU 69 + * @stable ICU 69 */ UMEASURE_PREFIX_YOCTO = UMEASURE_PREFIX_ONE + -24, @@ -270,7 +269,7 @@ typedef enum UMeasurePrefix { /** * Binary prefix: kibi, 1024^1. * - * @draft ICU 69 + * @stable ICU 69 */ UMEASURE_PREFIX_KIBI = UMEASURE_PREFIX_INTERNAL_ONE_BIN + 1, @@ -286,49 +285,49 @@ typedef enum UMeasurePrefix { /** * Binary prefix: mebi, 1024^2. * - * @draft ICU 69 + * @stable ICU 69 */ UMEASURE_PREFIX_MEBI = UMEASURE_PREFIX_INTERNAL_ONE_BIN + 2, /** * Binary prefix: gibi, 1024^3. * - * @draft ICU 69 + * @stable ICU 69 */ UMEASURE_PREFIX_GIBI = UMEASURE_PREFIX_INTERNAL_ONE_BIN + 3, /** * Binary prefix: tebi, 1024^4. * - * @draft ICU 69 + * @stable ICU 69 */ UMEASURE_PREFIX_TEBI = UMEASURE_PREFIX_INTERNAL_ONE_BIN + 4, /** * Binary prefix: pebi, 1024^5. * - * @draft ICU 69 + * @stable ICU 69 */ UMEASURE_PREFIX_PEBI = UMEASURE_PREFIX_INTERNAL_ONE_BIN + 5, /** * Binary prefix: exbi, 1024^6. * - * @draft ICU 69 + * @stable ICU 69 */ UMEASURE_PREFIX_EXBI = UMEASURE_PREFIX_INTERNAL_ONE_BIN + 6, /** * Binary prefix: zebi, 1024^7. * - * @draft ICU 69 + * @stable ICU 69 */ UMEASURE_PREFIX_ZEBI = UMEASURE_PREFIX_INTERNAL_ONE_BIN + 7, /** * Binary prefix: yobi, 1024^8. * - * @draft ICU 69 + * @stable ICU 69 */ UMEASURE_PREFIX_YOBI = UMEASURE_PREFIX_INTERNAL_ONE_BIN + 8, @@ -347,7 +346,7 @@ typedef enum UMeasurePrefix { * base is 10 for SI prefixes (kilo, micro) and 1024 for binary prefixes (kibi, * mebi). * - * @draft ICU 69 + * @stable ICU 69 */ U_CAPI int32_t U_EXPORT2 umeas_getPrefixBase(UMeasurePrefix unitPrefix); @@ -355,12 +354,10 @@ U_CAPI int32_t U_EXPORT2 umeas_getPrefixBase(UMeasurePrefix unitPrefix); * Returns the exponent of the factor associated with the given unit prefix, for * example 3 for kilo, -6 for micro, 1 for kibi, 2 for mebi, 3 for gibi. * - * @draft ICU 69 + * @stable ICU 69 */ U_CAPI int32_t U_EXPORT2 umeas_getPrefixPower(UMeasurePrefix unitPrefix); -#endif // U_HIDE_DRAFT_API - /** * A unit such as length, mass, volume, currency, etc. A unit is * coupled with a numeric amount to produce a Measure. @@ -481,7 +478,6 @@ class U_I18N_API MeasureUnit: public UObject { */ UMeasureUnitComplexity getComplexity(UErrorCode& status) const; -#ifndef U_HIDE_DRAFT_API /** * Creates a MeasureUnit which is this SINGLE unit augmented with the specified prefix. * For example, UMEASURE_PREFIX_KILO for "kilo", or UMEASURE_PREFIX_KIBI for "kibi". @@ -494,7 +490,7 @@ class U_I18N_API MeasureUnit: public UObject { * @param prefix The prefix, from UMeasurePrefix. * @param status Set if this is not a SINGLE unit or if another error occurs. * @return A new SINGLE unit. - * @draft ICU 69 + * @stable ICU 69 */ MeasureUnit withPrefix(UMeasurePrefix prefix, UErrorCode& status) const; @@ -510,10 +506,9 @@ class U_I18N_API MeasureUnit: public UObject { * @return The prefix of this SINGLE unit, from UMeasurePrefix. * @see umeas_getPrefixBase * @see umeas_getPrefixPower - * @draft ICU 69 + * @stable ICU 69 */ UMeasurePrefix getPrefix(UErrorCode& status) const; -#endif // U_HIDE_DRAFT_API /** * Creates a MeasureUnit which is this SINGLE unit augmented with the specified dimensionality @@ -989,23 +984,21 @@ class U_I18N_API MeasureUnit: public UObject { */ static MeasureUnit getKarat(); -#ifndef U_HIDE_DRAFT_API /** * Returns by pointer, unit of concentr: milligram-ofglucose-per-deciliter. * Caller owns returned value and must free it. * Also see {@link #getMilligramOfglucosePerDeciliter()}. * @param status ICU error code. - * @draft ICU 69 + * @stable ICU 69 */ static MeasureUnit *createMilligramOfglucosePerDeciliter(UErrorCode &status); /** * Returns by value, unit of concentr: milligram-ofglucose-per-deciliter. * Also see {@link #createMilligramOfglucosePerDeciliter()}. - * @draft ICU 69 + * @stable ICU 69 */ static MeasureUnit getMilligramOfglucosePerDeciliter(); -#endif /* U_HIDE_DRAFT_API */ /** * Returns by pointer, unit of concentr: milligram-per-deciliter. diff --git a/deps/icu-small/source/i18n/unicode/numberformatter.h b/deps/icu-small/source/i18n/unicode/numberformatter.h index ece433b55f09ea..711064ece8dbb3 100644 --- a/deps/icu-small/source/i18n/unicode/numberformatter.h +++ b/deps/icu-small/source/i18n/unicode/numberformatter.h @@ -22,6 +22,7 @@ #include "unicode/parseerr.h" #include "unicode/plurrule.h" #include "unicode/ucurr.h" +#include "unicode/unounclass.h" #include "unicode/unum.h" #include "unicode/unumberformatter.h" #include "unicode/uobject.h" @@ -640,6 +641,33 @@ class U_I18N_API Precision : public UMemory { */ static IncrementPrecision increment(double roundingIncrement); +#ifndef U_HIDE_DRAFT_API + /** + * Version of `Precision::increment()` that takes an integer at a particular power of 10. + * + * To round to the nearest 0.5 and display 2 fraction digits, with this function, you should write one of the following: + * + *

+     * Precision::incrementExact(5, -1).withMinFraction(2)
+     * Precision::incrementExact(50, -2).withMinFraction(2)
+     * Precision::incrementExact(50, -2)
+     * 
+ * + * This is analagous to ICU4J `Precision.increment(new BigDecimal("0.50"))`. + * + * This behavior is modeled after ECMA-402. For more information, see: + * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#roundingincrement + * + * @param mantissa + * The increment to which to round numbers. + * @param magnitude + * The power of 10 of the ones digit of the mantissa. + * @return A precision for chaining or passing to the NumberFormatter precision() setter. + * @draft ICU 71 + */ + static IncrementPrecision incrementExact(uint64_t mantissa, int16_t magnitude); +#endif // U_HIDE_DRAFT_API + /** * Show numbers rounded and padded according to the rules for the currency unit. The most common * rounding precision settings for currencies include Precision::fixedFraction(2), @@ -659,16 +687,14 @@ class U_I18N_API Precision : public UMemory { */ static CurrencyPrecision currency(UCurrencyUsage currencyUsage); -#ifndef U_HIDE_DRAFT_API /** * Configure how trailing zeros are displayed on numbers. For example, to hide trailing zeros * when the number is an integer, use UNUM_TRAILING_ZERO_HIDE_IF_WHOLE. * * @param trailingZeroDisplay Option to configure the display of trailing zeros. - * @draft ICU 69 + * @stable ICU 69 */ Precision trailingZeroDisplay(UNumberTrailingZeroDisplay trailingZeroDisplay) const; -#endif // U_HIDE_DRAFT_API private: enum PrecisionType { @@ -707,16 +733,23 @@ class U_I18N_API Precision : public UMemory { impl::digits_t fMaxSig; /** @internal (private) */ UNumberRoundingPriority fPriority; + /** + * Whether to retain trailing zeros based on the looser strategy. + * @internal (private) + */ + bool fRetain; } fracSig; /** @internal (private) */ struct IncrementSettings { // For RND_INCREMENT, RND_INCREMENT_ONE, and RND_INCREMENT_FIVE + // Note: This is a union, so we shouldn't own memory, since + // the default destructor would leak it. /** @internal (private) */ - double fIncrement; + uint64_t fIncrement; /** @internal (private) */ - impl::digits_t fMinFrac; + impl::digits_t fIncrementMagnitude; /** @internal (private) */ - impl::digits_t fMaxFrac; + impl::digits_t fMinFrac; } increment; UCurrencyUsage currencyUsage; // For RND_CURRENCY UErrorCode errorCode; // For RND_ERROR @@ -759,9 +792,10 @@ class U_I18N_API Precision : public UMemory { const FractionPrecision &base, int32_t minSig, int32_t maxSig, - UNumberRoundingPriority priority); + UNumberRoundingPriority priority, + bool retain); - static IncrementPrecision constructIncrement(double increment, int32_t minFrac); + static IncrementPrecision constructIncrement(uint64_t increment, impl::digits_t magnitude); static CurrencyPrecision constructCurrency(UCurrencyUsage usage); @@ -801,7 +835,6 @@ class U_I18N_API Precision : public UMemory { */ class U_I18N_API FractionPrecision : public Precision { public: -#ifndef U_HIDE_DRAFT_API /** * Override maximum fraction digits with maximum significant digits depending on the magnitude * of the number. See UNumberRoundingPriority. @@ -814,13 +847,12 @@ class U_I18N_API FractionPrecision : public Precision { * How to disambiguate between fraction digits and significant digits. * @return A precision for chaining or passing to the NumberFormatter precision() setter. * - * @draft ICU 69 + * @stable ICU 69 */ Precision withSignificantDigits( int32_t minSignificantDigits, int32_t maxSignificantDigits, UNumberRoundingPriority priority) const; -#endif // U_HIDE_DRAFT_API /** * Ensure that no less than this number of significant digits are retained when rounding @@ -1170,31 +1202,32 @@ class U_I18N_API Scale : public UMemory { namespace impl { -// Do not enclose entire StringProp with #ifndef U_HIDE_INTERNAL_API, needed for a protected field +// Do not enclose entire StringProp with #ifndef U_HIDE_INTERNAL_API, needed for a protected field. +// And do not enclose its class boilerplate within #ifndef U_HIDE_INTERNAL_API. /** * Manages NumberFormatterSettings::usage()'s char* instance on the heap. * @internal */ class U_I18N_API StringProp : public UMemory { -#ifndef U_HIDE_INTERNAL_API - public: + /** @internal */ + ~StringProp(); + /** @internal */ StringProp(const StringProp &other); /** @internal */ StringProp &operator=(const StringProp &other); +#ifndef U_HIDE_INTERNAL_API + /** @internal */ StringProp(StringProp &&src) U_NOEXCEPT; /** @internal */ StringProp &operator=(StringProp &&src) U_NOEXCEPT; - /** @internal */ - ~StringProp(); - /** @internal */ int16_t length() const { return fLength; @@ -2735,14 +2768,20 @@ class U_I18N_API FormattedNumber : public UMemory, public FormattedValue { */ MeasureUnit getOutputUnit(UErrorCode& status) const; -#ifndef U_HIDE_INTERNAL_API +#ifndef U_HIDE_DRAFT_API + /** - * Gets the gender of the formatted output. Returns "" when the gender is - * unknown, or for ungendered languages. + * Gets the noun class of the formatted output. Returns `OTHER` when the noun class + * is not supported yet. * - * @internal ICU 69 technology preview. + * @return `NounClass` + * @draft ICU 71. */ - const char *getGender(UErrorCode& status) const; + NounClass getNounClass(UErrorCode &status) const; + +#endif // U_HIDE_DRAFT_API + +#ifndef U_HIDE_INTERNAL_API /** * Gets the raw DecimalQuantity for plural rule selection. @@ -2758,6 +2797,18 @@ class U_I18N_API FormattedNumber : public UMemory, public FormattedValue { #endif /* U_HIDE_INTERNAL_API */ +#ifndef U_HIDE_DEPRECATED_API + + /** + * Gets the gender of the formatted output. Returns "" when the gender is + * unknown, or for ungendered languages. + * + * @deprecated This API is for ICU internal use only. + */ + const char *getGender(UErrorCode &status) const; + +#endif /* U_HIDE_DEPRECATED_API */ + private: // Can't use LocalPointer because UFormattedNumberData is forward-declared const impl::UFormattedNumberData *fData; diff --git a/deps/icu-small/source/i18n/unicode/rbtz.h b/deps/icu-small/source/i18n/unicode/rbtz.h index 1eca70c338bf60..4fbf330cef1c7b 100644 --- a/deps/icu-small/source/i18n/unicode/rbtz.h +++ b/deps/icu-small/source/i18n/unicode/rbtz.h @@ -303,16 +303,14 @@ class U_I18N_API RuleBasedTimeZone : public BasicTimeZone { virtual void getTimeZoneRules(const InitialTimeZoneRule*& initial, const TimeZoneRule* trsrules[], int32_t& trscount, UErrorCode& status) const override; -#ifndef U_FORCE_HIDE_DRAFT_API /** * Get time zone offsets from local wall time. - * @draft ICU 69 + * @stable ICU 69 */ virtual void getOffsetFromLocal( UDate date, UTimeZoneLocalOption nonExistingTimeOpt, UTimeZoneLocalOption duplicatedTimeOpt, int32_t& rawOffset, int32_t& dstOffset, UErrorCode& status) const override; -#endif /* U_FORCE_HIDE_DRAFT_API */ private: void deleteRules(void); diff --git a/deps/icu-small/source/i18n/unicode/simpletz.h b/deps/icu-small/source/i18n/unicode/simpletz.h index f5c155de466923..f73d823ee58f1c 100644 --- a/deps/icu-small/source/i18n/unicode/simpletz.h +++ b/deps/icu-small/source/i18n/unicode/simpletz.h @@ -620,16 +620,14 @@ class U_I18N_API SimpleTimeZone: public BasicTimeZone { virtual void getOffset(UDate date, UBool local, int32_t& rawOffset, int32_t& dstOffset, UErrorCode& ec) const override; -#ifndef U_FORCE_HIDE_DRAFT_API /** * Get time zone offsets from local wall time. - * @draft ICU 69 + * @stable ICU 69 */ virtual void getOffsetFromLocal( UDate date, UTimeZoneLocalOption nonExistingTimeOpt, UTimeZoneLocalOption duplicatedTimeOpt, int32_t& rawOffset, int32_t& dstOffset, UErrorCode& status) const override; -#endif /* U_FORCE_HIDE_DRAFT_API */ /** * Returns the TimeZone's raw GMT offset (i.e., the number of milliseconds to add diff --git a/deps/icu-small/source/i18n/unicode/ucal.h b/deps/icu-small/source/i18n/unicode/ucal.h index 94abae83919a85..3a4fb69fc3ed9d 100644 --- a/deps/icu-small/source/i18n/unicode/ucal.h +++ b/deps/icu-small/source/i18n/unicode/ucal.h @@ -1617,25 +1617,23 @@ U_CAPI int32_t U_EXPORT2 ucal_getTimeZoneIDForWindowsID(const UChar* winid, int32_t len, const char* region, UChar* id, int32_t idCapacity, UErrorCode* status); -#ifndef U_FORCE_HIDE_DRAFT_API /** * Options used by ucal_getTimeZoneOffsetFromLocal and BasicTimeZone::getOffsetFromLocal() * to specify how to interpret an input time when it does not exist, or when it is ambiguous, * around a time zone transition. - * @draft ICU 69 + * @stable ICU 69 */ enum UTimeZoneLocalOption { -#ifndef U_HIDE_DRAFT_API /** * An input time is always interpreted as local time before * a time zone transition. - * @draft ICU 69 + * @stable ICU 69 */ UCAL_TZ_LOCAL_FORMER = 0x04, /** * An input time is always interpreted as local time after * a time zone transition. - * @draft ICU 69 + * @stable ICU 69 */ UCAL_TZ_LOCAL_LATTER = 0x0C, /** @@ -1644,7 +1642,7 @@ enum UTimeZoneLocalOption { * sides of a time zone transition are standard time, * or daylight saving time, the local time before the * transition is used. - * @draft ICU 69 + * @stable ICU 69 */ UCAL_TZ_LOCAL_STANDARD_FORMER = UCAL_TZ_LOCAL_FORMER | 0x01, /** @@ -1653,7 +1651,7 @@ enum UTimeZoneLocalOption { * sides of a time zone transition are standard time, * or daylight saving time, the local time after the * transition is used. - * @draft ICU 69 + * @stable ICU 69 */ UCAL_TZ_LOCAL_STANDARD_LATTER = UCAL_TZ_LOCAL_LATTER | 0x01, /** @@ -1662,7 +1660,7 @@ enum UTimeZoneLocalOption { * sides of a time zone transition are standard time, * or daylight saving time, the local time before the * transition is used. - * @draft ICU 69 + * @stable ICU 69 */ UCAL_TZ_LOCAL_DAYLIGHT_FORMER = UCAL_TZ_LOCAL_FORMER | 0x03, /** @@ -1671,19 +1669,11 @@ enum UTimeZoneLocalOption { * sides of a time zone transition are standard time, * or daylight saving time, the local time after the * transition is used. - * @draft ICU 69 + * @stable ICU 69 */ UCAL_TZ_LOCAL_DAYLIGHT_LATTER = UCAL_TZ_LOCAL_LATTER | 0x03, -#else /* U_HIDE_DRAFT_API */ - /** - * Dummy value to prevent empty enum if U_HIDE_DRAFT_API. - * This will go away when draft conditionals are removed. - * @internal - */ - UCAL_TZ_LOCAL_NONE = 0, -#endif /* U_HIDE_DRAFT_API */ }; -typedef enum UTimeZoneLocalOption UTimeZoneLocalOption; /**< @draft ICU 69 */ +typedef enum UTimeZoneLocalOption UTimeZoneLocalOption; /**< @stable ICU 69 */ /** * Returns the time zone raw and GMT offset for the given moment @@ -1710,7 +1700,7 @@ typedef enum UTimeZoneLocalOption UTimeZoneLocalOption; /**< @draft ICU 69 */ * typically one hour. * If the status is set to one of the error code, the value set is unspecified. * @param status A pointer to a UErrorCode to receive any errors. -* @draft ICU 69 +* @stable ICU 69 */ U_CAPI void U_EXPORT2 ucal_getTimeZoneOffsetFromLocal( @@ -1718,7 +1708,6 @@ ucal_getTimeZoneOffsetFromLocal( UTimeZoneLocalOption nonExistingTimeOpt, UTimeZoneLocalOption duplicatedTimeOpt, int32_t* rawOffset, int32_t* dstOffset, UErrorCode* status); -#endif /* U_FORCE_HIDE_DRAFT_API */ #endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/unicode/ucol.h b/deps/icu-small/source/i18n/unicode/ucol.h index 6d22eb6069ec87..24963312216941 100644 --- a/deps/icu-small/source/i18n/unicode/ucol.h +++ b/deps/icu-small/source/i18n/unicode/ucol.h @@ -397,7 +397,7 @@ typedef enum { * @param status A pointer to a UErrorCode to receive any errors * @return A pointer to a UCollator, or 0 if an error occurred. * @see ucol_openRules - * @see ucol_safeClone + * @see ucol_clone * @see ucol_close * @stable ICU 2.0 */ @@ -425,7 +425,7 @@ ucol_open(const char *loc, UErrorCode *status); * @return A pointer to a UCollator. It is not guaranteed that NULL be returned in case * of error - please use status argument to check for errors. * @see ucol_open - * @see ucol_safeClone + * @see ucol_clone * @see ucol_close * @stable ICU 2.0 */ @@ -521,7 +521,7 @@ ucol_getContractionsAndExpansions( const UCollator *coll, * @param coll The UCollator to close. * @see ucol_open * @see ucol_openRules - * @see ucol_safeClone + * @see ucol_clone * @stable ICU 2.0 */ U_CAPI void U_EXPORT2 @@ -985,7 +985,6 @@ ucol_getShortDefinitionString(const UCollator *coll, * * @deprecated ICU 54 */ - U_DEPRECATED int32_t U_EXPORT2 ucol_normalizeShortDefinitionString(const char *source, char *destination, @@ -1310,6 +1309,20 @@ U_DEPRECATED void U_EXPORT2 ucol_restoreVariableTop(UCollator *coll, const uint32_t varTop, UErrorCode *status); #endif /* U_HIDE_DEPRECATED_API */ +/** + * Thread safe cloning operation. The result is a clone of a given collator. + * @param coll collator to be cloned + * @param status to indicate whether the operation went on smoothly or there were errors + * @return pointer to the new clone + * @see ucol_open + * @see ucol_openRules + * @see ucol_close + * @stable ICU 71 + */ +U_CAPI UCollator* U_EXPORT2 ucol_clone(const UCollator *coll, UErrorCode *status); + +#ifndef U_HIDE_DEPRECATED_API + /** * Thread safe cloning operation. The result is a clone of a given collator. * @param coll collator to be cloned @@ -1325,21 +1338,20 @@ ucol_restoreVariableTop(UCollator *coll, const uint32_t varTop, UErrorCode *stat * If *pBufferSize is not enough for a stack-based safe clone, * new memory will be allocated. * @param status to indicate whether the operation went on smoothly or there were errors - * An informational status value, U_SAFECLONE_ALLOCATED_ERROR, is used if any - * allocations were necessary. + * An informational status value, U_SAFECLONE_ALLOCATED_ERROR, is used + * if pBufferSize != NULL and any allocations were necessary * @return pointer to the new clone * @see ucol_open * @see ucol_openRules * @see ucol_close - * @stable ICU 2.0 + * @deprecated ICU 71 Use ucol_clone() instead. */ -U_CAPI UCollator* U_EXPORT2 +U_DEPRECATED UCollator* U_EXPORT2 ucol_safeClone(const UCollator *coll, void *stackBuffer, int32_t *pBufferSize, UErrorCode *status); -#ifndef U_HIDE_DEPRECATED_API /** default memory size for the new clone. * @deprecated ICU 52. Do not rely on ucol_safeClone() cloning into any provided buffer. diff --git a/deps/icu-small/source/i18n/unicode/udatpg.h b/deps/icu-small/source/i18n/unicode/udatpg.h index efe4357bfeecd2..684a905e42602e 100644 --- a/deps/icu-small/source/i18n/unicode/udatpg.h +++ b/deps/icu-small/source/i18n/unicode/udatpg.h @@ -492,6 +492,11 @@ udatpg_getFieldDisplayName(const UDateTimePatternGenerator *dtpg, * for those two skeletons, so the result is put together with this pattern, * resulting in "d-MMM h:mm". * + * There are four DateTimeFormats in a UDateTimePatternGenerator object, + * corresponding to date styles UDAT_FULL..UDAT_SHORT. This method sets + * all of them to the specified pattern. To set them individually, see + * udatpg_setDateTimeFormatForStyle. + * * @param dtpg a pointer to UDateTimePatternGenerator. * @param dtFormat * message format pattern, here {1} will be replaced by the date @@ -505,6 +510,12 @@ udatpg_setDateTimeFormat(const UDateTimePatternGenerator *dtpg, /** * Getter corresponding to setDateTimeFormat. + * + * There are four DateTimeFormats in a UDateTimePatternGenerator object, + * corresponding to date styles UDAT_FULL..UDAT_SHORT. This method gets + * the style for UDAT_MEDIUM (the default). To get them individually, see + * udatpg_getDateTimeFormatForStyle. + * * @param dtpg a pointer to UDateTimePatternGenerator. * @param pLength A pointer that will receive the length of the format * @return dateTimeFormat. @@ -514,6 +525,70 @@ U_CAPI const UChar * U_EXPORT2 udatpg_getDateTimeFormat(const UDateTimePatternGenerator *dtpg, int32_t *pLength); +#if !UCONFIG_NO_FORMATTING +#ifndef U_HIDE_DRAFT_API +/** + * dateTimeFormats are message patterns used to compose combinations of date + * and time patterns. There are four length styles, corresponding to the + * inferred style of the date pattern; these are UDateFormatStyle values: + * - UDAT_FULL (for date pattern with weekday and long month), else + * - UDAT_LONG (for a date pattern with long month), else + * - UDAT_MEDIUM (for a date pattern with abbreviated month), else + * - UDAT_SHORT (for any other date pattern). + * For details on dateTimeFormats, see + * https://www.unicode.org/reports/tr35/tr35-dates.html#dateTimeFormats. + * The default pattern in the root locale for all styles is "{1} {0}". + * + * @param udtpg + * a pointer to the UDateTimePatternGenerator + * @param style + * one of UDAT_FULL..UDAT_SHORT. Error if out of range. + * @param dateTimeFormat + * the new dateTimeFormat to set for the the specified style + * @param length + * the length of dateTimeFormat, or -1 if unknown and pattern + * is null-terminated + * @param pErrorCode + * a pointer to the UErrorCode (in/out parameter); if no failure + * status is already set, it will be set according to result of the + * function (e.g. U_ILLEGAL_ARGUMENT_ERROR for style out of range). + * @draft ICU 71 + */ +U_CAPI void U_EXPORT2 +udatpg_setDateTimeFormatForStyle(UDateTimePatternGenerator *udtpg, + UDateFormatStyle style, + const UChar *dateTimeFormat, int32_t length, + UErrorCode *pErrorCode); + +/** + * Getter corresponding to udatpg_setDateTimeFormatForStyle. + * + * @param udtpg + * a pointer to the UDateTimePatternGenerator + * @param style + * one of UDAT_FULL..UDAT_SHORT. Error if out of range. + * @param pLength + * a pointer that will receive the length of the format. May be NULL + * if length is not desired. + * @param pErrorCode + * a pointer to the UErrorCode (in/out parameter); if no failure + * status is already set, it will be set according to result of the + * function (e.g. U_ILLEGAL_ARGUMENT_ERROR for style out of range). + * @return + * pointer to the current dateTimeFormat (0 terminated) for the specified + * style, or empty string in case of error. The pointer and its contents + * may no longer be valid if udatpg_setDateTimeFormat is called, or + * udatpg_setDateTimeFormatForStyle for the same style is called, or the + * UDateTimePatternGenerator object is closed. + * @draft ICU 71 + */ +U_CAPI const UChar* U_EXPORT2 +udatpg_getDateTimeFormatForStyle(const UDateTimePatternGenerator *udtpg, + UDateFormatStyle style, int32_t *pLength, + UErrorCode *pErrorCode); +#endif /* U_HIDE_DRAFT_API */ +#endif /* #if !UCONFIG_NO_FORMATTING */ + /** * The decimal value is used in formatting fractions of seconds. If the * skeleton contains fractional seconds, then this is used with the diff --git a/deps/icu-small/source/i18n/unicode/unounclass.h b/deps/icu-small/source/i18n/unicode/unounclass.h new file mode 100644 index 00000000000000..1721dbd584fc4c --- /dev/null +++ b/deps/icu-small/source/i18n/unicode/unounclass.h @@ -0,0 +1,43 @@ +// © 2022 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#ifndef __UNOUNCLASS_H__ +#define __UNOUNCLASS_H__ + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/uversion.h" + +U_NAMESPACE_BEGIN + +#ifndef U_HIDE_DRAFT_API + +/** + * Represents all the grammatical noun classes that are supported by CLDR. + * + * @draft ICU 71. + */ +enum NounClass { + OTHER = 0, + NEUTER = 1, + FEMININE = 2, + MASCULINE = 3, + ANIMATE = 4, + INANIMATE = 5, + PERSONAL = 6, + COMMON = 7, +}; + +#endif // U_HIDE_DRAFT_API + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif // __UNOUNCLASS_H__ diff --git a/deps/icu-small/source/i18n/unicode/unum.h b/deps/icu-small/source/i18n/unicode/unum.h index 14f76168b61b5f..863695591ab2a5 100644 --- a/deps/icu-small/source/i18n/unicode/unum.h +++ b/deps/icu-small/source/i18n/unicode/unum.h @@ -303,23 +303,21 @@ typedef enum UNumberFormatRoundingMode { * @stable ICU 4.8 */ UNUM_ROUND_UNNECESSARY, -#ifndef U_HIDE_DRAFT_API /** * Rounds ties toward the odd number. - * @draft ICU 69 + * @stable ICU 69 */ UNUM_ROUND_HALF_ODD, /** * Rounds ties toward +∞. - * @draft ICU 69 + * @stable ICU 69 */ UNUM_ROUND_HALF_CEILING, /** * Rounds ties toward -∞. - * @draft ICU 69 + * @stable ICU 69 */ UNUM_ROUND_HALF_FLOOR, -#endif // U_HIDE_DRAFT_API } UNumberFormatRoundingMode; /** The possible number format pad positions. @@ -401,13 +399,24 @@ typedef enum UNumberFormatFields { UNUM_MEASURE_UNIT_FIELD, /** @stable ICU 64 */ UNUM_COMPACT_FIELD, +#ifndef U_HIDE_DRAFT_API + /** + * Approximately sign. In ICU 70, this was categorized under the generic SIGN field. + * @draft ICU 71 + */ + UNUM_APPROXIMATELY_SIGN_FIELD, +#endif // U_HIDE_DRAFT_API #ifndef U_HIDE_DEPRECATED_API /** * One more than the highest normal UNumberFormatFields value. * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. */ - UNUM_FIELD_COUNT = UNUM_SIGN_FIELD + 3 +#ifndef U_HIDE_DRAFT_API + UNUM_FIELD_COUNT = UNUM_COMPACT_FIELD + 2 +#else // U_HIDE_DRAFT_API (for UNUM_APPROXIMATELY_SIGN_FIELD) + UNUM_FIELD_COUNT = UNUM_COMPACT_FIELD + 1 +#endif // U_HIDE_DRAFT_API (for UNUM_APPROXIMATELY_SIGN_FIELD) #endif /* U_HIDE_DEPRECATED_API */ } UNumberFormatFields; diff --git a/deps/icu-small/source/i18n/unicode/unumberformatter.h b/deps/icu-small/source/i18n/unicode/unumberformatter.h index cb980cd94ddfc8..58a75baf073c43 100644 --- a/deps/icu-small/source/i18n/unicode/unumberformatter.h +++ b/deps/icu-small/source/i18n/unicode/unumberformatter.h @@ -78,7 +78,6 @@ * */ -#ifndef U_FORCE_HIDE_DRAFT_API /** * An enum declaring how to resolve conflicts between maximum fraction digits and maximum * significant digits. @@ -115,24 +114,23 @@ * Here, RELAXED favors Max-Fraction and STRICT favors Max-Significant. Note that this larger * number caused the two modes to favor the opposite result. * - * @draft ICU 69 + * @stable ICU 69 */ typedef enum UNumberRoundingPriority { /** * Favor greater precision by relaxing one of the rounding constraints. * - * @draft ICU 69 + * @stable ICU 69 */ UNUM_ROUNDING_PRIORITY_RELAXED, /** * Favor adherence to all rounding constraints by producing lower precision. * - * @draft ICU 69 + * @stable ICU 69 */ UNUM_ROUNDING_PRIORITY_STRICT, } UNumberRoundingPriority; -#endif // U_FORCE_HIDE_DRAFT_API /** * An enum declaring how to render units, including currencies. Example outputs when formatting 123 USD and 123 @@ -435,21 +433,19 @@ typedef enum UNumberSignDisplay { */ UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO, -#ifndef U_HIDE_DRAFT_API /** * Same as AUTO, but do not show the sign on negative zero. * - * @draft ICU 69 + * @stable ICU 69 */ UNUM_SIGN_NEGATIVE, /** * Same as ACCOUNTING, but do not show the sign on negative zero. * - * @draft ICU 69 + * @stable ICU 69 */ UNUM_SIGN_ACCOUNTING_NEGATIVE, -#endif // U_HIDE_DRAFT_API // Do not conditionalize the following with #ifndef U_HIDE_INTERNAL_API, // needed for unconditionalized struct MacroProps @@ -498,31 +494,29 @@ typedef enum UNumberDecimalSeparatorDisplay { UNUM_DECIMAL_SEPARATOR_COUNT } UNumberDecimalSeparatorDisplay; -#ifndef U_FORCE_HIDE_DRAFT_API /** * An enum declaring how to render trailing zeros. * * - UNUM_TRAILING_ZERO_AUTO: 0.90, 1.00, 1.10 * - UNUM_TRAILING_ZERO_HIDE_IF_WHOLE: 0.90, 1, 1.10 * - * @draft ICU 69 + * @stable ICU 69 */ typedef enum UNumberTrailingZeroDisplay { /** * Display trailing zeros according to the settings for minimum fraction and significant digits. * - * @draft ICU 69 + * @stable ICU 69 */ UNUM_TRAILING_ZERO_AUTO, /** * Same as AUTO, but hide trailing zeros after the decimal separator if they are all zero. * - * @draft ICU 69 + * @stable ICU 69 */ UNUM_TRAILING_ZERO_HIDE_IF_WHOLE, } UNumberTrailingZeroDisplay; -#endif // U_FORCE_HIDE_DRAFT_API struct UNumberFormatter; /** diff --git a/deps/icu-small/source/i18n/unicode/vtzone.h b/deps/icu-small/source/i18n/unicode/vtzone.h index e7d2f515410ee1..ecf335bbe3428c 100644 --- a/deps/icu-small/source/i18n/unicode/vtzone.h +++ b/deps/icu-small/source/i18n/unicode/vtzone.h @@ -264,16 +264,14 @@ class U_I18N_API VTimeZone : public BasicTimeZone { virtual void getOffset(UDate date, UBool local, int32_t& rawOffset, int32_t& dstOffset, UErrorCode& ec) const override; -#ifndef U_FORCE_HIDE_DRAFT_API /** * Get time zone offsets from local wall time. - * @draft ICU 69 + * @stable ICU 69 */ virtual void getOffsetFromLocal( UDate date, UTimeZoneLocalOption nonExistingTimeOpt, UTimeZoneLocalOption duplicatedTimeOpt, int32_t& rawOffset, int32_t& dstOffset, UErrorCode& status) const override; -#endif /* U_FORCE_HIDE_DRAFT_API */ /** * Sets the TimeZone's raw GMT offset (i.e., the number of milliseconds to add diff --git a/deps/icu-small/source/i18n/units_complexconverter.cpp b/deps/icu-small/source/i18n/units_complexconverter.cpp index 78cefbf7ebb733..ecbe3c787941a7 100644 --- a/deps/icu-small/source/i18n/units_complexconverter.cpp +++ b/deps/icu-small/source/i18n/units_complexconverter.cpp @@ -183,7 +183,7 @@ MaybeStackVector ComplexUnitsConverter::convert(double quantity, } else { quantity = remainder; } - } + } } applyRounder(intValues, quantity, rounder, status); @@ -210,7 +210,6 @@ MaybeStackVector ComplexUnitsConverter::convert(double quantity, } } - // Transfer values into result and return: for(int32_t i = 0, n = unitsConverters_.length(); i < n; ++i) { U_ASSERT(tmpResult[i] != nullptr); @@ -224,6 +223,12 @@ MaybeStackVector ComplexUnitsConverter::convert(double quantity, void ComplexUnitsConverter::applyRounder(MaybeStackArray &intValues, double &quantity, icu::number::impl::RoundingImpl *rounder, UErrorCode &status) const { + if (uprv_isInfinite(quantity) || uprv_isNaN(quantity)) { + // Inf and NaN can't be rounded, and calculating `carry` below is known + // to fail on Gentoo on HPPA and OpenSUSE on riscv64. Nothing to do. + return; + } + if (rounder == nullptr) { // Nothing to do for the quantity. return; diff --git a/deps/icu-small/source/i18n/units_complexconverter.h b/deps/icu-small/source/i18n/units_complexconverter.h index 5c669b45ddd7df..d56ce8d4ce378f 100644 --- a/deps/icu-small/source/i18n/units_complexconverter.h +++ b/deps/icu-small/source/i18n/units_complexconverter.h @@ -108,13 +108,15 @@ class U_I18N_API ComplexUnitsConverter : public UMemory { MaybeStackVector convert(double quantity, icu::number::impl::RoundingImpl *rounder, UErrorCode &status) const; - private: + // TODO(ICU-21937): Make it private after submitting the public units conversion API. MaybeStackVector unitsConverters_; + // TODO(ICU-21937): Make it private after submitting the public units conversion API. // Individual units of mixed units, sorted big to small, with indices // indicating the requested output mixed unit order. MaybeStackVector units_; + private: // Sorts units_, which must be populated before calling this, and populates // unitsConverters_. void init(const MeasureUnitImpl &inputUnit, const ConversionRates &ratesInfo, UErrorCode &status); diff --git a/deps/icu-small/source/i18n/units_converter.cpp b/deps/icu-small/source/i18n/units_converter.cpp index 7e946e584bb76a..82b8eea3d8cf6c 100644 --- a/deps/icu-small/source/i18n/units_converter.cpp +++ b/deps/icu-small/source/i18n/units_converter.cpp @@ -9,6 +9,7 @@ #include "cmemory.h" #include "double-conversion-string-to-double.h" #include "measunit_impl.h" +#include "putilimp.h" #include "uassert.h" #include "unicode/errorcode.h" #include "unicode/localpointer.h" @@ -588,10 +589,7 @@ double UnitsConverter::convert(double inputValue) const { if (conversionRate_.reciprocal) { if (result == 0) { - // TODO: demonstrate the resulting behaviour in tests... and figure - // out desired behaviour. (Theoretical result should be infinity, - // not 0.) - return 0.0; + return uprv_getInfinity(); } result = 1.0 / result; } @@ -603,10 +601,7 @@ double UnitsConverter::convertInverse(double inputValue) const { double result = inputValue; if (conversionRate_.reciprocal) { if (result == 0) { - // TODO: demonstrate the resulting behaviour in tests... and figure - // out desired behaviour. (Theoretical result should be infinity, - // not 0.) - return 0.0; + return uprv_getInfinity(); } result = 1.0 / result; } diff --git a/deps/icu-small/source/i18n/units_router.h b/deps/icu-small/source/i18n/units_router.h index b3300f7e27737a..d9fcffb2aa9e26 100644 --- a/deps/icu-small/source/i18n/units_router.h +++ b/deps/icu-small/source/i18n/units_router.h @@ -30,8 +30,6 @@ namespace units { struct RouteResult : UMemory { // A list of measures: a single measure for single units, multiple measures // for mixed units. - // - // TODO(icu-units/icu#21): figure out the right mixed unit API. MaybeStackVector measures; // The output unit for this RouteResult. This may be a MIXED unit - for diff --git a/deps/icu-small/source/i18n/uspoof_conf.cpp b/deps/icu-small/source/i18n/uspoof_conf.cpp index 04081cabfb0738..172c0711afbad3 100644 --- a/deps/icu-small/source/i18n/uspoof_conf.cpp +++ b/deps/icu-small/source/i18n/uspoof_conf.cpp @@ -63,23 +63,24 @@ U_NAMESPACE_USE // at the same time // -SPUString::SPUString(UnicodeString *s) { - fStr = s; +SPUString::SPUString(LocalPointer s) { + fStr = std::move(s); fCharOrStrTableIndex = 0; } SPUString::~SPUString() { - delete fStr; } -SPUStringPool::SPUStringPool(UErrorCode &status) : fVec(NULL), fHash(NULL) { - fVec = new UVector(status); - if (fVec == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; +SPUStringPool::SPUStringPool(UErrorCode &status) : fVec(nullptr), fHash(nullptr) { + LocalPointer vec(new UVector(status), status); + if (U_FAILURE(status)) { return; } + vec->setDeleter( + [](void *obj) {delete (SPUString *)obj;}); + fVec = vec.orphan(); fHash = uhash_open(uhash_hashUnicodeString, // key hash function uhash_compareUnicodeString, // Key Comparator NULL, // Value Comparator @@ -88,11 +89,6 @@ SPUStringPool::SPUStringPool(UErrorCode &status) : fVec(NULL), fHash(NULL) { SPUStringPool::~SPUStringPool() { - int i; - for (i=fVec->size()-1; i>=0; i--) { - SPUString *s = static_cast(fVec->elementAt(i)); - delete s; - } delete fVec; uhash_close(fHash); } @@ -135,18 +131,21 @@ void SPUStringPool::sort(UErrorCode &status) { SPUString *SPUStringPool::addString(UnicodeString *src, UErrorCode &status) { + LocalPointer lpSrc(src); + if (U_FAILURE(status)) { + return nullptr; + } SPUString *hashedString = static_cast(uhash_get(fHash, src)); - if (hashedString != NULL) { - delete src; - } else { - hashedString = new SPUString(src); - if (hashedString == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - uhash_put(fHash, src, hashedString, &status); - fVec->addElementX(hashedString, status); + if (hashedString != nullptr) { + return hashedString; + } + LocalPointer spuStr(new SPUString(std::move(lpSrc)), status); + hashedString = spuStr.getAlias(); + fVec->adoptElement(spuStr.orphan(), status); + if (U_FAILURE(status)) { + return nullptr; } + uhash_put(fHash, src, hashedString, &status); return hashedString; } diff --git a/deps/icu-small/source/i18n/uspoof_conf.h b/deps/icu-small/source/i18n/uspoof_conf.h index 600d7ea42a430d..1eeecdfd5e40d4 100644 --- a/deps/icu-small/source/i18n/uspoof_conf.h +++ b/deps/icu-small/source/i18n/uspoof_conf.h @@ -39,11 +39,12 @@ U_NAMESPACE_BEGIN // Instances of SPUString exist during the compilation process only. struct SPUString : public UMemory { - UnicodeString *fStr; // The actual string. - int32_t fCharOrStrTableIndex; // Index into the final runtime data for this - // string (or, for length 1, the single string char - // itself, there being no string table entry for it.) - SPUString(UnicodeString *s); + LocalPointer fStr; // The actual string. + int32_t fCharOrStrTableIndex; // Index into the final runtime data for this + // string (or, for length 1, the single string char + // itself, there being no string table entry for it.) + + SPUString(LocalPointer s); ~SPUString(); }; diff --git a/deps/icu-small/source/i18n/uspoof_impl.cpp b/deps/icu-small/source/i18n/uspoof_impl.cpp index b283d813210d9d..f96826f86ccc12 100644 --- a/deps/icu-small/source/i18n/uspoof_impl.cpp +++ b/deps/icu-small/source/i18n/uspoof_impl.cpp @@ -945,7 +945,7 @@ uspoof_swap(const UDataSwapper *ds, const void *inData, int32_t length, void *ou uint32_t magic = ds->readUInt32(spoofDH->fMagic); ds->writeUInt32((uint32_t *)&outputDH->fMagic, magic); - if (outputDH->fFormatVersion != spoofDH->fFormatVersion) { + if (inBytes != outBytes) { uprv_memcpy(outputDH->fFormatVersion, spoofDH->fFormatVersion, sizeof(spoofDH->fFormatVersion)); } // swap starting at fLength diff --git a/deps/icu-small/source/i18n/vtzone.cpp b/deps/icu-small/source/i18n/vtzone.cpp index 9111e08848f99a..06f0b84c0f5d06 100644 --- a/deps/icu-small/source/i18n/vtzone.cpp +++ b/deps/icu-small/source/i18n/vtzone.cpp @@ -24,14 +24,6 @@ U_NAMESPACE_BEGIN -// This is the deleter that will be use to remove TimeZoneRule -U_CDECL_BEGIN -static void U_CALLCONV -deleteTimeZoneRule(void* obj) { - delete (TimeZoneRule*) obj; -} -U_CDECL_END - // Smybol characters used by RFC2445 VTIMEZONE static const UChar COLON = 0x3A; /* : */ static const UChar SEMICOLON = 0x3B; /* ; */ @@ -976,22 +968,19 @@ VTimeZone::VTimeZone(const VTimeZone& source) if (source.vtzlines != nullptr) { UErrorCode status = U_ZERO_ERROR; int32_t size = source.vtzlines->size(); - vtzlines = new UVector(uprv_deleteUObject, uhash_compareUnicodeString, size, status); - if (vtzlines == nullptr) { + LocalPointer lpVtzLines( + new UVector(uprv_deleteUObject, uhash_compareUnicodeString, size, status), status); + if (U_FAILURE(status)) { return; } - if (U_SUCCESS(status)) { - for (int32_t i = 0; i < size; i++) { - UnicodeString *line = (UnicodeString*)source.vtzlines->elementAt(i); - vtzlines->addElementX(line->clone(), status); - if (U_FAILURE(status)) { - break; - } + for (int32_t i = 0; i < size; i++) { + UnicodeString *line = ((UnicodeString*)source.vtzlines->elementAt(i))->clone(); + lpVtzLines->adoptElement(line, status); + if (U_FAILURE(status) || line == nullptr) { + return; } } - if (U_FAILURE(status) && vtzlines != nullptr) { - delete vtzlines; - } + vtzlines = lpVtzLines.orphan(); } } @@ -1020,23 +1009,25 @@ VTimeZone::operator=(const VTimeZone& right) { } if (vtzlines != nullptr) { delete vtzlines; + vtzlines = nullptr; } if (right.vtzlines != nullptr) { UErrorCode status = U_ZERO_ERROR; int32_t size = right.vtzlines->size(); - vtzlines = new UVector(uprv_deleteUObject, uhash_compareUnicodeString, size, status); - if (vtzlines != nullptr && U_SUCCESS(status)) { + LocalPointer lpVtzLines( + new UVector(uprv_deleteUObject, uhash_compareUnicodeString, size, status), status); + if (U_SUCCESS(status)) { for (int32_t i = 0; i < size; i++) { - UnicodeString *line = (UnicodeString*)right.vtzlines->elementAt(i); - vtzlines->addElementX(line->clone(), status); + LocalPointer line( + ((UnicodeString*)right.vtzlines->elementAt(i))->clone(), status); + lpVtzLines->adoptElement(line.orphan(), status); if (U_FAILURE(status)) { break; } } - } - if (U_FAILURE(status) && vtzlines != nullptr) { - delete vtzlines; - vtzlines = nullptr; + if (U_SUCCESS(status)) { + vtzlines = lpVtzLines.orphan(); + } } } tzurl = right.tzurl; @@ -1272,10 +1263,9 @@ VTimeZone::getTimeZoneRules(const InitialTimeZoneRule*& initial, void VTimeZone::load(VTZReader& reader, UErrorCode& status) { - vtzlines = new UVector(uprv_deleteUObject, uhash_compareUnicodeString, DEFAULT_VTIMEZONE_LINES, status); - if (vtzlines == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - } + U_ASSERT(vtzlines == nullptr); + LocalPointer lpVtzLines( + new UVector(uprv_deleteUObject, uhash_compareUnicodeString, DEFAULT_VTIMEZONE_LINES, status), status); if (U_FAILURE(status)) { return; } @@ -1290,14 +1280,10 @@ VTimeZone::load(VTZReader& reader, UErrorCode& status) { // end of file if (start && line.startsWith(ICAL_END_VTIMEZONE, -1)) { LocalPointer element(new UnicodeString(line), status); + lpVtzLines->adoptElement(element.orphan(), status); if (U_FAILURE(status)) { - goto cleanupVtzlines; - } - vtzlines->addElementX(element.getAlias(), status); - if (U_FAILURE(status)) { - goto cleanupVtzlines; + return; } - element.orphan(); // on success, vtzlines owns the object. success = TRUE; } break; @@ -1312,14 +1298,10 @@ VTimeZone::load(VTZReader& reader, UErrorCode& status) { if (start) { if (line.length() > 0) { LocalPointer element(new UnicodeString(line), status); + lpVtzLines->adoptElement(element.orphan(), status); if (U_FAILURE(status)) { - goto cleanupVtzlines; + return; } - vtzlines->addElementX(element.getAlias(), status); - if (U_FAILURE(status)) { - goto cleanupVtzlines; - } - element.orphan(); // on success, vtzlines owns the object. } } line.remove(); @@ -1335,28 +1317,20 @@ VTimeZone::load(VTZReader& reader, UErrorCode& status) { if (start) { if (line.startsWith(ICAL_END_VTIMEZONE, -1)) { LocalPointer element(new UnicodeString(line), status); + lpVtzLines->adoptElement(element.orphan(), status); if (U_FAILURE(status)) { - goto cleanupVtzlines; - } - vtzlines->addElementX(element.getAlias(), status); - if (U_FAILURE(status)) { - goto cleanupVtzlines; + return; } - element.orphan(); // on success, vtzlines owns the object. success = TRUE; break; } } else { if (line.startsWith(ICAL_BEGIN_VTIMEZONE, -1)) { LocalPointer element(new UnicodeString(line), status); + lpVtzLines->adoptElement(element.orphan(), status); if (U_FAILURE(status)) { - goto cleanupVtzlines; - } - vtzlines->addElementX(element.getAlias(), status); - if (U_FAILURE(status)) { - goto cleanupVtzlines; + return; } - element.orphan(); // on success, vtzlines owns the object. line.remove(); start = TRUE; eol = FALSE; @@ -1371,14 +1345,10 @@ VTimeZone::load(VTZReader& reader, UErrorCode& status) { if (U_SUCCESS(status)) { status = U_INVALID_STATE_ERROR; } - goto cleanupVtzlines; + return; } + vtzlines = lpVtzLines.orphan(); parse(status); - return; - -cleanupVtzlines: - delete vtzlines; - vtzlines = nullptr; } // parser state @@ -1398,8 +1368,6 @@ VTimeZone::parse(UErrorCode& status) { status = U_INVALID_STATE_ERROR; return; } - InitialTimeZoneRule *initialRule = nullptr; - RuleBasedTimeZone *rbtz = nullptr; // timezone ID UnicodeString tzid; @@ -1418,28 +1386,16 @@ VTimeZone::parse(UErrorCode& status) { UnicodeString name; // RFC2445 prop name UnicodeString value; // RFC2445 prop value - UVector *dates = nullptr; // list of RDATE or RRULE strings - UVector *rules = nullptr; // list of TimeZoneRule instances - int32_t finalRuleIdx = -1; int32_t finalRuleCount = 0; - rules = new UVector(status); - if (rules == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - } - if (U_FAILURE(status)) { - goto cleanupParse; - } - // Set the deleter to remove TimeZoneRule vectors to avoid memory leaks due to unowned TimeZoneRules. - rules->setDeleter(deleteTimeZoneRule); + // Set the deleter on rules to remove TimeZoneRule vectors to avoid memory leaks due to unowned TimeZoneRules. + UVector rules(uprv_deleteUObject, nullptr, status); - dates = new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status); - if (dates == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - } + // list of RDATE or RRULE strings + UVector dates(uprv_deleteUObject, uhash_compareUnicodeString, status); if (U_FAILURE(status)) { - goto cleanupParse; + return; } for (n = 0; n < vtzlines->size(); n++) { @@ -1469,18 +1425,18 @@ VTimeZone::parse(UErrorCode& status) { // can be any value. lastmod = parseDateTimeString(value, 0, status); if (U_FAILURE(status)) { - goto cleanupParse; + return; } } else if (name.compare(ICAL_BEGIN, -1) == 0) { UBool isDST = (value.compare(ICAL_DAYLIGHT, -1) == 0); if (value.compare(ICAL_STANDARD, -1) == 0 || isDST) { // tzid must be ready at this point if (tzid.length() == 0) { - goto cleanupParse; + return; } // initialize current zone properties - if (dates->size() != 0) { - dates->removeAllElements(); + if (dates.size() != 0) { + dates.removeAllElements(); } isRRULE = FALSE; from.remove(); @@ -1491,7 +1447,7 @@ VTimeZone::parse(UErrorCode& status) { } else { // BEGIN property other than STANDARD/DAYLIGHT // must not be there. - goto cleanupParse; + return; } } else if (name.compare(ICAL_END, -1) == 0) { break; @@ -1509,50 +1465,42 @@ VTimeZone::parse(UErrorCode& status) { } else if (name.compare(ICAL_RDATE, -1) == 0) { // RDATE mixed with RRULE is not supported if (isRRULE) { - goto cleanupParse; + return; } // RDATE value may contain multiple date delimited // by comma UBool nextDate = TRUE; int32_t dstart = 0; - UnicodeString *dstr = nullptr; + LocalPointer dstr; while (nextDate) { int32_t dend = value.indexOf(COMMA, dstart); if (dend == -1) { - dstr = new UnicodeString(value, dstart); + dstr.adoptInsteadAndCheckErrorCode(new UnicodeString(value, dstart), status); nextDate = FALSE; } else { - dstr = new UnicodeString(value, dstart, dend - dstart); - } - if (dstr == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - } else { - dates->addElementX(dstr, status); + dstr.adoptInsteadAndCheckErrorCode(new UnicodeString(value, dstart, dend - dstart), status); } + dates.adoptElement(dstr.orphan(), status); if (U_FAILURE(status)) { - goto cleanupParse; + return; } dstart = dend + 1; } } else if (name.compare(ICAL_RRULE, -1) == 0) { // RRULE mixed with RDATE is not supported - if (!isRRULE && dates->size() != 0) { - goto cleanupParse; + if (!isRRULE && dates.size() != 0) { + return; } isRRULE = true; LocalPointer element(new UnicodeString(value), status); + dates.adoptElement(element.orphan(), status); if (U_FAILURE(status)) { - goto cleanupParse; - } - dates->addElementX(element.getAlias(), status); - if (U_FAILURE(status)) { - goto cleanupParse; + return; } - element.orphan(); // on success, dates owns the object. } else if (name.compare(ICAL_END, -1) == 0) { // Mandatory properties if (dtstart.length() == 0 || from.length() == 0 || to.length() == 0) { - goto cleanupParse; + return; } // if zonename is not available, create one from tzid if (zonename.length() == 0) { @@ -1560,7 +1508,7 @@ VTimeZone::parse(UErrorCode& status) { } // create a time zone rule - TimeZoneRule *rule = nullptr; + LocalPointer rule; int32_t fromOffset = 0; int32_t toOffset = 0; int32_t rawOffset = 0; @@ -1571,7 +1519,7 @@ VTimeZone::parse(UErrorCode& status) { fromOffset = offsetStrToMillis(from, status); toOffset = offsetStrToMillis(to, status); if (U_FAILURE(status)) { - goto cleanupParse; + return; } if (dst) { @@ -1592,18 +1540,20 @@ VTimeZone::parse(UErrorCode& status) { // start time start = parseDateTimeString(dtstart, fromOffset, status); if (U_FAILURE(status)) { - goto cleanupParse; + return; } // Create the rule UDate actualStart = MAX_MILLIS; if (isRRULE) { - rule = createRuleByRRULE(zonename, rawOffset, dstSavings, start, dates, fromOffset, status); + rule.adoptInsteadAndCheckErrorCode( + createRuleByRRULE(zonename, rawOffset, dstSavings, start, &dates, fromOffset, status), status); } else { - rule = createRuleByRDATE(zonename, rawOffset, dstSavings, start, dates, fromOffset, status); + rule.adoptInsteadAndCheckErrorCode( + createRuleByRDATE(zonename, rawOffset, dstSavings, start, &dates, fromOffset, status), status); } - if (U_FAILURE(status) || rule == nullptr) { - goto cleanupParse; + if (U_FAILURE(status)) { + return; } else { UBool startAvail = rule->getFirstStart(fromOffset, 0, actualStart); if (startAvail && actualStart < firstStart) { @@ -1626,9 +1576,9 @@ VTimeZone::parse(UErrorCode& status) { } } } - rules->addElementX(rule, status); + rules.adoptElement(rule.orphan(), status); if (U_FAILURE(status)) { - goto cleanupParse; + return; } state = VTZ; } @@ -1636,28 +1586,31 @@ VTimeZone::parse(UErrorCode& status) { } } // Must have at least one rule - if (rules->size() == 0) { - goto cleanupParse; + if (rules.size() == 0) { + return; } // Create a initial rule getDefaultTZName(tzid, FALSE, zonename); - initialRule = new InitialTimeZoneRule(zonename, initialRawOffset, initialDSTSavings); - if (initialRule == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - goto cleanupParse; + LocalPointer initialRule( + new InitialTimeZoneRule(zonename, initialRawOffset, initialDSTSavings), status); + if (U_FAILURE(status)) { + return; } // Finally, create the RuleBasedTimeZone - rbtz = new RuleBasedTimeZone(tzid, initialRule); - if (rbtz == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - goto cleanupParse; + // C++ awkwardness on memory allocation failure: the constructor wont be run, meaning + // that initialRule wont be adopted/deleted, as it normally would be. + LocalPointer rbtz( + new RuleBasedTimeZone(tzid, initialRule.getAlias()), status); + if (U_SUCCESS(status)) { + initialRule.orphan(); + } else { + return; } - initialRule = nullptr; // already adopted by RBTZ, no need to delete - for (n = 0; n < rules->size(); n++) { - TimeZoneRule *r = (TimeZoneRule*)rules->elementAt(n); + for (n = 0; n < rules.size(); n++) { + TimeZoneRule *r = (TimeZoneRule*)rules.elementAt(n); AnnualTimeZoneRule *atzrule = dynamic_cast(r); if (atzrule != nullptr) { if (atzrule->getEndYear() == AnnualTimeZoneRule::MAX_YEAR) { @@ -1669,18 +1622,18 @@ VTimeZone::parse(UErrorCode& status) { if (finalRuleCount > 2) { // Too many final rules status = U_ILLEGAL_ARGUMENT_ERROR; - goto cleanupParse; + return; } if (finalRuleCount == 1) { - if (rules->size() == 1) { + if (rules.size() == 1) { // Only one final rule, only governs the initial rule, // which is already initialized, thus, we do not need to // add this transition rule - rules->removeAllElements(); + rules.removeAllElements(); } else { // Normalize the final rule - AnnualTimeZoneRule *finalRule = (AnnualTimeZoneRule*)rules->elementAt(finalRuleIdx); + AnnualTimeZoneRule *finalRule = (AnnualTimeZoneRule*)rules.elementAt(finalRuleIdx); int32_t tmpRaw = finalRule->getRawOffset(); int32_t tmpDST = finalRule->getDSTSavings(); @@ -1688,11 +1641,11 @@ VTimeZone::parse(UErrorCode& status) { UDate finalStart, start; finalRule->getFirstStart(initialRawOffset, initialDSTSavings, finalStart); start = finalStart; - for (n = 0; n < rules->size(); n++) { + for (n = 0; n < rules.size(); n++) { if (finalRuleIdx == n) { continue; } - TimeZoneRule *r = (TimeZoneRule*)rules->elementAt(n); + TimeZoneRule *r = (TimeZoneRule*)rules.elementAt(n); UDate lastStart; r->getFinalStart(tmpRaw, tmpDST, lastStart); if (lastStart > start) { @@ -1704,78 +1657,58 @@ VTimeZone::parse(UErrorCode& status) { } } - TimeZoneRule *newRule = nullptr; + LocalPointer newRule; UnicodeString tznam; if (start == finalStart) { // Transform this into a single transition - newRule = new TimeArrayTimeZoneRule( - finalRule->getName(tznam), - finalRule->getRawOffset(), - finalRule->getDSTSavings(), - &finalStart, - 1, - DateTimeRule::UTC_TIME); + newRule.adoptInsteadAndCheckErrorCode( + new TimeArrayTimeZoneRule( + finalRule->getName(tznam), + finalRule->getRawOffset(), + finalRule->getDSTSavings(), + &finalStart, + 1, + DateTimeRule::UTC_TIME), + status); } else { // Update the end year int32_t y, m, d, dow, doy, mid; Grego::timeToFields(start, y, m, d, dow, doy, mid); - newRule = new AnnualTimeZoneRule( - finalRule->getName(tznam), - finalRule->getRawOffset(), - finalRule->getDSTSavings(), - *(finalRule->getRule()), - finalRule->getStartYear(), - y); + newRule.adoptInsteadAndCheckErrorCode( + new AnnualTimeZoneRule( + finalRule->getName(tznam), + finalRule->getRawOffset(), + finalRule->getDSTSavings(), + *(finalRule->getRule()), + finalRule->getStartYear(), + y), + status); } - if (newRule == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - goto cleanupParse; + if (U_FAILURE(status)) { + return; } - rules->removeElementAt(finalRuleIdx); - rules->addElementX(newRule, status); + rules.removeElementAt(finalRuleIdx); + rules.adoptElement(newRule.orphan(), status); if (U_FAILURE(status)) { - delete newRule; - goto cleanupParse; + return; } } } - while (!rules->isEmpty()) { - TimeZoneRule *tzr = (TimeZoneRule*)rules->orphanElementAt(0); + while (!rules.isEmpty()) { + TimeZoneRule *tzr = (TimeZoneRule*)rules.orphanElementAt(0); rbtz->addTransitionRule(tzr, status); if (U_FAILURE(status)) { - goto cleanupParse; + return; } } rbtz->complete(status); if (U_FAILURE(status)) { - goto cleanupParse; + return; } - delete rules; - delete dates; - tz = rbtz; + tz = rbtz.orphan(); setID(tzid); - return; - -cleanupParse: - if (rules != nullptr) { - while (!rules->isEmpty()) { - TimeZoneRule *r = (TimeZoneRule*)rules->orphanElementAt(0); - delete r; - } - delete rules; - } - if (dates != nullptr) { - delete dates; - } - if (initialRule != nullptr) { - delete initialRule; - } - if (rbtz != nullptr) { - delete rbtz; - } - return; } void @@ -1809,7 +1742,7 @@ VTimeZone::write(VTZWriter& writer, UErrorCode& status) const { icutzprop.append(u'['); icutzprop.append(icutzver); icutzprop.append(u']'); - customProps.addElementX(&icutzprop, status); + customProps.addElement(&icutzprop, status); } writeZone(writer, *tz, &customProps, status); } @@ -1827,34 +1760,35 @@ VTimeZone::write(UDate start, VTZWriter& writer, UErrorCode& status) const { // Extract rules applicable to dates after the start time getTimeZoneRulesAfter(start, initial, transitionRules, status); + LocalPointer lpInitial(initial); + LocalPointer lpTransitionRules(transitionRules); if (U_FAILURE(status)) { return; } // Create a RuleBasedTimeZone with the subset rule getID(tzid); - RuleBasedTimeZone rbtz(tzid, initial); - if (transitionRules != nullptr) { - while (!transitionRules->isEmpty()) { - TimeZoneRule *tr = (TimeZoneRule*)transitionRules->orphanElementAt(0); + RuleBasedTimeZone rbtz(tzid, lpInitial.orphan()); + if (lpTransitionRules.isValid()) { + U_ASSERT(transitionRules->hasDeleter()); // Assumed for U_FAILURE early return, below. + while (!lpTransitionRules->isEmpty()) { + TimeZoneRule *tr = (TimeZoneRule*)lpTransitionRules->orphanElementAt(0); rbtz.addTransitionRule(tr, status); if (U_FAILURE(status)) { - goto cleanupWritePartial; + return; } } - delete transitionRules; - transitionRules = nullptr; } rbtz.complete(status); if (U_FAILURE(status)) { - goto cleanupWritePartial; + return; } if (olsonzid.length() > 0 && icutzver.length() > 0) { UnicodeString *icutzprop = new UnicodeString(ICU_TZINFO_PROP); if (icutzprop == nullptr) { status = U_MEMORY_ALLOCATION_ERROR; - goto cleanupWritePartial; + return; } icutzprop->append(olsonzid); icutzprop->append((UChar)0x005B/*'['*/); @@ -1862,23 +1796,12 @@ VTimeZone::write(UDate start, VTZWriter& writer, UErrorCode& status) const { icutzprop->append(ICU_TZINFO_PARTIAL, -1); appendMillis(start, *icutzprop); icutzprop->append((UChar)0x005D/*']'*/); - customProps.addElementX(icutzprop, status); + customProps.adoptElement(icutzprop, status); if (U_FAILURE(status)) { - delete icutzprop; - goto cleanupWritePartial; + return; } } writeZone(writer, rbtz, &customProps, status); - return; - -cleanupWritePartial: - if (initial != nullptr) { - delete initial; - } - if (transitionRules != nullptr) { - U_ASSERT(transitionRules->hasDeleter()); - delete transitionRules; - } } void diff --git a/deps/icu-small/source/i18n/zonemeta.cpp b/deps/icu-small/source/i18n/zonemeta.cpp index b8afa4760f1823..e60215c9988e6d 100644 --- a/deps/icu-small/source/i18n/zonemeta.cpp +++ b/deps/icu-small/source/i18n/zonemeta.cpp @@ -97,21 +97,13 @@ deleteUCharString(void *obj) { uprv_free(entry); } -/** - * Deleter for UVector - */ -static void U_CALLCONV -deleteUVector(void *obj) { - delete (icu::UVector*) obj; -} - /** * Deleter for OlsonToMetaMappingEntry */ static void U_CALLCONV deleteOlsonToMetaMappingEntry(void *obj) { icu::OlsonToMetaMappingEntry *entry = (icu::OlsonToMetaMappingEntry*)obj; - uprv_free(entry); + delete entry; } U_CDECL_END @@ -477,11 +469,11 @@ ZoneMeta::getCanonicalCountry(const UnicodeString &tzid, UnicodeString &country, UErrorCode ec = U_ZERO_ERROR; if (singleZone) { if (!gSingleZoneCountries->contains((void*)region)) { - gSingleZoneCountries->addElementX((void*)region, ec); + gSingleZoneCountries->addElement((void*)region, ec); } } else { if (!gMultiZonesCountries->contains((void*)region)) { - gMultiZonesCountries->addElementX((void*)region, ec); + gMultiZonesCountries->addElement((void*)region, ec); } } } @@ -550,7 +542,7 @@ static void U_CALLCONV olsonToMetaInit(UErrorCode &status) { gOlsonToMeta = NULL; } else { uhash_setKeyDeleter(gOlsonToMeta, deleteUCharString); - uhash_setValueDeleter(gOlsonToMeta, deleteUVector); + uhash_setValueDeleter(gOlsonToMeta, uprv_deleteUObject); } } @@ -625,7 +617,7 @@ ZoneMeta::getMetazoneMappings(const UnicodeString &tzid) { UVector* ZoneMeta::createMetazoneMappings(const UnicodeString &tzid) { - UVector *mzMappings = NULL; + LocalPointer mzMappings; UErrorCode status = U_ZERO_ERROR; UnicodeString canonicalID; @@ -677,41 +669,32 @@ ZoneMeta::createMetazoneMappings(const UnicodeString &tzid) { continue; } - OlsonToMetaMappingEntry *entry = (OlsonToMetaMappingEntry*)uprv_malloc(sizeof(OlsonToMetaMappingEntry)); - if (entry == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; + LocalPointer entry(new OlsonToMetaMappingEntry, status); + if (U_FAILURE(status)) { break; } entry->mzid = mz_name; entry->from = from; entry->to = to; - if (mzMappings == NULL) { - mzMappings = new UVector(deleteOlsonToMetaMappingEntry, NULL, status); + if (mzMappings.isNull()) { + mzMappings.adoptInsteadAndCheckErrorCode( + new UVector(deleteOlsonToMetaMappingEntry, nullptr, status), status); if (U_FAILURE(status)) { - delete mzMappings; - mzMappings = NULL; - uprv_free(entry); break; } } - mzMappings->addElementX(entry, status); + mzMappings->adoptElement(entry.orphan(), status); if (U_FAILURE(status)) { break; } } ures_close(mz); - if (U_FAILURE(status)) { - if (mzMappings != NULL) { - delete mzMappings; - mzMappings = NULL; - } - } } } ures_close(rb); - return mzMappings; + return U_SUCCESS(status) ? mzMappings.orphan() : nullptr; } UnicodeString& U_EXPORT2 @@ -775,6 +758,7 @@ static void U_CALLCONV initAvailableMetaZoneIDs () { // No valueDeleter, because the vector maintain the value objects gMetaZoneIDs = new UVector(NULL, uhash_compareUChars, status); if (U_FAILURE(status) || gMetaZoneIDs == NULL) { + delete gMetaZoneIDs; gMetaZoneIDs = NULL; uhash_close(gMetaZoneIDTable); gMetaZoneIDTable = NULL; @@ -792,20 +776,22 @@ static void U_CALLCONV initAvailableMetaZoneIDs () { } const char *mzID = ures_getKey(res.getAlias()); int32_t len = static_cast(uprv_strlen(mzID)); - UChar *uMzID = (UChar*)uprv_malloc(sizeof(UChar) * (len + 1)); - if (uMzID == NULL) { + LocalMemory uMzID((UChar*)uprv_malloc(sizeof(UChar) * (len + 1))); + if (uMzID.isNull()) { status = U_MEMORY_ALLOCATION_ERROR; break; } - u_charsToUChars(mzID, uMzID, len); + u_charsToUChars(mzID, uMzID.getAlias(), len); uMzID[len] = 0; - UnicodeString *usMzID = new UnicodeString(uMzID); - if (uhash_get(gMetaZoneIDTable, usMzID) == NULL) { - gMetaZoneIDs->addElementX((void *)uMzID, status); - uhash_put(gMetaZoneIDTable, (void *)usMzID, (void *)uMzID, &status); - } else { - uprv_free(uMzID); - delete usMzID; + LocalPointer usMzID(new UnicodeString(uMzID.getAlias()), status); + if (U_FAILURE(status)) { + break; + } + if (uhash_get(gMetaZoneIDTable, usMzID.getAlias()) == NULL) { + // Note: gMetaZoneIDTable adopts its keys, but not its values. + // gMetaZoneIDs adopts its values. + uhash_put(gMetaZoneIDTable, usMzID.orphan(), uMzID.getAlias(), &status); + gMetaZoneIDs->adoptElement(uMzID.orphan(), status); } } ures_close(bundle); diff --git a/deps/icu-small/source/i18n/zonemeta.h b/deps/icu-small/source/i18n/zonemeta.h index f21399342b9e67..dd4fec957fedae 100644 --- a/deps/icu-small/source/i18n/zonemeta.h +++ b/deps/icu-small/source/i18n/zonemeta.h @@ -18,11 +18,11 @@ U_NAMESPACE_BEGIN -typedef struct OlsonToMetaMappingEntry { +struct OlsonToMetaMappingEntry : public UMemory { const UChar *mzid; // const because it's a reference to a resource bundle string. UDate from; UDate to; -} OlsonToMetaMappingEntry; +}; class UVector; class TimeZone; diff --git a/deps/icu-small/source/stubdata/BUILD b/deps/icu-small/source/stubdata/BUILD.bazel similarity index 100% rename from deps/icu-small/source/stubdata/BUILD rename to deps/icu-small/source/stubdata/BUILD.bazel diff --git a/deps/icu-small/source/tools/icuexportdata/icuexportdata.cpp b/deps/icu-small/source/tools/icuexportdata/icuexportdata.cpp index ef933676115e1b..7431ac74ab86f4 100644 --- a/deps/icu-small/source/tools/icuexportdata/icuexportdata.cpp +++ b/deps/icu-small/source/tools/icuexportdata/icuexportdata.cpp @@ -3,6 +3,7 @@ #include #include +#include #include "toolutil.h" #include "uoptions.h" #include "cmemory.h" @@ -11,8 +12,10 @@ #include "unicode/uchar.h" #include "unicode/errorcode.h" #include "unicode/uniset.h" +#include "unicode/uscript.h" #include "unicode/putil.h" #include "unicode/umutablecptrie.h" +#include "ucase.h" #include "writesrc.h" U_NAMESPACE_USE @@ -27,6 +30,75 @@ UBool haveCopyright = TRUE; UCPTrieType trieType = UCPTRIE_TYPE_SMALL; const char* destdir = ""; +// Mask constants for modified values in the Script CodePointTrie, values are logically 12-bits. +int16_t DATAEXPORT_SCRIPT_X_WITH_COMMON = 0x0400; +int16_t DATAEXPORT_SCRIPT_X_WITH_INHERITED = 0x0800; +int16_t DATAEXPORT_SCRIPT_X_WITH_OTHER = 0x0c00; + +// TODO(ICU-21821): Replace this with a call to a library function +int32_t scxCodePoints[] = { + 7415, 7377, 7380, 7387, 7390, 7391, 7394, 7395, 7396, 7397, + 7398, 7399, 7400, 7403, 7404, 7406, 7407, 7408, 7409, 113824, + 113825, 113826, 113827, 834, 837, 7616, 7617, 12294, 12350, 12351, + 12688, 12689, 12690, 12691, 12692, 12693, 12694, 12695, 12696, 12697, + 12698, 12699, 12700, 12701, 12702, 12703, 12736, 12737, 12738, 12739, + 12740, 12741, 12742, 12743, 12744, 12745, 12746, 12747, 12748, 12749, + 12750, 12751, 12752, 12753, 12754, 12755, 12756, 12757, 12758, 12759, + 12760, 12761, 12762, 12763, 12764, 12765, 12766, 12767, 12768, 12769, + 12770, 12771, 12832, 12833, 12834, 12835, 12836, 12837, 12838, 12839, + 12840, 12841, 12842, 12843, 12844, 12845, 12846, 12847, 12848, 12849, + 12850, 12851, 12852, 12853, 12854, 12855, 12856, 12857, 12858, 12859, + 12860, 12861, 12862, 12863, 12864, 12865, 12866, 12867, 12868, 12869, + 12870, 12871, 12928, 12929, 12930, 12931, 12932, 12933, 12934, 12935, + 12936, 12937, 12938, 12939, 12940, 12941, 12942, 12943, 12944, 12945, + 12946, 12947, 12948, 12949, 12950, 12951, 12952, 12953, 12954, 12955, + 12956, 12957, 12958, 12959, 12960, 12961, 12962, 12963, 12964, 12965, + 12966, 12967, 12968, 12969, 12970, 12971, 12972, 12973, 12974, 12975, + 12976, 12992, 12993, 12994, 12995, 12996, 12997, 12998, 12999, 13000, + 13001, 13002, 13003, 13055, 13144, 13145, 13146, 13147, 13148, 13149, + 13150, 13151, 13152, 13153, 13154, 13155, 13156, 13157, 13158, 13159, + 13160, 13161, 13162, 13163, 13164, 13165, 13166, 13167, 13168, 13179, + 13180, 13181, 13182, 13183, 13280, 13281, 13282, 13283, 13284, 13285, + 13286, 13287, 13288, 13289, 13290, 13291, 13292, 13293, 13294, 13295, + 13296, 13297, 13298, 13299, 13300, 13301, 13302, 13303, 13304, 13305, + 13306, 13307, 13308, 13309, 13310, 119648, 119649, 119650, 119651, 119652, + 119653, 119654, 119655, 119656, 119657, 119658, 119659, 119660, 119661, 119662, + 119663, 119664, 119665, 127568, 127569, 867, 868, 869, 870, 871, + 872, 873, 874, 875, 876, 877, 878, 879, 7418, 7674, + 66272, 66273, 66274, 66275, 66276, 66277, 66278, 66279, 66280, 66281, + 66282, 66283, 66284, 66285, 66286, 66287, 66288, 66289, 66290, 66291, + 66292, 66293, 66294, 66295, 66296, 66297, 66298, 66299, 1748, 64830, + 64831, 1611, 1612, 1613, 1614, 1615, 1616, 1617, 1618, 1619, + 1620, 1621, 1648, 65010, 65021, 7381, 7382, 7384, 7393, 7402, + 7405, 7413, 7414, 43249, 12330, 12331, 12332, 12333, 43471, 65794, + 65847, 65848, 65849, 65850, 65851, 65852, 65853, 65854, 65855, 1156, + 1159, 11843, 42607, 1157, 1158, 1155, 7672, 7379, 7411, 7416, + 7417, 7401, 7383, 7385, 7388, 7389, 7392, 43251, 4347, 3046, + 3047, 3048, 3049, 3050, 3051, 3052, 3053, 3054, 3055, 3056, + 3057, 3058, 3059, 70401, 70403, 70459, 70460, 73680, 73681, 73683, + 2790, 2791, 2792, 2793, 2794, 2795, 2796, 2797, 2798, 2799, + 2662, 2663, 2664, 2665, 2666, 2667, 2668, 2669, 2670, 2671, + 42752, 42753, 42754, 42755, 42756, 42757, 42758, 42759, 12337, 12338, + 12339, 12340, 12341, 12441, 12442, 12443, 12444, 12448, 12540, 65392, + 65438, 65439, 3302, 3303, 3304, 3305, 3306, 3307, 3308, 3309, + 3310, 3311, 8239, 68338, 6146, 6147, 6149, 1564, 1632, 1633, + 1634, 1635, 1636, 1637, 1638, 1639, 1640, 1641, 2534, 2535, + 2536, 2537, 2538, 2539, 2540, 2541, 2542, 2543, 4160, 4161, + 4162, 4163, 4164, 4165, 4166, 4167, 4168, 4169, 65792, 65793, + 65799, 65800, 65801, 65802, 65803, 65804, 65805, 65806, 65807, 65808, + 65809, 65810, 65811, 65812, 65813, 65814, 65815, 65816, 65817, 65818, + 65819, 65820, 65821, 65822, 65823, 65824, 65825, 65826, 65827, 65828, + 65829, 65830, 65831, 65832, 65833, 65834, 65835, 65836, 65837, 65838, + 65839, 65840, 65841, 65842, 65843, 7412, 8432, 12348, 12349, 43310, + 7376, 7378, 5941, 5942, 2406, 2407, 2408, 2409, 2410, 2411, + 2412, 2413, 2414, 2415, 12291, 12307, 12316, 12317, 12318, 12319, + 12336, 12343, 65093, 65094, 1548, 1563, 12289, 12290, 12296, 12297, + 12298, 12299, 12300, 12301, 12302, 12303, 12304, 12305, 12308, 12309, + 12310, 12311, 12312, 12313, 12314, 12315, 12539, 65377, 65378, 65379, + 65380, 65381, 7386, 1567, 7410, 1600, 43062, 43063, 43064, 43065, + 2386, 2385, 43059, 43060, 43061, 43056, 43057, 43058, 2404, 2405 + }; + void handleError(ErrorCode& status, const char* context) { if (status.isFailure()) { std::cerr << "Error: " << context << ": " << status.errorName() << std::endl; @@ -96,6 +168,110 @@ void dumpEnumeratedProperty(UProperty uproperty, FILE* f) { usrc_writeUCPTrie(f, shortPropName, utrie.getAlias(), UPRV_TARGET_SYNTAX_TOML); } +void dumpScriptExtensions(FILE* f) { + IcuToolErrorCode status("icuexportdata: dumpScriptExtensions"); + + fputs("[[script_extensions]]\n", f); + const char* scxFullPropName = u_getPropertyName(UCHAR_SCRIPT_EXTENSIONS, U_LONG_PROPERTY_NAME); + const char* scxShortPropName = u_getPropertyName(UCHAR_SCRIPT_EXTENSIONS, U_SHORT_PROPERTY_NAME); + fprintf(f, "long_name = \"%s\"\n", scxFullPropName); + if (scxShortPropName) fprintf(f, "short_name = \"%s\"\n", scxShortPropName); + + // We want to use 16 bits for our exported trie of sc/scx data because we + // need 12 bits to match the 12 bits of data stored for sc/scx in the trie + // in the uprops.icu data file. + UCPTrieValueWidth scWidth = UCPTRIE_VALUE_BITS_16; + + // Create a mutable UCPTrie builder populated with Script property values data. + const UCPMap* scInvMap = u_getIntPropertyMap(UCHAR_SCRIPT, status); + handleError(status, scxFullPropName); + LocalUMutableCPTriePointer builder(umutablecptrie_fromUCPMap(scInvMap, status)); + handleError(status, scxFullPropName); + + // The values for the output scx companion array. + // Invariant is that all subvectors are distinct. + std::vector< std::vector > outputDedupVec; + + // The sc/scx companion array is an array of arrays (of script codes) + fputs("script_code_array = [\n", f); + for(const UChar32 cp : scxCodePoints) { + // Get the Script value + uint32_t scVal = umutablecptrie_get(builder.getAlias(), cp); + // Get the Script_Extensions value (array of Script codes) + const int32_t SCX_ARRAY_CAPACITY = 32; + UScriptCode scxValArray[SCX_ARRAY_CAPACITY]; + int32_t numScripts = uscript_getScriptExtensions(cp, scxValArray, SCX_ARRAY_CAPACITY, status); + handleError(status, scxFullPropName); + + // Convert the scx array into a vector + std::vector scxValVec; + for(int i = 0; i < numScripts; i++) { + scxValVec.push_back(scxValArray[i]); + } + // Ensure that it is sorted + std::sort(scxValVec.begin(), scxValVec.end()); + // Copy the Script value into the first position of the scx array only + // if we have the "other" case (Script value is not Common nor Inherited). + // This offers faster access when users want only the Script value. + if (scVal != USCRIPT_COMMON && scVal != USCRIPT_INHERITED) { + scxValVec.insert(scxValVec.begin(), scVal); + } + + // See if there is already an scx value array matching the newly built one. + // If there is, then use its index. + // If not, then append the new value array. + bool isScxValUnique = true; + size_t outputIndex = 0; + for (outputIndex = 0; outputIndex < outputDedupVec.size(); outputIndex++) { + if (outputDedupVec[outputIndex] == scxValVec) { + isScxValUnique = false; + break; + } + } + + if (isScxValUnique) { + outputDedupVec.push_back(scxValVec); + usrc_writeArray(f, " [", scxValVec.data(), 16, scxValVec.size(), " ", "],\n"); + } + + // We must update the value in the UCPTrie for the code point to contain: + // 9..0 the Script code in the lower 10 bits when 11..10 is 0, else it is + // the index into the companion array + // 11..10 the same higher-order 2 bits in the trie in uprops.icu indicating whether + // 3: other + // 2: Script=Inherited + // 1: Script=Common + // 0: Script=value in 9..0 (N/A because we are in this loop to create the companion array for non-0 cases) + uint16_t mask = 0; + if (scVal == USCRIPT_COMMON) { + mask = DATAEXPORT_SCRIPT_X_WITH_COMMON; + } else if (scVal == USCRIPT_INHERITED) { + mask = DATAEXPORT_SCRIPT_X_WITH_INHERITED; + } else { + mask = DATAEXPORT_SCRIPT_X_WITH_OTHER; + } + + // The new trie value is the index into the new array with the high order bits set + uint32_t newScVal = outputIndex | mask; + + // Update the code point in the mutable trie builder with the trie value + umutablecptrie_set(builder.getAlias(), cp, newScVal, status); + handleError(status, scxFullPropName); + } + fputs("]\n\n", f); // Print the TOML close delimiter for the outer array. + + // Convert from mutable trie builder to immutable trie. + LocalUCPTriePointer utrie(umutablecptrie_buildImmutable( + builder.getAlias(), + trieType, + scWidth, + status)); + handleError(status, scxFullPropName); + + fputs("[script_extensions.code_point_trie]\n", f); + usrc_writeUCPTrie(f, scxShortPropName, utrie.getAlias(), UPRV_TARGET_SYNTAX_TOML); +} + FILE* prepareOutputFile(const char* basename) { IcuToolErrorCode status("icuexportdata"); CharString outFileName; @@ -158,45 +334,42 @@ static UOption options[]={ UOPTION_QUIET, }; -int main(int argc, char* argv[]) { - U_MAIN_INIT_ARGS(argc, argv); - - /* preset then read command line options */ - options[OPT_DESTDIR].value=u_getDataDirectory(); - argc=u_parseArgs(argc, argv, UPRV_LENGTHOF(options), options); - - if(options[OPT_VERSION].doesOccur) { - printf("icuexportdata version %s, ICU tool to dump data files for external consumers\n", - U_ICU_DATA_VERSION); - printf("%s\n", U_COPYRIGHT_STRING); - exit(0); - } - - /* error handling, printing usage message */ - if(argc<0) { - fprintf(stderr, - "error in command line argument \"%s\"\n", - argv[-argc]); - } else if(argc<2) { - argc=-1; - } - - /* get the options values */ - haveCopyright = options[OPT_COPYRIGHT].doesOccur; - destdir = options[OPT_DESTDIR].value; - VERBOSE = options[OPT_VERBOSE].doesOccur; - QUIET = options[OPT_QUIET].doesOccur; +void printHelp(FILE* stdfile, const char* program) { + fprintf(stdfile, + "usage: %s -m mode [-options] [--all | properties...]\n" + "\tdump Unicode property data to .toml files\n" + "options:\n" + "\t-h or -? or --help this usage text\n" + "\t-V or --version show a version message\n" + "\t-m or --mode mode: currently only 'uprops' and 'ucase', but more may be added\n" + "\t --trie-type set the trie type (small or fast, default small)\n" + "\t-d or --destdir destination directory, followed by the path\n" + "\t --all write out all properties known to icuexportdata\n" + "\t --index write an _index.toml summarizing all data exported\n" + "\t-c or --copyright include a copyright notice\n" + "\t-v or --verbose Turn on verbose output\n" + "\t-q or --quiet do not display warnings and progress\n", + program); +} +int exportUprops(int argc, char* argv[]) { // Load list of Unicode properties std::vector propNames; for (int i=1; i(i); const char* propName = u_getPropertyName(uprop, U_SHORT_PROPERTY_NAME); if (propName == NULL) { @@ -207,47 +380,10 @@ int main(int argc, char* argv[]) { } if (propName != NULL) { propNames.push_back(propName); + } else { + std::cerr << "Warning: Could not find name for: " << uprop << std::endl; } - } - } - - if (propNames.empty() - || options[OPT_HELP_H].doesOccur - || options[OPT_HELP_QUESTION_MARK].doesOccur - || !options[OPT_MODE].doesOccur) { - FILE *stdfile=argc<0 ? stderr : stdout; - fprintf(stdfile, - "usage: %s -m uprops [-options] [--all | properties...]\n" - "\tdump Unicode property data to .toml files\n" - "options:\n" - "\t-h or -? or --help this usage text\n" - "\t-V or --version show a version message\n" - "\t-m or --mode mode: currently only 'uprops', but more may be added\n" - "\t --trie-type set the trie type (small or fast, default small)\n" - "\t-d or --destdir destination directory, followed by the path\n" - "\t --all write out all properties known to icuexportdata\n" - "\t --index write an _index.toml summarizing all data exported\n" - "\t-c or --copyright include a copyright notice\n" - "\t-v or --verbose Turn on verbose output\n" - "\t-q or --quiet do not display warnings and progress\n", - argv[0]); - return argc<0 ? U_ILLEGAL_ARGUMENT_ERROR : U_ZERO_ERROR; - } - - const char* mode = options[OPT_MODE].value; - if (uprv_strcmp(mode, "uprops") != 0) { - fprintf(stderr, "Invalid option for --mode (must be uprops)\n"); - return U_ILLEGAL_ARGUMENT_ERROR; - } - - if (options[OPT_TRIE_TYPE].doesOccur) { - if (uprv_strcmp(options[OPT_TRIE_TYPE].value, "fast") == 0) { - trieType = UCPTRIE_TYPE_FAST; - } else if (uprv_strcmp(options[OPT_TRIE_TYPE].value, "small") == 0) { - trieType = UCPTRIE_TYPE_SMALL; - } else { - fprintf(stderr, "Invalid option for --trie-type (must be small or fast)\n"); - return U_ILLEGAL_ARGUMENT_ERROR; + i++; } } @@ -272,6 +408,8 @@ int main(int argc, char* argv[]) { dumpBinaryProperty(propEnum, f); } else if (UCHAR_INT_START <= propEnum && propEnum <= UCHAR_INT_LIMIT) { dumpEnumeratedProperty(propEnum, f); + } else if (propEnum == UCHAR_SCRIPT_EXTENSIONS) { + dumpScriptExtensions(f); } else { std::cerr << "Don't know how to write property: " << propEnum << std::endl; return U_INTERNAL_PROGRAM_ERROR; @@ -293,3 +431,134 @@ int main(int argc, char* argv[]) { return 0; } + +struct AddRangeHelper { + UMutableCPTrie* ucptrie; +}; + +static UBool U_CALLCONV +addRangeToUCPTrie(const void* context, UChar32 start, UChar32 end, uint32_t value) { + IcuToolErrorCode status("addRangeToUCPTrie"); + UMutableCPTrie* ucptrie = ((const AddRangeHelper*) context)->ucptrie; + umutablecptrie_setRange(ucptrie, start, end, value, status); + handleError(status, "setRange"); + + return TRUE; +} + +int exportCase(int argc, char* argv[]) { + if (argc > 1) { + fprintf(stderr, "ucase mode does not expect additional arguments\n"); + return U_ILLEGAL_ARGUMENT_ERROR; + } + (void) argv; // Suppress unused variable warning + + IcuToolErrorCode status("icuexportdata"); + LocalUMutableCPTriePointer builder(umutablecptrie_open(0, 0, status)); + handleError(status, "exportCase"); + + int32_t exceptionsLength, unfoldLength; + const UCaseProps *caseProps = ucase_getSingleton(&exceptionsLength, &unfoldLength); + const UTrie2* caseTrie = &caseProps->trie; + + AddRangeHelper helper = { builder.getAlias() }; + utrie2_enum(caseTrie, NULL, addRangeToUCPTrie, &helper); + + UCPTrieValueWidth width = UCPTRIE_VALUE_BITS_16; + LocalUCPTriePointer utrie(umutablecptrie_buildImmutable( + builder.getAlias(), + trieType, + width, + status)); + handleError(status, "exportCase"); + + FILE* f = prepareOutputFile("ucase"); + + UVersionInfo versionInfo; + u_getUnicodeVersion(versionInfo); + char uvbuf[U_MAX_VERSION_STRING_LENGTH]; + u_versionToString(versionInfo, uvbuf); + fprintf(f, "icu_version = \"%s\"\nunicode_version = \"%s\"\n\n", + U_ICU_VERSION, + uvbuf); + + fputs("[ucase.code_point_trie]\n", f); + usrc_writeUCPTrie(f, "case_trie", utrie.getAlias(), UPRV_TARGET_SYNTAX_TOML); + fputs("\n", f); + + const char* indent = " "; + const char* suffix = "\n]\n"; + + fputs("[ucase.exceptions]\n", f); + const char* exceptionsPrefix = "exceptions = [\n "; + int32_t exceptionsWidth = 16; + usrc_writeArray(f, exceptionsPrefix, caseProps->exceptions, exceptionsWidth, + exceptionsLength, indent, suffix); + fputs("\n", f); + + fputs("[ucase.unfold]\n", f); + const char* unfoldPrefix = "unfold = [\n "; + int32_t unfoldWidth = 16; + usrc_writeArray(f, unfoldPrefix, caseProps->unfold, unfoldWidth, + unfoldLength, indent, suffix); + + return 0; +} + +int main(int argc, char* argv[]) { + U_MAIN_INIT_ARGS(argc, argv); + + /* preset then read command line options */ + options[OPT_DESTDIR].value=u_getDataDirectory(); + argc=u_parseArgs(argc, argv, UPRV_LENGTHOF(options), options); + + if(options[OPT_VERSION].doesOccur) { + printf("icuexportdata version %s, ICU tool to dump data files for external consumers\n", + U_ICU_DATA_VERSION); + printf("%s\n", U_COPYRIGHT_STRING); + exit(0); + } + + /* error handling, printing usage message */ + if(argc<0) { + fprintf(stderr, + "error in command line argument \"%s\"\n", + argv[-argc]); + } + + if (argc < 0 + || options[OPT_HELP_H].doesOccur + || options[OPT_HELP_QUESTION_MARK].doesOccur + || !options[OPT_MODE].doesOccur) { + FILE *stdfile=argc<0 ? stderr : stdout; + printHelp(stdfile, argv[0]); + return argc<0 ? U_ILLEGAL_ARGUMENT_ERROR : U_ZERO_ERROR; + } + + /* get the options values */ + haveCopyright = options[OPT_COPYRIGHT].doesOccur; + destdir = options[OPT_DESTDIR].value; + VERBOSE = options[OPT_VERBOSE].doesOccur; + QUIET = options[OPT_QUIET].doesOccur; + + if (options[OPT_TRIE_TYPE].doesOccur) { + if (uprv_strcmp(options[OPT_TRIE_TYPE].value, "fast") == 0) { + trieType = UCPTRIE_TYPE_FAST; + } else if (uprv_strcmp(options[OPT_TRIE_TYPE].value, "small") == 0) { + trieType = UCPTRIE_TYPE_SMALL; + } else { + fprintf(stderr, "Invalid option for --trie-type (must be small or fast)\n"); + return U_ILLEGAL_ARGUMENT_ERROR; + } + } + + const char* mode = options[OPT_MODE].value; + if (uprv_strcmp(mode, "uprops") == 0) { + return exportUprops(argc, argv); + } else if (uprv_strcmp(mode, "ucase") == 0) { + return exportCase(argc, argv); + } + + fprintf(stderr, "Invalid option for --mode (must be uprops or ucase)\n"); + return U_ILLEGAL_ARGUMENT_ERROR; +} diff --git a/deps/icu-small/source/tools/toolutil/BUILD b/deps/icu-small/source/tools/toolutil/BUILD.bazel similarity index 100% rename from deps/icu-small/source/tools/toolutil/BUILD rename to deps/icu-small/source/tools/toolutil/BUILD.bazel diff --git a/deps/icu-small/source/tools/toolutil/toolutil.cpp b/deps/icu-small/source/tools/toolutil/toolutil.cpp index 1fc68aa69c84ff..a9dc37377a840a 100644 --- a/deps/icu-small/source/tools/toolutil/toolutil.cpp +++ b/deps/icu-small/source/tools/toolutil/toolutil.cpp @@ -228,18 +228,19 @@ uprv_compareGoldenFiles( std::ifstream ifs(goldenFilePath, std::ifstream::in); int32_t pos = 0; char c; - while ((c = ifs.get()) != std::char_traits::eof() && pos < bufferLen) { + while (ifs.get(c) && pos < bufferLen) { if (c != buffer[pos]) { // Files differ at this position - return pos; + break; } pos++; } - if (pos < bufferLen || c != std::char_traits::eof()) { - // Files are different lengths - return pos; + if (pos == bufferLen && ifs.eof()) { + // Files are same lengths + pos = -1; } - return -1; + ifs.close(); + return pos; } /*U_CAPI UDate U_EXPORT2 diff --git a/deps/nghttp2/lib/Makefile.in b/deps/nghttp2/lib/Makefile.in index 78e76e55d9683d..5653774d5d6a1c 100644 --- a/deps/nghttp2/lib/Makefile.in +++ b/deps/nghttp2/lib/Makefile.in @@ -362,6 +362,8 @@ LIBMRUBY_LIBS = @LIBMRUBY_LIBS@ LIBNGHTTP3_CFLAGS = @LIBNGHTTP3_CFLAGS@ LIBNGHTTP3_LIBS = @LIBNGHTTP3_LIBS@ LIBNGTCP2_CFLAGS = @LIBNGTCP2_CFLAGS@ +LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS = @LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS@ +LIBNGTCP2_CRYPTO_BORINGSSL_LIBS = @LIBNGTCP2_CRYPTO_BORINGSSL_LIBS@ LIBNGTCP2_CRYPTO_OPENSSL_CFLAGS = @LIBNGTCP2_CRYPTO_OPENSSL_CFLAGS@ LIBNGTCP2_CRYPTO_OPENSSL_LIBS = @LIBNGTCP2_CRYPTO_OPENSSL_LIBS@ LIBNGTCP2_LIBS = @LIBNGTCP2_LIBS@ @@ -408,6 +410,7 @@ PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ PYTHON_LIBS = @PYTHON_LIBS@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PLATFORM_SITE_PKG = @PYTHON_PLATFORM_SITE_PKG@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ PYTHON_VERSION = @PYTHON_VERSION@ diff --git a/deps/nghttp2/lib/includes/Makefile.in b/deps/nghttp2/lib/includes/Makefile.in index 834eff24ab9c67..327e523197e4ca 100644 --- a/deps/nghttp2/lib/includes/Makefile.in +++ b/deps/nghttp2/lib/includes/Makefile.in @@ -271,6 +271,8 @@ LIBMRUBY_LIBS = @LIBMRUBY_LIBS@ LIBNGHTTP3_CFLAGS = @LIBNGHTTP3_CFLAGS@ LIBNGHTTP3_LIBS = @LIBNGHTTP3_LIBS@ LIBNGTCP2_CFLAGS = @LIBNGTCP2_CFLAGS@ +LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS = @LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS@ +LIBNGTCP2_CRYPTO_BORINGSSL_LIBS = @LIBNGTCP2_CRYPTO_BORINGSSL_LIBS@ LIBNGTCP2_CRYPTO_OPENSSL_CFLAGS = @LIBNGTCP2_CRYPTO_OPENSSL_CFLAGS@ LIBNGTCP2_CRYPTO_OPENSSL_LIBS = @LIBNGTCP2_CRYPTO_OPENSSL_LIBS@ LIBNGTCP2_LIBS = @LIBNGTCP2_LIBS@ @@ -317,6 +319,7 @@ PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ PYTHON_LIBS = @PYTHON_LIBS@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PLATFORM_SITE_PKG = @PYTHON_PLATFORM_SITE_PKG@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ PYTHON_VERSION = @PYTHON_VERSION@ diff --git a/deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h b/deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h index 5f2454cfd7cbbb..c6082518c7b5fa 100644 --- a/deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h +++ b/deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h @@ -29,7 +29,7 @@ * @macro * Version number of the nghttp2 library release */ -#define NGHTTP2_VERSION "1.45.1" +#define NGHTTP2_VERSION "1.47.0" /** * @macro @@ -37,6 +37,6 @@ * release. This is a 24 bit number with 8 bits for major number, 8 bits * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203. */ -#define NGHTTP2_VERSION_NUM 0x012d01 +#define NGHTTP2_VERSION_NUM 0x012f00 #endif /* NGHTTP2VER_H */ diff --git a/deps/nghttp2/lib/nghttp2_buf.h b/deps/nghttp2/lib/nghttp2_buf.h index 06cce67a11bdea..45f62f16e271dc 100644 --- a/deps/nghttp2/lib/nghttp2_buf.h +++ b/deps/nghttp2/lib/nghttp2_buf.h @@ -99,7 +99,7 @@ void nghttp2_buf_free(nghttp2_buf *buf, nghttp2_mem *mem); * |new_cap|. If extensions took place, buffer pointers in |buf| will * change. * - * This function returns 0 if it succeeds, or one of the followings + * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM diff --git a/deps/nghttp2/lib/nghttp2_frame.c b/deps/nghttp2/lib/nghttp2_frame.c index 382a26c818dd7b..3648b2389d7cbf 100644 --- a/deps/nghttp2/lib/nghttp2_frame.c +++ b/deps/nghttp2/lib/nghttp2_frame.c @@ -654,8 +654,6 @@ int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame, var_gift_payloadlen = 0; } - payloadlen -= var_gift_payloadlen; - if (!var_gift_payloadlen) { var_gift_payload = NULL; } else { diff --git a/deps/nghttp2/lib/nghttp2_frame.h b/deps/nghttp2/lib/nghttp2_frame.h index 4b9222ac6dd685..3859926ebbcbac 100644 --- a/deps/nghttp2/lib/nghttp2_frame.h +++ b/deps/nghttp2/lib/nghttp2_frame.h @@ -46,7 +46,7 @@ #define NGHTTP2_MAX_FRAME_SIZE_MIN (1 << 14) #define NGHTTP2_MAX_PAYLOADLEN 16384 -/* The one frame buffer length for tranmission. We may use several of +/* The one frame buffer length for transmission. We may use several of them to support CONTINUATION. To account for Pad Length field, we allocate extra 1 byte, which saves extra large memcopying. */ #define NGHTTP2_FRAMEBUF_CHUNKLEN \ diff --git a/deps/nghttp2/lib/nghttp2_hd.c b/deps/nghttp2/lib/nghttp2_hd.c index 5e869315259921..30ee9b88920c0a 100644 --- a/deps/nghttp2/lib/nghttp2_hd.c +++ b/deps/nghttp2/lib/nghttp2_hd.c @@ -1263,6 +1263,8 @@ int nghttp2_hd_inflate_change_table_size( return NGHTTP2_ERR_INVALID_STATE; } + inflater->settings_hd_table_bufsize_max = settings_max_dynamic_table_size; + /* It seems that encoder is not required to send dynamic table size update if the table size is not changed after applying SETTINGS_HEADER_TABLE_SIZE. RFC 7541 is ambiguous here, but this @@ -1275,13 +1277,12 @@ int nghttp2_hd_inflate_change_table_size( /* Remember minimum value, and validate that encoder sends the value less than or equal to this. */ inflater->min_hd_table_bufsize_max = settings_max_dynamic_table_size; - } - inflater->settings_hd_table_bufsize_max = settings_max_dynamic_table_size; + inflater->ctx.hd_table_bufsize_max = settings_max_dynamic_table_size; - inflater->ctx.hd_table_bufsize_max = settings_max_dynamic_table_size; + hd_context_shrink_table_size(&inflater->ctx, NULL); + } - hd_context_shrink_table_size(&inflater->ctx, NULL); return 0; } diff --git a/deps/nghttp2/lib/nghttp2_map.c b/deps/nghttp2/lib/nghttp2_map.c index 5aab90b4daf473..e5db168ca2bc3b 100644 --- a/deps/nghttp2/lib/nghttp2_map.c +++ b/deps/nghttp2/lib/nghttp2_map.c @@ -189,6 +189,7 @@ static int map_resize(nghttp2_map *map, uint32_t new_tablelen, nghttp2_map_bucket *new_table; nghttp2_map_bucket *bkt; int rv; + (void)rv; new_table = nghttp2_mem_calloc(map->mem, new_tablelen, sizeof(nghttp2_map_bucket)); diff --git a/deps/nghttp2/lib/nghttp2_net.h b/deps/nghttp2/lib/nghttp2_net.h index 95ffee74a14fc9..582099b93dc0b8 100644 --- a/deps/nghttp2/lib/nghttp2_net.h +++ b/deps/nghttp2/lib/nghttp2_net.h @@ -42,7 +42,7 @@ #if defined(WIN32) /* Windows requires ws2_32 library for ntonl family functions. We define inline functions for those function so that we don't have - dependeny on that lib. */ + dependency on that lib. */ # ifdef _MSC_VER # define STIN static __inline diff --git a/deps/nghttp2/lib/nghttp2_outbound_item.h b/deps/nghttp2/lib/nghttp2_outbound_item.h index b5f503a312dd8c..bd4611b551bbbd 100644 --- a/deps/nghttp2/lib/nghttp2_outbound_item.h +++ b/deps/nghttp2/lib/nghttp2_outbound_item.h @@ -111,7 +111,7 @@ struct nghttp2_outbound_item { to this structure to avoid frequent memory allocation. */ nghttp2_ext_frame_payload ext_frame_payload; nghttp2_aux_data aux_data; - /* The priority used in priority comparion. Smaller is served + /* The priority used in priority comparison. Smaller is served earlier. For PING, SETTINGS and non-DATA frames (excluding response HEADERS frame) have dedicated cycle value defined above. For DATA frame, cycle is computed by taking into account of diff --git a/deps/nghttp2/lib/nghttp2_pq.h b/deps/nghttp2/lib/nghttp2_pq.h index 2d7b702ac18ad0..7b7b7392f8479c 100644 --- a/deps/nghttp2/lib/nghttp2_pq.h +++ b/deps/nghttp2/lib/nghttp2_pq.h @@ -114,7 +114,7 @@ typedef int (*nghttp2_pq_item_cb)(nghttp2_pq_entry *item, void *arg); void nghttp2_pq_update(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg); /* - * Applys |fun| to each item in |pq|. The |arg| is passed as arg + * Applies |fun| to each item in |pq|. The |arg| is passed as arg * parameter to callback function. This function must not change the * ordering key. If the return value from callback is nonzero, this * function returns 1 immediately without iterating remaining items. diff --git a/deps/nghttp2/lib/nghttp2_session.c b/deps/nghttp2/lib/nghttp2_session.c index 36f1179f72a225..380a47c1b1e82b 100644 --- a/deps/nghttp2/lib/nghttp2_session.c +++ b/deps/nghttp2/lib/nghttp2_session.c @@ -5341,7 +5341,7 @@ static ssize_t inbound_frame_compute_pad(nghttp2_inbound_frame *iframe) { /* * This function returns the effective payload length in the data of - * length |readlen| when the remaning payload is |payloadleft|. The + * length |readlen| when the remaining payload is |payloadleft|. The * |payloadleft| does not include |readlen|. If padding was started * strictly before this data chunk, this function returns -1. */ diff --git a/deps/nghttp2/lib/nghttp2_session.h b/deps/nghttp2/lib/nghttp2_session.h index 07bfbb6c90c8df..907b1704bc8412 100644 --- a/deps/nghttp2/lib/nghttp2_session.h +++ b/deps/nghttp2/lib/nghttp2_session.h @@ -408,7 +408,7 @@ int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id, uint32_t error_code); /* - * Adds PING frame. This is a convenient functin built on top of + * Adds PING frame. This is a convenient function built on top of * nghttp2_session_add_frame() to add PING easily. * * If the |opaque_data| is not NULL, it must point to 8 bytes memory diff --git a/deps/nghttp2/lib/nghttp2_stream.c b/deps/nghttp2/lib/nghttp2_stream.c index 96e1d9fe0f9b7e..f4c80a24b5e704 100644 --- a/deps/nghttp2/lib/nghttp2_stream.c +++ b/deps/nghttp2/lib/nghttp2_stream.c @@ -33,7 +33,7 @@ #include "nghttp2_frame.h" /* Maximum distance between any two stream's cycle in the same - prirority queue. Imagine stream A's cycle is A, and stream B's + priority queue. Imagine stream A's cycle is A, and stream B's cycle is B, and A < B. The cycle is unsigned 32 bit integer, it may get overflow. Because of how we calculate the next cycle value, if B - A is less than or equals to diff --git a/deps/nghttp2/lib/nghttp2_submit.c b/deps/nghttp2/lib/nghttp2_submit.c index 744a49cf6098ec..92fb03e8ca0f09 100644 --- a/deps/nghttp2/lib/nghttp2_submit.c +++ b/deps/nghttp2/lib/nghttp2_submit.c @@ -492,8 +492,6 @@ int nghttp2_session_set_local_window_size(nghttp2_session *session, return nghttp2_session_update_recv_stream_window_size(session, stream, 0, 1); } - - return 0; } int nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags, diff --git a/deps/npm/README.md b/deps/npm/README.md index 46846fa0087b50..fdc80bc796a268 100644 --- a/deps/npm/README.md +++ b/deps/npm/README.md @@ -1,7 +1,10 @@ -[![GitHub Workflow Status (branch)](https://img.shields.io/github/workflow/status/npm/cli/Node%20CI/latest)](https://github.com/npm/cli/actions?query=workflow%3A%22Node+CI%22+branch%3Alatest) [![Coveralls github branch](https://img.shields.io/coveralls/github/npm/cli/latest)](https://coveralls.io/github/npm/cli?branch=latest) - # npm - a JavaScript package manager +[![npm version](https://img.shields.io/npm/v/npm.svg)](https://npm.im/npm) +[![license](https://img.shields.io/npm/l/npm.svg)](https://npm.im/npm) +[![CI - cli](https://github.com/npm/cli/actions/workflows/ci.yml/badge.svg)](https://github.com/npm/cli/actions/workflows/ci.yml) +[![Benchmark Suite](https://github.com/npm/cli/actions/workflows/benchmark.yml/badge.svg)](https://github.com/npm/cli/actions/workflows/benchmark.yml) + ### Requirements One of the following versions of [Node.js](https://nodejs.org/en/download/) must be installed to run **`npm`**: diff --git a/deps/npm/docs/content/commands/npm-access.md b/deps/npm/docs/content/commands/npm-access.md index 1f661c911f47dc..162e94f1fec029 100644 --- a/deps/npm/docs/content/commands/npm-access.md +++ b/deps/npm/docs/content/commands/npm-access.md @@ -6,21 +6,27 @@ description: Set access level on published packages ### Synopsis + + + + ```bash npm access public [] npm access restricted [] - npm access grant [] npm access revoke [] - npm access 2fa-required [] npm access 2fa-not-required [] - npm access ls-packages [||] npm access ls-collaborators [ []] npm access edit [] ``` + + + + + ### Description Used to set access controls on private packages. diff --git a/deps/npm/docs/content/commands/npm-adduser.md b/deps/npm/docs/content/commands/npm-adduser.md index 21a31ca940e524..06eeb379c4dd88 100644 --- a/deps/npm/docs/content/commands/npm-adduser.md +++ b/deps/npm/docs/content/commands/npm-adduser.md @@ -6,12 +6,21 @@ description: Add a registry user account ### Synopsis + + + + ```bash -npm adduser [--registry=url] [--scope=@orgname] [--auth-type=legacy] +npm adduser aliases: login, add-user ``` + + + + + Note: This command is unaware of workspaces. ### Description diff --git a/deps/npm/docs/content/commands/npm-audit.md b/deps/npm/docs/content/commands/npm-audit.md index 58c614d793db29..0f164ac9d3ec51 100644 --- a/deps/npm/docs/content/commands/npm-audit.md +++ b/deps/npm/docs/content/commands/npm-audit.md @@ -6,13 +6,19 @@ description: Run a security audit ### Synopsis -```bash -npm audit [--json] [--production] [--audit-level=(low|moderate|high|critical)] -npm audit fix [--force|--package-lock-only|--dry-run|--production|--only=(dev|prod)] + + + -common options: [--production] [--only=(dev|prod)] +```bash +npm audit [fix] ``` + + + + + ### Description The audit command submits a description of the dependencies configured in @@ -240,6 +246,7 @@ mistakes, unnecessary performance degradation, and malicious input. * Allow conflicting peerDependencies to be installed in the root project. * Implicitly set `--yes` during `npm init`. * Allow clobbering existing values in `npm pkg` +* Allow unpublishing of entire packages (not just a single version). If you don't have a clear idea of what you want to do, it is strongly recommended that you do not use this option! @@ -300,6 +307,36 @@ variable will be set to `'production'` for all lifecycle scripts. +#### `foreground-scripts` + +* Default: false +* Type: Boolean + +Run all build scripts (ie, `preinstall`, `install`, and `postinstall`) +scripts for installed packages in the foreground process, sharing standard +input, output, and error with the main npm process. + +Note that this will generally make installs run slower, and be much noisier, +but can be useful for debugging. + + + + +#### `ignore-scripts` + +* Default: false +* Type: Boolean + +If true, npm does not run scripts specified in package.json files. + +Note that commands explicitly intended to run a particular script, such as +`npm start`, `npm stop`, `npm restart`, `npm test`, and `npm run-script` +will still run their intended script if `ignore-scripts` is set, but they +will *not* run any pre- or post-scripts. + + + + #### `workspace` * Default: @@ -357,6 +394,8 @@ When false, specifying individual workspaces via the `workspace` config, or all workspaces via the `workspaces` flag, will cause npm to operate only on the specified workspaces, and not on the root project. +This value is not exported to the environment for child processes. + diff --git a/deps/npm/docs/content/commands/npm-bin.md b/deps/npm/docs/content/commands/npm-bin.md index 2d7c1d5b8149ee..94b72cfd5c81ce 100644 --- a/deps/npm/docs/content/commands/npm-bin.md +++ b/deps/npm/docs/content/commands/npm-bin.md @@ -6,10 +6,19 @@ description: Display npm bin folder ### Synopsis + + + + ```bash -npm bin [-g|--global] +npm bin ``` + + + + + Note: This command is unaware of workspaces. ### Description diff --git a/deps/npm/docs/content/commands/npm-bugs.md b/deps/npm/docs/content/commands/npm-bugs.md index f92241a14b95c0..aeddeb848e81b8 100644 --- a/deps/npm/docs/content/commands/npm-bugs.md +++ b/deps/npm/docs/content/commands/npm-bugs.md @@ -6,12 +6,21 @@ description: Report bugs for a package in a web browser ### Synopsis + + + + ```bash -npm bugs [ [ ...]] +npm bugs [] -aliases: issues +alias: issues ``` + + + + + ### Description This command tries to guess at the likely location of a package's bug diff --git a/deps/npm/docs/content/commands/npm-cache.md b/deps/npm/docs/content/commands/npm-cache.md index 6497a3988c9387..091e26e8a71828 100644 --- a/deps/npm/docs/content/commands/npm-cache.md +++ b/deps/npm/docs/content/commands/npm-cache.md @@ -6,18 +6,26 @@ description: Manipulates packages cache ### Synopsis -```bash -npm cache add ... -npm cache add ... -npm cache add ... -npm cache add @... - -npm cache clean -aliases: npm cache clear, npm cache rm + + + +```bash +npm cache add +npm cache add +npm cache add +npm cache add +npm cache add @ +npm cache clean [] +npm cache ls [@] npm cache verify ``` + + + + + Note: This command is unaware of workspaces. ### Description diff --git a/deps/npm/docs/content/commands/npm-ci.md b/deps/npm/docs/content/commands/npm-ci.md index 1ce50c66d5fafa..b4ce811869bb2e 100644 --- a/deps/npm/docs/content/commands/npm-ci.md +++ b/deps/npm/docs/content/commands/npm-ci.md @@ -6,10 +6,21 @@ description: Install a project with a clean slate ### Synopsis + + + + ```bash npm ci + +aliases: clean-install, ic, install-clean, isntall-clean ``` + + + + + ### Description This command is similar to [`npm install`](/commands/npm-install), except @@ -35,6 +46,13 @@ In short, the main differences between using `npm install` and `npm ci` are: * It will never write to `package.json` or any of the package-locks: installs are essentially frozen. +NOTE: If you create your `package-lock.json` file by running `npm install` +with flags that can affect the shape of your dependency tree, such as +`--legacy-peer-deps`, you _must_ provide the same flags to `npm ci` or you +are likely to encounter errors. An easy way to do this is to run +`npm config set legacy-peer-deps=true --location=project` and commit the +`.npmrc` file to your repo. + ### Example Make sure you have a package-lock and an up-to-date install: @@ -83,6 +101,21 @@ submitted. +#### `foreground-scripts` + +* Default: false +* Type: Boolean + +Run all build scripts (ie, `preinstall`, `install`, and `postinstall`) +scripts for installed packages in the foreground process, sharing standard +input, output, and error with the main npm process. + +Note that this will generally make installs run slower, and be much noisier, +but can be useful for debugging. + + + + #### `ignore-scripts` * Default: false diff --git a/deps/npm/docs/content/commands/npm-completion.md b/deps/npm/docs/content/commands/npm-completion.md index 9dbd960913f270..d73a98f2e50f78 100644 --- a/deps/npm/docs/content/commands/npm-completion.md +++ b/deps/npm/docs/content/commands/npm-completion.md @@ -6,10 +6,19 @@ description: Tab Completion for npm ### Synopsis + + + + ```bash -source <(npm completion) +npm completion ``` + + + + + Note: This command is unaware of workspaces. ### Description diff --git a/deps/npm/docs/content/commands/npm-config.md b/deps/npm/docs/content/commands/npm-config.md index 2d77f045cbc472..a66a198ce42d17 100644 --- a/deps/npm/docs/content/commands/npm-config.md +++ b/deps/npm/docs/content/commands/npm-config.md @@ -6,18 +6,25 @@ description: Manage the npm configuration files ### Synopsis + + + + ```bash npm config set = [= ...] npm config get [ [ ...]] npm config delete [ ...] npm config list [--json] npm config edit -npm set = [= ...] -npm get [ [ ...]] alias: c ``` + + + + + Note: This command is unaware of workspaces. ### Description diff --git a/deps/npm/docs/content/commands/npm-dedupe.md b/deps/npm/docs/content/commands/npm-dedupe.md index 53d2e64272a67b..b9768c25db88d6 100644 --- a/deps/npm/docs/content/commands/npm-dedupe.md +++ b/deps/npm/docs/content/commands/npm-dedupe.md @@ -6,13 +6,21 @@ description: Reduce duplication in the package tree ### Synopsis + + + + ```bash npm dedupe -npm ddp -aliases: ddp +alias: ddp ``` + + + + + ### Description Searches the local package tree and attempts to simplify the overall @@ -72,11 +80,9 @@ result in new modules being installed. Using `npm find-dupes` will run the command in `--dry-run` mode. -Note that by default `npm dedupe` will not update the semver values of direct -dependencies in your project `package.json`, if you want to also update -values in `package.json` you can run: `npm dedupe --save` (or add the -`save=true` option to a [configuration file](/configuring-npm/npmrc) -to make that the default behavior). +Note: `npm dedupe` will never update the semver values of direct +dependencies in your project `package.json`, if you want to update +values in `package.json` you can run: `npm update --save` instead. ### Configuration @@ -145,6 +151,8 @@ When package package-locks are disabled, automatic pruning of extraneous modules will also be disabled. To remove extraneous modules with package-locks disabled use `npm prune`. +This configuration does not affect `npm ci`. + @@ -297,6 +305,8 @@ When false, specifying individual workspaces via the `workspace` config, or all workspaces via the `workspaces` flag, will cause npm to operate only on the specified workspaces, and not on the root project. +This value is not exported to the environment for child processes. + diff --git a/deps/npm/docs/content/commands/npm-deprecate.md b/deps/npm/docs/content/commands/npm-deprecate.md index 438a54ec6e4f36..4345120d3744b3 100644 --- a/deps/npm/docs/content/commands/npm-deprecate.md +++ b/deps/npm/docs/content/commands/npm-deprecate.md @@ -6,10 +6,19 @@ description: Deprecate a version of a package ### Synopsis + + + + ```bash -npm deprecate [@] +npm deprecate [@] ``` + + + + + Note: This command is unaware of workspaces. ### Description diff --git a/deps/npm/docs/content/commands/npm-diff.md b/deps/npm/docs/content/commands/npm-diff.md index 8d05df779f3ca5..7dcc8af7c3b4c7 100644 --- a/deps/npm/docs/content/commands/npm-diff.md +++ b/deps/npm/docs/content/commands/npm-diff.md @@ -6,14 +6,19 @@ description: The registry diff command ### Synopsis + + + + ```bash npm diff [...] -npm diff --diff= [...] -npm diff --diff= [--diff=] [...] -npm diff --diff= [--diff=] [...] -npm diff [--diff-ignore-all-space] [--diff-name-only] [...] ``` + + + + + ### Description Similar to its `git diff` counterpart, this command will print diff patches @@ -330,6 +335,8 @@ When false, specifying individual workspaces via the `workspace` config, or all workspaces via the `workspaces` flag, will cause npm to operate only on the specified workspaces, and not on the root project. +This value is not exported to the environment for child processes. + diff --git a/deps/npm/docs/content/commands/npm-dist-tag.md b/deps/npm/docs/content/commands/npm-dist-tag.md index a4e0243aac87b3..b9caf1fbe7fa9c 100644 --- a/deps/npm/docs/content/commands/npm-dist-tag.md +++ b/deps/npm/docs/content/commands/npm-dist-tag.md @@ -6,14 +6,23 @@ description: Modify package distribution tags ### Synopsis + + + + ```bash npm dist-tag add @ [] npm dist-tag rm npm dist-tag ls [] -aliases: dist-tags +alias: dist-tags ``` + + + + + ### Description Add, remove, and enumerate distribution tags on a package: @@ -150,6 +159,8 @@ When false, specifying individual workspaces via the `workspace` config, or all workspaces via the `workspaces` flag, will cause npm to operate only on the specified workspaces, and not on the root project. +This value is not exported to the environment for child processes. + diff --git a/deps/npm/docs/content/commands/npm-docs.md b/deps/npm/docs/content/commands/npm-docs.md index 970d17aa829c6e..790d563bdb1fb7 100644 --- a/deps/npm/docs/content/commands/npm-docs.md +++ b/deps/npm/docs/content/commands/npm-docs.md @@ -6,12 +6,21 @@ description: Open documentation for a package in a web browser ### Synopsis + + + + ```bash npm docs [ [ ...]] -aliases: home +alias: home ``` + + + + + ### Description This command tries to guess at the likely location of a package's @@ -107,6 +116,8 @@ When false, specifying individual workspaces via the `workspace` config, or all workspaces via the `workspaces` flag, will cause npm to operate only on the specified workspaces, and not on the root project. +This value is not exported to the environment for child processes. + diff --git a/deps/npm/docs/content/commands/npm-doctor.md b/deps/npm/docs/content/commands/npm-doctor.md index 0cce60c7b7b157..7fb63bab16e835 100644 --- a/deps/npm/docs/content/commands/npm-doctor.md +++ b/deps/npm/docs/content/commands/npm-doctor.md @@ -6,10 +6,19 @@ description: Check your npm environment ### Synopsis + + + + ```bash npm doctor ``` + + + + + Note: This command is unaware of workspaces. ### Description diff --git a/deps/npm/docs/content/commands/npm-edit.md b/deps/npm/docs/content/commands/npm-edit.md index 5ae7f2481ae456..39fc49592c571c 100644 --- a/deps/npm/docs/content/commands/npm-edit.md +++ b/deps/npm/docs/content/commands/npm-edit.md @@ -6,10 +6,19 @@ description: Edit an installed package ### Synopsis + + + + ```bash -npm edit +npm edit [/...] ``` + + + + + Note: This command is unaware of workspaces. ### Description diff --git a/deps/npm/docs/content/commands/npm-exec.md b/deps/npm/docs/content/commands/npm-exec.md index d154f5780b9c9c..8ccfa75c73386c 100644 --- a/deps/npm/docs/content/commands/npm-exec.md +++ b/deps/npm/docs/content/commands/npm-exec.md @@ -6,26 +6,23 @@ description: Run a command from a local or remote npm package ### Synopsis + + + + ```bash npm exec -- [@] [args...] npm exec --package=[@] -- [args...] npm exec -c ' [args...]' npm exec --package=foo -c ' [args...]' -npm exec [--ws] [-w [@] [args...] -npx -p [@] [args...] -npx -c ' [args...]' -npx -p [@] -c ' [args...]' -Run without --call or positional args to open interactive subshell +alias: x +``` -alias: npm x, npx + + -common options: ---package= (may be specified multiple times) --p is a shorthand for --package only when using npx executable --c --call= (may not be mixed with positional arguments) -``` + ### Description @@ -208,6 +205,8 @@ When false, specifying individual workspaces via the `workspace` config, or all workspaces via the `workspaces` flag, will cause npm to operate only on the specified workspaces, and not on the root project. +This value is not exported to the environment for child processes. + diff --git a/deps/npm/docs/content/commands/npm-explain.md b/deps/npm/docs/content/commands/npm-explain.md index 5f05cac0f906b0..765221056585d9 100644 --- a/deps/npm/docs/content/commands/npm-explain.md +++ b/deps/npm/docs/content/commands/npm-explain.md @@ -6,12 +6,21 @@ description: Explain installed packages ### Synopsis + + + + ```bash npm explain alias: why ``` + + + + + ### Description This command will print the chain of dependencies causing a given package diff --git a/deps/npm/docs/content/commands/npm-explore.md b/deps/npm/docs/content/commands/npm-explore.md index 3979da9573db00..90753c7e09199f 100644 --- a/deps/npm/docs/content/commands/npm-explore.md +++ b/deps/npm/docs/content/commands/npm-explore.md @@ -6,10 +6,19 @@ description: Browse an installed package ### Synopsis + + + + ```bash npm explore [ -- ] ``` + + + + + Note: This command is unaware of workspaces. ### Description diff --git a/deps/npm/docs/content/commands/npm-find-dupes.md b/deps/npm/docs/content/commands/npm-find-dupes.md index f7dc84f9c5306d..3549be47daae9c 100644 --- a/deps/npm/docs/content/commands/npm-find-dupes.md +++ b/deps/npm/docs/content/commands/npm-find-dupes.md @@ -6,10 +6,19 @@ description: Find duplication in the package tree ### Synopsis + + + + ```bash npm find-dupes ``` + + + + + ### Description Runs `npm dedupe` in `--dry-run` mode, making npm only output the @@ -82,6 +91,8 @@ When package package-locks are disabled, automatic pruning of extraneous modules will also be disabled. To remove extraneous modules with package-locks disabled use `npm prune`. +This configuration does not affect `npm ci`. + @@ -218,6 +229,8 @@ When false, specifying individual workspaces via the `workspace` config, or all workspaces via the `workspaces` flag, will cause npm to operate only on the specified workspaces, and not on the root project. +This value is not exported to the environment for child processes. + diff --git a/deps/npm/docs/content/commands/npm-fund.md b/deps/npm/docs/content/commands/npm-fund.md index 606b0a188c5549..5b96e91ab8ccb9 100644 --- a/deps/npm/docs/content/commands/npm-fund.md +++ b/deps/npm/docs/content/commands/npm-fund.md @@ -6,11 +6,19 @@ description: Retrieve funding information ### Synopsis + + + + ```bash -npm fund [] -npm fund [-w ] +npm fund [[<@scope>/]] ``` + + + + + ### Description This command retrieves information on how to fund the dependencies of a diff --git a/deps/npm/docs/content/commands/npm-help-search.md b/deps/npm/docs/content/commands/npm-help-search.md index 78553a14ecb01d..152f9f6bec16f1 100644 --- a/deps/npm/docs/content/commands/npm-help-search.md +++ b/deps/npm/docs/content/commands/npm-help-search.md @@ -6,10 +6,19 @@ description: Search npm help documentation ### Synopsis + + + + ```bash npm help-search ``` + + + + + Note: This command is unaware of workspaces. ### Description diff --git a/deps/npm/docs/content/commands/npm-help.md b/deps/npm/docs/content/commands/npm-help.md index a8002eef17156c..83c595db696b9c 100644 --- a/deps/npm/docs/content/commands/npm-help.md +++ b/deps/npm/docs/content/commands/npm-help.md @@ -6,10 +6,21 @@ description: Get help on npm ### Synopsis + + + + ```bash npm help [] + +alias: hlep ``` + + + + + Note: This command is unaware of workspaces. ### Description diff --git a/deps/npm/docs/content/commands/npm-hook.md b/deps/npm/docs/content/commands/npm-hook.md index c91bce3075e7b2..4a9805d02f9d43 100644 --- a/deps/npm/docs/content/commands/npm-hook.md +++ b/deps/npm/docs/content/commands/npm-hook.md @@ -6,13 +6,22 @@ description: Manage registry hooks ### Synopsis + + + + ```bash +npm hook add [--type=] npm hook ls [pkg] -npm hook add -npm hook update [secret] npm hook rm +npm hook update ``` + + + + + Note: This command is unaware of workspaces. ### Description diff --git a/deps/npm/docs/content/commands/npm-init.md b/deps/npm/docs/content/commands/npm-init.md index a608061a96d8dc..5a7dd4c97ac60e 100644 --- a/deps/npm/docs/content/commands/npm-init.md +++ b/deps/npm/docs/content/commands/npm-init.md @@ -6,13 +6,23 @@ description: Create a package.json file ### Synopsis + + + + ```bash -npm init [--yes|-y|--scope] -npm init <@scope> (same as `npm exec <@scope>/create`) -npm init [<@scope>/] (same as `npm exec [<@scope>/]create-`) -npm init [-w ] [args...] +npm init [--force|-f|--yes|-y|--scope] +npm init <@scope> (same as `npx <@scope>/create`) +npm init [<@scope>/] (same as `npx [<@scope>/]create-`) + +aliases: create, innit ``` + + + + + ### Description `npm init ` can be used to set up a new or existing npm @@ -38,6 +48,15 @@ strictly additive, so it will keep any fields and values that were already set. You can also use `-y`/`--yes` to skip the questionnaire altogether. If you pass `--scope`, it will create a scoped package. +*Note:* if a user already has the `create-` package +globally installed, that will be what `npm init` uses. If you want npm +to use the latest version, or another specific version you must specify +it: + +* `npm init foo@latest` # fetches and runs the latest `create-foo` from + the registry +* `npm init foo@1.2.3` # runs `create-foo@1.2.3` specifically + #### Forwarding additional options Any additional options will be passed directly to the command, so `npm init @@ -180,6 +199,7 @@ mistakes, unnecessary performance degradation, and malicious input. * Allow conflicting peerDependencies to be installed in the root project. * Implicitly set `--yes` during `npm init`. * Allow clobbering existing values in `npm pkg` +* Allow unpublishing of entire packages (not just a single version). If you don't have a clear idea of what you want to do, it is strongly recommended that you do not use this option! @@ -244,6 +264,8 @@ When false, specifying individual workspaces via the `workspace` config, or all workspaces via the `workspaces` flag, will cause npm to operate only on the specified workspaces, and not on the root project. +This value is not exported to the environment for child processes. + diff --git a/deps/npm/docs/content/commands/npm-install-ci-test.md b/deps/npm/docs/content/commands/npm-install-ci-test.md index 5c37ed8f56128b..0d9470acf58b3d 100644 --- a/deps/npm/docs/content/commands/npm-install-ci-test.md +++ b/deps/npm/docs/content/commands/npm-install-ci-test.md @@ -6,12 +6,21 @@ description: Install a project with a clean slate and run tests ### Synopsis + + + + ```bash npm install-ci-test -alias: npm cit +alias: cit ``` + + + + + ### Description This command runs `npm ci` followed immediately by `npm test`. @@ -34,6 +43,21 @@ submitted. +#### `foreground-scripts` + +* Default: false +* Type: Boolean + +Run all build scripts (ie, `preinstall`, `install`, and `postinstall`) +scripts for installed packages in the foreground process, sharing standard +input, output, and error with the main npm process. + +Note that this will generally make installs run slower, and be much noisier, +but can be useful for debugging. + + + + #### `ignore-scripts` * Default: false diff --git a/deps/npm/docs/content/commands/npm-install-test.md b/deps/npm/docs/content/commands/npm-install-test.md index c464e5bd0b8c64..8975fc4ce61dec 100644 --- a/deps/npm/docs/content/commands/npm-install-test.md +++ b/deps/npm/docs/content/commands/npm-install-test.md @@ -6,20 +6,30 @@ description: Install package(s) and run tests ### Synopsis + + + + ```bash -npm install-test (with no args, in package dir) -npm install-test [<@scope>/] -npm install-test [<@scope>/]@ -npm install-test [<@scope>/]@ -npm install-test [<@scope>/]@ +npm install-test [<@scope>/] +npm install-test [<@scope>/]@ +npm install-test [<@scope>/]@ +npm install-test [<@scope>/]@ +npm install-test @npm: +npm install-test npm install-test npm install-test -npm install-test +npm install-test +npm install-test / -alias: npm it -common options: [--save|--save-dev|--save-optional] [--save-exact] [--dry-run] +alias: it ``` + + + + + ### Description This command runs an `npm install` followed immediately by an `npm test`. It @@ -32,13 +42,15 @@ takes exactly the same arguments as `npm install`. #### `save` -* Default: true +* Default: `true` unless when using `npm update` where it defaults to `false` * Type: Boolean -Save installed packages to a package.json file as dependencies. +Save installed packages to a `package.json` file as dependencies. When used with the `npm rm` command, removes the dependency from -package.json. +`package.json`. + +Will also prevent writing to `package-lock.json` if set to `false`. @@ -99,6 +111,27 @@ will be preferred. +#### `omit` + +* Default: 'dev' if the `NODE_ENV` environment variable is set to + 'production', otherwise empty. +* Type: "dev", "optional", or "peer" (can be set multiple times) + +Dependency types to omit from the installation tree on disk. + +Note that these dependencies _are_ still resolved and added to the +`package-lock.json` or `npm-shrinkwrap.json` file. They are just not +physically installed on disk. + +If a package type appears in both the `--include` and `--omit` lists, then +it will be included. + +If the resulting omit list includes `'dev'`, then the `NODE_ENV` environment +variable will be set to `'production'` for all lifecycle scripts. + + + + #### `strict-peer-deps` * Default: false @@ -133,26 +166,22 @@ When package package-locks are disabled, automatic pruning of extraneous modules will also be disabled. To remove extraneous modules with package-locks disabled use `npm prune`. +This configuration does not affect `npm ci`. + -#### `omit` - -* Default: 'dev' if the `NODE_ENV` environment variable is set to - 'production', otherwise empty. -* Type: "dev", "optional", or "peer" (can be set multiple times) - -Dependency types to omit from the installation tree on disk. +#### `foreground-scripts` -Note that these dependencies _are_ still resolved and added to the -`package-lock.json` or `npm-shrinkwrap.json` file. They are just not -physically installed on disk. +* Default: false +* Type: Boolean -If a package type appears in both the `--include` and `--omit` lists, then -it will be included. +Run all build scripts (ie, `preinstall`, `install`, and `postinstall`) +scripts for installed packages in the foreground process, sharing standard +input, output, and error with the main npm process. -If the resulting omit list includes `'dev'`, then the `NODE_ENV` environment -variable will be set to `'production'` for all lifecycle scripts. +Note that this will generally make installs run slower, and be much noisier, +but can be useful for debugging. @@ -285,6 +314,8 @@ When false, specifying individual workspaces via the `workspace` config, or all workspaces via the `workspaces` flag, will cause npm to operate only on the specified workspaces, and not on the root project. +This value is not exported to the environment for child processes. + diff --git a/deps/npm/docs/content/commands/npm-install.md b/deps/npm/docs/content/commands/npm-install.md index f83ff511991b70..259ac41eaf05df 100644 --- a/deps/npm/docs/content/commands/npm-install.md +++ b/deps/npm/docs/content/commands/npm-install.md @@ -6,23 +6,30 @@ description: Install a package ### Synopsis + + + + ```bash -npm install (with no args, in package dir) -npm install [<@scope>/] -npm install [<@scope>/]@ -npm install [<@scope>/]@ -npm install [<@scope>/]@ +npm install [<@scope>/] +npm install [<@scope>/]@ +npm install [<@scope>/]@ +npm install [<@scope>/]@ npm install @npm: -npm install :/ -npm install +npm install npm install npm install -npm install +npm install +npm install / -aliases: npm i, npm add -common options: [-P|--save-prod|-D|--save-dev|-O|--save-optional|--save-peer] [-E|--save-exact] [-B|--save-bundle] [--no-save] [--dry-run] +aliases: add, i, in, ins, inst, insta, instal, isnt, isnta, isntal, isntall ``` + + + + + ### Description This command installs a package and any packages that it depends on. If the @@ -425,13 +432,15 @@ These are some of the most common options related to installation. #### `save` -* Default: true +* Default: `true` unless when using `npm update` where it defaults to `false` * Type: Boolean -Save installed packages to a package.json file as dependencies. +Save installed packages to a `package.json` file as dependencies. When used with the `npm rm` command, removes the dependency from -package.json. +`package.json`. + +Will also prevent writing to `package-lock.json` if set to `false`. @@ -492,6 +501,27 @@ will be preferred. +#### `omit` + +* Default: 'dev' if the `NODE_ENV` environment variable is set to + 'production', otherwise empty. +* Type: "dev", "optional", or "peer" (can be set multiple times) + +Dependency types to omit from the installation tree on disk. + +Note that these dependencies _are_ still resolved and added to the +`package-lock.json` or `npm-shrinkwrap.json` file. They are just not +physically installed on disk. + +If a package type appears in both the `--include` and `--omit` lists, then +it will be included. + +If the resulting omit list includes `'dev'`, then the `NODE_ENV` environment +variable will be set to `'production'` for all lifecycle scripts. + + + + #### `strict-peer-deps` * Default: false @@ -526,26 +556,22 @@ When package package-locks are disabled, automatic pruning of extraneous modules will also be disabled. To remove extraneous modules with package-locks disabled use `npm prune`. +This configuration does not affect `npm ci`. + -#### `omit` - -* Default: 'dev' if the `NODE_ENV` environment variable is set to - 'production', otherwise empty. -* Type: "dev", "optional", or "peer" (can be set multiple times) - -Dependency types to omit from the installation tree on disk. +#### `foreground-scripts` -Note that these dependencies _are_ still resolved and added to the -`package-lock.json` or `npm-shrinkwrap.json` file. They are just not -physically installed on disk. +* Default: false +* Type: Boolean -If a package type appears in both the `--include` and `--omit` lists, then -it will be included. +Run all build scripts (ie, `preinstall`, `install`, and `postinstall`) +scripts for installed packages in the foreground process, sharing standard +input, output, and error with the main npm process. -If the resulting omit list includes `'dev'`, then the `NODE_ENV` environment -variable will be set to `'production'` for all lifecycle scripts. +Note that this will generally make installs run slower, and be much noisier, +but can be useful for debugging. @@ -678,6 +704,8 @@ When false, specifying individual workspaces via the `workspace` config, or all workspaces via the `workspaces` flag, will cause npm to operate only on the specified workspaces, and not on the root project. +This value is not exported to the environment for child processes. + diff --git a/deps/npm/docs/content/commands/npm-link.md b/deps/npm/docs/content/commands/npm-link.md index d4ef41ae964628..fb7e46de04a090 100644 --- a/deps/npm/docs/content/commands/npm-link.md +++ b/deps/npm/docs/content/commands/npm-link.md @@ -6,13 +6,22 @@ description: Symlink a package folder ### Synopsis + + + + ```bash npm link (in package dir) npm link [<@scope>/][@] -alias: npm ln +alias: ln ``` + + + + + ### Description This is handy for installing your own stuff, so that you can work on it and @@ -116,13 +125,15 @@ workspace(s). #### `save` -* Default: true +* Default: `true` unless when using `npm update` where it defaults to `false` * Type: Boolean -Save installed packages to a package.json file as dependencies. +Save installed packages to a `package.json` file as dependencies. When used with the `npm rm` command, removes the dependency from -package.json. +`package.json`. + +Will also prevent writing to `package-lock.json` if set to `false`. @@ -217,6 +228,8 @@ When package package-locks are disabled, automatic pruning of extraneous modules will also be disabled. To remove extraneous modules with package-locks disabled use `npm prune`. +This configuration does not affect `npm ci`. + @@ -369,6 +382,8 @@ When false, specifying individual workspaces via the `workspace` config, or all workspaces via the `workspaces` flag, will cause npm to operate only on the specified workspaces, and not on the root project. +This value is not exported to the environment for child processes. + diff --git a/deps/npm/docs/content/commands/npm-logout.md b/deps/npm/docs/content/commands/npm-logout.md index cb7c8496fb4791..f0dd5cb856eaee 100644 --- a/deps/npm/docs/content/commands/npm-logout.md +++ b/deps/npm/docs/content/commands/npm-logout.md @@ -6,10 +6,19 @@ description: Log out of the registry ### Synopsis + + + + ```bash -npm logout [--registry=] [--scope=<@scope>] +npm logout ``` + + + + + Note: This command is unaware of workspaces. ### Description diff --git a/deps/npm/docs/content/commands/npm-ls.md b/deps/npm/docs/content/commands/npm-ls.md index 3b33f0a3605e08..8d4799777e20f0 100644 --- a/deps/npm/docs/content/commands/npm-ls.md +++ b/deps/npm/docs/content/commands/npm-ls.md @@ -6,12 +6,21 @@ description: List installed packages ### Synopsis + + + + ```bash npm ls [[<@scope>/] ...] -aliases: list, la, ll +alias: list ``` + + + + + ### Description This command will print to stdout all the versions of packages that are @@ -271,6 +280,8 @@ When false, specifying individual workspaces via the `workspace` config, or all workspaces via the `workspaces` flag, will cause npm to operate only on the specified workspaces, and not on the root project. +This value is not exported to the environment for child processes. + diff --git a/deps/npm/docs/content/commands/npm-org.md b/deps/npm/docs/content/commands/npm-org.md index 2f08f611529925..975581c860df64 100644 --- a/deps/npm/docs/content/commands/npm-org.md +++ b/deps/npm/docs/content/commands/npm-org.md @@ -6,12 +6,23 @@ description: Manage orgs ### Synopsis + + + + ```bash -npm org set [developer | admin | owner] -npm org rm -npm org ls [] +npm org set orgname username [developer | admin | owner] +npm org rm orgname username +npm org ls orgname [] + +alias: ogr ``` + + + + + Note: This command is unaware of workspaces. ### Example diff --git a/deps/npm/docs/content/commands/npm-outdated.md b/deps/npm/docs/content/commands/npm-outdated.md index 1b58a6afda64bb..6fa026550e7477 100644 --- a/deps/npm/docs/content/commands/npm-outdated.md +++ b/deps/npm/docs/content/commands/npm-outdated.md @@ -6,10 +6,19 @@ description: Check for outdated packages ### Synopsis + + + + ```bash npm outdated [[<@scope>/] ...] ``` + + + + + ### Description This command will check the registry to see if any (or, specific) installed diff --git a/deps/npm/docs/content/commands/npm-owner.md b/deps/npm/docs/content/commands/npm-owner.md index 74e7f84af6c804..0779984e19a9db 100644 --- a/deps/npm/docs/content/commands/npm-owner.md +++ b/deps/npm/docs/content/commands/npm-owner.md @@ -6,14 +6,23 @@ description: Manage package owners ### Synopsis + + + + ```bash npm owner add [<@scope>/] npm owner rm [<@scope>/] npm owner ls [<@scope>/] -aliases: author +alias: author ``` + + + + + Note: This command is unaware of workspaces. ### Description diff --git a/deps/npm/docs/content/commands/npm-pack.md b/deps/npm/docs/content/commands/npm-pack.md index 53945986837b94..fa6c005e9d2287 100644 --- a/deps/npm/docs/content/commands/npm-pack.md +++ b/deps/npm/docs/content/commands/npm-pack.md @@ -6,10 +6,19 @@ description: Create a tarball from a package ### Synopsis + + + + ```bash -npm pack [[<@scope>/]...] [--dry-run] [--json] +npm pack [[<@scope>/]...] ``` + + + + + ### Configuration @@ -113,6 +122,8 @@ When false, specifying individual workspaces via the `workspace` config, or all workspaces via the `workspaces` flag, will cause npm to operate only on the specified workspaces, and not on the root project. +This value is not exported to the environment for child processes. + diff --git a/deps/npm/docs/content/commands/npm-ping.md b/deps/npm/docs/content/commands/npm-ping.md index c59a56e611e54f..161d7292f8c977 100644 --- a/deps/npm/docs/content/commands/npm-ping.md +++ b/deps/npm/docs/content/commands/npm-ping.md @@ -6,10 +6,19 @@ description: Ping npm registry ### Synopsis + + + + ```bash -npm ping [--registry ] +npm ping ``` + + + + + Note: This command is unaware of workspaces. ### Description diff --git a/deps/npm/docs/content/commands/npm-pkg.md b/deps/npm/docs/content/commands/npm-pkg.md index beee9c1c4e78a9..576e1335efbba9 100644 --- a/deps/npm/docs/content/commands/npm-pkg.md +++ b/deps/npm/docs/content/commands/npm-pkg.md @@ -6,12 +6,23 @@ description: Manages your package.json ### Synopsis + + + + ```bash -npm pkg get [ [. ...]] -npm pkg set = [.= ...] -npm pkg delete [. ...] +npm pkg set = [= ...] +npm pkg get [ [ ...]] +npm pkg delete [ ...] +npm pkg set [[].= ...] +npm pkg set [[].= ...] ``` + + + + + ### Description A command that automates the management of `package.json` files. @@ -188,6 +199,7 @@ mistakes, unnecessary performance degradation, and malicious input. * Allow conflicting peerDependencies to be installed in the root project. * Implicitly set `--yes` during `npm init`. * Allow clobbering existing values in `npm pkg` +* Allow unpublishing of entire packages (not just a single version). If you don't have a clear idea of what you want to do, it is strongly recommended that you do not use this option! diff --git a/deps/npm/docs/content/commands/npm-prefix.md b/deps/npm/docs/content/commands/npm-prefix.md index 276a9e9e699100..39328bcc88a143 100644 --- a/deps/npm/docs/content/commands/npm-prefix.md +++ b/deps/npm/docs/content/commands/npm-prefix.md @@ -6,10 +6,19 @@ description: Display prefix ### Synopsis + + + + ```bash npm prefix [-g] ``` + + + + + Note: This command is unaware of workspaces. ### Description diff --git a/deps/npm/docs/content/commands/npm-profile.md b/deps/npm/docs/content/commands/npm-profile.md index cecc48518dbdb7..af1f9d8aa10633 100644 --- a/deps/npm/docs/content/commands/npm-profile.md +++ b/deps/npm/docs/content/commands/npm-profile.md @@ -6,14 +6,22 @@ description: Change settings on your registry profile ### Synopsis + + + + ```bash -npm profile get [--json|--parseable] [] -npm profile set [--json|--parseable] -npm profile set password -npm profile enable-2fa [auth-and-writes|auth-only] +npm profile enable-2fa [auth-only|auth-and-writes] npm profile disable-2fa +npm profile get [] +npm profile set ``` + + + + + Note: This command is unaware of workspaces. ### Description diff --git a/deps/npm/docs/content/commands/npm-prune.md b/deps/npm/docs/content/commands/npm-prune.md index 658ab2610e0ed9..81dccf889ce4d9 100644 --- a/deps/npm/docs/content/commands/npm-prune.md +++ b/deps/npm/docs/content/commands/npm-prune.md @@ -6,10 +6,19 @@ description: Remove extraneous packages ### Synopsis + + + + ```bash -npm prune [[<@scope>/]...] [--production] [--dry-run] [--json] +npm prune [[<@scope>/]...] ``` + + + + + ### Description This command removes "extraneous" packages. If a package name is provided, @@ -90,6 +99,36 @@ Not supported by all npm commands. +#### `foreground-scripts` + +* Default: false +* Type: Boolean + +Run all build scripts (ie, `preinstall`, `install`, and `postinstall`) +scripts for installed packages in the foreground process, sharing standard +input, output, and error with the main npm process. + +Note that this will generally make installs run slower, and be much noisier, +but can be useful for debugging. + + + + +#### `ignore-scripts` + +* Default: false +* Type: Boolean + +If true, npm does not run scripts specified in package.json files. + +Note that commands explicitly intended to run a particular script, such as +`npm start`, `npm stop`, `npm restart`, `npm test`, and `npm run-script` +will still run their intended script if `ignore-scripts` is set, but they +will *not* run any pre- or post-scripts. + + + + #### `workspace` * Default: @@ -147,6 +186,8 @@ When false, specifying individual workspaces via the `workspace` config, or all workspaces via the `workspaces` flag, will cause npm to operate only on the specified workspaces, and not on the root project. +This value is not exported to the environment for child processes. + diff --git a/deps/npm/docs/content/commands/npm-publish.md b/deps/npm/docs/content/commands/npm-publish.md index 6958b1066de7fd..2995f6bc81551e 100644 --- a/deps/npm/docs/content/commands/npm-publish.md +++ b/deps/npm/docs/content/commands/npm-publish.md @@ -6,13 +6,19 @@ description: Publish a package ### Synopsis -```bash -npm publish [|] [--tag ] [--access ] [--otp otpcode] [--dry-run] + + + -Publishes '.' if no argument supplied -Sets tag 'latest' if no --tag specified +```bash +npm publish [] ``` + + + + + ### Description Publishes a package to the registry so that it can be installed by name. @@ -232,6 +238,8 @@ When false, specifying individual workspaces via the `workspace` config, or all workspaces via the `workspaces` flag, will cause npm to operate only on the specified workspaces, and not on the root project. +This value is not exported to the environment for child processes. + diff --git a/deps/npm/docs/content/commands/npm-rebuild.md b/deps/npm/docs/content/commands/npm-rebuild.md index 75e71c60e6810a..bddd18c2bcaf43 100644 --- a/deps/npm/docs/content/commands/npm-rebuild.md +++ b/deps/npm/docs/content/commands/npm-rebuild.md @@ -6,12 +6,21 @@ description: Rebuild a package ### Synopsis + + + + ```bash npm rebuild [[<@scope>/][@] ...] alias: rb ``` + + + + + ### Description This command runs the `npm build` command on the matched folders. This is @@ -61,6 +70,21 @@ systems. +#### `foreground-scripts` + +* Default: false +* Type: Boolean + +Run all build scripts (ie, `preinstall`, `install`, and `postinstall`) +scripts for installed packages in the foreground process, sharing standard +input, output, and error with the main npm process. + +Note that this will generally make installs run slower, and be much noisier, +but can be useful for debugging. + + + + #### `ignore-scripts` * Default: false @@ -133,6 +157,8 @@ When false, specifying individual workspaces via the `workspace` config, or all workspaces via the `workspaces` flag, will cause npm to operate only on the specified workspaces, and not on the root project. +This value is not exported to the environment for child processes. + diff --git a/deps/npm/docs/content/commands/npm-repo.md b/deps/npm/docs/content/commands/npm-repo.md index cd47fde47127ee..404c724ff02906 100644 --- a/deps/npm/docs/content/commands/npm-repo.md +++ b/deps/npm/docs/content/commands/npm-repo.md @@ -6,10 +6,19 @@ description: Open package repository page in the browser ### Synopsis + + + + ```bash npm repo [ [ ...]] ``` + + + + + ### Description This command tries to guess at the likely location of a package's @@ -94,6 +103,8 @@ When false, specifying individual workspaces via the `workspace` config, or all workspaces via the `workspaces` flag, will cause npm to operate only on the specified workspaces, and not on the root project. +This value is not exported to the environment for child processes. + diff --git a/deps/npm/docs/content/commands/npm-restart.md b/deps/npm/docs/content/commands/npm-restart.md index 80f8ab77ef0183..f01cd014e74357 100644 --- a/deps/npm/docs/content/commands/npm-restart.md +++ b/deps/npm/docs/content/commands/npm-restart.md @@ -6,10 +6,19 @@ description: Restart a package ### Synopsis + + + + ```bash npm restart [-- ] ``` + + + + + ### Description This restarts a project. It is equivalent to running `npm run-script diff --git a/deps/npm/docs/content/commands/npm-root.md b/deps/npm/docs/content/commands/npm-root.md index 98d1108d33f758..40b58e4b33d0b2 100644 --- a/deps/npm/docs/content/commands/npm-root.md +++ b/deps/npm/docs/content/commands/npm-root.md @@ -6,10 +6,19 @@ description: Display npm root ### Synopsis + + + + ```bash -npm root [-g] +npm root ``` + + + + + ### Description Print the effective `node_modules` folder to standard out. diff --git a/deps/npm/docs/content/commands/npm-run-script.md b/deps/npm/docs/content/commands/npm-run-script.md index 6dd602d03e00ad..73c4b1c7a748a7 100644 --- a/deps/npm/docs/content/commands/npm-run-script.md +++ b/deps/npm/docs/content/commands/npm-run-script.md @@ -6,14 +6,21 @@ description: Run arbitrary package scripts ### Synopsis + + + + ```bash -npm run-script [--if-present] [--silent] [-- ] -npm run-script [--workspace=] -npm run-script [--workspaces] +npm run-script [-- ] aliases: run, rum, urn ``` + + + + + ### Description This runs an arbitrary command from a package's `"scripts"` object. If no @@ -196,6 +203,8 @@ When false, specifying individual workspaces via the `workspace` config, or all workspaces via the `workspaces` flag, will cause npm to operate only on the specified workspaces, and not on the root project. +This value is not exported to the environment for child processes. + @@ -211,6 +220,8 @@ it's present and fail if the script fails. This is useful, for example, when running scripts that may only apply for some builds in an otherwise generic CI setup. +This value is not exported to the environment for child processes. + diff --git a/deps/npm/docs/content/commands/npm-search.md b/deps/npm/docs/content/commands/npm-search.md index 252822e7198443..340dea9684d005 100644 --- a/deps/npm/docs/content/commands/npm-search.md +++ b/deps/npm/docs/content/commands/npm-search.md @@ -6,12 +6,21 @@ description: Search for packages ### Synopsis + + + + ```bash -npm search [-l|--long] [--json] [--parseable] [--no-description] [search terms ...] +npm search [search terms ...] -aliases: s, se, find +aliases: find, s, se ``` + + + + + Note: This command is unaware of workspaces. ### Description diff --git a/deps/npm/docs/content/commands/npm-set-script.md b/deps/npm/docs/content/commands/npm-set-script.md index 869ceede045ae3..0fc267f760c81f 100644 --- a/deps/npm/docs/content/commands/npm-set-script.md +++ b/deps/npm/docs/content/commands/npm-set-script.md @@ -7,10 +7,19 @@ description: Set tasks in the scripts section of package.json ### Synopsis An npm command that lets you create a task in the `scripts` section of the `package.json`. + + + + ```bash npm set-script [ + + + + + + + + + +ECMAScript Best Practices – test262 + + + +
+ +
+
+
+ Loading... + Loading... +
+
+
+ +
+ +
+

ECMAScript Best Practices test262 + ECMAScript.org

+
+ + +
+ +
+

What is test262 Best Practices?

+

test262 Best Practices is a supplemental test suite to test262 containing test cases that are not required by the ECMAScript specification, but deemed best practices for JavaScript implementers by Ecma's TC-39 committee.

+ +

Running the Tests

+

Click the “Run” tab at the top of this page for instructions and follow the instructions to run the tests.

+ + + +
+ +
+

Development

+

Test262 is being developed by the members of Ecma TC39. Ecma's intellectual property policies, permit only Ecma + members to directly contribute code to the project. However, a public mailing list is used to coordinate development of Test262. If you wish to participate in the discussion please subscribe. Bug reports and suggestions should be sent to the mailing list. +

+

+ Ecma members can find detailed instructions on Test262 development procedures at the Test262 Wiki. +

+
+ +
+ +

Please click on the Run All button to run all the tests. Once you start the test you may pause the test anytime by clicking on the Pause button. You can click on the Results tab once the test is completed or after pausing the test. The Reset button is for restarting the test run. You may run individual tests by clicking the Run button next to the tests listed below. If you wish to run several chapters in sequence, but not the entire test suite, click the Select button for the chapters you wish to run and then click the Run Selected button.

+ + +
+
+
+ + + + + Run All + Run Selected Tests + Pause + Resume + Reset +
+
+
+

+ Timer Value(ms) : +

+ + +
+ Tests to run:  | + Total tests ran: | + Pass: | + Fail: | + Failed to load: +

+
+ + +
+
+
+ + +
+
+
+
+ Test suite version:  | Test suite date: +
+
+ +
+
+
+ +
+
+
Total tests:
+ Passed: | Failed: | + Failed to load: +
+ +
+
+
Test results will be displayed after the tests are executed using the Run page.
+
+
+ Test suite version:  | Test suite date: +
+ +
+  100%  +  75% to 99.9%  +  50% to 75%   +  less than 50% +
+
+
+
+ + + + + diff --git a/deps/v8/third_party/test262-harness/src/templates/runner.intl402.html b/deps/v8/third_party/test262-harness/src/templates/runner.intl402.html new file mode 100644 index 00000000000000..349c637954e13a --- /dev/null +++ b/deps/v8/third_party/test262-harness/src/templates/runner.intl402.html @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + +ECMAScript Internationalization – test402 + + + +
+ +
+
+
+ Loading... + Loading... +
+
+
+ +
+ +
+

ECMAScript Internationalization test402 + ECMAScript.org

+
+ + +
+ +
+

What is test402?

+

test402 is a test suite intended to check agreement between JavaScript implementations and the ECMAScript Internationalization API Specification. + The test suite contains many individual tests, each of which tests some specific requirements of the ECMAScript Internationalization API Specification.

+

What is the ECMAScript Internationalization API?

+

The ECMAScript Internationalization API is a complement to the ECMAScript Language Specification, 5.1 edition. + It enables internationalization of JavaScript applications by providing collation (string comparison), number formatting, and date and time formatting, and lets applications choose the language and tailor the functionality to their needs. + The ECMAScript Internationalization API Specification 1.0 was approved as an official Ecma standard by the Ecma General Assembly in December 2012. + The ECMAScript Internationalization 1.0 standard is available in + PDF, + HTML, and + EPUB + versions from the Ecma International web site.

+

What is ECMAScript?

+

"ECMAScript" is the name under which the language more commonly known as "JavaScript" is standardized. Development of the ECMAScript standard is the responsibility of Technical Committee 39 (TC39) of Ecma International. + The ECMAScript Language Specification standard is officially known as ECMA-262. + ECMAScript 5.1 (or just ES5.1) is short hand for the "ECMA-262, 5.1 Edition ECMAScript Language Specification" the official name of the current edition of the standard. + ECMAScript 5.1 was approved as an official Ecma standard by the Ecma General Assembly in June 2011. + The ECMAScript 5.1 standard is available in PDF and HTML versions from the Ecma International web site.

+

Who creates and maintains test402?

+

Development of test402 is a project of Ecma TC39. + The testing framework and individual tests are created by member organizations of TC39 and contributed to Ecma for use in test402. + For more information about how test402 is developed and maintained click the “Development” tab at the top of this page.

+

What is the status of test402?

+

test402 is very very very incomplete. + It is still undergoing active development.

+

Where can I find out more?

+

Please visit our Frequently Asked Questions section on the ECMAScript Wiki.

+ +

Running the Tests

+

Click the “Run” tab at the top of this page for instructions and follow the instructions to run the tests.

+ + + +
+ +
+

Development

+

Test402 is being developed by the members of Ecma TC39. Ecma's intellectual property policies permit only Ecma + members to directly contribute code to the project. However, a public mailing list is used to coordinate development of test402 and its sibling test262. If you wish to participate in the discussion please subscribe. Bug reports and suggestions should be sent to the mailing list. +

+
+ +
+ +

Please click on the Run All button to run all the tests. Once you start the test you may pause the test anytime by clicking on the Pause button. You can click on the Results tab once the test is completed or after pausing the test. The Reset button is for restarting the test run. You may run individual tests by clicking the Run button next to the tests listed below. If you wish to run several chapters in sequence, but not the entire test suite, click the Select button for the chapters you wish to run and then click the Run Selected button.

+ + +
+
+
+ + + + + Run All + Run Selected Tests + Pause + Resume + Reset +
+
+
+

+ Timer Value(ms) : +

+ + +
+ Tests to run:  | + Total tests ran: | + Pass: | + Fail: | + Failed to load: +

+
+ + +
+
+
+ + +
+
+
+
+ Test suite version:  | Test suite date: +
+
+ +
+
+
+ +
+
+
Total tests:
+ Passed: | Failed: | + Failed to load: +
+ +
+
+
Test results will be displayed after the tests are executed using the Run page.
+
+
+ Test suite version:  | Test suite date: +
+ +
+  100%  +  75% to 99.9%  +  50% to 75%   +  less than 50% +
+
+
+
+ + + + + diff --git a/deps/v8/third_party/test262-harness/src/templates/runner.test262.html b/deps/v8/third_party/test262-harness/src/templates/runner.test262.html new file mode 100644 index 00000000000000..6eb493f1fd9981 --- /dev/null +++ b/deps/v8/third_party/test262-harness/src/templates/runner.test262.html @@ -0,0 +1,203 @@ + + + + + + + + + + + + + + +ECMAScript Language – test262 + + + +
+ +
+
+
+ Loading... + Loading... +
+
+
+ +
+ +
+

ECMAScript Language test262 + ECMAScript.org

+
+ + +
+ +
+

What is test262?

+

test262 is a test suite intended to check agreement between JavaScript implementations and ECMA-262, the ECMAScript Language Specification (currently 5.1 Edition). + The test suite contains thousands of individual tests, each of which tests some specific requirements of the ECMAScript Language Specification.

+

What is ECMAScript?

+

"ECMAScript" is the name under which the language more commonly known as "JavaScript" is standardized. Development of the ECMAScript standard is the responsibility of Technical Committee 39 (TC39) of Ecma International. + The ECMAScript Language Specification standard is officially known as ECMA-262. + ECMAScript 5.1 (or just ES5.1) is short hand for the "ECMA-262, 5.1 Edition ECMAScript Language Specification" the official name of the current edition of the standard. + ECMAScript 5.1 was approved as an official Ecma standard by the Ecma General Assembly in June 2011. + The ECMAScript 5.1 standard is available in PDF and HTML versions from the Ecma International web site.

+

Who creates and maintains test262?

+

+ Development of test262 is a project of Ecma TC39. The + testing framework and individual tests are created by + developers all over the world and contributed to Ecma for + use in test262. For more information about how test262 is + developed and maintained, click the + “Development” tab at the top of this page. +

+

What is the status of test262?

+

test262 is not yet complete. It is still undergoing active development. Some portions of the ES5 specification have very complete test coverage while other portions of the specification have only partial test coverage. Some tests may be invalid or may yield false positive or false negative results. A perfect passing score on test262 does not guarantee that a JavaScript implementation perfectly supports ES5. Because tests are being actively added and modified, tests results from different days or times may not be directly comparable. Click the “Development” tab at the top of this page for instructions for reporting test262 bugs.

+

Where can I find out more?

+

Please visit our Frequently Asked Questions section on the ECMAScript Wiki.

+ +

Running the Tests

+

Click the “Run” tab at the top of this page for instructions and follow the instructions to run the tests.

+ + + +
+ +
+

Development

+

+ Test262 is being developed as an open source project and + the maintainers are accepting patches from the community. + The project is maintained using the + git version control system and is currently + hosted on GitHub.com. Bug reports and patches may be + submitted to the GitHub repository. +

+ +

+ A public + mailing list is used to coordinate development of + test262. If you wish to participate in the discussion, + please subscribe. +

+
+ +
+ +

Please click on the Run All button to run all the tests. Once you start the test you may pause the test anytime by clicking on the Pause button. You can click on the Results tab once the test is completed or after pausing the test. The Reset button is for restarting the test run. You may run individual tests by clicking the Run button next to the tests listed below. If you wish to run several chapters in sequence, but not the entire test suite, click the Select button for the chapters you wish to run and then click the Run Selected button.

+ + +
+
+
+ + + + + Run All + Run Selected Tests + Pause + Resume + Reset +
+
+
+

+ Timer Value(ms) : +

+ + +
+ Tests To run:  | + Total tests ran: | + Pass: | + Fail: | + Failed to load: +

+
+ + +
+
+
+ + +
+
+
+
+ Test suite version:  | Test suite date: +
+
+ +
+
+
+ +
+
+
Total tests:
+ Passed: | Failed: | + Failed to load: +
+ +
+
+
Test results will be displayed after the tests are executed using the Run page.
+
+
+ Test suite version.:  | Test suite date: +
+ +
+  100%  +  75% to 99.9%  +  50% to 75%   +  less than 50% +
+
+
+
+ + + + + diff --git a/deps/v8/third_party/test262-harness/src/test262.py b/deps/v8/third_party/test262-harness/src/test262.py new file mode 100755 index 00000000000000..c92e5bf0cfb94f --- /dev/null +++ b/deps/v8/third_party/test262-harness/src/test262.py @@ -0,0 +1,664 @@ +#!/usr/bin/env python +# Copyright 2009 the Sputnik authors. All rights reserved. +# This code is governed by the BSD license found in the LICENSE file. + +# This is derived from sputnik.py, the Sputnik console test runner, +# with elements from packager.py, which is separately +# copyrighted. TODO: Refactor so there is less duplication between +# test262.py and packager.py. + + +import logging +import optparse +import os +from os import path +import platform +import re +import subprocess +import sys +import tempfile +import time +import xml.dom.minidom +import datetime +import shutil +import json +import stat +import xml.etree.ElementTree as xmlj +import unicodedata +from collections import Counter + + +from parseTestRecord import parseTestRecord, stripHeader + +from _packagerConfig import * + +class Test262Error(Exception): + def __init__(self, message): + self.message = message + +def ReportError(s): + raise Test262Error(s) + + + +if not os.path.exists(EXCLUDED_FILENAME): + print "Cannot generate (JSON) test262 tests without a file," + \ + " %s, showing which tests have been disabled!" % EXCLUDED_FILENAME + sys.exit(1) +EXCLUDE_LIST = xml.dom.minidom.parse(EXCLUDED_FILENAME) +EXCLUDE_REASON = EXCLUDE_LIST.getElementsByTagName("reason") +EXCLUDE_LIST = EXCLUDE_LIST.getElementsByTagName("test") +EXCLUDE_LIST = [x.getAttribute("id") for x in EXCLUDE_LIST] + + +def BuildOptions(): + result = optparse.OptionParser() + result.add_option("--command", default=None, help="The command-line to run") + result.add_option("--tests", default=path.abspath('.'), + help="Path to the tests") + result.add_option("--cat", default=False, action="store_true", + help="Print packaged test code that would be run") + result.add_option("--summary", default=False, action="store_true", + help="Print summary after running tests") + result.add_option("--full-summary", default=False, action="store_true", + help="Print summary and test output after running tests") + result.add_option("--strict_only", default=False, action="store_true", + help="Test only strict mode") + result.add_option("--non_strict_only", default=False, action="store_true", + help="Test only non-strict mode") + result.add_option("--unmarked_default", default="both", + help="default mode for tests of unspecified strictness") + result.add_option("--logname", help="Filename to save stdout to") + result.add_option("--junitname", help="Filename to save test results in JUnit XML format") + result.add_option("--loglevel", default="warning", + help="sets log level to debug, info, warning, error, or critical") + result.add_option("--print-handle", default="print", help="Command to print from console") + result.add_option("--list-includes", default=False, action="store_true", + help="List includes required by tests") + return result + + +def ValidateOptions(options): + if not options.command: + ReportError("A --command must be specified.") + if not path.exists(options.tests): + ReportError("Couldn't find test path '%s'" % options.tests) + + +placeHolderPattern = re.compile(r"\{\{(\w+)\}\}") + + +def IsWindows(): + p = platform.system() + return (p == 'Windows') or (p == 'Microsoft') + + +class TempFile(object): + + def __init__(self, suffix="", prefix="tmp", text=False): + self.suffix = suffix + self.prefix = prefix + self.text = text + self.fd = None + self.name = None + self.is_closed = False + self.Open() + + def Open(self): + (self.fd, self.name) = tempfile.mkstemp( + suffix = self.suffix, + prefix = self.prefix, + text = self.text) + + def Write(self, str): + os.write(self.fd, str) + + def Read(self): + f = file(self.name) + result = f.read() + f.close() + return result + + def Close(self): + if not self.is_closed: + self.is_closed = True + os.close(self.fd) + + def Dispose(self): + try: + self.Close() + os.unlink(self.name) + except OSError, e: + logging.error("Error disposing temp file: %s", str(e)) + + +class TestResult(object): + + def __init__(self, exit_code, stdout, stderr, case): + self.exit_code = exit_code + self.stdout = stdout + self.stderr = stderr + self.case = case + + def ReportOutcome(self, long_format): + name = self.case.GetName() + mode = self.case.GetMode() + if self.HasUnexpectedOutcome(): + if self.case.IsNegative(): + print "=== %s was expected to fail in %s, but didn't ===" % (name, mode) + print "--- expected error: %s ---\n" % self.case.GetNegativeType() + else: + if long_format: + print "=== %s failed in %s ===" % (name, mode) + else: + print "%s in %s: " % (name, mode) + self.WriteOutput(sys.stdout) + if long_format: + print "===" + elif self.case.IsNegative(): + print "%s failed in %s as expected" % (name, mode) + else: + print "%s passed in %s" % (name, mode) + + def WriteOutput(self, target): + out = self.stdout.strip() + if len(out) > 0: + target.write("--- output --- \n %s" % out) + err = self.stderr.strip() + if len(err) > 0: + target.write("--- errors --- \n %s" % err) + + # This is a way to make the output from the "whitespace" tests into valid XML + def SafeFormat(self, msg): + try: + msg = msg.encode(encoding='ascii', errors='strict') + msg = msg.replace('\u000Bx', '?') + msg = msg.replace('\u000Cx', '?') + except: + return 'Output contained invalid characters' + + def XmlAssemble(self, result): + test_name = self.case.GetName() + test_mode = self.case.GetMode() + testCaseElement = xmlj.Element("testcase") + testpath = self.TestPathManipulation(test_name) + testCaseElement.attrib["classname"] = "%s.%s" % (testpath[0] , testpath[1]) + testCaseElement.attrib["name"] = "%s %s" % (testpath[2].replace('.','_') , test_mode) + if self.HasUnexpectedOutcome(): + failureElement = xmlj.Element("failure") + out = self.stdout.strip().decode('utf-8') + err = self.stderr.strip().decode('utf-8') + if len(out) > 0: + failureElement.text = self.SafeFormat(out) + if len(err) > 0: + failureElement.text = self.SafeFormat(err) + testCaseElement.append(failureElement) + return testCaseElement + + def TestPathManipulation(self, test_name): + testdirlist = test_name.split('/') + testcase = testdirlist.pop() + testclass = testdirlist.pop() + testclass = testclass.replace('.','_') + if len(testdirlist) >= 1: + testpackage = testdirlist.pop(0) + else: + testpackage = testclass + return(testpackage,testclass,testcase) + + def HasFailed(self): + return self.exit_code != 0 + + def AsyncHasFailed(self): + return 'Test262:AsyncTestComplete' not in self.stdout + + def HasUnexpectedOutcome(self): + if self.case.IsAsyncTest(): + return self.AsyncHasFailed() or self.HasFailed() + elif self.case.IsNegative(): + return not (self.HasFailed() and self.case.NegativeMatch(self.GetErrorOutput())) + else: + return self.HasFailed() + + def GetErrorOutput(self): + if len(self.stderr) != 0: + return self.stderr + return self.stdout + + +class TestCase(object): + + def __init__(self, suite, name, full_path, strict_mode): + self.suite = suite + self.name = name + self.full_path = full_path + self.strict_mode = strict_mode + f = open(self.full_path) + self.contents = f.read() + f.close() + testRecord = parseTestRecord(self.contents, name) + self.test = testRecord["test"] + del testRecord["test"] + del testRecord["header"] + testRecord.pop("commentary", None) # do not throw if missing + self.testRecord = testRecord; + + self.validate() + + def NegativeMatch(self, stderr): + neg = re.compile(self.GetNegativeType()) + return re.search(neg, stderr) + + def GetNegative(self): + if not self.IsNegative(): + return None + return self.testRecord["negative"] + + def GetNegativeType(self): + negative = self.GetNegative() + return negative and negative["type"] + + def GetNegativePhase(self): + negative = self.GetNegative() + return negative and negative["phase"] + + def GetName(self): + return path.join(*self.name) + + def GetMode(self): + if self.strict_mode: + return "strict mode" + else: + return "non-strict mode" + + def GetPath(self): + return self.name + + def IsNegative(self): + return 'negative' in self.testRecord + + def IsOnlyStrict(self): + return 'onlyStrict' in self.testRecord + + def IsNoStrict(self): + return 'noStrict' in self.testRecord or self.IsRaw() + + def IsRaw(self): + return 'raw' in self.testRecord + + def IsAsyncTest(self): + return 'async' in self.testRecord + + def GetIncludeList(self): + if self.testRecord.get('includes'): + return self.testRecord['includes'] + return [] + + def GetAdditionalIncludes(self): + return '\n'.join([self.suite.GetInclude(include) for include in self.GetIncludeList()]) + + def GetSource(self): + if self.IsRaw(): + return self.test + + source = self.suite.GetInclude("sta.js") + \ + self.suite.GetInclude("cth.js") + \ + self.suite.GetInclude("assert.js") + + if self.IsAsyncTest(): + source = source + \ + self.suite.GetInclude("timer.js") + \ + self.suite.GetInclude("doneprintHandle.js").replace('print', self.suite.print_handle) + + source = source + \ + self.GetAdditionalIncludes() + \ + self.test + '\n' + + if self.GetNegativePhase() == "early": + source = ("throw 'Expected an early error, but code was executed.';\n" + + source) + + if self.strict_mode: + source = '"use strict";\nvar strict_mode = true;\n' + source + else: + # add comment line so line numbers match in both strict and non-strict version + source = '//"no strict";\nvar strict_mode = false;\n' + source + + return source + + def InstantiateTemplate(self, template, params): + def GetParameter(match): + key = match.group(1) + return params.get(key, match.group(0)) + return placeHolderPattern.sub(GetParameter, template) + + def Execute(self, command): + if IsWindows(): + args = '%s' % command + else: + args = command.split(" ") + stdout = TempFile(prefix="test262-out-") + stderr = TempFile(prefix="test262-err-") + try: + logging.info("exec: %s", str(args)) + process = subprocess.Popen( + args, + shell = IsWindows(), + stdout = stdout.fd, + stderr = stderr.fd + ) + code = process.wait() + out = stdout.Read() + err = stderr.Read() + finally: + stdout.Dispose() + stderr.Dispose() + return (code, out, err) + + def RunTestIn(self, command_template, tmp): + tmp.Write(self.GetSource()) + tmp.Close() + command = self.InstantiateTemplate(command_template, { + 'path': tmp.name + }) + (code, out, err) = self.Execute(command) + return TestResult(code, out, err, self) + + def Run(self, command_template): + tmp = TempFile(suffix=".js", prefix="test262-", text=True) + try: + result = self.RunTestIn(command_template, tmp) + finally: + tmp.Dispose() + return result + + def Print(self): + print self.GetSource() + + def validate(self): + flags = self.testRecord.get("flags") + phase = self.GetNegativePhase() + + if phase not in [None, "early", "runtime"]: + raise TypeError("Invalid value for negative phase: " + phase) + + if not flags: + return + + if 'raw' in flags: + if 'noStrict' in flags: + raise TypeError("The `raw` flag implies the `noStrict` flag") + elif 'onlyStrict' in flags: + raise TypeError( + "The `raw` flag is incompatible with the `onlyStrict` flag") + elif len(self.GetIncludeList()) > 0: + raise TypeError( + "The `raw` flag is incompatible with the `includes` tag") + +class ProgressIndicator(object): + + def __init__(self, count): + self.count = count + self.succeeded = 0 + self.failed = 0 + self.failed_tests = [] + + def HasRun(self, result): + result.ReportOutcome(True) + if result.HasUnexpectedOutcome(): + self.failed += 1 + self.failed_tests.append(result) + else: + self.succeeded += 1 + + +def MakePlural(n): + if (n == 1): + return (n, "") + else: + return (n, "s") + +def PercentFormat(partial, total): + return "%i test%s (%.1f%%)" % (MakePlural(partial) + + ((100.0 * partial)/total,)) + + +class TestSuite(object): + + def __init__(self, root, strict_only, non_strict_only, unmarked_default, print_handle): + # TODO: derive from packagerConfig.py + self.test_root = path.join(root, 'test') + self.lib_root = path.join(root, 'harness') + self.strict_only = strict_only + self.non_strict_only = non_strict_only + self.unmarked_default = unmarked_default + self.print_handle = print_handle + self.include_cache = { } + + + def Validate(self): + if not path.exists(self.test_root): + ReportError("No test repository found") + if not path.exists(self.lib_root): + ReportError("No test library found") + + def IsHidden(self, path): + return path.startswith('.') or path == 'CVS' + + def IsTestCase(self, path): + return path.endswith('.js') + + def ShouldRun(self, rel_path, tests): + if len(tests) == 0: + return True + for test in tests: + if test in rel_path: + return True + return False + + def GetInclude(self, name): + if not name in self.include_cache: + static = path.join(self.lib_root, name) + if path.exists(static): + f = open(static) + contents = stripHeader(f.read()) + contents = re.sub(r'\r\n', '\n', contents) + self.include_cache[name] = contents + "\n" + f.close() + else: + ReportError("Can't find: " + static) + return self.include_cache[name] + + def EnumerateTests(self, tests): + logging.info("Listing tests in %s", self.test_root) + cases = [] + for root, dirs, files in os.walk(self.test_root): + for f in [x for x in dirs if self.IsHidden(x)]: + dirs.remove(f) + dirs.sort() + for f in sorted(files): + if self.IsTestCase(f): + full_path = path.join(root, f) + if full_path.startswith(self.test_root): + rel_path = full_path[len(self.test_root)+1:] + else: + logging.warning("Unexpected path %s", full_path) + rel_path = full_path + if self.ShouldRun(rel_path, tests): + basename = path.basename(full_path)[:-3] + name = rel_path.split(path.sep)[:-1] + [basename] + if EXCLUDE_LIST.count(basename) >= 1: + print 'Excluded: ' + basename + else: + if not self.non_strict_only: + strict_case = TestCase(self, name, full_path, True) + if not strict_case.IsNoStrict(): + if strict_case.IsOnlyStrict() or \ + self.unmarked_default in ['both', 'strict']: + cases.append(strict_case) + if not self.strict_only: + non_strict_case = TestCase(self, name, full_path, False) + if not non_strict_case.IsOnlyStrict(): + if non_strict_case.IsNoStrict() or \ + self.unmarked_default in ['both', 'non_strict']: + cases.append(non_strict_case) + logging.info("Done listing tests") + return cases + + + def PrintSummary(self, progress, logfile): + + def write(s): + if logfile: + self.logf.write(s + "\n") + print s + + print + write("=== Summary ==="); + count = progress.count + succeeded = progress.succeeded + failed = progress.failed + write(" - Ran %i test%s" % MakePlural(count)) + if progress.failed == 0: + write(" - All tests succeeded") + else: + write(" - Passed " + PercentFormat(succeeded, count)) + write(" - Failed " + PercentFormat(failed, count)) + positive = [c for c in progress.failed_tests if not c.case.IsNegative()] + negative = [c for c in progress.failed_tests if c.case.IsNegative()] + if len(positive) > 0: + print + write("Failed Tests") + for result in positive: + write(" %s in %s" % (result.case.GetName(), result.case.GetMode())) + if len(negative) > 0: + print + write("Expected to fail but passed ---") + for result in negative: + write(" %s in %s" % (result.case.GetName(), result.case.GetMode())) + + def PrintFailureOutput(self, progress, logfile): + for result in progress.failed_tests: + if logfile: + self.WriteLog(result) + print + result.ReportOutcome(False) + + def Run(self, command_template, tests, print_summary, full_summary, logname, junitfile): + if not "{{path}}" in command_template: + command_template += " {{path}}" + cases = self.EnumerateTests(tests) + if len(cases) == 0: + ReportError("No tests to run") + progress = ProgressIndicator(len(cases)) + if logname: + self.logf = open(logname, "w") + if junitfile: + self.outfile = open(junitfile, "w") + TestSuitesElement = xmlj.Element("testsuites") + TestSuiteElement = xmlj.Element("testsuite") + TestSuitesElement.append(TestSuiteElement) + TestSuiteElement.attrib["name "] = "test262" + for x in range(len(EXCLUDE_LIST)): + if self.ShouldRun (unicode(EXCLUDE_LIST[x].encode('utf-8','ignore')), tests): + SkipCaseElement = xmlj.Element("testcase") + SkipCaseElement.attrib["classname"] = unicode(EXCLUDE_LIST[x]).encode('utf-8','ignore') + SkipCaseElement.attrib["name"] = unicode(EXCLUDE_LIST[x]).encode('utf-8','ignore') + SkipElement = xmlj.Element("skipped") + SkipElement.attrib["message"] = unicode(EXCLUDE_REASON[x].firstChild.nodeValue) + SkipCaseElement.append(SkipElement) + TestSuiteElement.append(SkipCaseElement) + + for case in cases: + result = case.Run(command_template) + if junitfile: + TestCaseElement = result.XmlAssemble(result) + TestSuiteElement.append(TestCaseElement) + if case == cases[len(cases)-1]: + xmlj.ElementTree(TestSuitesElement).write(junitfile, "UTF-8") + if logname: + self.WriteLog(result) + progress.HasRun(result) + + if print_summary: + self.PrintSummary(progress, logname) + if full_summary: + self.PrintFailureOutput(progress, logname) + else: + print + print "Use --full-summary to see output from failed tests" + print + return progress.failed + + def WriteLog(self, result): + name = result.case.GetName() + mode = result.case.GetMode() + if result.HasUnexpectedOutcome(): + if result.case.IsNegative(): + self.logf.write("=== %s was expected to fail in %s, but didn't === \n" % (name, mode)) + self.logf.write("--- expected error: %s ---\n" % result.case.GetNegativeType()) + result.WriteOutput(self.logf) + else: + self.logf.write("=== %s failed in %s === \n" % (name, mode)) + result.WriteOutput(self.logf) + self.logf.write("===\n") + elif result.case.IsNegative(): + self.logf.write("%s failed in %s as expected \n" % (name, mode)) + else: + self.logf.write("%s passed in %s \n" % (name, mode)) + + def Print(self, tests): + cases = self.EnumerateTests(tests) + if len(cases) > 0: + cases[0].Print() + + def ListIncludes(self, tests): + cases = self.EnumerateTests(tests) + includes_dict = Counter() + for case in cases: + includes = case.GetIncludeList() + includes_dict.update(includes) + + print includes_dict + + +def Main(): + code = 0 + parser = BuildOptions() + (options, args) = parser.parse_args() + ValidateOptions(options) + test_suite = TestSuite(options.tests, + options.strict_only, + options.non_strict_only, + options.unmarked_default, + options.print_handle) + test_suite.Validate() + if options.loglevel == 'debug': + logging.basicConfig(level=logging.DEBUG) + elif options.loglevel == 'info': + logging.basicConfig(level=logging.INFO) + elif options.loglevel == 'warning': + logging.basicConfig(level=logging.WARNING) + elif options.loglevel == 'error': + logging.basicConfig(level=logging.ERROR) + elif options.loglevel == 'critical': + logging.basicConfig(level=logging.CRITICAL) + if options.cat: + test_suite.Print(args) + elif options.list_includes: + test_suite.ListIncludes(args) + else: + code = test_suite.Run(options.command, args, + options.summary or options.full_summary, + options.full_summary, + options.logname, + options.junitname) + return code + +if __name__ == '__main__': + try: + code = Main() + sys.exit(code) + except Test262Error, e: + print "Error: %s" % e.message + sys.exit(1) diff --git a/deps/v8/third_party/test262-harness/test/README.md b/deps/v8/third_party/test262-harness/test/README.md new file mode 100644 index 00000000000000..6084f93b8e03f6 --- /dev/null +++ b/deps/v8/third_party/test262-harness/test/README.md @@ -0,0 +1,11 @@ +# Unit tests for python packaging tools + +This directory holds tests for the python code, not tests of EMCAScript + +## Running tests + +```` +$ cd tools/packaging/test +$ for x in test*.py; do python $x; done +```` + diff --git a/deps/v8/third_party/test262-harness/test/fixtures/negative.js b/deps/v8/third_party/test262-harness/test/fixtures/negative.js new file mode 100644 index 00000000000000..f772b2e8bdb06a --- /dev/null +++ b/deps/v8/third_party/test262-harness/test/fixtures/negative.js @@ -0,0 +1,11 @@ +// fake copyright comment +/*--- +info: > + Sample test info +description: Sample test description +negative: + phase: early + type: SyntaxError +---*/ + +??? diff --git a/deps/v8/third_party/test262-harness/test/fixtures/test262-old-headers.js b/deps/v8/third_party/test262-harness/test/fixtures/test262-old-headers.js new file mode 100644 index 00000000000000..ff41177c5ddb15 --- /dev/null +++ b/deps/v8/third_party/test262-harness/test/fixtures/test262-old-headers.js @@ -0,0 +1,19 @@ +// Copyright 2009 the Sputnik authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/** + * The production Block { } in strict code can't contain function + * declaration; + * + * @path bestPractice/Sbp_A1_T1.js + * @description Trying to declare function at the Block statement + * @onlyStrict + * @negative SyntaxError + * @bestPractice http://wiki.ecmascript.org/doku.php?id=conventions:no_non_standard_strict_decls + */ + +"use strict"; +{ + function __func(){} +} + diff --git a/deps/v8/third_party/test262-harness/test/fixtures/test262-yaml-headers.js b/deps/v8/third_party/test262-harness/test/fixtures/test262-yaml-headers.js new file mode 100644 index 00000000000000..e897237bcba714 --- /dev/null +++ b/deps/v8/third_party/test262-harness/test/fixtures/test262-yaml-headers.js @@ -0,0 +1,18 @@ +// Copyright 2009 the Sputnik authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +info: > + The production Block { } in strict code can't contain function + declaration; +description: Trying to declare function at the Block statement +negative: SyntaxError +bestPractice: "http://wiki.ecmascript.org/doku.php?id=conventions:no_non_standard_strict_decls" +flags: [onlyStrict] +---*/ + +"use strict"; +{ + function __func(){} +} + diff --git a/deps/v8/third_party/test262-harness/test/test_common.py b/deps/v8/third_party/test262-harness/test/test_common.py new file mode 100644 index 00000000000000..4c86b0a334b0c7 --- /dev/null +++ b/deps/v8/third_party/test262-harness/test/test_common.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python + +# Copyright 2014 by Sam Mikes. All rights reserved. +# This code is governed by the BSD license found in the LICENSE file. + +import unittest + +import os + +# add parent dir to search path +import sys +sys.path.append("src") + + +from _common import * + +def slurpFile(name): + with open('test/' + name) as f: + contents = f.read() + return contents + + +class TestOldParsing(unittest.TestCase): + + def test_test(self): + pass + + def test_overview(self): + name = 'fixtures/test262-old-headers.js' + contents = slurpFile(name) + record = convertDocString(contents) + + self.assertEqual("""The production Block { } in strict code can't contain function +declaration;""", record['commentary']) + + self.assertEqual("bestPractice/Sbp_A1_T1.js", record['path']) + self.assertEqual("Trying to declare function at the Block statement", + record['description']) + self.assertEqual("", record['onlyStrict']) + self.assertEqual("SyntaxError", record['negative']) + self.assertEqual("http://wiki.ecmascript.org/doku.php?id=conventions:no_non_standard_strict_decls", + record['bestPractice']) + + +class TestYAMLParsing(unittest.TestCase): + + def test_overview(self): + name = 'fixtures/test262-yaml-headers.js' + contents = slurpFile(name) + record = convertDocString(contents) + + self.assertEqual("The production Block { } in strict code can't contain function declaration;\n", record['commentary']) + + self.assertEqual("Trying to declare function at the Block statement", + record['description']) + self.assertEqual(['onlyStrict'], record['flags']) + self.assertEqual("", record['onlyStrict']) + self.assertEqual("SyntaxError", record['negative']) + self.assertEqual("http://wiki.ecmascript.org/doku.php?id=conventions:no_non_standard_strict_decls", + record['bestPractice']) + + +if __name__ == '__main__': + unittest.main() diff --git a/deps/v8/third_party/test262-harness/test/test_monkeyYaml.py b/deps/v8/third_party/test262-harness/test/test_monkeyYaml.py new file mode 100644 index 00000000000000..428e45b6a03682 --- /dev/null +++ b/deps/v8/third_party/test262-harness/test/test_monkeyYaml.py @@ -0,0 +1,210 @@ +#!/usr/bin/env python + +# Copyright 2014 by Sam Mikes. All rights reserved. +# This code is governed by the BSD license found in the LICENSE file. + +import unittest + +import os +import yaml +import imp + +# add parent dir to search path +import sys +sys.path.append("src") + +import _monkeyYaml as monkeyYaml + +class TestMonkeyYAMLParsing(unittest.TestCase): + + def test_empty(self): + self.assertEqual(monkeyYaml.load(""), yaml.load("")) + + def test_newline(self): + self.assertEqual(monkeyYaml.load("\n"), yaml.load("\n")) + + def test_oneline(self): + y = "foo: bar" + self.assertEqual(monkeyYaml.load(y), yaml.load(y)) + + def test_twolines(self): + y = "foo: bar\nbaz_bletch : blith:er" + self.assertEqual(monkeyYaml.load(y), yaml.load(y)) + + def test_multiLine(self): + y = "foo: >\n bar\nbaz: 3" + self.assertEqual(monkeyYaml.load(y), yaml.load(y)) + + def test_es5id(self): + y = "es5id: 15.2.3.6-4-102" + self.assertEqual(monkeyYaml.load(y), yaml.load(y)) + + def test_Multiline_1(self): + lines = [" foo"] + value = ">" + y = "\n".join([value] + lines) + (lines, value) = monkeyYaml.myMultiline(lines, value) + self.assertEqual(lines, []) + self.assertEqual(value, yaml.load(y)) + + def test_Multiline_2(self): + lines = [" foo", " bar"] + y = "\n".join([">"] + lines) + (lines, value) = monkeyYaml.myMultiline(lines) + self.assertEqual(lines, []) + self.assertEqual(value, yaml.load(y)) + + def test_Multiline_3(self): + lines = [" foo", " bar"] + y = "\n".join([">"] + lines) + (lines, value) = monkeyYaml.myMultiline(lines) + self.assertEqual(lines, []) + self.assertEqual(value, yaml.load(y)) + + def test_Multiline_4(self): + lines = [" foo", " bar", " other: 42"] + (lines, value) = monkeyYaml.myMultiline(lines) + self.assertEqual(lines, [" other: 42"]) + self.assertEqual(value, "foo bar") + + def test_myLeading(self): + self.assertEqual(2, monkeyYaml.myLeadingSpaces(" foo")) + self.assertEqual(2, monkeyYaml.myLeadingSpaces(" ")) + self.assertEqual(0, monkeyYaml.myLeadingSpaces("\t ")) + + def test_includes_flow(self): + y = "includes: [a.js,b.js, c_with_wings.js]\n" + self.assertEqual(monkeyYaml.load(y), yaml.load(y)) + + def test_myFlowList_1(self): + y = "[a.js,b.js, c_with_wings.js, 3, 4.12]" + self.assertEqual(monkeyYaml.myFlowList(y), ['a.js', 'b.js', 'c_with_wings.js', 3, 4.12]) + + def test_multiline_list_1(self): + y = "foo:\n - bar\n - baz" + self.assertEqual(monkeyYaml.load(y), yaml.load(y)) + + def test_multiline_list2(self): + self.assertEqual(monkeyYaml.myRemoveListHeader(2, " - foo"), "foo") + + def test_multiline_list3(self): + (lines, value) = monkeyYaml.myMultilineList([" - foo", " - bar", "baz: bletch"], "") + self.assertEqual(lines, ["baz: bletch"]) + self.assertEqual(value, ["foo", "bar"]) + + def test_multiline_list_carriage_return(self): + y = "foo:\r\n - bar\r\n - baz" + self.assertEqual(monkeyYaml.load(y), yaml.load(y)) + + def test_oneline_indented(self): + y = " foo: bar\n baz: baf\n" + self.assertEqual(monkeyYaml.load(y), yaml.load(y)) + + + def test_indentation_215(self): + self.maxDiff = None + y = """ + description: > + The method should exist on the Array prototype, and it should be writable + and configurable, but not enumerable. + includes: [propertyHelper.js] + es6id: 22.1.3.13 + """ + self.assertEqual(monkeyYaml.load(y), yaml.load(y)) + + def test_indentation_215_2(self): + self.maxDiff = None + y = """ + description: > + The method should exist + includes: [propertyHelper.js] + es6id: 22.1.3.13 + """ + self.assertEqual(monkeyYaml.load(y), yaml.load(y)) + + def test_line_folding(self): + self.maxDiff = None + y = """ +description: aaa + bbb +es6id: 19.1.2.1 +""" + self.assertEqual(monkeyYaml.load(y), yaml.load(y)) + + def test_line_folding_2(self): + self.maxDiff = None + y = """ +description: ccc + + ddd + +es6id: 19.1.2.1 +""" + self.assertEqual(monkeyYaml.load(y), yaml.load(y)) + + def test_line_folding_3(self): + self.maxDiff = None + y = """ +description: eee + + + fff +es6id: 19.1.2.1 +""" + self.assertEqual(monkeyYaml.load(y), yaml.load(y)) + + def test_line_folding_4(self): + self.maxDiff = None + y = """ +description: ggg + + hhh + iii + + jjj +es6id: 19.1.2.1 +""" + self.assertEqual(monkeyYaml.load(y), yaml.load(y)) + + def test_no_folding(self): + y = """ +description: | + This is text that, naively parsed, would appear + + to: have + nested: data +""" + self.assertEqual(monkeyYaml.load(y), yaml.load(y)) + + def test_value_multiline(self): + y = """ +description: + This is a multi-line value + + whose trailing newline should be stripped +""" + self.assertEqual(monkeyYaml.load(y), yaml.load(y)) + + def test_nested_1(self): + y = """ +es61d: 19.1.2.1 +negative: + stage: early + type: ReferenceError +description: foo +""" + self.assertEqual(monkeyYaml.load(y), yaml.load(y)) + + def test_nested_2(self): + y = """ +es61d: 19.1.2.1 +first: + second_a: + third: 1 + second_b: 3 +description: foo +""" + self.assertEqual(monkeyYaml.load(y), yaml.load(y)) + +if __name__ == '__main__': + unittest.main() diff --git a/deps/v8/third_party/test262-harness/test/test_parseTestRecord.py b/deps/v8/third_party/test262-harness/test/test_parseTestRecord.py new file mode 100644 index 00000000000000..36576b57b146f4 --- /dev/null +++ b/deps/v8/third_party/test262-harness/test/test_parseTestRecord.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python + +# Copyright 2014 by Sam Mikes. All rights reserved. +# This code is governed by the BSD license found in the LICENSE file. + +import unittest + +import os +import yaml + +# add parent dir to search path +import sys +sys.path.append("src") + +from parseTestRecord import * + +def slurpFile(name): + with open('test/' + name) as f: + contents = f.read() + return contents + + +class TestOldParsing(unittest.TestCase): + + def test_test(self): + self.assertTrue(True) + + def test_overview(self): + name = 'fixtures/test262-old-headers.js' + contents = slurpFile(name) + record = parseTestRecord(contents, name) + + self.assertEqual("""// Copyright 2009 the Sputnik authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file.""", + record['header']) + self.assertEqual("""The production Block { } in strict code can't contain function +declaration;""", record['commentary']) + + self.assertEqual("bestPractice/Sbp_A1_T1.js", record['path']) + self.assertEqual("Trying to declare function at the Block statement", + record['description']) + self.assertEqual("", record['onlyStrict']) + self.assertEqual("SyntaxError", record['negative']) + self.assertEqual("http://wiki.ecmascript.org/doku.php?id=conventions:no_non_standard_strict_decls", + record['bestPractice']) + + self.assertEqual(""""use strict"; +{ + function __func(){} +} + +""", record['test']) + + @unittest.expectedFailure + def test_nomatch(self): + with self.assertRaisesRegexp(Exception, "unrecognized"): + parseTestRecord("#!/usr/bin/env python", "random.py") + + def test_duplicate(self): + with self.assertRaisesRegexp(Exception, "duplicate: foo"): + parseTestRecord(""" +// Copyright + +/** + * @foo bar + * @foo bar + */ + +1; +""" + , "name") + + def test_malformed(self): + with self.assertRaisesRegexp(Exception, 'Malformed "@" attribute: name'): + parseTestRecord(""" +// Copyright + +/** + * @ baz + * @foo bar + */ + +1; +""" + , "name") + + def test_stripStars(self): + self.assertEqual("", stripStars("")) + self.assertEqual("foo", stripStars("\n* foo")) + self.assertEqual("@foo bar", stripStars("\n* @foo bar")) + self.assertEqual("@foo bar", stripStars("\n *@foo bar")) + + +class TestYAMLParsing(unittest.TestCase): + def test_test(self): + self.assertTrue(True) + + def test_split(self): + name = 'fixtures/test262-yaml-headers.js' + contents = slurpFile(name) + self.assertTrue('---' in contents) + match = matchParts(contents, name) + self.assertEqual("""--- +info: > + The production Block { } in strict code can't contain function + declaration; +description: Trying to declare function at the Block statement +negative: SyntaxError +bestPractice: "http://wiki.ecmascript.org/doku.php?id=conventions:no_non_standard_strict_decls" +flags: [onlyStrict] +---""", match.group(2)) + + def test_yamlParse(self): + text = """ +info: > + The production Block { } in strict code can't contain function + declaration; +description: Trying to declare function at the Block statement +negative: SyntaxError +bestPractice: "http://wiki.ecmascript.org/doku.php?id=conventions:no_non_standard_strict_decls" +flags: [onlyStrict]""" + parsed = yaml.load(text) + + self.assertEqual("Trying to declare function at the Block statement", + parsed['description']) + self.assertEqual("SyntaxError", parsed['negative']) + self.assertEqual('http://wiki.ecmascript.org/doku.php?id=conventions:no_non_standard_strict_decls', parsed['bestPractice']) + self.assertEqual(["onlyStrict"], parsed['flags']) + self.assertEqual("The production Block { } in strict code can't contain function declaration;\n", parsed['info']) + + def test_hasYAML(self): + self.assertTrue(hasYAML("---\n some: yaml\n\n---")) + self.assertFalse(hasYAML("\n* Test description\n *\n * @foo bar\n* @noStrict\n")) + + def test_fixturehasYAML(self): + name = 'fixtures/test262-yaml-headers.js' + contents = slurpFile(name) + self.assertTrue('---' in contents) + match = matchParts(contents, name) + self.assertTrue(hasYAML(match.group(2))) + + def test_missingKeys(self): + result = {} + yamlAttrParser(result, """--- + info: some info (note no flags or includes) +---""", "") + self.assertEqual("some info (note no flags or includes)", result['commentary']) + + def test_overview(self): + name = 'fixtures/test262-yaml-headers.js' + contents = slurpFile(name) + record = parseTestRecord(contents, name) + + self.assertEqual("""// Copyright 2009 the Sputnik authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file.""", + record['header']) + self.assertEqual("The production Block { } in strict code can't contain function declaration;\n", record['commentary']) + + self.assertEqual("Trying to declare function at the Block statement", + record['description']) + self.assertEqual(['onlyStrict'], record['flags']) + self.assertEqual("", record['onlyStrict']) + self.assertEqual("SyntaxError", record['negative']) + self.assertEqual("http://wiki.ecmascript.org/doku.php?id=conventions:no_non_standard_strict_decls", + record['bestPractice']) + + self.assertEqual(""""use strict"; +{ + function __func(){} +} + +""", record['test']) + + def test_negative(self): + name = 'fixtures/negative.js' + contents = slurpFile(name) + record = parseTestRecord(contents, name) + + self.assertEqual('early', record['negative']['phase']) + self.assertEqual('SyntaxError', record['negative']['type']) + +if __name__ == '__main__': + unittest.main() diff --git a/deps/v8/third_party/test262-harness/test/test_test262.py b/deps/v8/third_party/test262-harness/test/test_test262.py new file mode 100644 index 00000000000000..8cf41d79f0817d --- /dev/null +++ b/deps/v8/third_party/test262-harness/test/test_test262.py @@ -0,0 +1,274 @@ +#!/usr/bin/env python + +# Copyright 2014 by Sam Mikes. All rights reserved. +# This code is governed by the BSD license found in the LICENSE file. + +import unittest + +import sys +import os +import cStringIO +from functools import wraps + +sys.path.append("src") + +import test262 + +class TestTest262(unittest.TestCase): + + def test_that_tests_run(self): + self.assertEqual(1 + 2, 3) + +class MockTest(object): + + def __init__(self, name, negative): + self.name = name + self.negative = negative if negative else False + self.strict_mode = False + + def GetName(self): + return self.name + + def IsNegative(self): + return self.negative + + def GetMode(self): + if self.strict_mode: + return "strict mode" + + return "non-strict mode" + +class MockResult(object): + + def __init__(self, case): + self.case = case + + + +class TestTestSuite(unittest.TestCase): + + def test_that_tests_run(self): + self.assertEqual(1 + 2, 3) + + def test_create_test_suite(self): + test_suite = test262.TestSuite(".", + False, + False, + False, + None) + self.assertNotEqual(test_suite, None) + + def test_summary(self): + test_suite = test262.TestSuite(".", + False, + False, + False, + None) + + progress = test262.ProgressIndicator(100) + progress.succeeded = 98 + progress.failed = 2 + + result = mute(True)(test_suite.PrintSummary)(progress, None) + self.assertEqual(""" +=== Summary === + - Ran 100 tests + - Passed 98 tests (98.0%) + - Failed 2 tests (2.0%) +""", result) + + def test_summary_logfile(self): + test_suite = test262.TestSuite(".", + False, + False, + False, + None) + + progress = test262.ProgressIndicator(100) + progress.succeeded = 98 + progress.failed = 2 + + fake_log = cStringIO.StringIO() + test_suite.logf = fake_log + + result = mute(True)(test_suite.PrintSummary)(progress, True) + + expected_out = """ +=== Summary === + - Ran 100 tests + - Passed 98 tests (98.0%) + - Failed 2 tests (2.0%) +""" + + expected_log = """=== Summary === + - Ran 100 tests + - Passed 98 tests (98.0%) + - Failed 2 tests (2.0%) +""" + self.assertEqual(expected_out, result) + self.assertEqual(expected_log, fake_log.getvalue()) + + + def test_summary_withfails(self): + test_suite = test262.TestSuite(".", + False, + False, + False, + None) + + progress = test262.ProgressIndicator(100) + progress.succeeded = 98 + progress.failed = 2 + progress.failed_tests = [ + MockResult(MockTest("foo", False)), + MockResult(MockTest("bar", True)) + ] + + result = mute(True)(test_suite.PrintSummary)(progress, None) + self.assertEqual(""" +=== Summary === + - Ran 100 tests + - Passed 98 tests (98.0%) + - Failed 2 tests (2.0%) + +Failed Tests + foo in non-strict mode + +Expected to fail but passed --- + bar in non-strict mode +""", result) + + + def test_summary_withfails_andlog(self): + test_suite = test262.TestSuite(".", + False, + False, + False, + None) + + progress = test262.ProgressIndicator(100) + progress.succeeded = 98 + progress.failed = 2 + progress.failed_tests = [ + MockResult(MockTest("foo", False)), + MockResult(MockTest("bar", True)) + ] + + fake_log = cStringIO.StringIO() + test_suite.logf = fake_log + + expected_out = """ +=== Summary === + - Ran 100 tests + - Passed 98 tests (98.0%) + - Failed 2 tests (2.0%) + +Failed Tests + foo in non-strict mode + +Expected to fail but passed --- + bar in non-strict mode +""" + expected_log = """=== Summary === + - Ran 100 tests + - Passed 98 tests (98.0%) + - Failed 2 tests (2.0%) +Failed Tests + foo in non-strict mode +Expected to fail but passed --- + bar in non-strict mode +""" + + result = mute(True)(test_suite.PrintSummary)(progress, True) + self.assertEqual(expected_out, result) + self.assertEqual(expected_log, fake_log.getvalue()) + + + def test_summary_success_logfile(self): + test_suite = test262.TestSuite(".", + False, + False, + False, + None) + + progress = test262.ProgressIndicator(100) + progress.succeeded = 100 + progress.failed = 0 + + fake_log = cStringIO.StringIO() + test_suite.logf = fake_log + + result = mute(True)(test_suite.PrintSummary)(progress, True) + + expected_out = """ +=== Summary === + - Ran 100 tests + - All tests succeeded +""" + + expected_log = """=== Summary === + - Ran 100 tests + - All tests succeeded +""" + self.assertEqual(expected_out, result) + self.assertEqual(expected_log, fake_log.getvalue()) + + + def test_percent_format(self): + self.assertEqual(test262.PercentFormat(1, 100), "1 test (1.0%)") + self.assertEqual(test262.PercentFormat(0, 100), "0 tests (0.0%)") + self.assertEqual(test262.PercentFormat(99, 100), "99 tests (99.0%)") + + +# module level utility functions +# copied from https://stackoverflow.com/questions/2828953/silence-the-stdout-of-a-function-in-python-without-trashing-sys-stdout-and-resto + + +def mute(returns_output=False): + """ + Decorate a function that prints to stdout, intercepting the output. + If "returns_output" is True, the function will return a generator + yielding the printed lines instead of the return values. + + The decorator litterally hijack sys.stdout during each function + execution for ALL THE THREADS, so be careful with what you apply it to + and in which context. + + >>> def numbers(): + print "42" + print "1984" + ... + >>> numbers() + 42 + 1984 + >>> mute()(numbers)() + >>> list(mute(True)(numbers)()) + ['42', '1984'] + + """ + + def decorator(func): + + @wraps(func) + def wrapper(*args, **kwargs): + + saved_stdout = sys.stdout + sys.stdout = cStringIO.StringIO() + + try: + out = func(*args, **kwargs) + if returns_output: + out = sys.stdout.getvalue() + finally: + sys.stdout = saved_stdout + + return out + + return wrapper + + return decorator + + +if __name__ == '__main__': + unittest.main() + diff --git a/deps/v8/third_party/zlib/README.chromium b/deps/v8/third_party/zlib/README.chromium index c3c1ef69ad4656..db159bef60a9d9 100644 --- a/deps/v8/third_party/zlib/README.chromium +++ b/deps/v8/third_party/zlib/README.chromium @@ -1,7 +1,7 @@ Name: zlib Short Name: zlib URL: http://zlib.net/ -Version: 1.2.11 +Version: 1.2.12 CPEPrefix: cpe:/a:zlib:zlib:1.2.11 Security Critical: yes License: Custom license @@ -27,3 +27,4 @@ Local Modifications: - Plus the changes in 'patches' folder. - Code in contrib/ other than contrib/minizip was added to match zlib's contributor layout. + - Backported patches from 1.2.12 release (Work In Progress). diff --git a/deps/v8/third_party/zlib/contrib/optimizations/inffast_chunk.c b/deps/v8/third_party/zlib/contrib/optimizations/inffast_chunk.c index 4bacbc46f620a3..8d62920a21185f 100644 --- a/deps/v8/third_party/zlib/contrib/optimizations/inffast_chunk.c +++ b/deps/v8/third_party/zlib/contrib/optimizations/inffast_chunk.c @@ -95,7 +95,7 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ code const FAR *dcode; /* local strm->distcode */ unsigned lmask; /* mask for first level of length codes */ unsigned dmask; /* mask for first level of distance codes */ - code here; /* retrieved table entry */ + code const *here; /* retrieved table entry */ unsigned op; /* code bits, operation, extra bits, or */ /* window position, window bytes to copy */ unsigned len; /* match length, unused bytes */ @@ -139,20 +139,20 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ bits += 8; #endif } - here = lcode[hold & lmask]; + here = lcode + (hold & lmask); dolen: - op = (unsigned)(here.bits); + op = (unsigned)(here->bits); hold >>= op; bits -= op; - op = (unsigned)(here.op); + op = (unsigned)(here->op); if (op == 0) { /* literal */ - Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + Tracevv((stderr, here->val >= 0x20 && here->val < 0x7f ? "inflate: literal '%c'\n" : - "inflate: literal 0x%02x\n", here.val)); - *out++ = (unsigned char)(here.val); + "inflate: literal 0x%02x\n", here->val)); + *out++ = (unsigned char)(here->val); } else if (op & 16) { /* length base */ - len = (unsigned)(here.val); + len = (unsigned)(here->val); op &= 15; /* number of extra bits */ if (op) { if (bits < op) { @@ -182,14 +182,14 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ bits += 8; #endif } - here = dcode[hold & dmask]; + here = dcode + (hold & dmask); dodist: - op = (unsigned)(here.bits); + op = (unsigned)(here->bits); hold >>= op; bits -= op; - op = (unsigned)(here.op); + op = (unsigned)(here->op); if (op & 16) { /* distance base */ - dist = (unsigned)(here.val); + dist = (unsigned)(here->val); op &= 15; /* number of extra bits */ if (bits < op) { #ifdef INFLATE_CHUNK_READ_64LE @@ -295,7 +295,7 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ } } else if ((op & 64) == 0) { /* 2nd level distance code */ - here = dcode[here.val + (hold & ((1U << op) - 1))]; + here = dcode + here->val + (hold & ((1U << op) - 1)); goto dodist; } else { @@ -305,7 +305,7 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ } } else if ((op & 64) == 0) { /* 2nd level length code */ - here = lcode[here.val + (hold & ((1U << op) - 1))]; + here = lcode + here->val + (hold & ((1U << op) - 1)); goto dolen; } else if (op & 32) { /* end-of-block */ diff --git a/deps/v8/third_party/zlib/contrib/optimizations/inflate.c b/deps/v8/third_party/zlib/contrib/optimizations/inflate.c index 81d558bd6ef5fa..4841cd964cf2f7 100644 --- a/deps/v8/third_party/zlib/contrib/optimizations/inflate.c +++ b/deps/v8/third_party/zlib/contrib/optimizations/inflate.c @@ -131,6 +131,7 @@ z_streamp strm; state->mode = HEAD; state->last = 0; state->havedict = 0; + state->flags = -1; state->dmax = 32768U; state->head = Z_NULL; state->hold = 0; @@ -682,7 +683,6 @@ int flush; state->mode = FLAGS; break; } - state->flags = 0; /* expect zlib header */ if (state->head != Z_NULL) state->head->done = -1; if (!(state->wrap & 1) || /* check if zlib header allowed */ @@ -709,6 +709,7 @@ int flush; break; } state->dmax = 1U << len; + state->flags = 0; /* indicate zlib header */ Tracev((stderr, "inflate: zlib header ok\n")); strm->adler = state->check = adler32(0L, Z_NULL, 0); state->mode = hold & 0x200 ? DICTID : TYPE; @@ -1233,7 +1234,7 @@ int flush; case LENGTH: if (state->wrap && state->flags) { NEEDBITS(32); - if (hold != (state->total & 0xffffffffUL)) { + if ((state->wrap & 4) && hold != (state->total & 0xffffffff)) { strm->msg = (char *)"incorrect length check"; state->mode = BAD; break; @@ -1423,6 +1424,7 @@ int ZEXPORT inflateSync(strm) z_streamp strm; { unsigned len; /* number of bytes to look at or looked at */ + int flags; /* temporary to save header status */ unsigned long in, out; /* temporary to save total_in and total_out */ unsigned char buf[4]; /* to restore bit buffer to byte string */ struct inflate_state FAR *state; @@ -1455,9 +1457,15 @@ z_streamp strm; /* return no joy or set up to restart inflate() on a new block */ if (state->have != 4) return Z_DATA_ERROR; + if (state->flags == -1) + state->wrap = 0; /* if no header yet, treat as raw */ + else + state->wrap &= ~4; /* no point in computing a check value now */ + flags = state->flags; in = strm->total_in; out = strm->total_out; inflateReset(strm); strm->total_in = in; strm->total_out = out; + state->flags = flags; state->mode = TYPE; return Z_OK; } @@ -1553,7 +1561,7 @@ int check; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; - if (check) + if (check && state->wrap) state->wrap |= 4; else state->wrap &= ~4; diff --git a/deps/v8/third_party/zlib/crc32.c b/deps/v8/third_party/zlib/crc32.c index d4c3248d98415b..5ee3bd425e6c59 100644 --- a/deps/v8/third_party/zlib/crc32.c +++ b/deps/v8/third_party/zlib/crc32.c @@ -250,7 +250,7 @@ unsigned long ZEXPORT crc32_z(crc, buf, len) #endif /* DYNAMIC_CRC_TABLE */ #ifdef BYFOUR - if (sizeof(void *) == sizeof(ptrdiff_t)) { + if (sizeof(void *) == sizeof(z_size_t)) { z_crc_t endian; endian = 1; @@ -327,7 +327,7 @@ local unsigned long crc32_little(crc, buf, len) c = (z_crc_t)crc; c = ~c; - while (len && ((ptrdiff_t)buf & 3)) { + while (len && ((z_size_t)buf & 3)) { c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); len--; } @@ -367,7 +367,7 @@ local unsigned long crc32_big(crc, buf, len) c = ZSWAP32((z_crc_t)crc); c = ~c; - while (len && ((ptrdiff_t)buf & 3)) { + while (len && ((z_size_t)buf & 3)) { c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); len--; } diff --git a/deps/v8/third_party/zlib/deflate.c b/deps/v8/third_party/zlib/deflate.c index fc7ae45905fff1..5c7c718d29ad85 100644 --- a/deps/v8/third_party/zlib/deflate.c +++ b/deps/v8/third_party/zlib/deflate.c @@ -176,10 +176,15 @@ local const config configuration_table[10] = { /* =========================================================================== * Initialize the hash table (avoiding 64K overflow for 16 bit systems). * prev[] will be initialized on the fly. + * TODO(cavalcantii): optimization opportunity, check comments on: + * https://chromium-review.googlesource.com/c/chromium/src/+/3561506/ */ #define CLEAR_HASH(s) \ - s->head[s->hash_size-1] = NIL; \ - zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); + do { \ + s->head[s->hash_size-1] = NIL; \ + zmemzero((Bytef *)s->head, \ + (unsigned)(s->hash_size-1)*sizeof(*s->head)); \ + } while (0) /* =========================================================================== * Slide the hash table when sliding the window down (could be avoided with 32 @@ -534,13 +539,13 @@ int ZEXPORT deflateResetKeep (strm) #ifdef GZIP s->wrap == 2 ? GZIP_STATE : #endif - s->wrap ? INIT_STATE : BUSY_STATE; + INIT_STATE; strm->adler = #ifdef GZIP s->wrap == 2 ? crc32(0L, Z_NULL, 0) : #endif adler32(0L, Z_NULL, 0); - s->last_flush = Z_NO_FLUSH; + s->last_flush = -2; _tr_init(s); @@ -595,7 +600,8 @@ int ZEXPORT deflatePrime (strm, bits, value) if (deflateStateCheck(strm)) return Z_STREAM_ERROR; s = strm->state; - if (s->sym_buf < s->pending_out + ((Buf_size + 7) >> 3)) + if (bits < 0 || bits > 16 || + s->sym_buf < s->pending_out + ((Buf_size + 7) >> 3)) return Z_BUF_ERROR; do { put = Buf_size - s->bi_valid; @@ -633,12 +639,12 @@ int ZEXPORT deflateParams(strm, level, strategy) func = configuration_table[s->level].func; if ((strategy != s->strategy || func != configuration_table[level].func) && - s->high_water) { + s->last_flush != -2) { /* Flush the last buffer: */ int err = deflate(strm, Z_BLOCK); if (err == Z_STREAM_ERROR) return err; - if (strm->avail_out == 0) + if (strm->avail_in || (s->strstart - s->block_start) + s->lookahead) return Z_BUF_ERROR; } if (s->level != level) { @@ -857,6 +863,8 @@ int ZEXPORT deflate (strm, flush) } /* Write the header */ + if (s->status == INIT_STATE && s->wrap == 0) + s->status = BUSY_STATE; if (s->status == INIT_STATE) { /* zlib header */ uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; @@ -1589,6 +1597,8 @@ local void fill_window_c(s) s->match_start -= wsize; s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ s->block_start -= (long) wsize; + if (s->insert > s->strstart) + s->insert = s->strstart; slide_hash(s); more += wsize; } @@ -1818,6 +1828,7 @@ local block_state deflate_stored(s, flush) s->matches = 2; /* clear hash */ zmemcpy(s->window, s->strm->next_in - s->w_size, s->w_size); s->strstart = s->w_size; + s->insert = s->strstart; } else { if (s->window_size - s->strstart <= used) { @@ -1826,12 +1837,14 @@ local block_state deflate_stored(s, flush) zmemcpy(s->window, s->window + s->w_size, s->strstart); if (s->matches < 2) s->matches++; /* add a pending slide_hash() */ + if (s->insert > s->strstart) + s->insert = s->strstart; } zmemcpy(s->window + s->strstart, s->strm->next_in - used, used); s->strstart += used; + s->insert += MIN(used, s->w_size - s->insert); } s->block_start = s->strstart; - s->insert += MIN(used, s->w_size - s->insert); } if (s->high_water < s->strstart) s->high_water = s->strstart; @@ -1846,7 +1859,7 @@ local block_state deflate_stored(s, flush) return block_done; /* Fill the window with any remaining input. */ - have = s->window_size - s->strstart - 1; + have = s->window_size - s->strstart; if (s->strm->avail_in > have && s->block_start >= (long)s->w_size) { /* Slide the window down. */ s->block_start -= s->w_size; @@ -1855,12 +1868,15 @@ local block_state deflate_stored(s, flush) if (s->matches < 2) s->matches++; /* add a pending slide_hash() */ have += s->w_size; /* more space now */ + if (s->insert > s->strstart) + s->insert = s->strstart; } if (have > s->strm->avail_in) have = s->strm->avail_in; if (have) { deflate_read_buf(s->strm, s->window + s->strstart, have); s->strstart += have; + s->insert += MIN(have, s->w_size - s->insert); } if (s->high_water < s->strstart) s->high_water = s->strstart; diff --git a/deps/v8/third_party/zlib/google/BUILD.gn b/deps/v8/third_party/zlib/google/BUILD.gn index c29e8927806235..e996b167dba856 100644 --- a/deps/v8/third_party/zlib/google/BUILD.gn +++ b/deps/v8/third_party/zlib/google/BUILD.gn @@ -7,6 +7,7 @@ import("//build_overrides/build.gni") if (build_with_chromium) { static_library("zip") { sources = [ + "redact.h", "zip.cc", "zip.h", "zip_internal.cc", @@ -18,6 +19,7 @@ if (build_with_chromium) { ] deps = [ "//base", + "//base:i18n", "//third_party/zlib:minizip", ] } diff --git a/deps/v8/third_party/zlib/google/compression_utils_unittest.cc b/deps/v8/third_party/zlib/google/compression_utils_unittest.cc index 415b9ab9228928..76572e5a47eac4 100644 --- a/deps/v8/third_party/zlib/google/compression_utils_unittest.cc +++ b/deps/v8/third_party/zlib/google/compression_utils_unittest.cc @@ -7,9 +7,9 @@ #include #include +#include #include -#include "base/cxx17_backports.h" #include "testing/gtest/include/gtest/gtest.h" namespace compression { @@ -33,24 +33,24 @@ const uint8_t kCompressedData[] = { } // namespace TEST(CompressionUtilsTest, GzipCompression) { - std::string data(reinterpret_cast(kData), base::size(kData)); + std::string data(reinterpret_cast(kData), std::size(kData)); std::string compressed_data; EXPECT_TRUE(GzipCompress(data, &compressed_data)); std::string golden_compressed_data( reinterpret_cast(kCompressedData), - base::size(kCompressedData)); + std::size(kCompressedData)); EXPECT_EQ(golden_compressed_data, compressed_data); } TEST(CompressionUtilsTest, GzipUncompression) { std::string compressed_data(reinterpret_cast(kCompressedData), - base::size(kCompressedData)); + std::size(kCompressedData)); std::string uncompressed_data; EXPECT_TRUE(GzipUncompress(compressed_data, &uncompressed_data)); std::string golden_data(reinterpret_cast(kData), - base::size(kData)); + std::size(kData)); EXPECT_EQ(golden_data, uncompressed_data); } @@ -59,7 +59,7 @@ TEST(CompressionUtilsTest, GzipUncompressionFromSpanToString) { EXPECT_TRUE(GzipUncompress(kCompressedData, &uncompressed_data)); std::string golden_data(reinterpret_cast(kData), - base::size(kData)); + std::size(kData)); EXPECT_EQ(golden_data, uncompressed_data); } @@ -84,10 +84,10 @@ TEST(CompressionUtilsTest, LargeInput) { TEST(CompressionUtilsTest, InPlace) { const std::string original_data(reinterpret_cast(kData), - base::size(kData)); + std::size(kData)); const std::string golden_compressed_data( reinterpret_cast(kCompressedData), - base::size(kCompressedData)); + std::size(kCompressedData)); std::string data(original_data); EXPECT_TRUE(GzipCompress(data, &data)); diff --git a/deps/v8/third_party/zlib/google/redact.h b/deps/v8/third_party/zlib/google/redact.h new file mode 100644 index 00000000000000..ea7da16a52751c --- /dev/null +++ b/deps/v8/third_party/zlib/google/redact.h @@ -0,0 +1,31 @@ +// Copyright (c) 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#ifndef THIRD_PARTY_ZLIB_GOOGLE_REDACT_H_ +#define THIRD_PARTY_ZLIB_GOOGLE_REDACT_H_ + +#include + +#include "base/files/file_path.h" +#include "base/logging.h" + +namespace zip { + +// Redacts file paths in log messages. +// Example: +// LOG(ERROR) << "Cannot open " << Redact(path); +class Redact { + public: + explicit Redact(const base::FilePath& path) : path_(path) {} + + friend std::ostream& operator<<(std::ostream& out, const Redact&& r) { + return LOG_IS_ON(INFO) ? out << "'" << r.path_ << "'" : out << "(redacted)"; + } + + private: + const base::FilePath& path_; +}; + +} // namespace zip + +#endif // THIRD_PARTY_ZLIB_GOOGLE_REDACT_H_ diff --git a/deps/v8/third_party/zlib/google/zip.cc b/deps/v8/third_party/zlib/google/zip.cc index 7c46718808d27f..1a43196e99d5af 100644 --- a/deps/v8/third_party/zlib/google/zip.cc +++ b/deps/v8/third_party/zlib/google/zip.cc @@ -10,10 +10,12 @@ #include "base/bind.h" #include "base/files/file.h" #include "base/files/file_enumerator.h" +#include "base/files/file_util.h" #include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/strings/string_util.h" #include "build/build_config.h" +#include "third_party/zlib/google/redact.h" #include "third_party/zlib/google/zip_internal.h" #include "third_party/zlib/google/zip_reader.h" #include "third_party/zlib/google/zip_writer.h" @@ -25,14 +27,13 @@ bool IsHiddenFile(const base::FilePath& file_path) { return file_path.BaseName().value()[0] == '.'; } -bool ExcludeNoFilesFilter(const base::FilePath& file_path) { - return true; -} - // Creates a directory at |extract_dir|/|entry_path|, including any parents. bool CreateDirectory(const base::FilePath& extract_dir, const base::FilePath& entry_path) { - return base::CreateDirectory(extract_dir.Append(entry_path)); + const base::FilePath dir = extract_dir.Append(entry_path); + const bool ok = base::CreateDirectory(dir); + PLOG_IF(ERROR, !ok) << "Cannot create directory " << Redact(dir); + return ok; } // Creates a WriterDelegate that can write a file at |extract_dir|/|entry_path|. @@ -59,12 +60,13 @@ class DirectFileAccessor : public FileAccessor { const base::FilePath absolute_path = src_dir_.Append(path); if (base::DirectoryExists(absolute_path)) { files->emplace_back(); - LOG(ERROR) << "Cannot open '" << path << "': It is a directory"; + LOG(ERROR) << "Cannot open " << Redact(path) << ": It is a directory"; } else { - files->emplace_back(absolute_path, - base::File::FLAG_OPEN | base::File::FLAG_READ); - LOG_IF(ERROR, !files->back().IsValid()) - << "Cannot open '" << path << "'"; + const base::File& file = files->emplace_back( + absolute_path, base::File::FLAG_OPEN | base::File::FLAG_READ); + LOG_IF(ERROR, !file.IsValid()) + << "Cannot open " << Redact(path) << ": " + << base::File::ErrorToString(file.error_details()); } } @@ -97,7 +99,7 @@ class DirectFileAccessor : public FileAccessor { base::File::Info file_info; if (!base::GetFileInfo(src_dir_.Append(path), &file_info)) { - LOG(ERROR) << "Cannot get info of '" << path << "'"; + PLOG(ERROR) << "Cannot get info of " << Redact(path); return false; } @@ -125,7 +127,7 @@ bool Zip(const ZipParams& params) { std::unique_ptr zip_writer; -#if defined(OS_POSIX) +#if defined(OS_POSIX) || defined(OS_FUCHSIA) if (params.dest_fd != base::kInvalidPlatformFile) { DCHECK(params.dest_file.empty()); zip_writer = @@ -169,79 +171,82 @@ bool Zip(const ZipParams& params) { return zip_writer->Close(); } -bool Unzip(const base::FilePath& src_file, const base::FilePath& dest_dir) { - return UnzipWithFilterCallback( - src_file, dest_dir, base::BindRepeating(&ExcludeNoFilesFilter), true); -} - -bool UnzipWithFilterCallback(const base::FilePath& src_file, - const base::FilePath& dest_dir, - FilterCallback filter_cb, - bool log_skipped_files) { +bool Unzip(const base::FilePath& src_file, + const base::FilePath& dest_dir, + UnzipOptions options) { base::File file(src_file, base::File::FLAG_OPEN | base::File::FLAG_READ); if (!file.IsValid()) { - DLOG(WARNING) << "Cannot open '" << src_file << "'"; + PLOG(ERROR) << "Cannot open " << Redact(src_file) << ": " + << base::File::ErrorToString(file.error_details()); return false; } - return UnzipWithFilterAndWriters( - file.GetPlatformFile(), - base::BindRepeating(&CreateFilePathWriterDelegate, dest_dir), - base::BindRepeating(&CreateDirectory, dest_dir), std::move(filter_cb), - log_skipped_files); + DLOG_IF(WARNING, !base::IsDirectoryEmpty(dest_dir)) + << "ZIP extraction directory is not empty: " << dest_dir; + + return Unzip(file.GetPlatformFile(), + base::BindRepeating(&CreateFilePathWriterDelegate, dest_dir), + base::BindRepeating(&CreateDirectory, dest_dir), + std::move(options)); } -bool UnzipWithFilterAndWriters(const base::PlatformFile& src_file, - WriterFactory writer_factory, - DirectoryCreator directory_creator, - FilterCallback filter_cb, - bool log_skipped_files) { +bool Unzip(const base::PlatformFile& src_file, + WriterFactory writer_factory, + DirectoryCreator directory_creator, + UnzipOptions options) { ZipReader reader; + reader.SetEncoding(std::move(options.encoding)); + reader.SetPassword(std::move(options.password)); + if (!reader.OpenFromPlatformFile(src_file)) { - DLOG(WARNING) << "Cannot open '" << src_file << "'"; + LOG(ERROR) << "Cannot open ZIP from file handle " << src_file; return false; } - while (reader.HasMore()) { - if (!reader.OpenCurrentEntryInZip()) { - DLOG(WARNING) << "Failed to open the current file in zip"; - return false; + + while (const ZipReader::Entry* const entry = reader.Next()) { + if (entry->is_unsafe) { + LOG(ERROR) << "Found unsafe entry " << Redact(entry->path) << " in ZIP"; + if (!options.continue_on_error) + return false; + continue; } - const base::FilePath& entry_path = reader.current_entry_info()->file_path(); - if (reader.current_entry_info()->is_unsafe()) { - DLOG(WARNING) << "Found an unsafe file in zip " << entry_path; - return false; + + if (options.filter && !options.filter.Run(entry->path)) { + VLOG(1) << "Skipped ZIP entry " << Redact(entry->path); + continue; } - if (filter_cb.Run(entry_path)) { - if (reader.current_entry_info()->is_directory()) { - if (!directory_creator.Run(entry_path)) - return false; - } else { - std::unique_ptr writer = writer_factory.Run(entry_path); - if (!reader.ExtractCurrentEntry(writer.get(), - std::numeric_limits::max())) { - DLOG(WARNING) << "Failed to extract " << entry_path; + + if (entry->is_directory) { + // It's a directory. + if (!directory_creator.Run(entry->path)) { + LOG(ERROR) << "Cannot create directory " << Redact(entry->path); + if (!options.continue_on_error) return false; - } } - } else if (log_skipped_files) { - DLOG(WARNING) << "Skipped file " << entry_path; + + continue; } - if (!reader.AdvanceToNextEntry()) { - DLOG(WARNING) << "Failed to advance to the next file"; - return false; + // It's a file. + std::unique_ptr writer = writer_factory.Run(entry->path); + if (!writer || !reader.ExtractCurrentEntry(writer.get())) { + LOG(ERROR) << "Cannot extract file " << Redact(entry->path) + << " from ZIP"; + if (!options.continue_on_error) + return false; } } - return true; + + return reader.ok(); } bool ZipWithFilterCallback(const base::FilePath& src_dir, const base::FilePath& dest_file, - FilterCallback filter_cb) { + FilterCallback filter) { DCHECK(base::DirectoryExists(src_dir)); return Zip({.src_dir = src_dir, .dest_file = dest_file, - .filter_callback = std::move(filter_cb)}); + .filter_callback = std::move(filter)}); } bool Zip(const base::FilePath& src_dir, @@ -252,7 +257,7 @@ bool Zip(const base::FilePath& src_dir, .include_hidden_files = include_hidden_files}); } -#if defined(OS_POSIX) +#if defined(OS_POSIX) || defined(OS_FUCHSIA) bool ZipFiles(const base::FilePath& src_dir, Paths src_relative_paths, int dest_fd) { @@ -261,6 +266,6 @@ bool ZipFiles(const base::FilePath& src_dir, .dest_fd = dest_fd, .src_files = src_relative_paths}); } -#endif // defined(OS_POSIX) +#endif // defined(OS_POSIX) || defined(OS_FUCHSIA) } // namespace zip diff --git a/deps/v8/third_party/zlib/google/zip.h b/deps/v8/third_party/zlib/google/zip.h index ecaecb1dc9663c..25ec655caf314b 100644 --- a/deps/v8/third_party/zlib/google/zip.h +++ b/deps/v8/third_party/zlib/google/zip.h @@ -99,7 +99,7 @@ struct ZipParams { // Either dest_file or dest_fd should be set, but not both. base::FilePath dest_file; -#if defined(OS_POSIX) +#if defined(OS_POSIX) || defined(OS_FUCHSIA) // Destination file passed a file descriptor. // Either dest_file or dest_fd should be set, but not both. int dest_fd = base::kInvalidPlatformFile; @@ -159,7 +159,7 @@ bool Zip(const base::FilePath& src_dir, const base::FilePath& dest_file, bool include_hidden_files); -#if defined(OS_POSIX) +#if defined(OS_POSIX) || defined(OS_FUCHSIA) // Zips files listed in |src_relative_paths| to destination specified by file // descriptor |dest_fd|, without taking ownership of |dest_fd|. The paths listed // in |src_relative_paths| are relative to the |src_dir| and will be used as the @@ -168,35 +168,45 @@ bool Zip(const base::FilePath& src_dir, bool ZipFiles(const base::FilePath& src_dir, Paths src_relative_paths, int dest_fd); -#endif // defined(OS_POSIX) - -// Unzip the contents of zip_file into dest_dir. -// For each file in zip_file, include it only if the callback |filter_cb| -// returns true. Otherwise omit it. -// If |log_skipped_files| is true, files skipped during extraction are printed -// to debug log. -bool UnzipWithFilterCallback(const base::FilePath& zip_file, - const base::FilePath& dest_dir, - FilterCallback filter_cb, - bool log_skipped_files); - -// Unzip the contents of zip_file, using the writers provided by writer_factory. -// For each file in zip_file, include it only if the callback |filter_cb| -// returns true. Otherwise omit it. -// If |log_skipped_files| is true, files skipped during extraction are printed -// to debug log. +#endif // defined(OS_POSIX) || defined(OS_FUCHSIA) + +// Options of the Unzip function, with valid default values. +struct UnzipOptions { + // Encoding of entry paths in the ZIP archive. By default, paths are assumed + // to be in UTF-8. + std::string encoding; + + // Only extract the entries for which |filter_cb| returns true. By default, + // everything gets extracted. + FilterCallback filter; + + // Password to decrypt the encrypted files. + std::string password; + + // Should ignore errors when extracting files? + bool continue_on_error = false; +}; + typedef base::RepeatingCallback( const base::FilePath&)> WriterFactory; + typedef base::RepeatingCallback DirectoryCreator; -bool UnzipWithFilterAndWriters(const base::PlatformFile& zip_file, - WriterFactory writer_factory, - DirectoryCreator directory_creator, - FilterCallback filter_cb, - bool log_skipped_files); - -// Unzip the contents of zip_file into dest_dir. -bool Unzip(const base::FilePath& zip_file, const base::FilePath& dest_dir); + +// Unzips the contents of |zip_file|, using the writers provided by +// |writer_factory|. +bool Unzip(const base::PlatformFile& zip_file, + WriterFactory writer_factory, + DirectoryCreator directory_creator, + UnzipOptions options = {}); + +// Unzips the contents of |zip_file| into |dest_dir|. +// This function does not overwrite any existing file. +// A filename collision will result in an error. +// Therefore, |dest_dir| should initially be an empty directory. +bool Unzip(const base::FilePath& zip_file, + const base::FilePath& dest_dir, + UnzipOptions options = {}); } // namespace zip diff --git a/deps/v8/third_party/zlib/google/zip_internal.cc b/deps/v8/third_party/zlib/google/zip_internal.cc index 00e9eefe6c206b..1adf2e6d0e8fef 100644 --- a/deps/v8/third_party/zlib/google/zip_internal.cc +++ b/deps/v8/third_party/zlib/google/zip_internal.cc @@ -342,7 +342,7 @@ zipFile OpenForZipping(const std::string& file_name_utf8, int append_flag) { zip_func_ptrs); } -#if defined(OS_POSIX) +#if defined(OS_POSIX) || defined(OS_FUCHSIA) zipFile OpenFdForZipping(int zip_fd, int append_flag) { zlib_filefunc64_def zip_funcs; FillFdOpenFileFunc(&zip_funcs, zip_fd); diff --git a/deps/v8/third_party/zlib/google/zip_internal.h b/deps/v8/third_party/zlib/google/zip_internal.h index c7feba692be9fe..92833fa1702130 100644 --- a/deps/v8/third_party/zlib/google/zip_internal.h +++ b/deps/v8/third_party/zlib/google/zip_internal.h @@ -54,7 +54,7 @@ unzFile PrepareMemoryForUnzipping(const std::string& data); // Windows. |append_flag| will be passed to zipOpen2(). zipFile OpenForZipping(const std::string& file_name_utf8, int append_flag); -#if defined(OS_POSIX) +#if defined(OS_POSIX) || defined(OS_FUCHSIA) // Opens the file referred to by |zip_fd| for zipping. |append_flag| will be // passed to zipOpen2(). zipFile OpenFdForZipping(int zip_fd, int append_flag); diff --git a/deps/v8/third_party/zlib/google/zip_reader.cc b/deps/v8/third_party/zlib/google/zip_reader.cc index 53fa13fd99cb03..2cc101c75ce7c9 100644 --- a/deps/v8/third_party/zlib/google/zip_reader.cc +++ b/deps/v8/third_party/zlib/google/zip_reader.cc @@ -4,15 +4,23 @@ #include "third_party/zlib/google/zip_reader.h" +#include #include #include "base/bind.h" +#include "base/check.h" #include "base/files/file.h" +#include "base/files/file_util.h" +#include "base/i18n/icu_string_conversions.h" #include "base/logging.h" +#include "base/numerics/safe_conversions.h" +#include "base/strings/strcat.h" +#include "base/strings/string_piece.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/threading/sequenced_task_runner_handle.h" #include "build/build_config.h" +#include "third_party/zlib/google/redact.h" #include "third_party/zlib/google/zip_internal.h" #if defined(USE_SYSTEM_MINIZIP) @@ -24,116 +32,93 @@ #endif // defined(OS_WIN) #endif // defined(USE_SYSTEM_MINIZIP) -namespace zip { +#if defined(OS_POSIX) +#include +#endif +namespace zip { namespace { -// StringWriterDelegate -------------------------------------------------------- - -// A writer delegate that writes no more than |max_read_bytes| to a given -// std::string. -class StringWriterDelegate : public WriterDelegate { - public: - StringWriterDelegate(size_t max_read_bytes, std::string* output); - - StringWriterDelegate(const StringWriterDelegate&) = delete; - StringWriterDelegate& operator=(const StringWriterDelegate&) = delete; +enum UnzipError : int; + +std::ostream& operator<<(std::ostream& out, UnzipError error) { +#define SWITCH_ERR(X) \ + case X: \ + return out << #X; + switch (error) { + SWITCH_ERR(UNZ_OK); + SWITCH_ERR(UNZ_END_OF_LIST_OF_FILE); + SWITCH_ERR(UNZ_ERRNO); + SWITCH_ERR(UNZ_PARAMERROR); + SWITCH_ERR(UNZ_BADZIPFILE); + SWITCH_ERR(UNZ_INTERNALERROR); + SWITCH_ERR(UNZ_CRCERROR); + default: + return out << "UNZ" << static_cast(error); + } +#undef SWITCH_ERR +} - ~StringWriterDelegate() override; +bool IsValidFileNameCharacterOnWindows(char16_t c) { + if (c < 32) + return false; - // WriterDelegate methods: + switch (c) { + case '<': // Less than + case '>': // Greater than + case ':': // Colon + case '"': // Double quote + case '|': // Vertical bar or pipe + case '?': // Question mark + case '*': // Asterisk + case '/': // Forward slash + case '\\': // Backslash + return false; + } - // Returns true. - bool PrepareOutput() override; + return true; +} - // Appends |num_bytes| bytes from |data| to the output string. Returns false - // if |num_bytes| will cause the string to exceed |max_read_bytes|. - bool WriteBytes(const char* data, int num_bytes) override; +// A writer delegate that writes to a given string. +class StringWriterDelegate : public WriterDelegate { + public: + explicit StringWriterDelegate(std::string* output) : output_(output) {} - void SetTimeModified(const base::Time& time) override; + // WriterDelegate methods: + bool WriteBytes(const char* data, int num_bytes) override { + output_->append(data, num_bytes); + return true; + } private: - size_t max_read_bytes_; - std::string* output_; + std::string* const output_; }; -StringWriterDelegate::StringWriterDelegate(size_t max_read_bytes, - std::string* output) - : max_read_bytes_(max_read_bytes), - output_(output) { -} - -StringWriterDelegate::~StringWriterDelegate() { -} - -bool StringWriterDelegate::PrepareOutput() { - return true; -} - -bool StringWriterDelegate::WriteBytes(const char* data, int num_bytes) { - if (output_->size() + num_bytes > max_read_bytes_) - return false; - output_->append(data, num_bytes); - return true; -} - -void StringWriterDelegate::SetTimeModified(const base::Time& time) { - // Do nothing. +#if defined(OS_POSIX) +void SetPosixFilePermissions(int fd, int mode) { + base::stat_wrapper_t sb; + if (base::File::Fstat(fd, &sb)) { + return; + } + mode_t new_mode = sb.st_mode; + // Transfer the executable bit only if the file is readable. + if ((sb.st_mode & S_IRUSR) == S_IRUSR && (mode & S_IXUSR) == S_IXUSR) { + new_mode |= S_IXUSR; + } + if ((sb.st_mode & S_IRGRP) == S_IRGRP && (mode & S_IXGRP) == S_IXGRP) { + new_mode |= S_IXGRP; + } + if ((sb.st_mode & S_IROTH) == S_IROTH && (mode & S_IXOTH) == S_IXOTH) { + new_mode |= S_IXOTH; + } + if (new_mode != sb.st_mode) { + fchmod(fd, new_mode); + } } +#endif } // namespace -// TODO(satorux): The implementation assumes that file names in zip files -// are encoded in UTF-8. This is true for zip files created by Zip() -// function in zip.h, but not true for user-supplied random zip files. -ZipReader::EntryInfo::EntryInfo(const std::string& file_name_in_zip, - const unz_file_info& raw_file_info) - : file_path_(base::FilePath::FromUTF8Unsafe(file_name_in_zip)), - is_directory_(false), - is_unsafe_(false), - is_encrypted_(false) { - original_size_ = raw_file_info.uncompressed_size; - - // Directory entries in zip files end with "/". - is_directory_ = base::EndsWith(file_name_in_zip, "/", - base::CompareCase::INSENSITIVE_ASCII); - - // Check the file name here for directory traversal issues. - is_unsafe_ = file_path_.ReferencesParent(); - - // We also consider that the file name is unsafe, if it's invalid UTF-8. - std::u16string file_name_utf16; - if (!base::UTF8ToUTF16(file_name_in_zip.data(), file_name_in_zip.size(), - &file_name_utf16)) { - is_unsafe_ = true; - } - - // We also consider that the file name is unsafe, if it's absolute. - // On Windows, IsAbsolute() returns false for paths starting with "/". - if (file_path_.IsAbsolute() || - base::StartsWith(file_name_in_zip, "/", - base::CompareCase::INSENSITIVE_ASCII)) - is_unsafe_ = true; - - // Whether the file is encrypted is bit 0 of the flag. - is_encrypted_ = raw_file_info.flag & 1; - - // Construct the last modified time. The timezone info is not present in - // zip files, so we construct the time as local time. - base::Time::Exploded exploded_time = {}; // Zero-clear. - exploded_time.year = raw_file_info.tmu_date.tm_year; - // The month in zip file is 0-based, whereas ours is 1-based. - exploded_time.month = raw_file_info.tmu_date.tm_mon + 1; - exploded_time.day_of_month = raw_file_info.tmu_date.tm_mday; - exploded_time.hour = raw_file_info.tmu_date.tm_hour; - exploded_time.minute = raw_file_info.tmu_date.tm_min; - exploded_time.second = raw_file_info.tmu_date.tm_sec; - exploded_time.millisecond = 0; - - if (!base::Time::FromUTCExploded(exploded_time, &last_modified_)) - last_modified_ = base::Time::UnixEpoch(); -} - ZipReader::ZipReader() { Reset(); } @@ -142,13 +127,14 @@ ZipReader::~ZipReader() { Close(); } -bool ZipReader::Open(const base::FilePath& zip_file_path) { +bool ZipReader::Open(const base::FilePath& zip_path) { DCHECK(!zip_file_); // Use of "Unsafe" function does not look good, but there is no way to do // this safely on Linux. See file_util.h for details. - zip_file_ = internal::OpenForUnzipping(zip_file_path.AsUTF8Unsafe()); + zip_file_ = internal::OpenForUnzipping(zip_path.AsUTF8Unsafe()); if (!zip_file_) { + LOG(ERROR) << "Cannot open ZIP archive " << Redact(zip_path); return false; } @@ -164,6 +150,7 @@ bool ZipReader::OpenFromPlatformFile(base::PlatformFile zip_fd) { zip_file_ = internal::OpenHandleForUnzipping(zip_fd); #endif if (!zip_file_) { + LOG(ERROR) << "Cannot open ZIP from file handle " << zip_fd; return false; } @@ -179,107 +166,242 @@ bool ZipReader::OpenFromString(const std::string& data) { void ZipReader::Close() { if (zip_file_) { - unzClose(zip_file_); + if (const UnzipError err{unzClose(zip_file_)}; err != UNZ_OK) { + LOG(ERROR) << "Error while closing ZIP archive: " << err; + } } Reset(); } -bool ZipReader::HasMore() { - return !reached_end_; -} - -bool ZipReader::AdvanceToNextEntry() { +const ZipReader::Entry* ZipReader::Next() { DCHECK(zip_file_); - // Should not go further if we already reached the end. if (reached_end_) - return false; + return nullptr; - unz_file_pos position = {}; - if (unzGetFilePos(zip_file_, &position) != UNZ_OK) - return false; - const int current_entry_index = position.num_of_file; - // If we are currently at the last entry, then the next position is the - // end of the zip file, so mark that we reached the end. - if (current_entry_index + 1 == num_entries_) { - reached_end_ = true; - } else { - DCHECK_LT(current_entry_index + 1, num_entries_); - if (unzGoToNextFile(zip_file_) != UNZ_OK) { - return false; + DCHECK(ok_); + + // Move to the next entry if we're not trying to open the first entry. + if (next_index_ > 0) { + if (const UnzipError err{unzGoToNextFile(zip_file_)}; err != UNZ_OK) { + reached_end_ = true; + if (err != UNZ_END_OF_LIST_OF_FILE) { + LOG(ERROR) << "Cannot go to next entry in ZIP: " << err; + ok_ = false; + } + return nullptr; } } - current_entry_info_.reset(); - return true; + + next_index_++; + + if (!OpenEntry()) { + reached_end_ = true; + ok_ = false; + return nullptr; + } + + return &entry_; } -bool ZipReader::OpenCurrentEntryInZip() { +bool ZipReader::OpenEntry() { DCHECK(zip_file_); - unz_file_info raw_file_info = {}; - char raw_file_name_in_zip[internal::kZipMaxPath] = {}; - const int result = unzGetCurrentFileInfo(zip_file_, - &raw_file_info, - raw_file_name_in_zip, - sizeof(raw_file_name_in_zip) - 1, - NULL, // extraField. - 0, // extraFieldBufferSize. - NULL, // szComment. - 0); // commentBufferSize. - if (result != UNZ_OK) + // Get entry info. + unz_file_info64 info = {}; + char path_in_zip[internal::kZipMaxPath] = {}; + if (const UnzipError err{unzGetCurrentFileInfo64( + zip_file_, &info, path_in_zip, sizeof(path_in_zip) - 1, nullptr, 0, + nullptr, 0)}; + err != UNZ_OK) { + LOG(ERROR) << "Cannot get entry from ZIP: " << err; return false; - if (raw_file_name_in_zip[0] == '\0') + } + + entry_.path_in_original_encoding = path_in_zip; + + // Convert path from original encoding to Unicode. + std::u16string path_in_utf16; + const char* const encoding = encoding_.empty() ? "UTF-8" : encoding_.c_str(); + if (!base::CodepageToUTF16(entry_.path_in_original_encoding, encoding, + base::OnStringConversionError::SUBSTITUTE, + &path_in_utf16)) { + LOG(ERROR) << "Cannot convert path from encoding " << encoding; return false; - current_entry_info_.reset( - new EntryInfo(raw_file_name_in_zip, raw_file_info)); + } + + // Normalize path. + Normalize(path_in_utf16); + + entry_.original_size = info.uncompressed_size; + + // The file content of this entry is encrypted if flag bit 0 is set. + entry_.is_encrypted = info.flag & 1; + + // Construct the last modified time. The timezone info is not present in ZIP + // archives, so we construct the time as UTC. + base::Time::Exploded exploded_time = {}; + exploded_time.year = info.tmu_date.tm_year; + exploded_time.month = info.tmu_date.tm_mon + 1; // 0-based vs 1-based + exploded_time.day_of_month = info.tmu_date.tm_mday; + exploded_time.hour = info.tmu_date.tm_hour; + exploded_time.minute = info.tmu_date.tm_min; + exploded_time.second = info.tmu_date.tm_sec; + exploded_time.millisecond = 0; + + if (!base::Time::FromUTCExploded(exploded_time, &entry_.last_modified)) + entry_.last_modified = base::Time::UnixEpoch(); + +#if defined(OS_POSIX) + entry_.posix_mode = (info.external_fa >> 16L) & (S_IRWXU | S_IRWXG | S_IRWXO); +#else + entry_.posix_mode = 0; +#endif + return true; } +void ZipReader::Normalize(base::StringPiece16 in) { + entry_.is_unsafe = true; + + // Directory entries in ZIP have a path ending with "/". + entry_.is_directory = base::EndsWith(in, u"/"); + + std::u16string normalized_path; + if (base::StartsWith(in, u"/")) { + normalized_path = u"ROOT"; + entry_.is_unsafe = false; + } + + for (;;) { + // Consume initial path separators. + const base::StringPiece16::size_type i = in.find_first_not_of(u'/'); + if (i == base::StringPiece16::npos) + break; + + in.remove_prefix(i); + DCHECK(!in.empty()); + + // Isolate next path component. + const base::StringPiece16 part = in.substr(0, in.find_first_of(u'/')); + DCHECK(!part.empty()); + + in.remove_prefix(part.size()); + + if (!normalized_path.empty()) + normalized_path += u'/'; + + if (part == u".") { + normalized_path += u"DOT"; + entry_.is_unsafe = true; + continue; + } + + if (part == u"..") { + normalized_path += u"UP"; + entry_.is_unsafe = true; + continue; + } + + // Windows has more restrictions than other systems when it comes to valid + // file paths. Replace Windows-invalid characters on all systems for + // consistency. In particular, this prevents a path component containing + // colon and backslash from being misinterpreted as an absolute path on + // Windows. + for (const char16_t c : part) { + normalized_path += IsValidFileNameCharacterOnWindows(c) ? c : 0xFFFD; + } + + entry_.is_unsafe = false; + } + + // If the entry is a directory, add the final path separator to the entry + // path. + if (entry_.is_directory && !normalized_path.empty()) { + normalized_path += u'/'; + entry_.is_unsafe = false; + } + + entry_.path = base::FilePath::FromUTF16Unsafe(normalized_path); + + // By construction, we should always get a relative path. + DCHECK(!entry_.path.IsAbsolute()) << entry_.path; +} + bool ZipReader::ExtractCurrentEntry(WriterDelegate* delegate, uint64_t num_bytes_to_extract) const { DCHECK(zip_file_); - - const int open_result = unzOpenCurrentFile(zip_file_); - if (open_result != UNZ_OK) + DCHECK_LT(0, next_index_); + DCHECK(ok_); + DCHECK(!reached_end_); + + // Use password only for encrypted files. For non-encrypted files, no password + // is needed, and must be nullptr. + const char* const password = + entry_.is_encrypted ? password_.c_str() : nullptr; + if (const UnzipError err{unzOpenCurrentFilePassword(zip_file_, password)}; + err != UNZ_OK) { + LOG(ERROR) << "Cannot open file " << Redact(entry_.path) + << " from ZIP: " << err; return false; + } + DCHECK(delegate); if (!delegate->PrepareOutput()) return false; - std::unique_ptr buf(new char[internal::kZipBufSize]); uint64_t remaining_capacity = num_bytes_to_extract; bool entire_file_extracted = false; while (remaining_capacity > 0) { + char buf[internal::kZipBufSize]; const int num_bytes_read = - unzReadCurrentFile(zip_file_, buf.get(), internal::kZipBufSize); + unzReadCurrentFile(zip_file_, buf, internal::kZipBufSize); if (num_bytes_read == 0) { entire_file_extracted = true; break; - } else if (num_bytes_read < 0) { - // If num_bytes_read < 0, then it's a specific UNZ_* error code. + } + + if (num_bytes_read < 0) { + LOG(ERROR) << "Cannot read file " << Redact(entry_.path) + << " from ZIP: " << UnzipError(num_bytes_read); break; - } else if (num_bytes_read > 0) { - uint64_t num_bytes_to_write = std::min( - remaining_capacity, base::checked_cast(num_bytes_read)); - if (!delegate->WriteBytes(buf.get(), num_bytes_to_write)) - break; - if (remaining_capacity == base::checked_cast(num_bytes_read)) { - // Ensures function returns true if the entire file has been read. - entire_file_extracted = - (unzReadCurrentFile(zip_file_, buf.get(), 1) == 0); - } - CHECK_GE(remaining_capacity, num_bytes_to_write); - remaining_capacity -= num_bytes_to_write; } + + DCHECK_LT(0, num_bytes_read); + CHECK_LE(num_bytes_read, internal::kZipBufSize); + + uint64_t num_bytes_to_write = std::min( + remaining_capacity, base::checked_cast(num_bytes_read)); + if (!delegate->WriteBytes(buf, num_bytes_to_write)) + break; + + if (remaining_capacity == base::checked_cast(num_bytes_read)) { + // Ensures function returns true if the entire file has been read. + const int n = unzReadCurrentFile(zip_file_, buf, 1); + entire_file_extracted = (n == 0); + LOG_IF(ERROR, n < 0) << "Cannot read file " << Redact(entry_.path) + << " from ZIP: " << UnzipError(n); + } + + CHECK_GE(remaining_capacity, num_bytes_to_write); + remaining_capacity -= num_bytes_to_write; } - unzCloseCurrentFile(zip_file_); + if (const UnzipError err{unzCloseCurrentFile(zip_file_)}; err != UNZ_OK) { + LOG(ERROR) << "Cannot extract file " << Redact(entry_.path) + << " from ZIP: " << err; + entire_file_extracted = false; + } - if (entire_file_extracted && - current_entry_info()->last_modified() != base::Time::UnixEpoch()) { - delegate->SetTimeModified(current_entry_info()->last_modified()); + if (entire_file_extracted) { + delegate->SetPosixFilePermissions(entry_.posix_mode); + if (entry_.last_modified != base::Time::UnixEpoch()) { + delegate->SetTimeModified(entry_.last_modified); + } + } else { + delegate->OnError(); } return entire_file_extracted; @@ -289,25 +411,33 @@ void ZipReader::ExtractCurrentEntryToFilePathAsync( const base::FilePath& output_file_path, SuccessCallback success_callback, FailureCallback failure_callback, - const ProgressCallback& progress_callback) { + ProgressCallback progress_callback) { DCHECK(zip_file_); - DCHECK(current_entry_info_.get()); + DCHECK_LT(0, next_index_); + DCHECK(ok_); + DCHECK(!reached_end_); // If this is a directory, just create it and return. - if (current_entry_info()->is_directory()) { + if (entry_.is_directory) { if (base::CreateDirectory(output_file_path)) { base::SequencedTaskRunnerHandle::Get()->PostTask( FROM_HERE, std::move(success_callback)); } else { - DVLOG(1) << "Unzip failed: unable to create directory."; + LOG(ERROR) << "Cannot create directory " << Redact(output_file_path); base::SequencedTaskRunnerHandle::Get()->PostTask( FROM_HERE, std::move(failure_callback)); } return; } - if (unzOpenCurrentFile(zip_file_) != UNZ_OK) { - DVLOG(1) << "Unzip failed: unable to open current zip entry."; + // Use password only for encrypted files. For non-encrypted files, no password + // is needed, and must be nullptr. + const char* const password = + entry_.is_encrypted ? password_.c_str() : nullptr; + if (const UnzipError err{unzOpenCurrentFilePassword(zip_file_, password)}; + err != UNZ_OK) { + LOG(ERROR) << "Cannot open file " << Redact(entry_.path) + << " from ZIP: " << err; base::SequencedTaskRunnerHandle::Get()->PostTask( FROM_HERE, std::move(failure_callback)); return; @@ -315,7 +445,7 @@ void ZipReader::ExtractCurrentEntryToFilePathAsync( base::FilePath output_dir_path = output_file_path.DirName(); if (!base::CreateDirectory(output_dir_path)) { - DVLOG(1) << "Unzip failed: unable to create containing directory."; + LOG(ERROR) << "Cannot create directory " << Redact(output_dir_path); base::SequencedTaskRunnerHandle::Get()->PostTask( FROM_HERE, std::move(failure_callback)); return; @@ -325,8 +455,7 @@ void ZipReader::ExtractCurrentEntryToFilePathAsync( base::File output_file(output_file_path, flags); if (!output_file.IsValid()) { - DVLOG(1) << "Unzip failed: unable to create platform file at " - << output_file_path.value(); + LOG(ERROR) << "Cannot create file " << Redact(output_file_path); base::SequencedTaskRunnerHandle::Get()->PostTask( FROM_HERE, std::move(failure_callback)); return; @@ -336,7 +465,7 @@ void ZipReader::ExtractCurrentEntryToFilePathAsync( FROM_HERE, base::BindOnce(&ZipReader::ExtractChunk, weak_ptr_factory_.GetWeakPtr(), std::move(output_file), std::move(success_callback), - std::move(failure_callback), progress_callback, + std::move(failure_callback), std::move(progress_callback), 0 /* initial offset */)); } @@ -344,120 +473,136 @@ bool ZipReader::ExtractCurrentEntryToString(uint64_t max_read_bytes, std::string* output) const { DCHECK(output); DCHECK(zip_file_); + DCHECK_LT(0, next_index_); + DCHECK(ok_); + DCHECK(!reached_end_); + + output->clear(); - if (max_read_bytes == 0) { - output->clear(); + if (max_read_bytes == 0) return true; - } - if (current_entry_info()->is_directory()) { - output->clear(); + if (entry_.is_directory) return true; - } - // The original_size() is the best hint for the real size, so it saves - // doing reallocations for the common case when the uncompressed size is - // correct. However, we need to assume that the uncompressed size could be - // incorrect therefore this function needs to read as much data as possible. - std::string contents; - contents.reserve( - static_cast(std::min(base::checked_cast(max_read_bytes), - current_entry_info()->original_size()))); - - StringWriterDelegate writer(max_read_bytes, &contents); - if (!ExtractCurrentEntry(&writer, max_read_bytes)) { - if (contents.length() < max_read_bytes) { - // There was an error in extracting entry. If ExtractCurrentEntry() - // returns false, the entire file was not read - in which case - // contents.length() should equal |max_read_bytes| unless an error - // occurred which caused extraction to be aborted. - output->clear(); - } else { - // |num_bytes| is less than the length of current entry. - output->swap(contents); - } - return false; - } - output->swap(contents); - return true; + // The original_size is the best hint for the real size, so it saves doing + // reallocations for the common case when the uncompressed size is correct. + // However, we need to assume that the uncompressed size could be incorrect + // therefore this function needs to read as much data as possible. + output->reserve(base::checked_cast(std::min( + max_read_bytes, base::checked_cast(entry_.original_size)))); + + StringWriterDelegate writer(output); + return ExtractCurrentEntry(&writer, max_read_bytes); } bool ZipReader::OpenInternal() { DCHECK(zip_file_); unz_global_info zip_info = {}; // Zero-clear. - if (unzGetGlobalInfo(zip_file_, &zip_info) != UNZ_OK) { + if (const UnzipError err{unzGetGlobalInfo(zip_file_, &zip_info)}; + err != UNZ_OK) { + LOG(ERROR) << "Cannot get ZIP info: " << err; return false; } - num_entries_ = zip_info.number_entry; - if (num_entries_ < 0) - return false; - // We are already at the end if the zip file is empty. - reached_end_ = (num_entries_ == 0); + num_entries_ = zip_info.number_entry; + reached_end_ = (num_entries_ <= 0); + ok_ = true; return true; } void ZipReader::Reset() { - zip_file_ = NULL; + zip_file_ = nullptr; num_entries_ = 0; - reached_end_ = false; - current_entry_info_.reset(); + next_index_ = 0; + reached_end_ = true; + ok_ = false; + entry_ = {}; } void ZipReader::ExtractChunk(base::File output_file, SuccessCallback success_callback, FailureCallback failure_callback, - const ProgressCallback& progress_callback, - const int64_t offset) { + ProgressCallback progress_callback, + int64_t offset) { char buffer[internal::kZipBufSize]; - const int num_bytes_read = unzReadCurrentFile(zip_file_, - buffer, - internal::kZipBufSize); + const int num_bytes_read = + unzReadCurrentFile(zip_file_, buffer, internal::kZipBufSize); if (num_bytes_read == 0) { - unzCloseCurrentFile(zip_file_); - std::move(success_callback).Run(); - } else if (num_bytes_read < 0) { - DVLOG(1) << "Unzip failed: error while reading zipfile " - << "(" << num_bytes_read << ")"; - std::move(failure_callback).Run(); - } else { - if (num_bytes_read != output_file.Write(offset, buffer, num_bytes_read)) { - DVLOG(1) << "Unzip failed: unable to write all bytes to target."; + if (const UnzipError err{unzCloseCurrentFile(zip_file_)}; err != UNZ_OK) { + LOG(ERROR) << "Cannot extract file " << Redact(entry_.path) + << " from ZIP: " << err; std::move(failure_callback).Run(); return; } - int64_t current_progress = offset + num_bytes_read; + std::move(success_callback).Run(); + return; + } - progress_callback.Run(current_progress); + if (num_bytes_read < 0) { + LOG(ERROR) << "Cannot read file " << Redact(entry_.path) + << " from ZIP: " << UnzipError(num_bytes_read); + std::move(failure_callback).Run(); + return; + } - base::SequencedTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindOnce(&ZipReader::ExtractChunk, weak_ptr_factory_.GetWeakPtr(), - std::move(output_file), std::move(success_callback), - std::move(failure_callback), progress_callback, - current_progress)); + if (num_bytes_read != output_file.Write(offset, buffer, num_bytes_read)) { + LOG(ERROR) << "Cannot write " << num_bytes_read + << " bytes to file at offset " << offset; + std::move(failure_callback).Run(); + return; } + + offset += num_bytes_read; + progress_callback.Run(offset); + + base::SequencedTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(&ZipReader::ExtractChunk, weak_ptr_factory_.GetWeakPtr(), + std::move(output_file), std::move(success_callback), + std::move(failure_callback), std::move(progress_callback), + offset)); } // FileWriterDelegate ---------------------------------------------------------- -FileWriterDelegate::FileWriterDelegate(base::File* file) : file_(file) {} - -FileWriterDelegate::FileWriterDelegate(std::unique_ptr file) - : file_(file.get()), owned_file_(std::move(file)) {} +FileWriterDelegate::FileWriterDelegate(base::File* file) : file_(file) { + DCHECK(file_); +} -FileWriterDelegate::~FileWriterDelegate() { - if (!file_->SetLength(file_length_)) { - DVPLOG(1) << "Failed updating length of written file"; - } +FileWriterDelegate::FileWriterDelegate(base::File owned_file) + : owned_file_(std::move(owned_file)) { + DCHECK_EQ(file_, &owned_file_); } +FileWriterDelegate::~FileWriterDelegate() {} + bool FileWriterDelegate::PrepareOutput() { - return file_->Seek(base::File::FROM_BEGIN, 0) >= 0; + DCHECK(file_); + + if (!file_->IsValid()) { + LOG(ERROR) << "File is not valid"; + return false; + } + + const int64_t length = file_->GetLength(); + if (length < 0) { + PLOG(ERROR) << "Cannot get length of file handle " + << file_->GetPlatformFile(); + return false; + } + + // Just log a warning if the file is not empty. + // See crbug.com/1309879 and crbug.com/774762. + LOG_IF(WARNING, length > 0) + << "File handle " << file_->GetPlatformFile() + << " is not empty: Its length is " << length << " bytes"; + + return true; } bool FileWriterDelegate::WriteBytes(const char* data, int num_bytes) { @@ -471,32 +616,65 @@ void FileWriterDelegate::SetTimeModified(const base::Time& time) { file_->SetTimes(base::Time::Now(), time); } +void FileWriterDelegate::SetPosixFilePermissions(int mode) { +#if defined(OS_POSIX) + zip::SetPosixFilePermissions(file_->GetPlatformFile(), mode); +#endif +} + +void FileWriterDelegate::OnError() { + file_length_ = 0; + file_->SetLength(0); +} + // FilePathWriterDelegate ------------------------------------------------------ -FilePathWriterDelegate::FilePathWriterDelegate( - const base::FilePath& output_file_path) - : output_file_path_(output_file_path) {} +FilePathWriterDelegate::FilePathWriterDelegate(base::FilePath output_file_path) + : FileWriterDelegate(base::File()), + output_file_path_(std::move(output_file_path)) {} FilePathWriterDelegate::~FilePathWriterDelegate() {} bool FilePathWriterDelegate::PrepareOutput() { // We can't rely on parent directory entries being specified in the // zip, so we make sure they are created. - if (!base::CreateDirectory(output_file_path_.DirName())) + if (const base::FilePath dir = output_file_path_.DirName(); + !base::CreateDirectory(dir)) { + PLOG(ERROR) << "Cannot create directory " << Redact(dir); return false; + } - file_.Initialize(output_file_path_, - base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); - return file_.IsValid(); -} + owned_file_.Initialize(output_file_path_, + base::File::FLAG_CREATE | base::File::FLAG_WRITE); + if (!owned_file_.IsValid()) { + PLOG(ERROR) << "Cannot create file " << Redact(output_file_path_) << ": " + << base::File::ErrorToString(owned_file_.error_details()); + return false; + } + + const int64_t length = owned_file_.GetLength(); + if (length < 0) { + PLOG(ERROR) << "Cannot get length of file " << Redact(output_file_path_); + return false; + } + + if (length > 0) { + LOG(ERROR) << "File " << Redact(output_file_path_) + << " is not empty: Its length is " << length << " bytes"; + return false; + } -bool FilePathWriterDelegate::WriteBytes(const char* data, int num_bytes) { - return num_bytes == file_.WriteAtCurrentPos(data, num_bytes); + return true; } -void FilePathWriterDelegate::SetTimeModified(const base::Time& time) { - file_.Close(); - base::TouchFile(output_file_path_, base::Time::Now(), time); +void FilePathWriterDelegate::OnError() { + FileWriterDelegate::OnError(); + owned_file_.Close(); + + if (!base::DeleteFile(output_file_path_)) { + LOG(ERROR) << "Cannot delete partially extracted file " + << Redact(output_file_path_); + } } } // namespace zip diff --git a/deps/v8/third_party/zlib/google/zip_reader.h b/deps/v8/third_party/zlib/google/zip_reader.h index e1ca7aa417c55d..703b74be038f7a 100644 --- a/deps/v8/third_party/zlib/google/zip_reader.h +++ b/deps/v8/third_party/zlib/google/zip_reader.h @@ -7,14 +7,15 @@ #include #include +#include #include #include #include "base/callback.h" #include "base/files/file.h" #include "base/files/file_path.h" -#include "base/files/file_util.h" #include "base/memory/weak_ptr.h" +#include "base/numerics/safe_conversions.h" #include "base/time/time.h" #if defined(USE_SYSTEM_MINIZIP) @@ -33,33 +34,47 @@ class WriterDelegate { // Invoked once before any data is streamed out to pave the way (e.g., to open // the output file). Return false on failure to cancel extraction. - virtual bool PrepareOutput() = 0; + virtual bool PrepareOutput() { return true; } // Invoked to write the next chunk of data. Return false on failure to cancel // extraction. - virtual bool WriteBytes(const char* data, int num_bytes) = 0; + virtual bool WriteBytes(const char* data, int num_bytes) { return true; } // Sets the last-modified time of the data. - virtual void SetTimeModified(const base::Time& time) = 0; + virtual void SetTimeModified(const base::Time& time) {} + + // Called with the POSIX file permissions of the data; POSIX implementations + // may apply some of the permissions (for example, the executable bit) to the + // output file. + virtual void SetPosixFilePermissions(int mode) {} + + // Called if an error occurred while extracting the file. The WriterDelegate + // can then remove and clean up the partially extracted data. + virtual void OnError() {} }; -// This class is used for reading zip files. A typical use case of this -// class is to scan entries in a zip file and extract them. The code will -// look like: +// This class is used for reading ZIP archives. A typical use case of this class +// is to scan entries in a ZIP archive and extract them. The code will look +// like: // // ZipReader reader; -// reader.Open(zip_file_path); -// while (reader.HasMore()) { -// reader.OpenCurrentEntryInZip(); -// const base::FilePath& entry_path = -// reader.current_entry_info()->file_path(); -// auto writer = CreateFilePathWriterDelegate(extract_dir, entry_path); -// reader.ExtractCurrentEntry(writer, std::numeric_limits::max()); -// reader.AdvanceToNextEntry(); +// if (!reader.Open(zip_path)) { +// // Cannot open +// return; +// } +// +// while (const ZipReader::entry* entry = reader.Next()) { +// auto writer = CreateFilePathWriterDelegate(extract_dir, entry->path); +// if (!reader.ExtractCurrentEntry(writer)) { +// // Cannot extract +// return; +// } // } // -// For simplicity, error checking is omitted in the example code above. The -// production code should check return values from all of these functions. +// if (!reader.ok()) { +// // Error while enumerating entries +// return; +// } // class ZipReader { public: @@ -71,54 +86,56 @@ class ZipReader { // of bytes that have been processed so far. using ProgressCallback = base::RepeatingCallback; - // This class represents information of an entry (file or directory) in - // a zip file. - class EntryInfo { - public: - EntryInfo(const std::string& filename_in_zip, - const unz_file_info& raw_file_info); + // Information of an entry (file or directory) in a ZIP archive. + struct Entry { + // Path of this entry, in its original encoding as it is stored in the ZIP + // archive. The encoding is not specified here. It might or might not be + // UTF-8, and the caller needs to use other means to determine the encoding + // if it wants to interpret this path correctly. + std::string path_in_original_encoding; + + // Path of the entry, converted to Unicode. This path is relative (eg + // "foo/bar.txt"). Absolute paths (eg "/foo/bar.txt") or paths containing + // ".." or "." components (eg "../foo/bar.txt") are converted to safe + // relative paths. Eg: + // (In ZIP) -> (Entry.path) + // /foo/bar -> ROOT/foo/bar + // ../a -> UP/a + // ./a -> DOT/a + base::FilePath path; + + // Size of the original uncompressed file, or 0 if the entry is a directory. + // This value should not be trusted, because it is stored as metadata in the + // ZIP archive and can be different from the real uncompressed size. + int64_t original_size; + + // Last modified time. If the timestamp stored in the ZIP archive is not + // valid, the Unix epoch will be returned. + // + // The timestamp stored in the ZIP archive uses the MS-DOS date and time + // format. + // + // http://msdn.microsoft.com/en-us/library/ms724247(v=vs.85).aspx + // + // As such the following limitations apply: + // * Only years from 1980 to 2107 can be represented. + // * The timestamp has a 2-second resolution. + // * There is no timezone information, so the time is interpreted as UTC. + base::Time last_modified; - EntryInfo(const EntryInfo&) = delete; - EntryInfo& operator=(const EntryInfo&) = delete; + // True if the entry is a directory. + // False if the entry is a file. + bool is_directory; - // Returns the file path. The path is usually relative like - // "foo/bar.txt", but if it's absolute, is_unsafe() returns true. - const base::FilePath& file_path() const { return file_path_; } + // True if the entry path cannot be converted to a safe relative path. This + // happens if a file entry (not a directory) has a filename "." or "..". + bool is_unsafe; - // Returns the size of the original file (i.e. after uncompressed). - // Returns 0 if the entry is a directory. - // Note: this value should not be trusted, because it is stored as metadata - // in the zip archive and can be different from the real uncompressed size. - int64_t original_size() const { return original_size_; } + // True if the file content is encrypted. + bool is_encrypted; - // Returns the last modified time. If the time stored in the zip file was - // not valid, the unix epoch will be returned. - // - // The time stored in the zip archive uses the MS-DOS date and time format. - // http://msdn.microsoft.com/en-us/library/ms724247(v=vs.85).aspx - // As such the following limitations apply: - // * only years from 1980 to 2107 can be represented. - // * the time stamp has a 2 second resolution. - // * there's no timezone information, so the time is interpreted as local. - base::Time last_modified() const { return last_modified_; } - - // Returns true if the entry is a directory. - bool is_directory() const { return is_directory_; } - - // Returns true if the entry is unsafe, like having ".." or invalid - // UTF-8 characters in its file name, or the file path is absolute. - bool is_unsafe() const { return is_unsafe_; } - - // Returns true if the entry is encrypted. - bool is_encrypted() const { return is_encrypted_; } - - private: - const base::FilePath file_path_; - int64_t original_size_; - base::Time last_modified_; - bool is_directory_; - bool is_unsafe_; - bool is_encrypted_; + // Entry POSIX permissions (POSIX systems only). + int posix_mode; }; ZipReader(); @@ -128,11 +145,11 @@ class ZipReader { ~ZipReader(); - // Opens the zip file specified by |zip_file_path|. Returns true on + // Opens the ZIP archive specified by |zip_path|. Returns true on // success. - bool Open(const base::FilePath& zip_file_path); + bool Open(const base::FilePath& zip_path); - // Opens the zip file referred to by the platform file |zip_fd|, without + // Opens the ZIP archive referred to by the platform file |zip_fd|, without // taking ownership of |zip_fd|. Returns true on success. bool OpenFromPlatformFile(base::PlatformFile zip_fd); @@ -141,72 +158,94 @@ class ZipReader { // string until it finishes extracting files. bool OpenFromString(const std::string& data); - // Closes the currently opened zip file. This function is called in the + // Closes the currently opened ZIP archive. This function is called in the // destructor of the class, so you usually don't need to call this. void Close(); - // Returns true if there is at least one entry to read. This function is - // used to scan entries with AdvanceToNextEntry(), like: - // - // while (reader.HasMore()) { - // // Do something with the current file here. - // reader.AdvanceToNextEntry(); - // } - bool HasMore(); + // Sets the encoding of entry paths in the ZIP archive. + // By default, paths are assumed to be in UTF-8. + void SetEncoding(std::string encoding) { encoding_ = std::move(encoding); } - // Advances the next entry. Returns true on success. - bool AdvanceToNextEntry(); + // Sets the decryption password that will be used to decrypt encrypted file in + // the ZIP archive. + void SetPassword(std::string password) { password_ = std::move(password); } - // Opens the current entry in the zip file. On success, returns true and - // updates the the current entry state (i.e. current_entry_info() is - // updated). This function should be called before operations over the - // current entry like ExtractCurrentEntryToFile(). + // Gets the next entry. Returns null if there is no more entry, or if an error + // occurred while scanning entries. The returned Entry is owned by this + // ZipReader, and is valid until Next() is called again or until this + // ZipReader is closed. + // + // This function should be called before operations over the current entry + // like ExtractCurrentEntryToFile(). // - // Note that there is no CloseCurrentEntryInZip(). The the current entry - // state is reset automatically as needed. - bool OpenCurrentEntryInZip(); + // while (const ZipReader::Entry* entry = reader.Next()) { + // // Do something with the current entry here. + // ... + // } + // + // // Finished scanning entries. + // // Check if the scanning stopped because of an error. + // if (!reader.ok()) { + // // There was an error. + // ... + // } + const Entry* Next(); + + // Returns true if the enumeration of entries was successful, or false if it + // stopped because of an error. + bool ok() const { return ok_; } // Extracts |num_bytes_to_extract| bytes of the current entry to |delegate|, - // starting from the beginning of the entry. Return value specifies whether - // the entire file was extracted. + // starting from the beginning of the entry. + // + // Returns true if the entire file was extracted without error. + // + // Precondition: Next() returned a non-null Entry. bool ExtractCurrentEntry(WriterDelegate* delegate, - uint64_t num_bytes_to_extract) const; + uint64_t num_bytes_to_extract = + std::numeric_limits::max()) const; - // Asynchronously extracts the current entry to the given output file path. - // If the current entry is a directory it just creates the directory - // synchronously instead. OpenCurrentEntryInZip() must be called beforehand. - // success_callback will be called on success and failure_callback will be - // called on failure. progress_callback will be called at least once. + // Asynchronously extracts the current entry to the given output file path. If + // the current entry is a directory it just creates the directory + // synchronously instead. + // + // |success_callback| will be called on success and |failure_callback| will be + // called on failure. |progress_callback| will be called at least once. // Callbacks will be posted to the current MessageLoop in-order. + // + // Precondition: Next() returned a non-null Entry. void ExtractCurrentEntryToFilePathAsync( const base::FilePath& output_file_path, SuccessCallback success_callback, FailureCallback failure_callback, - const ProgressCallback& progress_callback); + ProgressCallback progress_callback); // Extracts the current entry into memory. If the current entry is a - // directory, the |output| parameter is set to the empty string. If the - // current entry is a file, the |output| parameter is filled with its - // contents. OpenCurrentEntryInZip() must be called beforehand. Note: the - // |output| parameter can be filled with a big amount of data, avoid passing - // it around by value, but by reference or pointer. Note: the value returned - // by EntryInfo::original_size() cannot be trusted, so the real size of the - // uncompressed contents can be different. |max_read_bytes| limits the ammount - // of memory used to carry the entry. Returns true if the entire content is - // read. If the entry is bigger than |max_read_bytes|, returns false and - // |output| is filled with |max_read_bytes| of data. If an error occurs, - // returns false, and |output| is set to the empty string. + // directory, |*output| is set to the empty string. If the current entry is a + // file, |*output| is filled with its contents. + // + // The value in |Entry::original_size| cannot be trusted, so the real size of + // the uncompressed contents can be different. |max_read_bytes| limits the + // amount of memory used to carry the entry. + // + // Returns true if the entire content is read without error. If the content is + // bigger than |max_read_bytes|, this function returns false and |*output| is + // filled with |max_read_bytes| of data. If an error occurs, this function + // returns false and |*output| contains the content extracted so far, which + // might be garbage data. + // + // Precondition: Next() returned a non-null Entry. bool ExtractCurrentEntryToString(uint64_t max_read_bytes, std::string* output) const; - // Returns the current entry info. Returns NULL if the current entry is - // not yet opened. OpenCurrentEntryInZip() must be called beforehand. - EntryInfo* current_entry_info() const { - return current_entry_info_.get(); + bool ExtractCurrentEntryToString(std::string* output) const { + return ExtractCurrentEntryToString( + base::checked_cast(output->max_size()), output); } - // Returns the number of entries in the zip file. - // Open() must be called beforehand. + // Returns the number of entries in the ZIP archive. + // + // Precondition: one of the Open() methods returned true. int num_entries() const { return num_entries_; } private: @@ -216,23 +255,39 @@ class ZipReader { // Resets the internal state. void Reset(); + // Opens the current entry in the ZIP archive. On success, returns true and + // updates the current entry state |entry_|. + // + // Note that there is no matching CloseEntry(). The current entry state is + // reset automatically as needed. + bool OpenEntry(); + + // Normalizes the given path passed as UTF-16 string piece. Sets entry_.path, + // entry_.is_directory and entry_.is_unsafe. + void Normalize(base::StringPiece16 in); + // Extracts a chunk of the file to the target. Will post a task for the next // chunk and success/failure/progress callbacks as necessary. void ExtractChunk(base::File target_file, SuccessCallback success_callback, FailureCallback failure_callback, - const ProgressCallback& progress_callback, + ProgressCallback progress_callback, const int64_t offset); + std::string encoding_; + std::string password_; unzFile zip_file_; int num_entries_; + int next_index_; bool reached_end_; - std::unique_ptr current_entry_info_; + bool ok_; + Entry entry_; base::WeakPtrFactory weak_ptr_factory_{this}; }; -// A writer delegate that writes to a given File. +// A writer delegate that writes to a given File. It is recommended that this +// file be initially empty. class FileWriterDelegate : public WriterDelegate { public: // Constructs a FileWriterDelegate that manipulates |file|. The delegate will @@ -241,17 +296,14 @@ class FileWriterDelegate : public WriterDelegate { explicit FileWriterDelegate(base::File* file); // Constructs a FileWriterDelegate that takes ownership of |file|. - explicit FileWriterDelegate(std::unique_ptr file); + explicit FileWriterDelegate(base::File owned_file); FileWriterDelegate(const FileWriterDelegate&) = delete; FileWriterDelegate& operator=(const FileWriterDelegate&) = delete; - // Truncates the file to the number of bytes written. ~FileWriterDelegate() override; - // WriterDelegate methods: - - // Seeks to the beginning of the file, returning false if the seek fails. + // Returns true if the file handle passed to the constructor is valid. bool PrepareOutput() override; // Writes |num_bytes| bytes of |data| to the file, returning false on error or @@ -261,45 +313,48 @@ class FileWriterDelegate : public WriterDelegate { // Sets the last-modified time of the data. void SetTimeModified(const base::Time& time) override; - // Return the actual size of the file. - int64_t file_length() { return file_length_; } + // On POSIX systems, sets the file to be executable if the source file was + // executable. + void SetPosixFilePermissions(int mode) override; - private: - // The file the delegate modifies. - base::File* file_; + // Empties the file to avoid leaving garbage data in it. + void OnError() override; + + // Gets the number of bytes written into the file. + int64_t file_length() { return file_length_; } + protected: // The delegate can optionally own the file it modifies, in which case // owned_file_ is set and file_ is an alias for owned_file_. - std::unique_ptr owned_file_; + base::File owned_file_; + + // The file the delegate modifies. + base::File* const file_ = &owned_file_; int64_t file_length_ = 0; }; -// A writer delegate that writes a file at a given path. -class FilePathWriterDelegate : public WriterDelegate { +// A writer delegate that creates and writes a file at a given path. This does +// not overwrite any existing file. +class FilePathWriterDelegate : public FileWriterDelegate { public: - explicit FilePathWriterDelegate(const base::FilePath& output_file_path); + explicit FilePathWriterDelegate(base::FilePath output_file_path); FilePathWriterDelegate(const FilePathWriterDelegate&) = delete; FilePathWriterDelegate& operator=(const FilePathWriterDelegate&) = delete; ~FilePathWriterDelegate() override; - // WriterDelegate methods: - - // Creates the output file and any necessary intermediate directories. + // Creates the output file and any necessary intermediate directories. Does + // not overwrite any existing file, and returns false if the output file + // cannot be created because another file conflicts with it. bool PrepareOutput() override; - // Writes |num_bytes| bytes of |data| to the file, returning false if not all - // bytes could be written. - bool WriteBytes(const char* data, int num_bytes) override; - - // Sets the last-modified time of the data. - void SetTimeModified(const base::Time& time) override; + // Deletes the output file. + void OnError() override; private: - base::FilePath output_file_path_; - base::File file_; + const base::FilePath output_file_path_; }; } // namespace zip diff --git a/deps/v8/third_party/zlib/google/zip_reader_unittest.cc b/deps/v8/third_party/zlib/google/zip_reader_unittest.cc index c1d654afe9ad9f..31dceaccad30de 100644 --- a/deps/v8/third_party/zlib/google/zip_reader_unittest.cc +++ b/deps/v8/third_party/zlib/google/zip_reader_unittest.cc @@ -8,13 +8,14 @@ #include #include -#include +#include #include +#include #include "base/bind.h" #include "base/check.h" -#include "base/cxx17_backports.h" #include "base/files/file.h" +#include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/hash/md5.h" @@ -23,15 +24,20 @@ #include "base/strings/string_piece.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" +#include "base/test/bind.h" #include "base/test/task_environment.h" #include "base/time/time.h" +#include "build/build_config.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/platform_test.h" #include "third_party/zlib/google/zip_internal.h" -using ::testing::Return; using ::testing::_; +using ::testing::ElementsAre; +using ::testing::ElementsAreArray; +using ::testing::Return; +using ::testing::SizeIs; namespace { @@ -39,10 +45,7 @@ const static std::string kQuuxExpectedMD5 = "d1ae4ac8a17a0e09317113ab284b57a6"; class FileWrapper { public: - typedef enum { - READ_ONLY, - READ_WRITE - } AccessMode; + typedef enum { READ_ONLY, READ_WRITE } AccessMode; FileWrapper(const base::FilePath& path, AccessMode mode) { int flags = base::File::FLAG_READ; @@ -73,18 +76,13 @@ class MockUnzipListener : public base::SupportsWeakPtr { : success_calls_(0), failure_calls_(0), progress_calls_(0), - current_progress_(0) { - } + current_progress_(0) {} // Success callback for async functions. - void OnUnzipSuccess() { - success_calls_++; - } + void OnUnzipSuccess() { success_calls_++; } // Failure callback for async functions. - void OnUnzipFailure() { - failure_calls_++; - } + void OnUnzipFailure() { failure_calls_++; } // Progress callback for async functions. void OnUnzipProgress(int64_t progress) { @@ -111,184 +109,189 @@ class MockWriterDelegate : public zip::WriterDelegate { MOCK_METHOD0(PrepareOutput, bool()); MOCK_METHOD2(WriteBytes, bool(const char*, int)); MOCK_METHOD1(SetTimeModified, void(const base::Time&)); + MOCK_METHOD1(SetPosixFilePermissions, void(int)); + MOCK_METHOD0(OnError, void()); }; bool ExtractCurrentEntryToFilePath(zip::ZipReader* reader, base::FilePath path) { zip::FilePathWriterDelegate writer(path); - return reader->ExtractCurrentEntry(&writer, - std::numeric_limits::max()); + return reader->ExtractCurrentEntry(&writer); } -bool LocateAndOpenEntry(zip::ZipReader* reader, - const base::FilePath& path_in_zip) { +const zip::ZipReader::Entry* LocateAndOpenEntry( + zip::ZipReader* const reader, + const base::FilePath& path_in_zip) { + DCHECK(reader); + EXPECT_TRUE(reader->ok()); + // The underlying library can do O(1) access, but ZipReader does not expose // that. O(N) access is acceptable for these tests. - while (reader->HasMore()) { - if (!reader->OpenCurrentEntryInZip()) - return false; - if (reader->current_entry_info()->file_path() == path_in_zip) - return true; - reader->AdvanceToNextEntry(); + while (const zip::ZipReader::Entry* const entry = reader->Next()) { + EXPECT_TRUE(reader->ok()); + if (entry->path == path_in_zip) + return entry; } - return false; + + EXPECT_TRUE(reader->ok()); + return nullptr; } -} // namespace +using Paths = std::vector; + +} // namespace namespace zip { // Make the test a PlatformTest to setup autorelease pools properly on Mac. class ZipReaderTest : public PlatformTest { protected: - virtual void SetUp() { + void SetUp() override { PlatformTest::SetUp(); ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); test_dir_ = temp_dir_.GetPath(); - - ASSERT_TRUE(GetTestDataDirectory(&test_data_dir_)); - - test_zip_file_ = test_data_dir_.AppendASCII("test.zip"); - encrypted_zip_file_ = test_data_dir_.AppendASCII("test_encrypted.zip"); - evil_zip_file_ = test_data_dir_.AppendASCII("evil.zip"); - evil_via_invalid_utf8_zip_file_ = test_data_dir_.AppendASCII( - "evil_via_invalid_utf8.zip"); - evil_via_absolute_file_name_zip_file_ = test_data_dir_.AppendASCII( - "evil_via_absolute_file_name.zip"); - - test_zip_contents_.insert(base::FilePath(FILE_PATH_LITERAL("foo/"))); - test_zip_contents_.insert(base::FilePath(FILE_PATH_LITERAL("foo/bar/"))); - test_zip_contents_.insert( - base::FilePath(FILE_PATH_LITERAL("foo/bar/baz.txt"))); - test_zip_contents_.insert( - base::FilePath(FILE_PATH_LITERAL("foo/bar/quux.txt"))); - test_zip_contents_.insert( - base::FilePath(FILE_PATH_LITERAL("foo/bar.txt"))); - test_zip_contents_.insert(base::FilePath(FILE_PATH_LITERAL("foo.txt"))); - test_zip_contents_.insert( - base::FilePath(FILE_PATH_LITERAL("foo/bar/.hidden"))); } - virtual void TearDown() { - PlatformTest::TearDown(); + static base::FilePath GetTestDataDirectory() { + base::FilePath path; + CHECK(base::PathService::Get(base::DIR_SOURCE_ROOT, &path)); + return path.AppendASCII("third_party") + .AppendASCII("zlib") + .AppendASCII("google") + .AppendASCII("test") + .AppendASCII("data"); } - bool GetTestDataDirectory(base::FilePath* path) { - bool success = base::PathService::Get(base::DIR_SOURCE_ROOT, path); - EXPECT_TRUE(success); - if (!success) - return false; - *path = path->AppendASCII("third_party"); - *path = path->AppendASCII("zlib"); - *path = path->AppendASCII("google"); - *path = path->AppendASCII("test"); - *path = path->AppendASCII("data"); - return true; - } + static Paths GetPaths(const base::FilePath& zip_path, + base::StringPiece encoding = {}) { + Paths paths; + + if (ZipReader reader; reader.Open(zip_path)) { + if (!encoding.empty()) + reader.SetEncoding(std::string(encoding)); + + while (const ZipReader::Entry* const entry = reader.Next()) { + EXPECT_TRUE(reader.ok()); + paths.push_back(entry->path); + } + + EXPECT_TRUE(reader.ok()); + } - bool CompareFileAndMD5(const base::FilePath& path, - const std::string expected_md5) { - // Read the output file and compute the MD5. - std::string output; - if (!base::ReadFileToString(path, &output)) - return false; - const std::string md5 = base::MD5String(output); - return expected_md5 == md5; + return paths; } // The path to temporary directory used to contain the test operations. base::FilePath test_dir_; // The path to the test data directory where test.zip etc. are located. - base::FilePath test_data_dir_; + const base::FilePath data_dir_ = GetTestDataDirectory(); // The path to test.zip in the test data directory. - base::FilePath test_zip_file_; - // The path to test_encrypted.zip in the test data directory. - base::FilePath encrypted_zip_file_; - // The path to evil.zip in the test data directory. - base::FilePath evil_zip_file_; - // The path to evil_via_invalid_utf8.zip in the test data directory. - base::FilePath evil_via_invalid_utf8_zip_file_; - // The path to evil_via_absolute_file_name.zip in the test data directory. - base::FilePath evil_via_absolute_file_name_zip_file_; - std::set test_zip_contents_; - + const base::FilePath test_zip_file_ = data_dir_.AppendASCII("test.zip"); + const Paths test_zip_contents_ = { + base::FilePath(FILE_PATH_LITERAL("foo/")), + base::FilePath(FILE_PATH_LITERAL("foo/bar/")), + base::FilePath(FILE_PATH_LITERAL("foo/bar/baz.txt")), + base::FilePath(FILE_PATH_LITERAL("foo/bar/quux.txt")), + base::FilePath(FILE_PATH_LITERAL("foo/bar.txt")), + base::FilePath(FILE_PATH_LITERAL("foo.txt")), + base::FilePath(FILE_PATH_LITERAL("foo/bar/.hidden")), + }; base::ScopedTempDir temp_dir_; - base::test::TaskEnvironment task_environment_; }; TEST_F(ZipReaderTest, Open_ValidZipFile) { ZipReader reader; - ASSERT_TRUE(reader.Open(test_zip_file_)); + EXPECT_TRUE(reader.Open(test_zip_file_)); + EXPECT_TRUE(reader.ok()); } TEST_F(ZipReaderTest, Open_ValidZipPlatformFile) { ZipReader reader; + EXPECT_FALSE(reader.ok()); FileWrapper zip_fd_wrapper(test_zip_file_, FileWrapper::READ_ONLY); - ASSERT_TRUE(reader.OpenFromPlatformFile(zip_fd_wrapper.platform_file())); + EXPECT_TRUE(reader.OpenFromPlatformFile(zip_fd_wrapper.platform_file())); + EXPECT_TRUE(reader.ok()); } TEST_F(ZipReaderTest, Open_NonExistentFile) { ZipReader reader; - ASSERT_FALSE(reader.Open(test_data_dir_.AppendASCII("nonexistent.zip"))); + EXPECT_FALSE(reader.ok()); + EXPECT_FALSE(reader.Open(data_dir_.AppendASCII("nonexistent.zip"))); + EXPECT_FALSE(reader.ok()); } TEST_F(ZipReaderTest, Open_ExistentButNonZipFile) { ZipReader reader; - ASSERT_FALSE(reader.Open(test_data_dir_.AppendASCII("create_test_zip.sh"))); + EXPECT_FALSE(reader.ok()); + EXPECT_FALSE(reader.Open(data_dir_.AppendASCII("create_test_zip.sh"))); + EXPECT_FALSE(reader.ok()); } -// Iterate through the contents in the test zip file, and compare that the -// contents collected from the zip reader matches the expected contents. +TEST_F(ZipReaderTest, Open_EmptyFile) { + ZipReader reader; + EXPECT_FALSE(reader.ok()); + EXPECT_FALSE(reader.Open(data_dir_.AppendASCII("empty.zip"))); + EXPECT_FALSE(reader.ok()); +} + +// Iterate through the contents in the test ZIP archive, and compare that the +// contents collected from the ZipReader matches the expected contents. TEST_F(ZipReaderTest, Iteration) { - std::set actual_contents; + Paths actual_contents; ZipReader reader; - ASSERT_TRUE(reader.Open(test_zip_file_)); - while (reader.HasMore()) { - ASSERT_TRUE(reader.OpenCurrentEntryInZip()); - actual_contents.insert(reader.current_entry_info()->file_path()); - ASSERT_TRUE(reader.AdvanceToNextEntry()); + EXPECT_FALSE(reader.ok()); + EXPECT_TRUE(reader.Open(test_zip_file_)); + EXPECT_TRUE(reader.ok()); + while (const ZipReader::Entry* const entry = reader.Next()) { + EXPECT_TRUE(reader.ok()); + actual_contents.push_back(entry->path); } - EXPECT_FALSE(reader.AdvanceToNextEntry()); // Shouldn't go further. - EXPECT_EQ(test_zip_contents_.size(), - static_cast(reader.num_entries())); - EXPECT_EQ(test_zip_contents_.size(), actual_contents.size()); - EXPECT_EQ(test_zip_contents_, actual_contents); + + EXPECT_TRUE(reader.ok()); + EXPECT_FALSE(reader.Next()); // Shouldn't go further. + EXPECT_TRUE(reader.ok()); + + EXPECT_THAT(actual_contents, SizeIs(reader.num_entries())); + EXPECT_THAT(actual_contents, ElementsAreArray(test_zip_contents_)); } -// Open the test zip file from a file descriptor, iterate through its contents, -// and compare that they match the expected contents. +// Open the test ZIP archive from a file descriptor, iterate through its +// contents, and compare that they match the expected contents. TEST_F(ZipReaderTest, PlatformFileIteration) { - std::set actual_contents; + Paths actual_contents; ZipReader reader; FileWrapper zip_fd_wrapper(test_zip_file_, FileWrapper::READ_ONLY); - ASSERT_TRUE(reader.OpenFromPlatformFile(zip_fd_wrapper.platform_file())); - while (reader.HasMore()) { - ASSERT_TRUE(reader.OpenCurrentEntryInZip()); - actual_contents.insert(reader.current_entry_info()->file_path()); - ASSERT_TRUE(reader.AdvanceToNextEntry()); + EXPECT_TRUE(reader.OpenFromPlatformFile(zip_fd_wrapper.platform_file())); + EXPECT_TRUE(reader.ok()); + while (const ZipReader::Entry* const entry = reader.Next()) { + EXPECT_TRUE(reader.ok()); + actual_contents.push_back(entry->path); } - EXPECT_FALSE(reader.AdvanceToNextEntry()); // Shouldn't go further. - EXPECT_EQ(test_zip_contents_.size(), - static_cast(reader.num_entries())); - EXPECT_EQ(test_zip_contents_.size(), actual_contents.size()); - EXPECT_EQ(test_zip_contents_, actual_contents); + + EXPECT_TRUE(reader.ok()); + EXPECT_FALSE(reader.Next()); // Shouldn't go further. + EXPECT_TRUE(reader.ok()); + + EXPECT_THAT(actual_contents, SizeIs(reader.num_entries())); + EXPECT_THAT(actual_contents, ElementsAreArray(test_zip_contents_)); } -TEST_F(ZipReaderTest, current_entry_info_RegularFile) { +TEST_F(ZipReaderTest, RegularFile) { ZipReader reader; ASSERT_TRUE(reader.Open(test_zip_file_)); base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/quux.txt")); - ASSERT_TRUE(LocateAndOpenEntry(&reader, target_path)); - ZipReader::EntryInfo* current_entry_info = reader.current_entry_info(); - EXPECT_EQ(target_path, current_entry_info->file_path()); - EXPECT_EQ(13527, current_entry_info->original_size()); + const ZipReader::Entry* entry = LocateAndOpenEntry(&reader, target_path); + ASSERT_TRUE(entry); + + EXPECT_EQ(target_path, entry->path); + EXPECT_EQ(13527, entry->original_size); // The expected time stamp: 2009-05-29 06:22:20 base::Time::Exploded exploded = {}; // Zero-clear. - current_entry_info->last_modified().UTCExplode(&exploded); + entry->last_modified.UTCExplode(&exploded); EXPECT_EQ(2009, exploded.year); EXPECT_EQ(5, exploded.month); EXPECT_EQ(29, exploded.day_of_month); @@ -297,67 +300,106 @@ TEST_F(ZipReaderTest, current_entry_info_RegularFile) { EXPECT_EQ(20, exploded.second); EXPECT_EQ(0, exploded.millisecond); - EXPECT_FALSE(current_entry_info->is_unsafe()); - EXPECT_FALSE(current_entry_info->is_directory()); + EXPECT_FALSE(entry->is_unsafe); + EXPECT_FALSE(entry->is_directory); } -TEST_F(ZipReaderTest, current_entry_info_DotDotFile) { +TEST_F(ZipReaderTest, DotDotFile) { ZipReader reader; - ASSERT_TRUE(reader.Open(evil_zip_file_)); + ASSERT_TRUE(reader.Open(data_dir_.AppendASCII("evil.zip"))); base::FilePath target_path(FILE_PATH_LITERAL( - "../levilevilevilevilevilevilevilevilevilevilevilevil")); - ASSERT_TRUE(LocateAndOpenEntry(&reader, target_path)); - ZipReader::EntryInfo* current_entry_info = reader.current_entry_info(); - EXPECT_EQ(target_path, current_entry_info->file_path()); - - // This file is unsafe because of ".." in the file name. - EXPECT_TRUE(current_entry_info->is_unsafe()); - EXPECT_FALSE(current_entry_info->is_directory()); + "UP/levilevilevilevilevilevilevilevilevilevilevilevil")); + const ZipReader::Entry* entry = LocateAndOpenEntry(&reader, target_path); + ASSERT_TRUE(entry); + EXPECT_EQ(target_path, entry->path); + EXPECT_FALSE(entry->is_unsafe); + EXPECT_FALSE(entry->is_directory); } -TEST_F(ZipReaderTest, current_entry_info_InvalidUTF8File) { +TEST_F(ZipReaderTest, InvalidUTF8File) { ZipReader reader; - ASSERT_TRUE(reader.Open(evil_via_invalid_utf8_zip_file_)); - // The evil file is the 2nd file in the zip file. - // We cannot locate by the file name ".\x80.\\evil.txt", - // as FilePath may internally convert the string. - ASSERT_TRUE(reader.AdvanceToNextEntry()); - ASSERT_TRUE(reader.OpenCurrentEntryInZip()); - ZipReader::EntryInfo* current_entry_info = reader.current_entry_info(); + ASSERT_TRUE(reader.Open(data_dir_.AppendASCII("evil_via_invalid_utf8.zip"))); + base::FilePath target_path = base::FilePath::FromUTF8Unsafe(".�.�evil.txt"); + const ZipReader::Entry* entry = LocateAndOpenEntry(&reader, target_path); + ASSERT_TRUE(entry); + EXPECT_EQ(target_path, entry->path); + EXPECT_FALSE(entry->is_unsafe); + EXPECT_FALSE(entry->is_directory); +} - // This file is unsafe because of invalid UTF-8 in the file name. - EXPECT_TRUE(current_entry_info->is_unsafe()); - EXPECT_FALSE(current_entry_info->is_directory()); +// By default, file paths in ZIPs are interpreted as UTF-8. But in this test, +// the ZIP archive contains file paths that are actually encoded in Shift JIS. +// The SJIS-encoded paths are thus wrongly interpreted as UTF-8, resulting in +// garbled paths. Invalid UTF-8 sequences are safely converted to the +// replacement character �. +TEST_F(ZipReaderTest, EncodingSjisAsUtf8) { + EXPECT_THAT( + GetPaths(data_dir_.AppendASCII("SJIS Bug 846195.zip")), + ElementsAre( + base::FilePath::FromUTF8Unsafe("�V�����t�H���_/SJIS_835C_��.txt"), + base::FilePath::FromUTF8Unsafe( + "�V�����t�H���_/�V�����e�L�X�g �h�L�������g.txt"))); } -TEST_F(ZipReaderTest, current_entry_info_AbsoluteFile) { - ZipReader reader; - ASSERT_TRUE(reader.Open(evil_via_absolute_file_name_zip_file_)); - base::FilePath target_path(FILE_PATH_LITERAL("/evil.txt")); - ASSERT_TRUE(LocateAndOpenEntry(&reader, target_path)); - ZipReader::EntryInfo* current_entry_info = reader.current_entry_info(); - EXPECT_EQ(target_path, current_entry_info->file_path()); +// In this test, SJIS-encoded paths are interpreted as Code Page 1252. This +// results in garbled paths. Note the presence of C1 control codes U+0090 and +// U+0081 in the garbled paths. +TEST_F(ZipReaderTest, EncodingSjisAs1252) { + EXPECT_THAT( + GetPaths(data_dir_.AppendASCII("SJIS Bug 846195.zip"), "windows-1252"), + ElementsAre(base::FilePath::FromUTF8Unsafe( + "\u0090V‚µ‚¢ƒtƒHƒ‹ƒ_/SJIS_835C_ƒ�.txt"), + base::FilePath::FromUTF8Unsafe( + "\u0090V‚µ‚¢ƒtƒHƒ‹ƒ_/\u0090V‚µ‚¢ƒeƒLƒXƒg " + "ƒhƒLƒ…ƒ\u0081ƒ“ƒg.txt"))); +} + +// In this test, SJIS-encoded paths are interpreted as Code Page 866. This +// results in garbled paths. +TEST_F(ZipReaderTest, EncodingSjisAsIbm866) { + EXPECT_THAT( + GetPaths(data_dir_.AppendASCII("SJIS Bug 846195.zip"), "IBM866"), + ElementsAre( + base::FilePath::FromUTF8Unsafe("РVВ╡ВвГtГHГЛГ_/SJIS_835C_Г�.txt"), + base::FilePath::FromUTF8Unsafe( + "РVВ╡ВвГtГHГЛГ_/РVВ╡ВвГeГLГXГg ГhГLГЕГБГУГg.txt"))); +} + +// Tests that SJIS-encoded paths are correctly converted to Unicode. +TEST_F(ZipReaderTest, EncodingSjis) { + EXPECT_THAT( + GetPaths(data_dir_.AppendASCII("SJIS Bug 846195.zip"), "Shift_JIS"), + ElementsAre( + base::FilePath::FromUTF8Unsafe("新しいフォルダ/SJIS_835C_ソ.txt"), + base::FilePath::FromUTF8Unsafe( + "新しいフォルダ/新しいテキスト ドキュメント.txt"))); +} - // This file is unsafe because of the absolute file name. - EXPECT_TRUE(current_entry_info->is_unsafe()); - EXPECT_FALSE(current_entry_info->is_directory()); +TEST_F(ZipReaderTest, AbsoluteFile) { + ZipReader reader; + ASSERT_TRUE( + reader.Open(data_dir_.AppendASCII("evil_via_absolute_file_name.zip"))); + base::FilePath target_path(FILE_PATH_LITERAL("ROOT/evil.txt")); + const ZipReader::Entry* entry = LocateAndOpenEntry(&reader, target_path); + ASSERT_TRUE(entry); + EXPECT_EQ(target_path, entry->path); + EXPECT_FALSE(entry->is_unsafe); + EXPECT_FALSE(entry->is_directory); } -TEST_F(ZipReaderTest, current_entry_info_Directory) { +TEST_F(ZipReaderTest, Directory) { ZipReader reader; ASSERT_TRUE(reader.Open(test_zip_file_)); base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/")); - ASSERT_TRUE(LocateAndOpenEntry(&reader, target_path)); - ZipReader::EntryInfo* current_entry_info = reader.current_entry_info(); - - EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("foo/bar/")), - current_entry_info->file_path()); + const ZipReader::Entry* entry = LocateAndOpenEntry(&reader, target_path); + ASSERT_TRUE(entry); + EXPECT_EQ(target_path, entry->path); // The directory size should be zero. - EXPECT_EQ(0, current_entry_info->original_size()); + EXPECT_EQ(0, entry->original_size); // The expected time stamp: 2009-05-31 15:49:52 base::Time::Exploded exploded = {}; // Zero-clear. - current_entry_info->last_modified().UTCExplode(&exploded); + entry->last_modified.UTCExplode(&exploded); EXPECT_EQ(2009, exploded.year); EXPECT_EQ(5, exploded.month); EXPECT_EQ(31, exploded.day_of_month); @@ -366,22 +408,91 @@ TEST_F(ZipReaderTest, current_entry_info_Directory) { EXPECT_EQ(52, exploded.second); EXPECT_EQ(0, exploded.millisecond); - EXPECT_FALSE(current_entry_info->is_unsafe()); - EXPECT_TRUE(current_entry_info->is_directory()); + EXPECT_FALSE(entry->is_unsafe); + EXPECT_TRUE(entry->is_directory); } -TEST_F(ZipReaderTest, current_entry_info_EncryptedFile) { +TEST_F(ZipReaderTest, EncryptedFile_WrongPassword) { ZipReader reader; - base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/quux.txt")); + ASSERT_TRUE(reader.Open(data_dir_.AppendASCII("Different Encryptions.zip"))); + reader.SetPassword("wrong password"); - ASSERT_TRUE(reader.Open(encrypted_zip_file_)); - ASSERT_TRUE(LocateAndOpenEntry(&reader, target_path)); - EXPECT_TRUE(reader.current_entry_info()->is_encrypted()); - reader.Close(); + { + const ZipReader::Entry* entry = reader.Next(); + ASSERT_TRUE(entry); + EXPECT_EQ(base::FilePath::FromASCII("ClearText.txt"), entry->path); + EXPECT_FALSE(entry->is_directory); + EXPECT_FALSE(entry->is_encrypted); + std::string contents = "dummy"; + EXPECT_TRUE(reader.ExtractCurrentEntryToString(&contents)); + EXPECT_EQ("This is not encrypted.\n", contents); + } - ASSERT_TRUE(reader.Open(test_zip_file_)); - ASSERT_TRUE(LocateAndOpenEntry(&reader, target_path)); - EXPECT_FALSE(reader.current_entry_info()->is_encrypted()); + for (const base::StringPiece path : { + "Encrypted AES-128.txt", + "Encrypted AES-192.txt", + "Encrypted AES-256.txt", + "Encrypted ZipCrypto.txt", + }) { + const ZipReader::Entry* entry = reader.Next(); + ASSERT_TRUE(entry); + EXPECT_EQ(base::FilePath::FromASCII(path), entry->path); + EXPECT_FALSE(entry->is_directory); + EXPECT_TRUE(entry->is_encrypted); + std::string contents = "dummy"; + EXPECT_FALSE(reader.ExtractCurrentEntryToString(&contents)); + } + + EXPECT_FALSE(reader.Next()); + EXPECT_TRUE(reader.ok()); +} + +TEST_F(ZipReaderTest, EncryptedFile_RightPassword) { + ZipReader reader; + ASSERT_TRUE(reader.Open(data_dir_.AppendASCII("Different Encryptions.zip"))); + reader.SetPassword("password"); + + { + const ZipReader::Entry* entry = reader.Next(); + ASSERT_TRUE(entry); + EXPECT_EQ(base::FilePath::FromASCII("ClearText.txt"), entry->path); + EXPECT_FALSE(entry->is_directory); + EXPECT_FALSE(entry->is_encrypted); + std::string contents = "dummy"; + EXPECT_TRUE(reader.ExtractCurrentEntryToString(&contents)); + EXPECT_EQ("This is not encrypted.\n", contents); + } + + // TODO(crbug.com/1296838) Support AES encryption. + for (const base::StringPiece path : { + "Encrypted AES-128.txt", + "Encrypted AES-192.txt", + "Encrypted AES-256.txt", + }) { + const ZipReader::Entry* entry = reader.Next(); + ASSERT_TRUE(entry); + EXPECT_EQ(base::FilePath::FromASCII(path), entry->path); + EXPECT_FALSE(entry->is_directory); + EXPECT_TRUE(entry->is_encrypted); + std::string contents = "dummy"; + EXPECT_FALSE(reader.ExtractCurrentEntryToString(&contents)); + EXPECT_EQ("", contents); + } + + { + const ZipReader::Entry* entry = reader.Next(); + ASSERT_TRUE(entry); + EXPECT_EQ(base::FilePath::FromASCII("Encrypted ZipCrypto.txt"), + entry->path); + EXPECT_FALSE(entry->is_directory); + EXPECT_TRUE(entry->is_encrypted); + std::string contents = "dummy"; + EXPECT_TRUE(reader.ExtractCurrentEntryToString(&contents)); + EXPECT_EQ("This is encrypted with ZipCrypto.\n", contents); + } + + EXPECT_FALSE(reader.Next()); + EXPECT_TRUE(reader.ok()); } // Verifies that the ZipReader class can extract a file from a zip archive @@ -404,7 +515,7 @@ TEST_F(ZipReaderTest, OpenFromString) { "\x50\x75\x78\x0b\x00\x01\x04\x8e\xf0\x00\x00\x04\x88\x13\x00\x00" "\x50\x4b\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00\x4e\x00\x00\x00" "\x52\x00\x00\x00\x00\x00"; - std::string data(kTestData, base::size(kTestData)); + std::string data(kTestData, std::size(kTestData)); ZipReader reader; ASSERT_TRUE(reader.OpenFromString(data)); base::FilePath target_path(FILE_PATH_LITERAL("test.txt")); @@ -413,8 +524,8 @@ TEST_F(ZipReaderTest, OpenFromString) { test_dir_.AppendASCII("test.txt"))); std::string actual; - ASSERT_TRUE(base::ReadFileToString( - test_dir_.AppendASCII("test.txt"), &actual)); + ASSERT_TRUE( + base::ReadFileToString(test_dir_.AppendASCII("test.txt"), &actual)); EXPECT_EQ(std::string("This is a test.\n"), actual); } @@ -445,8 +556,8 @@ TEST_F(ZipReaderTest, ExtractToFileAsync_RegularFile) { EXPECT_LE(1, listener.progress_calls()); std::string output; - ASSERT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("quux.txt"), - &output)); + ASSERT_TRUE( + base::ReadFileToString(test_dir_.AppendASCII("quux.txt"), &output)); const std::string md5 = base::MD5String(output); EXPECT_EQ(kQuuxExpectedMD5, md5); @@ -456,6 +567,103 @@ TEST_F(ZipReaderTest, ExtractToFileAsync_RegularFile) { EXPECT_EQ(file_size, listener.current_progress()); } +TEST_F(ZipReaderTest, ExtractToFileAsync_Encrypted_NoPassword) { + MockUnzipListener listener; + + ZipReader reader; + ASSERT_TRUE(reader.Open(data_dir_.AppendASCII("Different Encryptions.zip"))); + ASSERT_TRUE(LocateAndOpenEntry( + &reader, base::FilePath::FromASCII("Encrypted ZipCrypto.txt"))); + const base::FilePath target_path = test_dir_.AppendASCII("extracted"); + reader.ExtractCurrentEntryToFilePathAsync( + target_path, + base::BindOnce(&MockUnzipListener::OnUnzipSuccess, listener.AsWeakPtr()), + base::BindOnce(&MockUnzipListener::OnUnzipFailure, listener.AsWeakPtr()), + base::BindRepeating(&MockUnzipListener::OnUnzipProgress, + listener.AsWeakPtr())); + + EXPECT_EQ(0, listener.success_calls()); + EXPECT_EQ(0, listener.failure_calls()); + EXPECT_EQ(0, listener.progress_calls()); + + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(0, listener.success_calls()); + EXPECT_EQ(1, listener.failure_calls()); + EXPECT_LE(1, listener.progress_calls()); + + // The extracted file contains rubbish data. + // We probably shouldn't even look at it. + std::string contents; + ASSERT_TRUE(base::ReadFileToString(target_path, &contents)); + EXPECT_NE("", contents); + EXPECT_EQ(contents.size(), listener.current_progress()); +} + +TEST_F(ZipReaderTest, ExtractToFileAsync_Encrypted_RightPassword) { + MockUnzipListener listener; + + ZipReader reader; + reader.SetPassword("password"); + ASSERT_TRUE(reader.Open(data_dir_.AppendASCII("Different Encryptions.zip"))); + ASSERT_TRUE(LocateAndOpenEntry( + &reader, base::FilePath::FromASCII("Encrypted ZipCrypto.txt"))); + const base::FilePath target_path = test_dir_.AppendASCII("extracted"); + reader.ExtractCurrentEntryToFilePathAsync( + target_path, + base::BindOnce(&MockUnzipListener::OnUnzipSuccess, listener.AsWeakPtr()), + base::BindOnce(&MockUnzipListener::OnUnzipFailure, listener.AsWeakPtr()), + base::BindRepeating(&MockUnzipListener::OnUnzipProgress, + listener.AsWeakPtr())); + + EXPECT_EQ(0, listener.success_calls()); + EXPECT_EQ(0, listener.failure_calls()); + EXPECT_EQ(0, listener.progress_calls()); + + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(1, listener.success_calls()); + EXPECT_EQ(0, listener.failure_calls()); + EXPECT_LE(1, listener.progress_calls()); + + std::string contents; + ASSERT_TRUE(base::ReadFileToString(target_path, &contents)); + EXPECT_EQ("This is encrypted with ZipCrypto.\n", contents); + EXPECT_EQ(contents.size(), listener.current_progress()); +} + +TEST_F(ZipReaderTest, ExtractToFileAsync_WrongCrc) { + MockUnzipListener listener; + + ZipReader reader; + ASSERT_TRUE(reader.Open(data_dir_.AppendASCII("Wrong CRC.zip"))); + ASSERT_TRUE( + LocateAndOpenEntry(&reader, base::FilePath::FromASCII("Corrupted.txt"))); + const base::FilePath target_path = test_dir_.AppendASCII("extracted"); + reader.ExtractCurrentEntryToFilePathAsync( + target_path, + base::BindOnce(&MockUnzipListener::OnUnzipSuccess, listener.AsWeakPtr()), + base::BindOnce(&MockUnzipListener::OnUnzipFailure, listener.AsWeakPtr()), + base::BindRepeating(&MockUnzipListener::OnUnzipProgress, + listener.AsWeakPtr())); + + EXPECT_EQ(0, listener.success_calls()); + EXPECT_EQ(0, listener.failure_calls()); + EXPECT_EQ(0, listener.progress_calls()); + + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(0, listener.success_calls()); + EXPECT_EQ(1, listener.failure_calls()); + EXPECT_LE(1, listener.progress_calls()); + + std::string contents; + ASSERT_TRUE(base::ReadFileToString(target_path, &contents)); + EXPECT_EQ("This file has been changed after its CRC was computed.\n", + contents); + EXPECT_EQ(contents.size(), listener.current_progress()); +} + // Verifies that the asynchronous extraction to a file works. TEST_F(ZipReaderTest, ExtractToFileAsync_Directory) { MockUnzipListener listener; @@ -490,7 +698,7 @@ TEST_F(ZipReaderTest, ExtractCurrentEntryToString) { // sizes from 0 to 7 bytes respectively, being the contents of each file a // substring of "0123456" starting at '0'. base::FilePath test_zip_file = - test_data_dir_.AppendASCII("test_mismatch_size.zip"); + data_dir_.AppendASCII("test_mismatch_size.zip"); ZipReader reader; std::string contents; @@ -515,7 +723,7 @@ TEST_F(ZipReaderTest, ExtractCurrentEntryToString) { } // More than necessary byte read limit: must pass. - EXPECT_TRUE(reader.ExtractCurrentEntryToString(16, &contents)); + EXPECT_TRUE(reader.ExtractCurrentEntryToString(&contents)); EXPECT_EQ(std::string(base::StringPiece("0123456", i)), contents); } reader.Close(); @@ -526,7 +734,7 @@ TEST_F(ZipReaderTest, ExtractPartOfCurrentEntry) { // sizes from 0 to 7 bytes respectively, being the contents of each file a // substring of "0123456" starting at '0'. base::FilePath test_zip_file = - test_data_dir_.AppendASCII("test_mismatch_size.zip"); + data_dir_.AppendASCII("test_mismatch_size.zip"); ZipReader reader; std::string contents; @@ -564,6 +772,37 @@ TEST_F(ZipReaderTest, ExtractPartOfCurrentEntry) { reader.Close(); } +TEST_F(ZipReaderTest, ExtractPosixPermissions) { + base::ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + + ZipReader reader; + ASSERT_TRUE(reader.Open(data_dir_.AppendASCII("test_posix_permissions.zip"))); + for (auto entry : {"0.txt", "1.txt", "2.txt", "3.txt"}) { + ASSERT_TRUE(LocateAndOpenEntry(&reader, base::FilePath::FromASCII(entry))); + FilePathWriterDelegate delegate(temp_dir.GetPath().AppendASCII(entry)); + ASSERT_TRUE(reader.ExtractCurrentEntry(&delegate)); + } + reader.Close(); + +#if defined(OS_POSIX) + // This assumes a umask of at least 0400. + int mode = 0; + EXPECT_TRUE(base::GetPosixFilePermissions( + temp_dir.GetPath().AppendASCII("0.txt"), &mode)); + EXPECT_EQ(mode & 0700, 0700); + EXPECT_TRUE(base::GetPosixFilePermissions( + temp_dir.GetPath().AppendASCII("1.txt"), &mode)); + EXPECT_EQ(mode & 0700, 0600); + EXPECT_TRUE(base::GetPosixFilePermissions( + temp_dir.GetPath().AppendASCII("2.txt"), &mode)); + EXPECT_EQ(mode & 0700, 0700); + EXPECT_TRUE(base::GetPosixFilePermissions( + temp_dir.GetPath().AppendASCII("3.txt"), &mode)); + EXPECT_EQ(mode & 0700, 0600); +#endif +} + // This test exposes http://crbug.com/430959, at least on OS X TEST_F(ZipReaderTest, DISABLED_LeakDetectionTest) { for (int i = 0; i < 100000; ++i) { @@ -578,45 +817,40 @@ TEST_F(ZipReaderTest, DISABLED_LeakDetectionTest) { TEST_F(ZipReaderTest, ExtractCurrentEntryPrepareFailure) { testing::StrictMock mock_writer; - EXPECT_CALL(mock_writer, PrepareOutput()) - .WillOnce(Return(false)); + EXPECT_CALL(mock_writer, PrepareOutput()).WillOnce(Return(false)); base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/quux.txt")); ZipReader reader; ASSERT_TRUE(reader.Open(test_zip_file_)); ASSERT_TRUE(LocateAndOpenEntry(&reader, target_path)); - ASSERT_FALSE(reader.ExtractCurrentEntry( - &mock_writer, std::numeric_limits::max())); + ASSERT_FALSE(reader.ExtractCurrentEntry(&mock_writer)); } -// Test that when WriterDelegate::WriteBytes returns false, no other methods on -// the delegate are called and the extraction fails. +// Test that when WriterDelegate::WriteBytes returns false, only the OnError +// method on the delegate is called and the extraction fails. TEST_F(ZipReaderTest, ExtractCurrentEntryWriteBytesFailure) { testing::StrictMock mock_writer; - EXPECT_CALL(mock_writer, PrepareOutput()) - .WillOnce(Return(true)); - EXPECT_CALL(mock_writer, WriteBytes(_, _)) - .WillOnce(Return(false)); + EXPECT_CALL(mock_writer, PrepareOutput()).WillOnce(Return(true)); + EXPECT_CALL(mock_writer, WriteBytes(_, _)).WillOnce(Return(false)); + EXPECT_CALL(mock_writer, OnError()); base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/quux.txt")); ZipReader reader; ASSERT_TRUE(reader.Open(test_zip_file_)); ASSERT_TRUE(LocateAndOpenEntry(&reader, target_path)); - ASSERT_FALSE(reader.ExtractCurrentEntry( - &mock_writer, std::numeric_limits::max())); + ASSERT_FALSE(reader.ExtractCurrentEntry(&mock_writer)); } // Test that extraction succeeds when the writer delegate reports all is well. TEST_F(ZipReaderTest, ExtractCurrentEntrySuccess) { testing::StrictMock mock_writer; - EXPECT_CALL(mock_writer, PrepareOutput()) - .WillOnce(Return(true)); - EXPECT_CALL(mock_writer, WriteBytes(_, _)) - .WillRepeatedly(Return(true)); + EXPECT_CALL(mock_writer, PrepareOutput()).WillOnce(Return(true)); + EXPECT_CALL(mock_writer, WriteBytes(_, _)).WillRepeatedly(Return(true)); + EXPECT_CALL(mock_writer, SetPosixFilePermissions(_)); EXPECT_CALL(mock_writer, SetTimeModified(_)); base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/quux.txt")); @@ -624,8 +858,38 @@ TEST_F(ZipReaderTest, ExtractCurrentEntrySuccess) { ASSERT_TRUE(reader.Open(test_zip_file_)); ASSERT_TRUE(LocateAndOpenEntry(&reader, target_path)); - ASSERT_TRUE(reader.ExtractCurrentEntry(&mock_writer, - std::numeric_limits::max())); + ASSERT_TRUE(reader.ExtractCurrentEntry(&mock_writer)); +} + +TEST_F(ZipReaderTest, WrongCrc) { + ZipReader reader; + ASSERT_TRUE(reader.Open(data_dir_.AppendASCII("Wrong CRC.zip"))); + + const ZipReader::Entry* const entry = + LocateAndOpenEntry(&reader, base::FilePath::FromASCII("Corrupted.txt")); + ASSERT_TRUE(entry); + + std::string contents = "dummy"; + EXPECT_FALSE(reader.ExtractCurrentEntryToString(&contents)); + EXPECT_EQ("This file has been changed after its CRC was computed.\n", + contents); + + contents = "dummy"; + EXPECT_FALSE( + reader.ExtractCurrentEntryToString(entry->original_size + 1, &contents)); + EXPECT_EQ("This file has been changed after its CRC was computed.\n", + contents); + + contents = "dummy"; + EXPECT_FALSE( + reader.ExtractCurrentEntryToString(entry->original_size, &contents)); + EXPECT_EQ("This file has been changed after its CRC was computed.\n", + contents); + + contents = "dummy"; + EXPECT_FALSE( + reader.ExtractCurrentEntryToString(entry->original_size - 1, &contents)); + EXPECT_EQ("This file has been changed after its CRC was computed.", contents); } class FileWriterDelegateTest : public ::testing::Test { @@ -639,34 +903,39 @@ class FileWriterDelegateTest : public ::testing::Test { ASSERT_TRUE(file_.IsValid()); } - // Writes data to the file, leaving the current position at the end of the - // write. - void PopulateFile() { - static const char kSomeData[] = "this sure is some data."; - static const size_t kSomeDataLen = sizeof(kSomeData) - 1; - ASSERT_NE(-1LL, file_.Write(0LL, kSomeData, kSomeDataLen)); - } - base::FilePath temp_file_path_; base::File file_; }; -TEST_F(FileWriterDelegateTest, WriteToStartAndTruncate) { - // Write stuff and advance. - PopulateFile(); +TEST_F(FileWriterDelegateTest, WriteToEnd) { + const std::string payload = "This is the actualy payload data.\n"; - // This should rewind, write, then truncate. - static const char kSomeData[] = "short"; - static const int kSomeDataLen = sizeof(kSomeData) - 1; { FileWriterDelegate writer(&file_); + EXPECT_EQ(0, writer.file_length()); ASSERT_TRUE(writer.PrepareOutput()); - ASSERT_TRUE(writer.WriteBytes(kSomeData, kSomeDataLen)); + ASSERT_TRUE(writer.WriteBytes(payload.data(), payload.size())); + EXPECT_EQ(payload.size(), writer.file_length()); } - ASSERT_EQ(kSomeDataLen, file_.GetLength()); - char buf[kSomeDataLen] = {}; - ASSERT_EQ(kSomeDataLen, file_.Read(0LL, buf, kSomeDataLen)); - ASSERT_EQ(std::string(kSomeData), std::string(buf, kSomeDataLen)); + + EXPECT_EQ(payload.size(), file_.GetLength()); +} + +TEST_F(FileWriterDelegateTest, EmptyOnError) { + const std::string payload = "This is the actualy payload data.\n"; + + { + FileWriterDelegate writer(&file_); + EXPECT_EQ(0, writer.file_length()); + ASSERT_TRUE(writer.PrepareOutput()); + ASSERT_TRUE(writer.WriteBytes(payload.data(), payload.size())); + EXPECT_EQ(payload.size(), writer.file_length()); + EXPECT_EQ(payload.size(), file_.GetLength()); + writer.OnError(); + EXPECT_EQ(0, writer.file_length()); + } + + EXPECT_EQ(0, file_.GetLength()); } } // namespace zip diff --git a/deps/v8/third_party/zlib/google/zip_unittest.cc b/deps/v8/third_party/zlib/google/zip_unittest.cc index 944930ffc84e39..435d7b02ee2344 100644 --- a/deps/v8/third_party/zlib/google/zip_unittest.cc +++ b/deps/v8/third_party/zlib/google/zip_unittest.cc @@ -5,9 +5,11 @@ #include #include -#include -#include +#include +#include #include +#include +#include #include #include "base/bind.h" @@ -18,10 +20,13 @@ #include "base/files/scoped_temp_dir.h" #include "base/logging.h" #include "base/path_service.h" +#include "base/strings/strcat.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/test/bind.h" +#include "base/time/time.h" #include "build/build_config.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/platform_test.h" #include "third_party/zlib/google/zip.h" @@ -33,6 +38,23 @@ namespace { +using testing::UnorderedElementsAre; +using testing::UnorderedElementsAreArray; + +std::vector GetRelativePaths(const base::FilePath& dir, + base::FileEnumerator::FileType type) { + std::vector got_paths; + base::FileEnumerator files(dir, true, type); + for (base::FilePath path = files.Next(); !path.empty(); path = files.Next()) { + base::FilePath relative; + EXPECT_TRUE(dir.AppendRelativePath(path, &relative)); + got_paths.push_back(relative.NormalizePathSeparatorsTo('/').AsUTF8Unsafe()); + } + + EXPECT_EQ(base::File::FILE_OK, files.GetError()); + return got_paths; +} + bool CreateFile(const std::string& content, base::FilePath* file_path, base::File* file) { @@ -47,6 +69,46 @@ bool CreateFile(const std::string& content, return file->IsValid(); } +// A WriterDelegate that logs progress once per second. +class ProgressWriterDelegate : public zip::WriterDelegate { + public: + explicit ProgressWriterDelegate(int64_t expected_size) + : expected_size_(expected_size) { + CHECK_GT(expected_size_, 0); + } + + bool WriteBytes(const char* data, int num_bytes) override { + received_bytes_ += num_bytes; + LogProgressIfNecessary(); + return true; + } + + void SetTimeModified(const base::Time& time) override { LogProgress(); } + + int64_t received_bytes() const { return received_bytes_; } + + private: + void LogProgressIfNecessary() { + const base::TimeTicks now = base::TimeTicks::Now(); + if (next_progress_report_time_ > now) + return; + + next_progress_report_time_ = now + progress_period_; + LogProgress(); + } + + void LogProgress() const { + LOG(INFO) << "Unzipping... " << std::setw(3) + << (100 * received_bytes_ / expected_size_) << "%"; + } + + const base::TimeDelta progress_period_ = base::Seconds(1); + base::TimeTicks next_progress_report_time_ = + base::TimeTicks::Now() + progress_period_; + const uint64_t expected_size_; + int64_t received_bytes_ = 0; +}; + // A virtual file system containing: // /test // /test/foo.txt @@ -153,8 +215,8 @@ class VirtualFileSystem : public zip::FileAccessor { std::vector files, subdirs; }; - std::map file_tree_; - std::map files_; + std::unordered_map file_tree_; + std::unordered_map files_; }; // static @@ -192,41 +254,38 @@ class ZipTest : public PlatformTest { virtual void TearDown() { PlatformTest::TearDown(); } - bool GetTestDataDirectory(base::FilePath* path) { - bool success = base::PathService::Get(base::DIR_SOURCE_ROOT, path); + static base::FilePath GetDataDirectory() { + base::FilePath path; + bool success = base::PathService::Get(base::DIR_SOURCE_ROOT, &path); EXPECT_TRUE(success); - if (!success) - return false; - for (const base::StringPiece s : - {"third_party", "zlib", "google", "test", "data"}) { - *path = path->AppendASCII(s); - } - return true; + return std::move(path) + .AppendASCII("third_party") + .AppendASCII("zlib") + .AppendASCII("google") + .AppendASCII("test") + .AppendASCII("data"); } void TestUnzipFile(const base::FilePath::StringType& filename, bool expect_hidden_files) { - base::FilePath test_dir; - ASSERT_TRUE(GetTestDataDirectory(&test_dir)); - TestUnzipFile(test_dir.Append(filename), expect_hidden_files); + TestUnzipFile(GetDataDirectory().Append(filename), expect_hidden_files); } void TestUnzipFile(const base::FilePath& path, bool expect_hidden_files) { - ASSERT_TRUE(base::PathExists(path)) << "no file " << path.value(); + ASSERT_TRUE(base::PathExists(path)) << "no file " << path; ASSERT_TRUE(zip::Unzip(path, test_dir_)); - base::FilePath original_dir; - ASSERT_TRUE(GetTestDataDirectory(&original_dir)); - original_dir = original_dir.AppendASCII("test"); + base::FilePath original_dir = GetDataDirectory().AppendASCII("test"); base::FileEnumerator files( test_dir_, true, base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES); - base::FilePath unzipped_entry_path = files.Next(); + size_t count = 0; - while (!unzipped_entry_path.empty()) { + for (base::FilePath unzipped_entry_path = files.Next(); + !unzipped_entry_path.empty(); unzipped_entry_path = files.Next()) { EXPECT_EQ(zip_contents_.count(unzipped_entry_path), 1U) - << "Couldn't find " << unzipped_entry_path.value(); + << "Couldn't find " << unzipped_entry_path; count++; if (base::PathExists(unzipped_entry_path) && @@ -242,13 +301,12 @@ class ZipTest : public PlatformTest { << "Original file '" << original_path << "' and unzipped file '" << unzipped_entry_path << "' have different contents"; } - unzipped_entry_path = files.Next(); } + EXPECT_EQ(base::File::FILE_OK, files.GetError()); size_t expected_count = 0; - for (std::set::iterator iter = zip_contents_.begin(); - iter != zip_contents_.end(); ++iter) { - if (expect_hidden_files || iter->BaseName().value()[0] != '.') + for (const base::FilePath& path : zip_contents_) { + if (expect_hidden_files || path.BaseName().value()[0] != '.') ++expected_count; } @@ -314,12 +372,23 @@ class ZipTest : public PlatformTest { base::ScopedTempDir temp_dir_; // Hard-coded contents of a known zip file. - std::set zip_contents_; + std::unordered_set zip_contents_; // Hard-coded list of relative paths for a zip file created with ZipFiles. std::vector zip_file_list_; }; +TEST_F(ZipTest, UnzipNoSuchFile) { + EXPECT_FALSE(zip::Unzip(GetDataDirectory().AppendASCII("No Such File.zip"), + test_dir_)); + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES), + UnorderedElementsAre()); + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::DIRECTORIES), + UnorderedElementsAre()); +} + TEST_F(ZipTest, Unzip) { TestUnzipFile(FILE_PATH_LITERAL("test.zip"), true); } @@ -329,108 +398,617 @@ TEST_F(ZipTest, UnzipUncompressed) { } TEST_F(ZipTest, UnzipEvil) { - base::FilePath path; - ASSERT_TRUE(GetTestDataDirectory(&path)); - path = path.AppendASCII("evil.zip"); + base::FilePath path = GetDataDirectory().AppendASCII("evil.zip"); // Unzip the zip file into a sub directory of test_dir_ so evil.zip // won't create a persistent file outside test_dir_ in case of a // failure. base::FilePath output_dir = test_dir_.AppendASCII("out"); - ASSERT_FALSE(zip::Unzip(path, output_dir)); - base::FilePath evil_file = output_dir; - evil_file = evil_file.AppendASCII( - "../levilevilevilevilevilevilevilevilevilevilevilevil"); - ASSERT_FALSE(base::PathExists(evil_file)); + EXPECT_TRUE(zip::Unzip(path, output_dir)); + EXPECT_TRUE(base::PathExists(output_dir.AppendASCII( + "UP/levilevilevilevilevilevilevilevilevilevilevilevil"))); } TEST_F(ZipTest, UnzipEvil2) { - base::FilePath path; - ASSERT_TRUE(GetTestDataDirectory(&path)); - // The zip file contains an evil file with invalid UTF-8 in its file - // name. - path = path.AppendASCII("evil_via_invalid_utf8.zip"); + // The ZIP file contains a file with invalid UTF-8 in its file name. + base::FilePath path = + GetDataDirectory().AppendASCII("evil_via_invalid_utf8.zip"); // See the comment at UnzipEvil() for why we do this. base::FilePath output_dir = test_dir_.AppendASCII("out"); - // This should fail as it contains an evil file. - ASSERT_FALSE(zip::Unzip(path, output_dir)); - base::FilePath evil_file = output_dir; - evil_file = evil_file.AppendASCII("../evil.txt"); - ASSERT_FALSE(base::PathExists(evil_file)); + ASSERT_TRUE(zip::Unzip(path, output_dir)); + ASSERT_TRUE(base::PathExists( + output_dir.Append(base::FilePath::FromUTF8Unsafe(".�.�evil.txt")))); + ASSERT_FALSE(base::PathExists(output_dir.AppendASCII("../evil.txt"))); } TEST_F(ZipTest, UnzipWithFilter) { auto filter = base::BindRepeating([](const base::FilePath& path) { return path.BaseName().MaybeAsASCII() == "foo.txt"; }); - base::FilePath path; - ASSERT_TRUE(GetTestDataDirectory(&path)); - ASSERT_TRUE(zip::UnzipWithFilterCallback(path.AppendASCII("test.zip"), - test_dir_, filter, false)); - // Only foo.txt should have been extracted. The following paths should not - // be extracted: - // foo/ - // foo/bar.txt - // foo/bar/ - // foo/bar/.hidden - // foo/bar/baz.txt - // foo/bar/quux.txt - ASSERT_TRUE(base::PathExists(test_dir_.AppendASCII("foo.txt"))); - base::FileEnumerator extractedFiles( - test_dir_, - false, // Do not enumerate recursively - the file must be in the root. - base::FileEnumerator::FileType::FILES); - int extracted_count = 0; - while (!extractedFiles.Next().empty()) - ++extracted_count; - ASSERT_EQ(1, extracted_count); - - base::FileEnumerator extractedDirs( - test_dir_, - false, // Do not enumerate recursively - we require zero directories. - base::FileEnumerator::FileType::DIRECTORIES); - extracted_count = 0; - while (!extractedDirs.Next().empty()) - ++extracted_count; - ASSERT_EQ(0, extracted_count); + ASSERT_TRUE(zip::Unzip(GetDataDirectory().AppendASCII("test.zip"), test_dir_, + {.filter = std::move(filter)})); + // Only foo.txt should have been extracted. + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES), + UnorderedElementsAre("foo.txt")); + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::DIRECTORIES), + UnorderedElementsAre()); +} + +TEST_F(ZipTest, UnzipEncryptedWithRightPassword) { + // TODO(crbug.com/1296838) Also check the AES-encrypted files. + auto filter = base::BindRepeating([](const base::FilePath& path) { + return !base::StartsWith(path.MaybeAsASCII(), "Encrypted AES"); + }); + + ASSERT_TRUE(zip::Unzip( + GetDataDirectory().AppendASCII("Different Encryptions.zip"), test_dir_, + {.filter = std::move(filter), .password = "password"})); + + std::string contents; + ASSERT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("ClearText.txt"), + &contents)); + EXPECT_EQ("This is not encrypted.\n", contents); + + ASSERT_TRUE(base::ReadFileToString( + test_dir_.AppendASCII("Encrypted ZipCrypto.txt"), &contents)); + EXPECT_EQ("This is encrypted with ZipCrypto.\n", contents); +} + +TEST_F(ZipTest, UnzipEncryptedWithWrongPassword) { + // TODO(crbug.com/1296838) Also check the AES-encrypted files. + auto filter = base::BindRepeating([](const base::FilePath& path) { + return !base::StartsWith(path.MaybeAsASCII(), "Encrypted AES"); + }); + + ASSERT_FALSE(zip::Unzip( + GetDataDirectory().AppendASCII("Different Encryptions.zip"), test_dir_, + {.filter = std::move(filter), .password = "wrong"})); + + std::string contents; + ASSERT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("ClearText.txt"), + &contents)); + EXPECT_EQ("This is not encrypted.\n", contents); + + // No rubbish file should be left behind. + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES), + UnorderedElementsAre("ClearText.txt")); +} + +TEST_F(ZipTest, UnzipEncryptedWithNoPassword) { + // TODO(crbug.com/1296838) Also check the AES-encrypted files. + auto filter = base::BindRepeating([](const base::FilePath& path) { + return !base::StartsWith(path.MaybeAsASCII(), "Encrypted AES"); + }); + + ASSERT_FALSE( + zip::Unzip(GetDataDirectory().AppendASCII("Different Encryptions.zip"), + test_dir_, {.filter = std::move(filter)})); + + std::string contents; + ASSERT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("ClearText.txt"), + &contents)); + EXPECT_EQ("This is not encrypted.\n", contents); + + // No rubbish file should be left behind. + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES), + UnorderedElementsAre("ClearText.txt")); +} + +TEST_F(ZipTest, UnzipEncryptedContinueOnError) { + EXPECT_TRUE( + zip::Unzip(GetDataDirectory().AppendASCII("Different Encryptions.zip"), + test_dir_, {.continue_on_error = true})); + + std::string contents; + EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("ClearText.txt"), + &contents)); + EXPECT_EQ("This is not encrypted.\n", contents); + + // No rubbish file should be left behind. + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES), + UnorderedElementsAre("ClearText.txt")); +} + +TEST_F(ZipTest, UnzipWrongCrc) { + ASSERT_FALSE( + zip::Unzip(GetDataDirectory().AppendASCII("Wrong CRC.zip"), test_dir_)); + + // No rubbish file should be left behind. + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES), + UnorderedElementsAre()); +} + +TEST_F(ZipTest, UnzipRepeatedDirName) { + EXPECT_TRUE(zip::Unzip( + GetDataDirectory().AppendASCII("Repeated Dir Name.zip"), test_dir_)); + + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES), + UnorderedElementsAre()); + + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::DIRECTORIES), + UnorderedElementsAre("repeated")); +} + +TEST_F(ZipTest, UnzipRepeatedFileName) { + EXPECT_FALSE(zip::Unzip( + GetDataDirectory().AppendASCII("Repeated File Name.zip"), test_dir_)); + + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES), + UnorderedElementsAre("repeated")); + + std::string contents; + EXPECT_TRUE( + base::ReadFileToString(test_dir_.AppendASCII("repeated"), &contents)); + EXPECT_EQ("First file", contents); +} + +TEST_F(ZipTest, UnzipCannotCreateEmptyDir) { + EXPECT_FALSE(zip::Unzip( + GetDataDirectory().AppendASCII("Empty Dir Same Name As File.zip"), + test_dir_)); + + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES), + UnorderedElementsAre("repeated")); + + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::DIRECTORIES), + UnorderedElementsAre()); + + std::string contents; + EXPECT_TRUE( + base::ReadFileToString(test_dir_.AppendASCII("repeated"), &contents)); + EXPECT_EQ("First file", contents); +} + +TEST_F(ZipTest, UnzipCannotCreateParentDir) { + EXPECT_FALSE(zip::Unzip( + GetDataDirectory().AppendASCII("Parent Dir Same Name As File.zip"), + test_dir_)); + + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES), + UnorderedElementsAre("repeated")); + + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::DIRECTORIES), + UnorderedElementsAre()); + + std::string contents; + EXPECT_TRUE( + base::ReadFileToString(test_dir_.AppendASCII("repeated"), &contents)); + EXPECT_EQ("First file", contents); +} + +// TODO(crbug.com/1311140) Detect and rename reserved file names on Windows. +TEST_F(ZipTest, UnzipWindowsSpecialNames) { + EXPECT_TRUE( + zip::Unzip(GetDataDirectory().AppendASCII("Windows Special Names.zip"), + test_dir_, {.continue_on_error = true})); + + std::unordered_set want_paths = { + "First", + "Last", + "CLOCK$", + " NUL.txt", +#ifndef OS_WIN + "NUL", + "NUL ", + "NUL.", + "NUL .", + "NUL.txt", + "NUL.tar.gz", + "NUL..txt", + "NUL...txt", + "NUL .txt", + "NUL .txt", + "NUL ..txt", +#ifndef OS_MAC + "Nul.txt", +#endif + "nul.very long extension", + "a/NUL", + "CON", + "PRN", + "AUX", + "COM1", + "COM2", + "COM3", + "COM4", + "COM5", + "COM6", + "COM7", + "COM8", + "COM9", + "LPT1", + "LPT2", + "LPT3", + "LPT4", + "LPT5", + "LPT6", + "LPT7", + "LPT8", + "LPT9", +#endif + }; + + const std::vector got_paths = + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES); + + for (const std::string& path : got_paths) { + const bool ok = want_paths.erase(path); + +#ifdef OS_WIN + if (!ok) { + // See crbug.com/1313991: Different versions of Windows treat these + // filenames differently. No hard error here if there is an unexpected + // file. + LOG(WARNING) << "Found unexpected file: " << std::quoted(path); + continue; + } +#else + EXPECT_TRUE(ok) << "Found unexpected file: " << std::quoted(path); +#endif + + std::string contents; + EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII(path), &contents)); + EXPECT_EQ(base::StrCat({"This is: ", path}), contents); + } + + for (const std::string& path : want_paths) { + EXPECT_TRUE(false) << "Cannot find expected file: " << std::quoted(path); + } +} + +TEST_F(ZipTest, UnzipDifferentCases) { +#if defined(OS_WIN) || defined(OS_MAC) + // Only the first file (with mixed case) is extracted. + EXPECT_FALSE(zip::Unzip(GetDataDirectory().AppendASCII( + "Repeated File Name With Different Cases.zip"), + test_dir_)); + + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES), + UnorderedElementsAre("Case")); + + std::string contents; + EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("Case"), &contents)); + EXPECT_EQ("Mixed case 111", contents); +#else + // All the files are extracted. + EXPECT_TRUE(zip::Unzip(GetDataDirectory().AppendASCII( + "Repeated File Name With Different Cases.zip"), + test_dir_)); + + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES), + UnorderedElementsAre("Case", "case", "CASE")); + + std::string contents; + EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("Case"), &contents)); + EXPECT_EQ("Mixed case 111", contents); + + EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("case"), &contents)); + EXPECT_EQ("Lower case 22", contents); + + EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("CASE"), &contents)); + EXPECT_EQ("Upper case 3", contents); +#endif +} + +TEST_F(ZipTest, UnzipDifferentCasesContinueOnError) { + EXPECT_TRUE(zip::Unzip(GetDataDirectory().AppendASCII( + "Repeated File Name With Different Cases.zip"), + test_dir_, {.continue_on_error = true})); + + std::string contents; + +#if defined(OS_WIN) || defined(OS_MAC) + // Only the first file (with mixed case) has been extracted. + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES), + UnorderedElementsAre("Case")); + + EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("Case"), &contents)); + EXPECT_EQ("Mixed case 111", contents); +#else + // All the files have been extracted. + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES), + UnorderedElementsAre("Case", "case", "CASE")); + + EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("Case"), &contents)); + EXPECT_EQ("Mixed case 111", contents); + + EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("case"), &contents)); + EXPECT_EQ("Lower case 22", contents); + + EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("CASE"), &contents)); + EXPECT_EQ("Upper case 3", contents); +#endif +} + +TEST_F(ZipTest, UnzipMixedPaths) { + EXPECT_TRUE(zip::Unzip(GetDataDirectory().AppendASCII("Mixed Paths.zip"), + test_dir_, {.continue_on_error = true})); + + std::unordered_set want_paths = { +#ifdef OS_WIN + "Dot", // + "Space→", // +#else + " ", // + "AUX", // Disappears on Windows + "COM1", // Disappears on Windows + "COM2", // Disappears on Windows + "COM3", // Disappears on Windows + "COM4", // Disappears on Windows + "COM5", // Disappears on Windows + "COM6", // Disappears on Windows + "COM7", // Disappears on Windows + "COM8", // Disappears on Windows + "COM9", // Disappears on Windows + "CON", // Disappears on Windows + "Dot .", // + "LPT1", // Disappears on Windows + "LPT2", // Disappears on Windows + "LPT3", // Disappears on Windows + "LPT4", // Disappears on Windows + "LPT5", // Disappears on Windows + "LPT6", // Disappears on Windows + "LPT7", // Disappears on Windows + "LPT8", // Disappears on Windows + "LPT9", // Disappears on Windows + "NUL ..txt", // Disappears on Windows + "NUL .txt", // Disappears on Windows + "NUL ", // Disappears on Windows + "NUL .", // Disappears on Windows + "NUL .txt", // Disappears on Windows + "NUL", // Disappears on Windows + "NUL.", // Disappears on Windows + "NUL...txt", // Disappears on Windows + "NUL..txt", // Disappears on Windows + "NUL.tar.gz", // Disappears on Windows + "NUL.txt", // Disappears on Windows + "PRN", // Disappears on Windows + "Space→ ", // + "c/NUL", // Disappears on Windows + "nul.very long extension", // Disappears on Windows +#ifndef OS_MAC + "CASE", // Conflicts with "Case" + "case", // Conflicts with "Case" +#endif +#endif + " NUL.txt", // + " ←Space", // + "$HOME", // + "%TMP", // + "-", // + "...Three", // + "..Two", // + ".One", // + "Ampersand &", // + "Angle ��", // + "At @", // + "Backslash1→�", // + "Backslash3→�←Backslash4", // + "Backspace �", // + "Backtick `", // + "Bell �", // + "CLOCK$", // + "Caret ^", // + "Carriage Return �", // + "Case", // + "Colon �", // + "Comma ,", // + "Curly {}", // + "C�", // + "C��", // + "C��Temp", // + "C��Temp�", // + "C��Temp�File", // + "Dash -", // + "Delete \x7F", // + "Dollar $", // + "Double quote �", // + "Equal =", // + "Escape �", // + "Euro €", // + "Exclamation !", // + "FileOrDir", // + "First", // + "Hash #", // + "Last", // + "Line Feed �", // + "Percent %", // + "Pipe �", // + "Plus +", // + "Question �", // + "Quote '", // + "ROOT/At The Top", // + "ROOT/UP/Over The Top", // + "ROOT/dev/null", // + "Round ()", // + "Semicolon ;", // + "Smile \U0001F642", // + "Square []", // + "Star �", // + "String Terminator \u009C", // + "Tab �", // + "Tilde ~", // + "UP/One Level Up", // + "UP/UP/Two Levels Up", // + "Underscore _", // + "a/DOT/b", // + "a/UP/b", // + "u/v/w/x/y/z", // + "~", // + "�←Backslash2", // + "��server�share�file", // + }; + + const std::vector got_paths = + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES); + + for (const std::string& path : got_paths) { + const bool ok = want_paths.erase(path); +#ifdef OS_WIN + // See crbug.com/1313991: Different versions of Windows treat reserved + // Windows filenames differently. No hard error here if there is an + // unexpected file. + LOG_IF(WARNING, !ok) << "Found unexpected file: " << std::quoted(path); +#else + EXPECT_TRUE(ok) << "Found unexpected file: " << std::quoted(path); +#endif + } + + for (const std::string& path : want_paths) { + EXPECT_TRUE(false) << "Cannot find expected file: " << std::quoted(path); + } + + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::DIRECTORIES), + UnorderedElementsAreArray({ + "Empty", + "ROOT", + "ROOT/Empty", + "ROOT/UP", + "ROOT/dev", + "UP", + "UP/UP", + "a", + "a/DOT", + "a/UP", + "c", + "u", + "u/v", + "u/v/w", + "u/v/w/x", + "u/v/w/x/y", + })); } TEST_F(ZipTest, UnzipWithDelegates) { - auto filter = - base::BindRepeating([](const base::FilePath& path) { return true; }); - auto dir_creator = base::BindRepeating( - [](const base::FilePath& extract_dir, const base::FilePath& entry_path) { - return base::CreateDirectory(extract_dir.Append(entry_path)); - }, - test_dir_); - auto writer = base::BindRepeating( - [](const base::FilePath& extract_dir, const base::FilePath& entry_path) - -> std::unique_ptr { + auto dir_creator = + base::BindLambdaForTesting([this](const base::FilePath& entry_path) { + return base::CreateDirectory(test_dir_.Append(entry_path)); + }); + auto writer = + base::BindLambdaForTesting([this](const base::FilePath& entry_path) + -> std::unique_ptr { return std::make_unique( - extract_dir.Append(entry_path)); - }, - test_dir_); - base::FilePath path; - ASSERT_TRUE(GetTestDataDirectory(&path)); - base::File file(path.AppendASCII("test.zip"), + test_dir_.Append(entry_path)); + }); + + base::File file(GetDataDirectory().AppendASCII("test.zip"), base::File::Flags::FLAG_OPEN | base::File::Flags::FLAG_READ); - ASSERT_TRUE(zip::UnzipWithFilterAndWriters(file.GetPlatformFile(), writer, - dir_creator, filter, false)); + EXPECT_TRUE(zip::Unzip(file.GetPlatformFile(), writer, dir_creator)); base::FilePath dir = test_dir_; base::FilePath dir_foo = dir.AppendASCII("foo"); base::FilePath dir_foo_bar = dir_foo.AppendASCII("bar"); - ASSERT_TRUE(base::PathExists(dir.AppendASCII("foo.txt"))); - ASSERT_TRUE(base::PathExists(dir_foo)); - ASSERT_TRUE(base::PathExists(dir_foo.AppendASCII("bar.txt"))); - ASSERT_TRUE(base::PathExists(dir_foo_bar)); - ASSERT_TRUE(base::PathExists(dir_foo_bar.AppendASCII(".hidden"))); - ASSERT_TRUE(base::PathExists(dir_foo_bar.AppendASCII("baz.txt"))); - ASSERT_TRUE(base::PathExists(dir_foo_bar.AppendASCII("quux.txt"))); + EXPECT_TRUE(base::PathExists(dir.AppendASCII("foo.txt"))); + EXPECT_TRUE(base::DirectoryExists(dir_foo)); + EXPECT_TRUE(base::PathExists(dir_foo.AppendASCII("bar.txt"))); + EXPECT_TRUE(base::DirectoryExists(dir_foo_bar)); + EXPECT_TRUE(base::PathExists(dir_foo_bar.AppendASCII(".hidden"))); + EXPECT_TRUE(base::PathExists(dir_foo_bar.AppendASCII("baz.txt"))); + EXPECT_TRUE(base::PathExists(dir_foo_bar.AppendASCII("quux.txt"))); +} + +TEST_F(ZipTest, UnzipOnlyDirectories) { + auto dir_creator = + base::BindLambdaForTesting([this](const base::FilePath& entry_path) { + return base::CreateDirectory(test_dir_.Append(entry_path)); + }); + + // Always return a null WriterDelegate. + auto writer = + base::BindLambdaForTesting([](const base::FilePath& entry_path) { + return std::unique_ptr(); + }); + + base::File file(GetDataDirectory().AppendASCII("test.zip"), + base::File::Flags::FLAG_OPEN | base::File::Flags::FLAG_READ); + EXPECT_TRUE(zip::Unzip(file.GetPlatformFile(), writer, dir_creator, + {.continue_on_error = true})); + base::FilePath dir = test_dir_; + base::FilePath dir_foo = dir.AppendASCII("foo"); + base::FilePath dir_foo_bar = dir_foo.AppendASCII("bar"); + EXPECT_FALSE(base::PathExists(dir.AppendASCII("foo.txt"))); + EXPECT_TRUE(base::DirectoryExists(dir_foo)); + EXPECT_FALSE(base::PathExists(dir_foo.AppendASCII("bar.txt"))); + EXPECT_TRUE(base::DirectoryExists(dir_foo_bar)); + EXPECT_FALSE(base::PathExists(dir_foo_bar.AppendASCII(".hidden"))); + EXPECT_FALSE(base::PathExists(dir_foo_bar.AppendASCII("baz.txt"))); + EXPECT_FALSE(base::PathExists(dir_foo_bar.AppendASCII("quux.txt"))); +} + +// Tests that a ZIP archive containing SJIS-encoded file names can be correctly +// extracted if the encoding is specified. +TEST_F(ZipTest, UnzipSjis) { + ASSERT_TRUE(zip::Unzip(GetDataDirectory().AppendASCII("SJIS Bug 846195.zip"), + test_dir_, {.encoding = "Shift_JIS"})); + + const base::FilePath dir = + test_dir_.Append(base::FilePath::FromUTF8Unsafe("新しいフォルダ")); + EXPECT_TRUE(base::DirectoryExists(dir)); + + std::string contents; + ASSERT_TRUE(base::ReadFileToString( + dir.Append(base::FilePath::FromUTF8Unsafe("SJIS_835C_ソ.txt")), + &contents)); + EXPECT_EQ( + "This file's name contains 0x5c (backslash) as the 2nd byte of Japanese " + "characater \"\x83\x5c\" when encoded in Shift JIS.", + contents); + + ASSERT_TRUE(base::ReadFileToString(dir.Append(base::FilePath::FromUTF8Unsafe( + "新しいテキスト ドキュメント.txt")), + &contents)); + EXPECT_EQ("This file name is coded in Shift JIS in the archive.", contents); +} + +// Tests that a ZIP archive containing SJIS-encoded file names can be extracted +// even if the encoding is not specified. In this case, file names are +// interpreted as UTF-8, which leads to garbled names where invalid UTF-8 +// sequences are replaced with the character �. Nevertheless, the files are +// safely extracted and readable. +TEST_F(ZipTest, UnzipSjisAsUtf8) { + ASSERT_TRUE(zip::Unzip(GetDataDirectory().AppendASCII("SJIS Bug 846195.zip"), + test_dir_)); + + EXPECT_FALSE(base::DirectoryExists( + test_dir_.Append(base::FilePath::FromUTF8Unsafe("新しいフォルダ")))); + + const base::FilePath dir = + test_dir_.Append(base::FilePath::FromUTF8Unsafe("�V�����t�H���_")); + EXPECT_TRUE(base::DirectoryExists(dir)); + + std::string contents; + ASSERT_TRUE(base::ReadFileToString( + dir.Append(base::FilePath::FromUTF8Unsafe("SJIS_835C_��.txt")), + &contents)); + EXPECT_EQ( + "This file's name contains 0x5c (backslash) as the 2nd byte of Japanese " + "characater \"\x83\x5c\" when encoded in Shift JIS.", + contents); + + ASSERT_TRUE(base::ReadFileToString(dir.Append(base::FilePath::FromUTF8Unsafe( + "�V�����e�L�X�g �h�L�������g.txt")), + &contents)); + EXPECT_EQ("This file name is coded in Shift JIS in the archive.", contents); } TEST_F(ZipTest, Zip) { - base::FilePath src_dir; - ASSERT_TRUE(GetTestDataDirectory(&src_dir)); - src_dir = src_dir.AppendASCII("test"); + base::FilePath src_dir = GetDataDirectory().AppendASCII("test"); base::ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); @@ -441,9 +1019,7 @@ TEST_F(ZipTest, Zip) { } TEST_F(ZipTest, ZipIgnoreHidden) { - base::FilePath src_dir; - ASSERT_TRUE(GetTestDataDirectory(&src_dir)); - src_dir = src_dir.AppendASCII("test"); + base::FilePath src_dir = GetDataDirectory().AppendASCII("test"); base::ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); @@ -454,9 +1030,7 @@ TEST_F(ZipTest, ZipIgnoreHidden) { } TEST_F(ZipTest, ZipNonASCIIDir) { - base::FilePath src_dir; - ASSERT_TRUE(GetTestDataDirectory(&src_dir)); - src_dir = src_dir.AppendASCII("test"); + base::FilePath src_dir = GetDataDirectory().AppendASCII("test"); base::ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); @@ -492,11 +1066,9 @@ TEST_F(ZipTest, ZipTimeStamp) { TestTimeStamp("02 Jan 2038 23:59:58", VALID_YEAR); } -#if defined(OS_POSIX) +#if defined(OS_POSIX) || defined(OS_FUCHSIA) TEST_F(ZipTest, ZipFiles) { - base::FilePath src_dir; - ASSERT_TRUE(GetTestDataDirectory(&src_dir)); - src_dir = src_dir.AppendASCII("test"); + base::FilePath src_dir = GetDataDirectory().AppendASCII("test"); base::ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); @@ -513,25 +1085,20 @@ TEST_F(ZipTest, ZipFiles) { EXPECT_TRUE(reader.Open(zip_name)); EXPECT_EQ(zip_file_list_.size(), static_cast(reader.num_entries())); for (size_t i = 0; i < zip_file_list_.size(); ++i) { - EXPECT_TRUE(reader.HasMore()); - EXPECT_TRUE(reader.OpenCurrentEntryInZip()); - const zip::ZipReader::EntryInfo* entry_info = reader.current_entry_info(); - EXPECT_EQ(entry_info->file_path(), zip_file_list_[i]); - reader.AdvanceToNextEntry(); + const zip::ZipReader::Entry* const entry = reader.Next(); + ASSERT_TRUE(entry); + EXPECT_EQ(entry->path, zip_file_list_[i]); } } -#endif // defined(OS_POSIX) +#endif // defined(OS_POSIX) || defined(OS_FUCHSIA) TEST_F(ZipTest, UnzipFilesWithIncorrectSize) { - base::FilePath test_data_folder; - ASSERT_TRUE(GetTestDataDirectory(&test_data_folder)); - // test_mismatch_size.zip contains files with names from 0.txt to 7.txt with // sizes from 0 to 7 bytes respectively, but the metadata in the zip file says // the uncompressed size is 3 bytes. The ZipReader and minizip code needs to // be clever enough to get all the data out. base::FilePath test_zip_file = - test_data_folder.AppendASCII("test_mismatch_size.zip"); + GetDataDirectory().AppendASCII("test_mismatch_size.zip"); base::ScopedTempDir scoped_temp_dir; ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir()); @@ -578,9 +1145,7 @@ TEST_F(ZipTest, ZipWithFileAccessor) { // Tests progress reporting while zipping files. TEST_F(ZipTest, ZipProgress) { - base::FilePath src_dir; - ASSERT_TRUE(GetTestDataDirectory(&src_dir)); - src_dir = src_dir.AppendASCII("test"); + base::FilePath src_dir = GetDataDirectory().AppendASCII("test"); base::ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); @@ -617,9 +1182,7 @@ TEST_F(ZipTest, ZipProgress) { // Tests throttling of progress reporting while zipping files. TEST_F(ZipTest, ZipProgressPeriod) { - base::FilePath src_dir; - ASSERT_TRUE(GetTestDataDirectory(&src_dir)); - src_dir = src_dir.AppendASCII("test"); + base::FilePath src_dir = GetDataDirectory().AppendASCII("test"); base::ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); @@ -658,9 +1221,7 @@ TEST_F(ZipTest, ZipProgressPeriod) { // Tests cancellation while zipping files. TEST_F(ZipTest, ZipCancel) { - base::FilePath src_dir; - ASSERT_TRUE(GetTestDataDirectory(&src_dir)); - src_dir = src_dir.AppendASCII("test"); + base::FilePath src_dir = GetDataDirectory().AppendASCII("test"); base::ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); @@ -783,8 +1344,28 @@ TEST_F(ZipTest, NestedZip) { // Tests that there is no 2GB or 4GB limits. Tests that big files can be zipped // (crbug.com/1207737) and that big ZIP files can be created -// (crbug.com/1221447). +// (crbug.com/1221447). Tests that the big ZIP can be opened, that its entries +// are correctly enumerated (crbug.com/1298347), and that the big file can be +// extracted. +// +// Because this test is dealing with big files, it tends to take a lot of disk +// space and time (crbug.com/1299736). Therefore, it only gets run on a few bots +// (ChromeOS and Windows). +// +// This test is too slow with TSAN. +// OS Fuchsia does not seem to support large files. +// Some 32-bit Android waterfall and CQ try bots are running out of space when +// performing this test (android-asan, android-11-x86-rel, +// android-marshmallow-x86-rel-non-cq). +// Some Mac, Linux and Debug (dbg) bots tend to time out when performing this +// test (crbug.com/1299736, crbug.com/1300448). +#if defined(THREAD_SANITIZER) || BUILDFLAG(IS_FUCHSIA) || \ + BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \ + BUILDFLAG(IS_CHROMEOS_LACROS) || !defined(NDEBUG) TEST_F(ZipTest, DISABLED_BigFile) { +#else +TEST_F(ZipTest, BigFile) { +#endif base::ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); @@ -795,15 +1376,26 @@ TEST_F(ZipTest, DISABLED_BigFile) { // purpose of this test, it doesn't really matter. const int64_t src_size = 5'000'000'000; + const base::FilePath src_file = src_dir.AppendASCII("src.zip"); + LOG(INFO) << "Creating big file " << src_file; { - base::File f(src_dir.AppendASCII("src.zip"), - base::File::FLAG_CREATE | base::File::FLAG_WRITE); + base::File f(src_file, base::File::FLAG_CREATE | base::File::FLAG_WRITE); ASSERT_TRUE(f.SetLength(src_size)); } // Zip the dummy ZIP file. const base::FilePath dest_file = temp_dir.GetPath().AppendASCII("dest.zip"); - EXPECT_TRUE(zip::Zip({.src_dir = src_dir, .dest_file = dest_file})); + LOG(INFO) << "Zipping big file into " << dest_file; + zip::ProgressCallback progress_callback = + base::BindLambdaForTesting([&](const zip::Progress& progress) { + LOG(INFO) << "Zipping... " << std::setw(3) + << (100 * progress.bytes / src_size) << "%"; + return true; + }); + EXPECT_TRUE(zip::Zip({.src_dir = src_dir, + .dest_file = dest_file, + .progress_callback = std::move(progress_callback), + .progress_period = base::Seconds(1)})); // Since the dummy source (inner) ZIP file should simply be stored in the // destination (outer) ZIP file, the destination file should be bigger than @@ -812,6 +1404,25 @@ TEST_F(ZipTest, DISABLED_BigFile) { ASSERT_TRUE(base::GetFileSize(dest_file, &dest_file_size)); EXPECT_GT(dest_file_size, src_size + 100); EXPECT_LT(dest_file_size, src_size + 300); + + LOG(INFO) << "Reading big ZIP " << dest_file; + zip::ZipReader reader; + ASSERT_TRUE(reader.Open(dest_file)); + + const zip::ZipReader::Entry* const entry = reader.Next(); + ASSERT_TRUE(entry); + EXPECT_EQ(FP("src.zip"), entry->path); + EXPECT_EQ(src_size, entry->original_size); + EXPECT_FALSE(entry->is_directory); + EXPECT_FALSE(entry->is_encrypted); + + ProgressWriterDelegate writer(src_size); + EXPECT_TRUE(reader.ExtractCurrentEntry(&writer, + std::numeric_limits::max())); + EXPECT_EQ(src_size, writer.received_bytes()); + + EXPECT_FALSE(reader.Next()); + EXPECT_TRUE(reader.ok()); } } // namespace diff --git a/deps/v8/third_party/zlib/google/zip_writer.cc b/deps/v8/third_party/zlib/google/zip_writer.cc index 201f1997b5b4fe..e3f677fe328082 100644 --- a/deps/v8/third_party/zlib/google/zip_writer.cc +++ b/deps/v8/third_party/zlib/google/zip_writer.cc @@ -10,23 +10,12 @@ #include "base/logging.h" #include "base/strings/strcat.h" #include "base/strings/string_util.h" +#include "third_party/zlib/google/redact.h" #include "third_party/zlib/google/zip_internal.h" namespace zip { namespace internal { -class Redact { - public: - explicit Redact(const base::FilePath& path) : path_(path) {} - - friend std::ostream& operator<<(std::ostream& out, const Redact&& r) { - return LOG_IS_ON(INFO) ? out << "'" << r.path_ << "'" : out << "(redacted)"; - } - - private: - const base::FilePath& path_; -}; - bool ZipWriter::ShouldContinue() { if (!progress_callback_) return true; @@ -134,7 +123,7 @@ bool ZipWriter::AddDirectoryEntry(const base::FilePath& path) { return AddDirectoryContents(path); } -#if defined(OS_POSIX) +#if defined(OS_POSIX) || defined(OS_FUCHSIA) // static std::unique_ptr ZipWriter::CreateWithFd( int zip_file_fd, diff --git a/deps/v8/third_party/zlib/google/zip_writer.h b/deps/v8/third_party/zlib/google/zip_writer.h index fcc96275004af6..aa3c965d911599 100644 --- a/deps/v8/third_party/zlib/google/zip_writer.h +++ b/deps/v8/third_party/zlib/google/zip_writer.h @@ -36,7 +36,7 @@ class ZipWriter { // Creates a writer that will write a ZIP file to |zip_file_fd| or |zip_file| // and which entries are relative to |file_accessor|'s source directory. // All file reads are performed using |file_accessor|. -#if defined(OS_POSIX) +#if defined(OS_POSIX) || defined(OS_FUCHSIA) static std::unique_ptr CreateWithFd(int zip_file_fd, FileAccessor* file_accessor); #endif diff --git a/deps/v8/third_party/zlib/gzguts.h b/deps/v8/third_party/zlib/gzguts.h index 990a4d25149337..6378d468a258b1 100644 --- a/deps/v8/third_party/zlib/gzguts.h +++ b/deps/v8/third_party/zlib/gzguts.h @@ -39,7 +39,7 @@ # include #endif -#if defined(_WIN32) || defined(__CYGWIN__) +#if defined(_WIN32) # define WIDECHAR #endif diff --git a/deps/v8/third_party/zlib/gzlib.c b/deps/v8/third_party/zlib/gzlib.c index 4105e6aff92594..4838bf04745beb 100644 --- a/deps/v8/third_party/zlib/gzlib.c +++ b/deps/v8/third_party/zlib/gzlib.c @@ -5,7 +5,7 @@ #include "gzguts.h" -#if defined(_WIN32) && !defined(__BORLANDC__) && !defined(__MINGW32__) +#if defined(_WIN32) && !defined(__BORLANDC__) # define LSEEK _lseeki64 #else #if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0 @@ -397,7 +397,7 @@ z_off64_t ZEXPORT gzseek64(file, offset, whence) /* if within raw area while reading, just go there */ if (state->mode == GZ_READ && state->how == COPY && state->x.pos + offset >= 0) { - ret = LSEEK(state->fd, offset - state->x.have, SEEK_CUR); + ret = LSEEK(state->fd, offset - (z_off64_t)state->x.have, SEEK_CUR); if (ret == -1) return -1; state->x.have = 0; diff --git a/deps/v8/third_party/zlib/gzread.c b/deps/v8/third_party/zlib/gzread.c index 832d3ef98c5948..85776cd25769dc 100644 --- a/deps/v8/third_party/zlib/gzread.c +++ b/deps/v8/third_party/zlib/gzread.c @@ -314,9 +314,9 @@ local z_size_t gz_read(state, buf, len) got = 0; do { /* set n to the maximum amount of len that fits in an unsigned int */ - n = -1; + n = (unsigned)-1; if (n > len) - n = len; + n = (unsigned)len; /* first just try copying data from the output buffer */ if (state->x.have) { @@ -397,7 +397,7 @@ int ZEXPORT gzread(file, buf, len) } /* read len or fewer bytes to buf */ - len = gz_read(state, buf, len); + len = (unsigned)gz_read(state, buf, len); /* check for an error */ if (len == 0 && state->err != Z_OK && state->err != Z_BUF_ERROR) @@ -451,7 +451,6 @@ z_size_t ZEXPORT gzfread(buf, size, nitems, file) int ZEXPORT gzgetc(file) gzFile file; { - int ret; unsigned char buf[1]; gz_statep state; @@ -473,8 +472,7 @@ int ZEXPORT gzgetc(file) } /* nothing there -- try gz_read() */ - ret = gz_read(state, buf, 1); - return ret < 1 ? -1 : buf[0]; + return gz_read(state, buf, 1) < 1 ? -1 : buf[0]; } int ZEXPORT gzgetc_(file) diff --git a/deps/v8/third_party/zlib/gzwrite.c b/deps/v8/third_party/zlib/gzwrite.c index c7b5651d70b994..52381332edd157 100644 --- a/deps/v8/third_party/zlib/gzwrite.c +++ b/deps/v8/third_party/zlib/gzwrite.c @@ -209,7 +209,7 @@ local z_size_t gz_write(state, buf, len) state->in); copy = state->size - have; if (copy > len) - copy = len; + copy = (unsigned)len; memcpy(state->in + have, buf, copy); state->strm.avail_in += copy; state->x.pos += copy; @@ -229,7 +229,7 @@ local z_size_t gz_write(state, buf, len) do { unsigned n = (unsigned)-1; if (n > len) - n = len; + n = (unsigned)len; state->strm.avail_in = n; state->x.pos += n; if (gz_comp(state, Z_NO_FLUSH) == -1) @@ -349,12 +349,11 @@ int ZEXPORT gzputc(file, c) } /* -- see zlib.h -- */ -int ZEXPORT gzputs(file, str) +int ZEXPORT gzputs(file, s) gzFile file; - const char *str; + const char *s; { - int ret; - z_size_t len; + z_size_t len, put; gz_statep state; /* get internal structure */ @@ -367,9 +366,13 @@ int ZEXPORT gzputs(file, str) return -1; /* write string */ - len = strlen(str); - ret = gz_write(state, str, len); - return ret == 0 && len != 0 ? -1 : ret; + len = strlen(s); + if ((int)len < 0 || (unsigned)len != len) { + gz_error(state, Z_STREAM_ERROR, "string length does not fit in int"); + return -1; + } + put = gz_write(state, s, len); + return put < len ? -1 : (int)len; } #if defined(STDC) || defined(Z_HAVE_STDARG_H) @@ -441,7 +444,7 @@ int ZEXPORTVA gzvprintf(gzFile file, const char *format, va_list va) strm->avail_in = state->size; if (gz_comp(state, Z_NO_FLUSH) == -1) return state->err; - memcpy(state->in, state->in + state->size, left); + memmove(state->in, state->in + state->size, left); strm->next_in = state->in; strm->avail_in = left; } @@ -540,7 +543,7 @@ int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, strm->avail_in = state->size; if (gz_comp(state, Z_NO_FLUSH) == -1) return state->err; - memcpy(state->in, state->in + state->size, left); + memmove(state->in, state->in + state->size, left); strm->next_in = state->in; strm->avail_in = left; } diff --git a/deps/v8/third_party/zlib/inffast.c b/deps/v8/third_party/zlib/inffast.c index 2797e8a03c5151..d89ad5ccdee0e2 100644 --- a/deps/v8/third_party/zlib/inffast.c +++ b/deps/v8/third_party/zlib/inffast.c @@ -74,7 +74,7 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ code const FAR *dcode; /* local strm->distcode */ unsigned lmask; /* mask for first level of length codes */ unsigned dmask; /* mask for first level of distance codes */ - code here; /* retrieved table entry */ + code const *here; /* retrieved table entry */ unsigned op; /* code bits, operation, extra bits, or */ /* window position, window bytes to copy */ unsigned len; /* match length, unused bytes */ @@ -111,20 +111,20 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ hold += (unsigned long)(*in++) << bits; bits += 8; } - here = lcode[hold & lmask]; + here = lcode + (hold & lmask); dolen: - op = (unsigned)(here.bits); + op = (unsigned)(here->bits); hold >>= op; bits -= op; - op = (unsigned)(here.op); + op = (unsigned)(here->op); if (op == 0) { /* literal */ - Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + Tracevv((stderr, here->val >= 0x20 && here->val < 0x7f ? "inflate: literal '%c'\n" : - "inflate: literal 0x%02x\n", here.val)); - *out++ = (unsigned char)(here.val); + "inflate: literal 0x%02x\n", here->val)); + *out++ = (unsigned char)(here->val); } else if (op & 16) { /* length base */ - len = (unsigned)(here.val); + len = (unsigned)(here->val); op &= 15; /* number of extra bits */ if (op) { if (bits < op) { @@ -142,14 +142,14 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ hold += (unsigned long)(*in++) << bits; bits += 8; } - here = dcode[hold & dmask]; + here = dcode + (hold & dmask); dodist: - op = (unsigned)(here.bits); + op = (unsigned)(here->bits); hold >>= op; bits -= op; - op = (unsigned)(here.op); + op = (unsigned)(here->op); if (op & 16) { /* distance base */ - dist = (unsigned)(here.val); + dist = (unsigned)(here->val); op &= 15; /* number of extra bits */ if (bits < op) { hold += (unsigned long)(*in++) << bits; @@ -268,7 +268,7 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ } } else if ((op & 64) == 0) { /* 2nd level distance code */ - here = dcode[here.val + (hold & ((1U << op) - 1))]; + here = dcode + here->val + (hold & ((1U << op) - 1)); goto dodist; } else { @@ -278,7 +278,7 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ } } else if ((op & 64) == 0) { /* 2nd level length code */ - here = lcode[here.val + (hold & ((1U << op) - 1))]; + here = lcode + here->val + (hold & ((1U << op) - 1)); goto dolen; } else if (op & 32) { /* end-of-block */ diff --git a/deps/v8/third_party/zlib/inflate.c b/deps/v8/third_party/zlib/inflate.c index 68902e81bd4be9..7543c33def95c8 100644 --- a/deps/v8/third_party/zlib/inflate.c +++ b/deps/v8/third_party/zlib/inflate.c @@ -130,6 +130,7 @@ z_streamp strm; state->mode = HEAD; state->last = 0; state->havedict = 0; + state->flags = -1; state->dmax = 32768U; state->head = Z_NULL; state->hold = 0; @@ -671,7 +672,6 @@ int flush; state->mode = FLAGS; break; } - state->flags = 0; /* expect zlib header */ if (state->head != Z_NULL) state->head->done = -1; if (!(state->wrap & 1) || /* check if zlib header allowed */ @@ -698,6 +698,7 @@ int flush; break; } state->dmax = 1U << len; + state->flags = 0; /* indicate zlib header */ Tracev((stderr, "inflate: zlib header ok\n")); strm->adler = state->check = adler32(0L, Z_NULL, 0); state->mode = hold & 0x200 ? DICTID : TYPE; @@ -1223,7 +1224,7 @@ int flush; case LENGTH: if (state->wrap && state->flags) { NEEDBITS(32); - if (hold != (state->total & 0xffffffffUL)) { + if ((state->wrap & 4) && hold != (state->total & 0xffffffff)) { strm->msg = (char *)"incorrect length check"; state->mode = BAD; break; @@ -1403,6 +1404,7 @@ int ZEXPORT inflateSync(strm) z_streamp strm; { unsigned len; /* number of bytes to look at or looked at */ + int flags; /* temporary to save header status */ unsigned long in, out; /* temporary to save total_in and total_out */ unsigned char buf[4]; /* to restore bit buffer to byte string */ struct inflate_state FAR *state; @@ -1435,9 +1437,15 @@ z_streamp strm; /* return no joy or set up to restart inflate() on a new block */ if (state->have != 4) return Z_DATA_ERROR; + if (state->flags == -1) + state->wrap = 0; /* if no header yet, treat as raw */ + else + state->wrap &= ~4; /* no point in computing a check value now */ + flags = state->flags; in = strm->total_in; out = strm->total_out; inflateReset(strm); strm->total_in = in; strm->total_out = out; + state->flags = flags; state->mode = TYPE; return Z_OK; } @@ -1533,7 +1541,7 @@ int check; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; - if (check) + if (check && state->wrap) state->wrap |= 4; else state->wrap &= ~4; diff --git a/deps/v8/third_party/zlib/inflate.h b/deps/v8/third_party/zlib/inflate.h index a46cce6b6d05ef..98679fa9bc245e 100644 --- a/deps/v8/third_party/zlib/inflate.h +++ b/deps/v8/third_party/zlib/inflate.h @@ -86,7 +86,8 @@ struct inflate_state { int wrap; /* bit 0 true for zlib, bit 1 true for gzip, bit 2 true to validate check value */ int havedict; /* true if dictionary provided */ - int flags; /* gzip header method and flags (0 if zlib) */ + int flags; /* gzip header method and flags, 0 if zlib, or + -1 if raw or no header yet */ unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ unsigned long check; /* protected copy of check value */ unsigned long total; /* protected copy of output count */ diff --git a/deps/v8/third_party/zlib/patches/0009-infcover-oob.patch b/deps/v8/third_party/zlib/patches/0009-infcover-oob.patch new file mode 100644 index 00000000000000..648360f332d49c --- /dev/null +++ b/deps/v8/third_party/zlib/patches/0009-infcover-oob.patch @@ -0,0 +1,24 @@ +From 75690b2683667be5535ac6243438115dc9c40f6a Mon Sep 17 00:00:00 2001 +From: Florian Mayer +Date: Wed, 16 Mar 2022 16:38:36 -0700 +Subject: [PATCH] Fix out of bounds in infcover.c. + +--- + test/infcover.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/test/infcover.c b/test/infcover.c +index 2be01646c..a6d83693c 100644 +--- a/test/infcover.c ++++ b/test/infcover.c +@@ -373,7 +373,9 @@ local void cover_support(void) + mem_setup(&strm); + strm.avail_in = 0; + strm.next_in = Z_NULL; +- ret = inflateInit_(&strm, ZLIB_VERSION - 1, (int)sizeof(z_stream)); ++ char versioncpy[] = ZLIB_VERSION; ++ versioncpy[0] -= 1; ++ ret = inflateInit_(&strm, versioncpy, (int)sizeof(z_stream)); + assert(ret == Z_VERSION_ERROR); + mem_done(&strm, "wrong version"); + diff --git a/deps/v8/third_party/zlib/trees.c b/deps/v8/third_party/zlib/trees.c index 5f89d056ef9692..decaeb7c3c7f0c 100644 --- a/deps/v8/third_party/zlib/trees.c +++ b/deps/v8/third_party/zlib/trees.c @@ -149,7 +149,7 @@ local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, local void compress_block OF((deflate_state *s, const ct_data *ltree, const ct_data *dtree)); local int detect_data_type OF((deflate_state *s)); -local unsigned bi_reverse OF((unsigned value, int length)); +local unsigned bi_reverse OF((unsigned code, int len)); local void bi_windup OF((deflate_state *s)); local void bi_flush OF((deflate_state *s)); @@ -870,7 +870,8 @@ void ZLIB_INTERNAL _tr_stored_block(s, buf, stored_len, last) bi_windup(s); /* align on byte boundary */ put_short(s, (ush)stored_len); put_short(s, (ush)~stored_len); - zmemcpy(s->pending_buf + s->pending, (Bytef *)buf, stored_len); + if (stored_len) + zmemcpy(s->pending_buf + s->pending, (Bytef *)buf, stored_len); s->pending += stored_len; #ifdef ZLIB_DEBUG s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; diff --git a/deps/v8/third_party/zlib/zlib.h b/deps/v8/third_party/zlib/zlib.h index 99fd467f6b1a54..589f865eeca7f7 100644 --- a/deps/v8/third_party/zlib/zlib.h +++ b/deps/v8/third_party/zlib/zlib.h @@ -543,8 +543,7 @@ ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, int strategy)); This is another version of deflateInit with more compression options. The - fields next_in, zalloc, zfree and opaque must be initialized before by the - caller. + fields zalloc, zfree and opaque must be initialized before by the caller. The method parameter is the compression method. It must be Z_DEFLATED in this version of the library. @@ -712,11 +711,12 @@ ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, used to switch between compression and straight copy of the input data, or to switch to a different kind of input data requiring a different strategy. If the compression approach (which is a function of the level) or the - strategy is changed, and if any input has been consumed in a previous - deflate() call, then the input available so far is compressed with the old - level and strategy using deflate(strm, Z_BLOCK). There are three approaches - for the compression levels 0, 1..3, and 4..9 respectively. The new level - and strategy will take effect at the next call of deflate(). + strategy is changed, and if there have been any deflate() calls since the + state was initialized or reset, then the input available so far is + compressed with the old level and strategy using deflate(strm, Z_BLOCK). + There are three approaches for the compression levels 0, 1..3, and 4..9 + respectively. The new level and strategy will take effect at the next call + of deflate(). If a deflate(strm, Z_BLOCK) is performed by deflateParams(), and it does not have enough output space to complete, then the parameter change will not @@ -865,9 +865,11 @@ ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, detection, or add 16 to decode only the gzip format (the zlib format will return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a CRC-32 instead of an Adler-32. Unlike the gunzip utility and gzread() (see - below), inflate() will not automatically decode concatenated gzip streams. - inflate() will return Z_STREAM_END at the end of the gzip stream. The state - would need to be reset to continue decoding a subsequent gzip stream. + below), inflate() will *not* automatically decode concatenated gzip members. + inflate() will return Z_STREAM_END at the end of the gzip member. The state + would need to be reset to continue decoding a subsequent gzip member. This + *must* be done if there is more data after a gzip member, in order for the + decompression to be compliant with the gzip standard (RFC 1952). inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_VERSION_ERROR if the zlib library version is incompatible with the @@ -1739,7 +1741,7 @@ ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); if (crc != original_crc) error(); */ -ZEXTERN uLong ZEXPORT crc32_z OF((uLong adler, const Bytef *buf, +ZEXTERN uLong ZEXPORT crc32_z OF((uLong crc, const Bytef *buf, z_size_t len)); /* Same as crc32(), but with a size_t length. @@ -1916,7 +1918,7 @@ ZEXTERN int ZEXPORT inflateValidate OF((z_streamp, int)); ZEXTERN unsigned long ZEXPORT inflateCodesUsed OF ((z_streamp)); ZEXTERN int ZEXPORT inflateResetKeep OF((z_streamp)); ZEXTERN int ZEXPORT deflateResetKeep OF((z_streamp)); -#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(Z_SOLO) +#if defined(_WIN32) && !defined(Z_SOLO) ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path, const char *mode)); #endif diff --git a/deps/v8/third_party/zlib/zutil.c b/deps/v8/third_party/zlib/zutil.c index a76c6b0c7e557f..dcab28a0d5177a 100644 --- a/deps/v8/third_party/zlib/zutil.c +++ b/deps/v8/third_party/zlib/zutil.c @@ -136,8 +136,8 @@ const char * ZEXPORT zError(err) return ERR_MSG(err); } -#if defined(_WIN32_WCE) - /* The Microsoft C Run-Time Library for Windows CE doesn't have +#if defined(_WIN32_WCE) && _WIN32_WCE < 0x800 + /* The older Microsoft C Run-Time Library for Windows CE doesn't have * errno. We define it as a global variable to simplify porting. * Its value is always 0 and should not be used. */ diff --git a/deps/v8/third_party/zlib/zutil.h b/deps/v8/third_party/zlib/zutil.h index 4425bcf75eb38c..ec1993f3f7044a 100644 --- a/deps/v8/third_party/zlib/zutil.h +++ b/deps/v8/third_party/zlib/zutil.h @@ -44,10 +44,6 @@ # endif #endif -#ifdef Z_SOLO - typedef long ptrdiff_t; /* guess -- will be caught if guess is wrong */ -#endif - #ifndef local # define local static #endif @@ -185,10 +181,6 @@ extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ #if (defined(_MSC_VER) && (_MSC_VER > 600)) && !defined __INTERIX # if defined(_WIN32_WCE) # define fdopen(fd,mode) NULL /* No fdopen() */ -# ifndef _PTRDIFF_T_DEFINED - typedef int ptrdiff_t; -# define _PTRDIFF_T_DEFINED -# endif # else # define fdopen(fd,type) _fdopen(fd,type) # endif diff --git a/deps/v8/tools/BUILD.gn b/deps/v8/tools/BUILD.gn index 2f8197dd3697a9..e168a05d4e4253 100644 --- a/deps/v8/tools/BUILD.gn +++ b/deps/v8/tools/BUILD.gn @@ -11,7 +11,7 @@ group("gn_all") { data_deps = [ ":v8_check_static_initializers", "debug_helper:v8_debug_helper", - "gcmole:v8_run_gcmole", + "gcmole:v8_gcmole_files", "jsfunfuzz:v8_jsfunfuzz", ] diff --git a/deps/v8/tools/PRESUBMIT.py b/deps/v8/tools/PRESUBMIT.py index c883782ecf2d27..ded0016793a405 100644 --- a/deps/v8/tools/PRESUBMIT.py +++ b/deps/v8/tools/PRESUBMIT.py @@ -2,7 +2,13 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +# This line is 'magic' in that git-cl looks for it to decide whether to +# use Python3 instead of Python2 when running the code in this file. +USE_PYTHON3 = True + + def CheckChangeOnCommit(input_api, output_api): tests = input_api.canned_checks.GetUnitTestsInDirectory( - input_api, output_api, 'unittests', files_to_check=[r'.+_test\.py$']) + input_api, output_api, 'unittests', files_to_check=[r'.+_test\.py$'], + run_on_python2=False) return input_api.RunTests(tests) diff --git a/deps/v8/tools/callstats-from-telemetry.sh b/deps/v8/tools/callstats-from-telemetry.sh index cea471cde82316..3572b7e6ba8bc8 100755 --- a/deps/v8/tools/callstats-from-telemetry.sh +++ b/deps/v8/tools/callstats-from-telemetry.sh @@ -46,7 +46,7 @@ fi OUT=out.json if [[ -e $OUT ]]; then echo "# Creating backup for $OUT" - cp --backup=numbered $OUT $OUT.bak + cp $OUT $OUT.bak fi echo "# Writing to $OUT" @@ -54,11 +54,14 @@ echo "# Writing to $OUT" function convert { NAME=$1 JSON=$2 - du -sh $JSON; - echo "Converting NAME=$NAME"; - echo "," >> $OUT; - echo "\"$NAME\": " >> $OUT; - jq '[.traceEvents[].args | select(."runtime-call-stats" != null) | ."runtime-call-stats"]' $JSON >> $OUT; + # Check if any json file exists: + if ls $JSON 1> /dev/null 2>&1; then + du -sh $JSON; + echo "Converting NAME=$NAME"; + echo "," >> $OUT; + echo "\"$NAME\": " >> $OUT; + jq '[.traceEvents[].args | select(."runtime-call-stats" != null) | ."runtime-call-stats"]' $JSON >> $OUT; + fi } diff --git a/deps/v8/tools/callstats.html b/deps/v8/tools/callstats.html index d00f48d8b556b3..2a797680bcbdeb 100644 --- a/deps/v8/tools/callstats.html +++ b/deps/v8/tools/callstats.html @@ -1543,23 +1543,27 @@ // Instead of the default multi-page JSON: // {"Version 1": { "Page 1": ..., ...}, "Version 2": {...}, ...} // In this case insert a single "Default" version as top-level entry. - let firstProperty = (object) => { + const firstProperty = (object) => { for (let key in object) return object[key]; }; - let maybePage = firstProperty(json); - let maybeMetrics = firstProperty(maybePage); - let tempName = name ? name : new Date().toISOString(); - tempName = window.prompt('Enter a name for the loaded file:', tempName); - if ('count' in maybeMetrics && 'duration' in maybeMetrics) { + const maybeMetrics = firstProperty(json); + const maybeMetric = firstProperty(maybeMetrics); + const tempName = name ? name : new Date().toISOString(); + const getFileName = + () => window.prompt('Enter a name for the loaded file:', tempName); + if ('count' in maybeMetric && 'duration' in maybeMetric) { return { - [tempName]: json + [getFileName()]: json } } // Legacy fallback where the metrics are encoded as arrays: // { PAGE: [[metric_name, ...], [...], ]} - if (Array.isArray(maybeMetrics)) { + // Also, make sure we don't have the versioned array-style: + // { VERSION: { PAGE: [[metric_name, ...], [...], ]}, ...} + const innerArray = firstProperty(maybeMetrics); + if (Array.isArray(maybeMetric) && !Array.isArray(innerArray)) { return { - [tempName]: json + [getFileName()]: json } } return json @@ -2705,4 +2709,4 @@

Aggregated raw txt output

- \ No newline at end of file + diff --git a/deps/v8/tools/callstats.py b/deps/v8/tools/callstats.py index f756757a9a60c6..1b76d0c166b6bb 100755 --- a/deps/v8/tools/callstats.py +++ b/deps/v8/tools/callstats.py @@ -74,7 +74,7 @@ def start_replay_server(args, sites, discard_output=True): with open(os.devnull, 'w') as null: server = subprocess.Popen(cmd_args, stdout=null, stderr=null) else: - server = subprocess.Popen(cmd_args) + server = subprocess.Popen(cmd_args) print("RUNNING REPLAY SERVER: %s with PID=%s" % (args.replay_bin, server.pid)) print("=" * 80) return {'process': server, 'injection': injection} @@ -320,7 +320,7 @@ def do_run_replay_server(args): try: replay_server['process'].wait() finally: - stop_replay_server(replay_server) + stop_replay_server(replay_server) # Calculate statistics. @@ -493,12 +493,18 @@ def stats(s, units=""): print_entry("Total", S["Total"]) +def extract_domain(filename): + # Extract domain name: domain#123.txt or domain_123.txt + match = re.match(r'^(.*?)[^a-zA-Z]?[0-9]+?.txt', filename) + domain = match.group(1) + return domain + + def do_stats(args): domains = {} for path in args.logfiles: filename = os.path.basename(path) - m = re.match(r'^([^#]+)(#.*)?$', filename) - domain = m.group(1) + domain = extract_domain(filename) if domain not in domains: domains[domain] = {} read_stats(path, domains[domain], args) if args.aggregate: @@ -558,8 +564,7 @@ def _read_logs(args): if version not in versions: versions[version] = {} for filename in files: if filename.endswith(".txt"): - m = re.match(r'^([^#]+)(#.*)?\.txt$', filename) - domain = m.group(1) + domain = extract_domain(filename) if domain not in versions[version]: versions[version][domain] = {} read_stats(os.path.join(root, filename), versions[version][domain], args) diff --git a/deps/v8/tools/chrome/linux-perf-renderer-cmd.sh b/deps/v8/tools/chrome/linux-perf-renderer-cmd.sh new file mode 100755 index 00000000000000..4fe4e516bd2991 --- /dev/null +++ b/deps/v8/tools/chrome/linux-perf-renderer-cmd.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +# Copyright 2022 the V8 project authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +PERF_DATA_DIR="." +PERF_DATA_PREFIX="chrome_renderer" +RENDERER_ID="0" +for i in "$@"; do + case $i in + --help) + echo "Usage: path/to/chrome --renderer-cmd-prefix='$0 [OPTION]' [CHROME OPTIONS]" + echo "This script is mostly used in conjuction with linux_perf.py to run linux-perf" + echo "for each renderer process." + echo "It generates perf.data files that can be read by pprof or linux-perf." + echo "" + echo 'File naming: ${OUT_DIR}/${PREFIX}_${PARENT_PID}_${RENDERER_ID}.perf.data' + echo "" + echo "Options:" + echo " --perf-data-dir=OUT_DIR Change the location where perf.data is written." + echo " Default: '$PERF_DATA_DIR'" + echo " --perf-data-prefix=PREFIX Set a custom prefex for all generated perf.data files." + echo " Default: '$PERF_DATA_PREFIX'" + exit + ;; + --perf-data-dir=*) + PERF_DATA_DIR="${i#*=}" + shift + ;; + --perf-data-prefix=*) + PERF_DATA_PREFIX="${i#*=}" + shift + ;; + --renderer-client-id=*) + # Don't shift this option since it is passed in (and used by) chrome. + RENDERER_ID="${i#*=}" + ;; + *) + ;; + esac +done + + +PERF_OUTPUT="$PERF_DATA_DIR/${PERF_DATA_PREFIX}_${PPID}_${RENDERER_ID}.perf.data" +perf record --call-graph=fp --clockid=mono --freq=max --output="${PERF_OUTPUT}" -- $@ diff --git a/deps/v8/tools/chrome/linux_perf.py b/deps/v8/tools/chrome/linux_perf.py new file mode 100755 index 00000000000000..91d30857246993 --- /dev/null +++ b/deps/v8/tools/chrome/linux_perf.py @@ -0,0 +1,207 @@ +#!/usr/bin/env python3 +# Copyright 2022 the V8 project authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import optparse +from pathlib import Path +from re import A +import os +import shlex +from signal import SIGQUIT +import subprocess +import signal +import tempfile +import time +import psutil +import multiprocessing + +from unittest import result + +renderer_cmd_file = Path(__file__).parent / 'linux-perf-renderer-cmd.sh' +assert renderer_cmd_file.is_file() +renderer_cmd_prefix = f"{renderer_cmd_file} --perf-data-prefix=chrome_renderer" + +# ============================================================================== + +usage = """Usage: %prog $CHROME_BIN [OPTION]... -- [CHROME_OPTION]... [URL] + +This script runs linux-perf on all render process with custom V8 logging to get +support to resolve JS function names. + +The perf data is written to OUT_DIR separate by renderer process. + +See http://v8.dev//linux-perf for more detailed instructions. +""" +parser = optparse.OptionParser(usage=usage) +parser.add_option( + '--perf-data-dir', + default=None, + metavar="OUT_DIR", + help="Output directory for linux perf profile files") +parser.add_option( + "--profile-browser-process", + action="store_true", + default=False, + help="Also start linux-perf for the browser process. " + "By default only renderer processes are sampled. " + "Outputs 'browser_*.perf.data' in the CDW") +parser.add_option("--timeout", type=int, help="Stop chrome after N seconds") + +chrome_options = optparse.OptionGroup( + parser, "Chrome-forwarded Options", + "These convenience for a better script experience that are forward directly" + "to chrome. Any other chrome option can be passed after the '--' arguments" + "separator.") +chrome_options.add_option("--user-data-dir", dest="user_data_dir", default=None) +chrome_options.add_option("--js-flags", dest="js_flags") +chrome_options.add_option( + "--renderer-cmd-prefix", + default=None, + help=f"Set command prefix, used for each new chrome renderer process." + "Default: {renderer_cmd_prefix}") +FEATURES_DOC = "See chrome's base/feature_list.h source file for more dertails" +chrome_options.add_option( + "--enable-features", + help="Comma-separated list of enabled chrome features. " + FEATURES_DOC) +chrome_options.add_option( + "--disable-features", + help="Command-separated list of disabled chrome features. " + FEATURES_DOC) +parser.add_option_group(chrome_options) + + +# ============================================================================== +def log(*args): + print("") + print("=" * 80) + print(*args) + print("=" * 80) + + +# ============================================================================== + +(options, args) = parser.parse_args() + +if len(args) == 0: + parser.error("No chrome binary provided") + +chrome_bin = Path(args.pop(0)) +if not chrome_bin.exists(): + parser.error(f"Chrome '{chrome_bin}' does not exist") + +if options.renderer_cmd_prefix is not None: + if options.perf_data_dir is not None: + parser.error("Cannot specify --perf-data-dir " + "if a custom --renderer-cmd-prefix is provided") +else: + options.renderer_cmd_prefix = str(renderer_cmd_file) + +if options.perf_data_dir is None: + options.perf_data_dir = Path.cwd() +else: + options.perf_data_dir = Path(options.perf_data_dir).absolute() + +if not options.perf_data_dir.is_dir(): + parser.error(f"--perf-data-dir={options.perf_data_dir} " + "is not an directory or does not exist.") + +if options.timeout and options.timeout < 2: + parser.error("--timeout should be more than 2 seconds") + +# ============================================================================== +old_cwd = Path.cwd() +os.chdir(options.perf_data_dir) + +# ============================================================================== +JS_FLAGS_PERF = ("--perf-prof --no-write-protect-code-memory " + "--interpreted-frames-native-stack") + +with tempfile.TemporaryDirectory(prefix="chrome-") as tmp_dir_path: + tempdir = Path(tmp_dir_path) + cmd = [ + str(chrome_bin), + ] + if options.user_data_dir is None: + cmd.append(f"--user-data-dir={tempdir}") + cmd += [ + "--no-sandbox", "--incognito", "--enable-benchmarking", "--no-first-run", + "--no-default-browser-check", + f"--renderer-cmd-prefix={options.renderer_cmd_prefix}", + f"--js-flags={JS_FLAGS_PERF}" + ] + if options.js_flags: + cmd += [f"--js-flags={options.js_flags}"] + if options.enable_features: + cmd += [f"--enable-features={options.enable_features}"] + if options.disable_features: + cmd += [f"--disable-features={options.disable_features}"] + cmd += args + log("CHROME CMD: ", shlex.join(cmd)) + + if options.profile_browser_process: + perf_data_file = f"{tempdir.name}_browser.perf.data" + perf_cmd = [ + "perf", "record", "--call-graph=fp", "--freq=max", "--clockid=mono", + f"--output={perf_data_file}", "--" + ] + cmd = perf_cmd + cmd + log("LINUX PERF CMD: ", shlex.join(cmd)) + + if options.timeout is None: + subprocess.run(cmd) + else: + process = subprocess.Popen(cmd) + time.sleep(options.timeout) + log(f"QUITING chrome child processes after {options.timeout}s timeout") + current_process = psutil.Process() + children = current_process.children(recursive=True) + for child in children: + if "chrome" in child.name() or "content_shell" in child.name(): + print(f" quitting PID={child.pid}") + child.send_signal(signal.SIGQUIT) + # Wait for linux-perf to write out files + time.sleep(1) + process.send_signal(signal.SIGQUIT) + process.wait() + +# ============================================================================== +log("PARALLEL POST PROCESSING: Injecting JS symbols") + + +def inject_v8_symbols(perf_dat_file): + output_file = perf_dat_file.with_suffix(".data.jitted") + cmd = [ + "perf", "inject", "--jit", f"--input={perf_dat_file}", + f"--output={output_file}" + ] + try: + subprocess.run(cmd) + print(f"Processed: {output_file}") + except: + print(shlex.join(cmd)) + return None + return output_file + + +results = [] +with multiprocessing.Pool() as pool: + results = list( + pool.imap_unordered(inject_v8_symbols, + options.perf_data_dir.glob("*perf.data"))) + +results = list(filter(lambda x: x is not None, results)) +if len(results) == 0: + print("No perf files were successfully processed" + " Check for errors or partial results in '{options.perf_data_dir}'") + exit(1) +log(f"RESULTS in '{options.perf_data_dir}'") +results.sort(key=lambda x: x.stat().st_size) +BYTES_TO_MIB = 1 / 1024 / 1024 +for output_file in reversed(results): + print( + f"{output_file.name:67}{(output_file.stat().st_size*BYTES_TO_MIB):10.2f}MiB" + ) + +log("PPROF EXAMPLE") +path_strings = map(lambda f: str(f.relative_to(old_cwd)), results) +print(f"pprof -flame { ' '.join(path_strings)}") diff --git a/deps/v8/tools/clusterfuzz/BUILD.gn b/deps/v8/tools/clusterfuzz/BUILD.gn deleted file mode 100644 index 54e4fded96f9c8..00000000000000 --- a/deps/v8/tools/clusterfuzz/BUILD.gn +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright 2017 the V8 project authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("../../gni/v8.gni") - -if (v8_correctness_fuzzer) { - copy("v8_correctness_fuzzer_resources") { - sources = [ - "v8_commands.py", - "v8_foozzie.py", - "v8_foozzie_harness_adjust.js", - "v8_fuzz_config.py", - "v8_fuzz_experiments.json", - "v8_fuzz_flags.json", - "v8_mock.js", - "v8_mock_archs.js", - "v8_mock_webassembly.js", - "v8_smoke_tests.js", - "v8_suppressions.js", - "v8_suppressions.py", - ] - outputs = [ "$root_out_dir/{{source_file_part}}" ] - } -} diff --git a/deps/v8/tools/clusterfuzz/PRESUBMIT.py b/deps/v8/tools/clusterfuzz/PRESUBMIT.py deleted file mode 100644 index a98bb24413653b..00000000000000 --- a/deps/v8/tools/clusterfuzz/PRESUBMIT.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright 2018 the V8 project authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -def CheckChangeOnCommit(input_api, output_api): - tests = input_api.canned_checks.GetUnitTestsInDirectory( - input_api, output_api, '.', files_to_check=['v8_foozzie_test.py$']) - return input_api.RunTests(tests) diff --git a/deps/v8/tools/clusterfuzz/foozzie/BUILD.gn b/deps/v8/tools/clusterfuzz/foozzie/BUILD.gn new file mode 100644 index 00000000000000..90113e5aa4f5f5 --- /dev/null +++ b/deps/v8/tools/clusterfuzz/foozzie/BUILD.gn @@ -0,0 +1,25 @@ +# Copyright 2017 the V8 project authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("../../../gni/v8.gni") + +if (v8_correctness_fuzzer) { + copy("v8_correctness_fuzzer_resources") { + sources = [ + "v8_commands.py", + "v8_foozzie.py", + "v8_foozzie_harness_adjust.js", + "v8_fuzz_config.py", + "v8_fuzz_experiments.json", + "v8_fuzz_flags.json", + "v8_mock.js", + "v8_mock_archs.js", + "v8_mock_webassembly.js", + "v8_smoke_tests.js", + "v8_suppressions.js", + "v8_suppressions.py", + ] + outputs = [ "$root_out_dir/{{source_file_part}}" ] + } +} diff --git a/deps/v8/tools/clusterfuzz/foozzie/PRESUBMIT.py b/deps/v8/tools/clusterfuzz/foozzie/PRESUBMIT.py new file mode 100644 index 00000000000000..cc94c5146d085e --- /dev/null +++ b/deps/v8/tools/clusterfuzz/foozzie/PRESUBMIT.py @@ -0,0 +1,30 @@ +# Copyright 2018 the V8 project authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import json + +# This line is 'magic' in that git-cl looks for it to decide whether to +# use Python3 instead of Python2 when running the code in this file. +USE_PYTHON3 = True + + +def _RunTests(input_api, output_api): + return input_api.RunTests( + input_api.canned_checks.GetUnitTestsInDirectory( + input_api, output_api, '.', files_to_check=[r'.+_test\.py$'])) + + +def _CommonChecks(input_api, output_api): + """Checks common to both upload and commit.""" + checks = [ + _RunTests, + ] + + return sum([check(input_api, output_api) for check in checks], []) + +def CheckChangeOnCommit(input_api, output_api): + return _CommonChecks(input_api, output_api) + +def CheckChangeOnUpload(input_api, output_api): + return _CommonChecks(input_api, output_api) diff --git a/deps/v8/tools/clusterfuzz/testdata/baseline/d8.py b/deps/v8/tools/clusterfuzz/foozzie/testdata/baseline/d8.py similarity index 100% rename from deps/v8/tools/clusterfuzz/testdata/baseline/d8.py rename to deps/v8/tools/clusterfuzz/foozzie/testdata/baseline/d8.py diff --git a/deps/v8/tools/clusterfuzz/testdata/baseline/v8_build_config.json b/deps/v8/tools/clusterfuzz/foozzie/testdata/baseline/v8_build_config.json similarity index 100% rename from deps/v8/tools/clusterfuzz/testdata/baseline/v8_build_config.json rename to deps/v8/tools/clusterfuzz/foozzie/testdata/baseline/v8_build_config.json diff --git a/deps/v8/tools/clusterfuzz/testdata/build1/d8.py b/deps/v8/tools/clusterfuzz/foozzie/testdata/build1/d8.py similarity index 100% rename from deps/v8/tools/clusterfuzz/testdata/build1/d8.py rename to deps/v8/tools/clusterfuzz/foozzie/testdata/build1/d8.py diff --git a/deps/v8/tools/clusterfuzz/testdata/build1/v8_build_config.json b/deps/v8/tools/clusterfuzz/foozzie/testdata/build1/v8_build_config.json similarity index 100% rename from deps/v8/tools/clusterfuzz/testdata/build1/v8_build_config.json rename to deps/v8/tools/clusterfuzz/foozzie/testdata/build1/v8_build_config.json diff --git a/deps/v8/tools/clusterfuzz/testdata/build2/d8.py b/deps/v8/tools/clusterfuzz/foozzie/testdata/build2/d8.py similarity index 100% rename from deps/v8/tools/clusterfuzz/testdata/build2/d8.py rename to deps/v8/tools/clusterfuzz/foozzie/testdata/build2/d8.py diff --git a/deps/v8/tools/clusterfuzz/testdata/build2/v8_build_config.json b/deps/v8/tools/clusterfuzz/foozzie/testdata/build2/v8_build_config.json similarity index 100% rename from deps/v8/tools/clusterfuzz/testdata/build2/v8_build_config.json rename to deps/v8/tools/clusterfuzz/foozzie/testdata/build2/v8_build_config.json diff --git a/deps/v8/tools/clusterfuzz/testdata/build3/d8.py b/deps/v8/tools/clusterfuzz/foozzie/testdata/build3/d8.py similarity index 100% rename from deps/v8/tools/clusterfuzz/testdata/build3/d8.py rename to deps/v8/tools/clusterfuzz/foozzie/testdata/build3/d8.py diff --git a/deps/v8/tools/clusterfuzz/testdata/build3/v8_build_config.json b/deps/v8/tools/clusterfuzz/foozzie/testdata/build3/v8_build_config.json similarity index 100% rename from deps/v8/tools/clusterfuzz/testdata/build3/v8_build_config.json rename to deps/v8/tools/clusterfuzz/foozzie/testdata/build3/v8_build_config.json diff --git a/deps/v8/tools/clusterfuzz/testdata/failure_output.txt b/deps/v8/tools/clusterfuzz/foozzie/testdata/failure_output.txt similarity index 76% rename from deps/v8/tools/clusterfuzz/testdata/failure_output.txt rename to deps/v8/tools/clusterfuzz/foozzie/testdata/failure_output.txt index 91c06467ea11c9..3d8715d5838d37 100644 --- a/deps/v8/tools/clusterfuzz/testdata/failure_output.txt +++ b/deps/v8/tools/clusterfuzz/foozzie/testdata/failure_output.txt @@ -9,9 +9,9 @@ # Compared x64,ignition with x64,ignition_turbo # # Flags of x64,ignition: ---correctness-fuzzer-suppressions --expose-gc --fuzzing --allow-natives-for-differential-fuzzing --invoke-weak-callbacks --omit-quit --es-staging --wasm-staging --no-wasm-async-compilation --suppress-asm-messages --random-seed 12345 --turbo-filter=~ --no-opt --no-sparkplug --liftoff --no-wasm-tier-up --flag1 --flag2=0 +--correctness-fuzzer-suppressions --expose-gc --fuzzing --allow-natives-for-differential-fuzzing --invoke-weak-callbacks --omit-quit --harmony --wasm-staging --no-wasm-async-compilation --suppress-asm-messages --random-seed 12345 --turbo-filter=~ --no-opt --no-sparkplug --liftoff --no-wasm-tier-up --flag1 --flag2=0 # Flags of x64,ignition_turbo: ---correctness-fuzzer-suppressions --expose-gc --fuzzing --allow-natives-for-differential-fuzzing --invoke-weak-callbacks --omit-quit --es-staging --wasm-staging --no-wasm-async-compilation --suppress-asm-messages --random-seed 12345 --flag3 +--correctness-fuzzer-suppressions --expose-gc --fuzzing --allow-natives-for-differential-fuzzing --invoke-weak-callbacks --omit-quit --harmony --wasm-staging --no-wasm-async-compilation --suppress-asm-messages --random-seed 12345 --flag3 # # Difference: - unknown diff --git a/deps/v8/tools/clusterfuzz/testdata/failure_output_arch.txt b/deps/v8/tools/clusterfuzz/foozzie/testdata/failure_output_arch.txt similarity index 77% rename from deps/v8/tools/clusterfuzz/testdata/failure_output_arch.txt rename to deps/v8/tools/clusterfuzz/foozzie/testdata/failure_output_arch.txt index 68af8a5dcfbeb3..5d32d9bab85ac9 100644 --- a/deps/v8/tools/clusterfuzz/testdata/failure_output_arch.txt +++ b/deps/v8/tools/clusterfuzz/foozzie/testdata/failure_output_arch.txt @@ -9,9 +9,9 @@ # Compared x64,ignition with x64,ignition_turbo # # Flags of x64,ignition: ---correctness-fuzzer-suppressions --expose-gc --fuzzing --allow-natives-for-differential-fuzzing --invoke-weak-callbacks --omit-quit --es-staging --wasm-staging --no-wasm-async-compilation --suppress-asm-messages --random-seed 12345 --turbo-filter=~ --no-opt --no-sparkplug --liftoff --no-wasm-tier-up +--correctness-fuzzer-suppressions --expose-gc --fuzzing --allow-natives-for-differential-fuzzing --invoke-weak-callbacks --omit-quit --harmony --wasm-staging --no-wasm-async-compilation --suppress-asm-messages --random-seed 12345 --turbo-filter=~ --no-opt --no-sparkplug --liftoff --no-wasm-tier-up # Flags of x64,ignition_turbo: ---correctness-fuzzer-suppressions --expose-gc --fuzzing --allow-natives-for-differential-fuzzing --invoke-weak-callbacks --omit-quit --es-staging --wasm-staging --no-wasm-async-compilation --suppress-asm-messages --random-seed 12345 --bad-flag +--correctness-fuzzer-suppressions --expose-gc --fuzzing --allow-natives-for-differential-fuzzing --invoke-weak-callbacks --omit-quit --harmony --wasm-staging --no-wasm-async-compilation --suppress-asm-messages --random-seed 12345 --bad-flag # # Difference: + bad behavior diff --git a/deps/v8/tools/clusterfuzz/testdata/failure_output_second.txt b/deps/v8/tools/clusterfuzz/foozzie/testdata/failure_output_second.txt similarity index 77% rename from deps/v8/tools/clusterfuzz/testdata/failure_output_second.txt rename to deps/v8/tools/clusterfuzz/foozzie/testdata/failure_output_second.txt index 75da7f5a992b16..67ce872e4b6d23 100644 --- a/deps/v8/tools/clusterfuzz/testdata/failure_output_second.txt +++ b/deps/v8/tools/clusterfuzz/foozzie/testdata/failure_output_second.txt @@ -9,9 +9,9 @@ # Compared x64,ignition with ia32,ignition_turbo # # Flags of x64,ignition: ---correctness-fuzzer-suppressions --expose-gc --fuzzing --allow-natives-for-differential-fuzzing --invoke-weak-callbacks --omit-quit --es-staging --wasm-staging --no-wasm-async-compilation --suppress-asm-messages --random-seed 12345 --turbo-filter=~ --no-opt --no-sparkplug --liftoff --no-wasm-tier-up +--correctness-fuzzer-suppressions --expose-gc --fuzzing --allow-natives-for-differential-fuzzing --invoke-weak-callbacks --omit-quit --harmony --wasm-staging --no-wasm-async-compilation --suppress-asm-messages --random-seed 12345 --turbo-filter=~ --no-opt --no-sparkplug --liftoff --no-wasm-tier-up # Flags of ia32,ignition_turbo: ---correctness-fuzzer-suppressions --expose-gc --fuzzing --allow-natives-for-differential-fuzzing --invoke-weak-callbacks --omit-quit --es-staging --wasm-staging --no-wasm-async-compilation --suppress-asm-messages --random-seed 12345 --very-bad-flag +--correctness-fuzzer-suppressions --expose-gc --fuzzing --allow-natives-for-differential-fuzzing --invoke-weak-callbacks --omit-quit --harmony --wasm-staging --no-wasm-async-compilation --suppress-asm-messages --random-seed 12345 --very-bad-flag # # Difference: + very bad behavior diff --git a/deps/v8/tools/clusterfuzz/testdata/fuzz-123.js b/deps/v8/tools/clusterfuzz/foozzie/testdata/fuzz-123.js similarity index 100% rename from deps/v8/tools/clusterfuzz/testdata/fuzz-123.js rename to deps/v8/tools/clusterfuzz/foozzie/testdata/fuzz-123.js diff --git a/deps/v8/tools/clusterfuzz/testdata/smoke_test_output.txt b/deps/v8/tools/clusterfuzz/foozzie/testdata/smoke_test_output.txt similarity index 78% rename from deps/v8/tools/clusterfuzz/testdata/smoke_test_output.txt rename to deps/v8/tools/clusterfuzz/foozzie/testdata/smoke_test_output.txt index c2c1378ec8130f..618c2104204506 100644 --- a/deps/v8/tools/clusterfuzz/testdata/smoke_test_output.txt +++ b/deps/v8/tools/clusterfuzz/foozzie/testdata/smoke_test_output.txt @@ -9,9 +9,9 @@ # Compared x64,ignition with x64,ignition_turbo # # Flags of x64,ignition: ---correctness-fuzzer-suppressions --expose-gc --fuzzing --allow-natives-for-differential-fuzzing --invoke-weak-callbacks --omit-quit --es-staging --wasm-staging --no-wasm-async-compilation --suppress-asm-messages --random-seed 12345 --turbo-filter=~ --no-opt --no-sparkplug --liftoff --no-wasm-tier-up +--correctness-fuzzer-suppressions --expose-gc --fuzzing --allow-natives-for-differential-fuzzing --invoke-weak-callbacks --omit-quit --harmony --wasm-staging --no-wasm-async-compilation --suppress-asm-messages --random-seed 12345 --turbo-filter=~ --no-opt --no-sparkplug --liftoff --no-wasm-tier-up # Flags of x64,ignition_turbo: ---correctness-fuzzer-suppressions --expose-gc --fuzzing --allow-natives-for-differential-fuzzing --invoke-weak-callbacks --omit-quit --es-staging --wasm-staging --no-wasm-async-compilation --suppress-asm-messages --random-seed 12345 +--correctness-fuzzer-suppressions --expose-gc --fuzzing --allow-natives-for-differential-fuzzing --invoke-weak-callbacks --omit-quit --harmony --wasm-staging --no-wasm-async-compilation --suppress-asm-messages --random-seed 12345 # # Difference: - unknown diff --git a/deps/v8/tools/clusterfuzz/toolchain/BUILD.gn b/deps/v8/tools/clusterfuzz/foozzie/toolchain/BUILD.gn similarity index 100% rename from deps/v8/tools/clusterfuzz/toolchain/BUILD.gn rename to deps/v8/tools/clusterfuzz/foozzie/toolchain/BUILD.gn diff --git a/deps/v8/tools/clusterfuzz/v8_commands.py b/deps/v8/tools/clusterfuzz/foozzie/v8_commands.py similarity index 93% rename from deps/v8/tools/clusterfuzz/v8_commands.py rename to deps/v8/tools/clusterfuzz/foozzie/v8_commands.py index f03161c2c481db..c6c85f1ac2ddfd 100644 --- a/deps/v8/tools/clusterfuzz/v8_commands.py +++ b/deps/v8/tools/clusterfuzz/foozzie/v8_commands.py @@ -16,16 +16,16 @@ # List of default flags passed to each d8 run. DEFAULT_FLAGS = [ - '--correctness-fuzzer-suppressions', - '--expose-gc', - '--fuzzing', - '--allow-natives-for-differential-fuzzing', - '--invoke-weak-callbacks', - '--omit-quit', - '--es-staging', - '--wasm-staging', - '--no-wasm-async-compilation', - '--suppress-asm-messages', + '--correctness-fuzzer-suppressions', + '--expose-gc', + '--fuzzing', + '--allow-natives-for-differential-fuzzing', + '--invoke-weak-callbacks', + '--omit-quit', + '--harmony', + '--wasm-staging', + '--no-wasm-async-compilation', + '--suppress-asm-messages', ] BASE_PATH = os.path.dirname(os.path.abspath(__file__)) diff --git a/deps/v8/tools/clusterfuzz/v8_foozzie.py b/deps/v8/tools/clusterfuzz/foozzie/v8_foozzie.py similarity index 99% rename from deps/v8/tools/clusterfuzz/v8_foozzie.py rename to deps/v8/tools/clusterfuzz/foozzie/v8_foozzie.py index 656bc89ed34465..485d1d601a1e7c 100755 --- a/deps/v8/tools/clusterfuzz/v8_foozzie.py +++ b/deps/v8/tools/clusterfuzz/foozzie/v8_foozzie.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright 2016 the V8 project authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. diff --git a/deps/v8/tools/clusterfuzz/v8_foozzie_harness_adjust.js b/deps/v8/tools/clusterfuzz/foozzie/v8_foozzie_harness_adjust.js similarity index 100% rename from deps/v8/tools/clusterfuzz/v8_foozzie_harness_adjust.js rename to deps/v8/tools/clusterfuzz/foozzie/v8_foozzie_harness_adjust.js diff --git a/deps/v8/tools/clusterfuzz/v8_foozzie_test.py b/deps/v8/tools/clusterfuzz/foozzie/v8_foozzie_test.py similarity index 99% rename from deps/v8/tools/clusterfuzz/v8_foozzie_test.py rename to deps/v8/tools/clusterfuzz/foozzie/v8_foozzie_test.py index a8ba74364bc264..fe149620f938af 100755 --- a/deps/v8/tools/clusterfuzz/v8_foozzie_test.py +++ b/deps/v8/tools/clusterfuzz/foozzie/v8_foozzie_test.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright 2016 the V8 project authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. diff --git a/deps/v8/tools/clusterfuzz/v8_fuzz_config.py b/deps/v8/tools/clusterfuzz/foozzie/v8_fuzz_config.py similarity index 100% rename from deps/v8/tools/clusterfuzz/v8_fuzz_config.py rename to deps/v8/tools/clusterfuzz/foozzie/v8_fuzz_config.py diff --git a/deps/v8/tools/clusterfuzz/v8_fuzz_experiments.json b/deps/v8/tools/clusterfuzz/foozzie/v8_fuzz_experiments.json similarity index 100% rename from deps/v8/tools/clusterfuzz/v8_fuzz_experiments.json rename to deps/v8/tools/clusterfuzz/foozzie/v8_fuzz_experiments.json diff --git a/deps/v8/tools/clusterfuzz/v8_fuzz_flags.json b/deps/v8/tools/clusterfuzz/foozzie/v8_fuzz_flags.json similarity index 90% rename from deps/v8/tools/clusterfuzz/v8_fuzz_flags.json rename to deps/v8/tools/clusterfuzz/foozzie/v8_fuzz_flags.json index 7aefe69267280a..c9c107e0e847e6 100644 --- a/deps/v8/tools/clusterfuzz/v8_fuzz_flags.json +++ b/deps/v8/tools/clusterfuzz/foozzie/v8_fuzz_flags.json @@ -27,8 +27,9 @@ [0.1, "--no-enable-popcnt"], [0.25, "--no-lazy-feedback-allocation"], [0.1, "--no-lazy-feedback-allocation --interrupt-budget=100"], - [0.05, "--budget-for-feedback-vector-allocation=0"], + [0.05, "--interrupt-budget-for-feedback-allocation=0"], [0.1, "--no-wasm-generic-wrapper"], [0.1, "--turbo-force-mid-tier-regalloc"], - [0.0001, "--simulate-errors"] + [0.0001, "--simulate-errors"], + [0.25, "--compact-maps"] ] diff --git a/deps/v8/tools/clusterfuzz/v8_mock.js b/deps/v8/tools/clusterfuzz/foozzie/v8_mock.js similarity index 100% rename from deps/v8/tools/clusterfuzz/v8_mock.js rename to deps/v8/tools/clusterfuzz/foozzie/v8_mock.js diff --git a/deps/v8/tools/clusterfuzz/v8_mock_archs.js b/deps/v8/tools/clusterfuzz/foozzie/v8_mock_archs.js similarity index 100% rename from deps/v8/tools/clusterfuzz/v8_mock_archs.js rename to deps/v8/tools/clusterfuzz/foozzie/v8_mock_archs.js diff --git a/deps/v8/tools/clusterfuzz/v8_mock_webassembly.js b/deps/v8/tools/clusterfuzz/foozzie/v8_mock_webassembly.js similarity index 100% rename from deps/v8/tools/clusterfuzz/v8_mock_webassembly.js rename to deps/v8/tools/clusterfuzz/foozzie/v8_mock_webassembly.js diff --git a/deps/v8/tools/clusterfuzz/v8_smoke_tests.js b/deps/v8/tools/clusterfuzz/foozzie/v8_smoke_tests.js similarity index 100% rename from deps/v8/tools/clusterfuzz/v8_smoke_tests.js rename to deps/v8/tools/clusterfuzz/foozzie/v8_smoke_tests.js diff --git a/deps/v8/tools/clusterfuzz/v8_suppressions.js b/deps/v8/tools/clusterfuzz/foozzie/v8_suppressions.js similarity index 100% rename from deps/v8/tools/clusterfuzz/v8_suppressions.js rename to deps/v8/tools/clusterfuzz/foozzie/v8_suppressions.js diff --git a/deps/v8/tools/clusterfuzz/v8_suppressions.py b/deps/v8/tools/clusterfuzz/foozzie/v8_suppressions.py similarity index 100% rename from deps/v8/tools/clusterfuzz/v8_suppressions.py rename to deps/v8/tools/clusterfuzz/foozzie/v8_suppressions.py diff --git a/deps/v8/tools/clusterfuzz/js_fuzzer/exceptions.js b/deps/v8/tools/clusterfuzz/js_fuzzer/exceptions.js index 4a571d5dd0dfde..6aa3094179aa87 100644 --- a/deps/v8/tools/clusterfuzz/js_fuzzer/exceptions.js +++ b/deps/v8/tools/clusterfuzz/js_fuzzer/exceptions.js @@ -97,14 +97,14 @@ const DISALLOWED_FLAGS = [ // stabilized yet and would cause too much noise when enabled. /^--experimental-.*/, - // Disallowed due to noise. We explicitly add --es-staging to job + // Disallowed due to noise. We explicitly add --harmony to job // definitions, and all of these features are staged before launch. /^--harmony-.*/, // Disallowed because they are passed explicitly on the command line. '--allow-natives-syntax', '--debug-code', - '--es-staging', + '--harmony', '--wasm-staging', '--expose-gc', '--expose_gc', diff --git a/deps/v8/tools/clusterfuzz/js_fuzzer/foozzie_launcher.py b/deps/v8/tools/clusterfuzz/js_fuzzer/foozzie_launcher.py index b1f892c64a356d..e046d9f43e6cbe 100644 --- a/deps/v8/tools/clusterfuzz/js_fuzzer/foozzie_launcher.py +++ b/deps/v8/tools/clusterfuzz/js_fuzzer/foozzie_launcher.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright 2020 the V8 project authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -7,6 +7,9 @@ """ Launcher for the foozzie differential-fuzzing harness. Wraps foozzie with Python2 for backwards-compatibility when bisecting. + +Obsolete now after switching to Python3 entirely. We keep the launcher +for a transition period. """ import os @@ -22,6 +25,7 @@ args = sys.argv[2:] else: args = sys.argv[1:] - process = subprocess.Popen(['python2'] + args) + process = subprocess.Popen(['python3'] + args) + process = subprocess.Popen(args) process.communicate() sys.exit(process.returncode) diff --git a/deps/v8/tools/clusterfuzz/trials/BUILD.gn b/deps/v8/tools/clusterfuzz/trials/BUILD.gn new file mode 100644 index 00000000000000..d3c8bf6aab3d93 --- /dev/null +++ b/deps/v8/tools/clusterfuzz/trials/BUILD.gn @@ -0,0 +1,8 @@ +# Copyright 2022 the V8 project authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +copy("v8_clusterfuzz_resources") { + sources = [ "clusterfuzz_trials_config.json" ] + outputs = [ "$root_out_dir/{{source_file_part}}" ] +} diff --git a/deps/v8/tools/clusterfuzz/trials/PRESUBMIT.py b/deps/v8/tools/clusterfuzz/trials/PRESUBMIT.py new file mode 100644 index 00000000000000..16a64abccc4b3a --- /dev/null +++ b/deps/v8/tools/clusterfuzz/trials/PRESUBMIT.py @@ -0,0 +1,57 @@ +# Copyright 2022 the V8 project authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import json + +# This line is 'magic' in that git-cl looks for it to decide whether to +# use Python3 instead of Python2 when running the code in this file. +USE_PYTHON3 = True + + +def _CheckTrialsConfig(input_api, output_api): + def FilterFile(affected_file): + return input_api.FilterSourceFile( + affected_file, + files_to_check=(r'.+clusterfuzz_trials_config\.json',)) + + results = [] + for f in input_api.AffectedFiles( + file_filter=FilterFile, include_deletes=False): + with open(f.AbsoluteLocalPath()) as j: + try: + trials = json.load(j) + for trial in trials: + if not all( + k in trial for k in ('app_args', 'app_name', 'probability')): + results.append('trial {} is not configured correctly'.format(trial)) + if trial['app_name'] != 'd8': + results.append('trial {} has an incorrect app_name'.format(trial)) + if not isinstance(trial['probability'], float): + results.append('trial {} probability is not a float'.format(trial)) + if not (0 <= trial['probability'] <= 1): + results.append( + 'trial {} has invalid probability value'.format(trial)) + if not isinstance(trial['app_args'], str) or not trial['app_args']: + results.append( + 'trial {} should have a non-empty string for app_args'.format( + trial)) + except Exception as e: + results.append( + 'JSON validation failed for %s. Error:\n%s' % (f.LocalPath(), e)) + + return [output_api.PresubmitError(r) for r in results] + +def _CommonChecks(input_api, output_api): + """Checks common to both upload and commit.""" + checks = [ + _CheckTrialsConfig, + ] + + return sum([check(input_api, output_api) for check in checks], []) + +def CheckChangeOnCommit(input_api, output_api): + return _CommonChecks(input_api, output_api) + +def CheckChangeOnUpload(input_api, output_api): + return _CommonChecks(input_api, output_api) diff --git a/deps/v8/tools/clusterfuzz/trials/clusterfuzz_trials_config.json b/deps/v8/tools/clusterfuzz/trials/clusterfuzz_trials_config.json new file mode 100644 index 00000000000000..682972d5b24766 --- /dev/null +++ b/deps/v8/tools/clusterfuzz/trials/clusterfuzz_trials_config.json @@ -0,0 +1,38 @@ +[ + {"app_args": "--assert-types", "app_name": "d8", "probability": 0.25}, + {"app_args": "--interrupt-budget-for-feedback-vector-allocation=0", "app_name": "d8", "probability": 0.05}, + {"app_args": "--compact-maps", "app_name": "d8", "probability": 0.25}, + {"app_args": "--force-slow-path", "app_name": "d8", "probability": 0.05}, + {"app_args": "--future", "app_name": "d8", "probability": 0.25}, + {"app_args": "--interrupt-budget=1000", "app_name": "d8", "probability": 0.25}, + {"app_args": "--jitless", "app_name": "d8", "probability": 0.1}, + {"app_args": "--random-gc-interval=2000", "app_name": "d8", "probability": 0.05}, + {"app_args": "--noanalyze-environment-liveness", "app_name": "d8", "probability": 0.1}, + {"app_args": "--no-enable-avx", "app_name": "d8", "probability": 0.1}, + {"app_args": "--no-enable-bmi1", "app_name": "d8", "probability": 0.1}, + {"app_args": "--no-enable-bmi2", "app_name": "d8", "probability": 0.1}, + {"app_args": "--no-enable-fma3", "app_name": "d8", "probability": 0.1}, + {"app_args": "--no-enable-lzcnt", "app_name": "d8", "probability": 0.1}, + {"app_args": "--no-enable-popcnt", "app_name": "d8", "probability": 0.1}, + {"app_args": "--no-enable-sahf", "app_name": "d8", "probability": 0.1}, + {"app_args": "--no-enable-sse3", "app_name": "d8", "probability": 0.1}, + {"app_args": "--no-enable-sse4_1", "app_name": "d8", "probability": 0.1}, + {"app_args": "--no-enable-sse4_2", "app_name": "d8", "probability": 0.1}, + {"app_args": "--no-enable-ssse3", "app_name": "d8", "probability": 0.1}, + {"app_args": "--no-lazy-feedback-allocation", "app_name": "d8", "probability": 0.35}, + {"app_args": "--no-regexp-tier-up", "app_name": "d8", "probability": 0.2}, + {"app_args": "--no-untrusted-code-mitigations", "app_name": "d8", "probability": 0.1}, + {"app_args": "--no-use-ic", "app_name": "d8", "probability": 0.25}, + {"app_args": "--no-wasm-generic-wrapper", "app_name": "d8", "probability": 0.1}, + {"app_args": "--regexp-interpret-all", "app_name": "d8", "probability": 0.1}, + {"app_args": "--simulate-errors", "app_name": "d8", "probability": 0.001}, + {"app_args": "--stress-compaction-random", "app_name": "d8", "probability": 0.05}, + {"app_args": "--stress-concurrent-inlining", "app_name": "d8", "probability": 0.25}, + {"app_args": "--stress-concurrent-inlining-attach-code", "app_name": "d8", "probability": 0.25}, + {"app_args": "--stress-flush-code", "app_name": "d8", "probability": 0.25}, + {"app_args": "--stress-marking=100", "app_name": "d8", "probability": 0.05}, + {"app_args": "--stress-scavenge=100", "app_name": "d8", "probability": 0.05}, + {"app_args": "--turbo-instruction-scheduling", "app_name": "d8", "probability": 0.1}, + {"app_args": "--turbo-stress-instruction-scheduling", "app_name": "d8", "probability": 0.1}, + {"app_args": "--wasm-code-gc --stress-wasm-code-gc", "app_name": "d8", "probability": 0.1} +] diff --git a/deps/v8/tools/codemap.mjs b/deps/v8/tools/codemap.mjs index 55327b6982ee5c..8d1e00c9e96b50 100644 --- a/deps/v8/tools/codemap.mjs +++ b/deps/v8/tools/codemap.mjs @@ -27,6 +27,15 @@ import { SplayTree } from "./splaytree.mjs"; +/** +* The number of alignment bits in a page address. +*/ +const kPageAlignment = 12; +/** +* Page size in bytes. +*/ +const kPageSize = 1 << kPageAlignment; + /** * Constructs a mapper that maps addresses into code entries. * @@ -56,19 +65,7 @@ export class CodeMap { /** * Map of memory pages occupied with static code. */ - pages_ = []; - - - /** - * The number of alignment bits in a page address. - */ - static PAGE_ALIGNMENT = 12; - - - /** - * Page size in bytes. - */ - static PAGE_SIZE = 1 << CodeMap.PAGE_ALIGNMENT; + pages_ = new Set(); /** @@ -130,9 +127,8 @@ export class CodeMap { * @private */ markPages_(start, end) { - for (let addr = start; addr <= end; - addr += CodeMap.PAGE_SIZE) { - this.pages_[(addr / CodeMap.PAGE_SIZE)|0] = 1; + for (let addr = start; addr <= end; addr += kPageSize) { + this.pages_.add((addr / kPageSize) | 0); } } @@ -144,7 +140,7 @@ export class CodeMap { let addr = end - 1; while (addr >= start) { const node = tree.findGreatestLessThan(addr); - if (!node) break; + if (node === null) break; const start2 = node.key, end2 = start2 + node.value.size; if (start2 < end && start < end2) to_delete.push(start2); addr = start2 - 1; @@ -164,7 +160,7 @@ export class CodeMap { */ findInTree_(tree, addr) { const node = tree.findGreatestLessThan(addr); - return node && this.isAddressBelongsTo_(addr, node) ? node : null; + return node !== null && this.isAddressBelongsTo_(addr, node) ? node : null; } /** @@ -175,22 +171,23 @@ export class CodeMap { * @param {number} addr Address. */ findAddress(addr) { - const pageAddr = (addr / CodeMap.PAGE_SIZE)|0; - if (pageAddr in this.pages_) { + const pageAddr = (addr / kPageSize) | 0; + if (this.pages_.has(pageAddr)) { // Static code entries can contain "holes" of unnamed code. // In this case, the whole library is assigned to this address. let result = this.findInTree_(this.statics_, addr); - if (!result) { + if (result === null) { result = this.findInTree_(this.libraries_, addr); - if (!result) return null; + if (result === null) return null; } return {entry: result.value, offset: addr - result.key}; } - const min = this.dynamics_.findMin(); const max = this.dynamics_.findMax(); - if (max != null && addr < (max.key + max.value.size) && addr >= min.key) { + if (max === null) return null; + const min = this.dynamics_.findMin(); + if (addr >= min.key && addr < (max.key + max.value.size)) { const dynaEntry = this.findInTree_(this.dynamics_, addr); - if (dynaEntry == null) return null; + if (dynaEntry === null) return null; // Dedupe entry name. const entry = dynaEntry.value; if (!entry.nameUpdated_) { @@ -210,7 +207,7 @@ export class CodeMap { */ findEntry(addr) { const result = this.findAddress(addr); - return result ? result.entry : null; + return result !== null ? result.entry : null; } /** @@ -220,7 +217,7 @@ export class CodeMap { */ findDynamicEntryByStartAddress(addr) { const node = this.dynamics_.find(addr); - return node ? node.value : null; + return node !== null ? node.value : null; } /** diff --git a/deps/v8/tools/compare_torque_output.py b/deps/v8/tools/compare_torque_output.py index 50e93a7538dfdb..4ef01217dc655a 100644 --- a/deps/v8/tools/compare_torque_output.py +++ b/deps/v8/tools/compare_torque_output.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright 2020 the V8 project authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. diff --git a/deps/v8/tools/csvparser.mjs b/deps/v8/tools/csvparser.mjs index c43ee4c4fcf9b2..2f94d7910651e3 100644 --- a/deps/v8/tools/csvparser.mjs +++ b/deps/v8/tools/csvparser.mjs @@ -38,19 +38,17 @@ export class CsvParser { escapeField(string) { let nextPos = string.indexOf("\\"); if (nextPos === -1) return string; - - let result = string.substring(0, nextPos); + let result = [string.substring(0, nextPos)]; // Escape sequences of the form \x00 and \u0000; - let endPos = string.length; let pos = 0; while (nextPos !== -1) { - let escapeIdentifier = string.charAt(nextPos + 1); + const escapeIdentifier = string[nextPos + 1]; pos = nextPos + 2; if (escapeIdentifier === 'n') { - result += '\n'; + result.push('\n'); nextPos = pos; } else if (escapeIdentifier === '\\') { - result += '\\'; + result.push('\\'); nextPos = pos; } else { if (escapeIdentifier === 'x') { @@ -61,11 +59,11 @@ export class CsvParser { nextPos = pos + 4; } // Convert the selected escape sequence to a single character. - let escapeChars = string.substring(pos, nextPos); + const escapeChars = string.substring(pos, nextPos); if (escapeChars === '2C') { - result += ','; + result.push(','); } else { - result += String.fromCharCode(parseInt(escapeChars, 16)); + result.push(String.fromCharCode(parseInt(escapeChars, 16))); } } @@ -74,12 +72,13 @@ export class CsvParser { nextPos = string.indexOf("\\", pos); // If there are no more escape sequences consume the rest of the string. if (nextPos === -1) { - result += string.substr(pos); + result.push(string.substr(pos)); + break; } else if (pos !== nextPos) { - result += string.substring(pos, nextPos); + result.push(string.substring(pos, nextPos)); } } - return result; + return result.join(''); } /** diff --git a/deps/v8/tools/debug_helper/debug-macro-shims.h b/deps/v8/tools/debug_helper/debug-macro-shims.h index 948482810b601a..02deb3d766fa74 100644 --- a/deps/v8/tools/debug_helper/debug-macro-shims.h +++ b/deps/v8/tools/debug_helper/debug-macro-shims.h @@ -8,6 +8,7 @@ #ifndef V8_TORQUE_DEBUG_MACRO_SHIMS_H_ #define V8_TORQUE_DEBUG_MACRO_SHIMS_H_ +#include "src/numbers/integer-literal.h" #include "src/objects/smi.h" #include "tools/debug_helper/debug-helper-internal.h" @@ -66,6 +67,14 @@ inline Value IntPtrMul(d::MemoryAccessor accessor, intptr_t a, intptr_t b) { return {d::MemoryAccessResult::kOk, a * b}; } +inline Value IntPtrLessThan(d::MemoryAccessor accessor, intptr_t a, + intptr_t b) { + return {d::MemoryAccessResult::kOk, a < b}; +} +inline Value IntPtrLessThanOrEqual(d::MemoryAccessor accessor, intptr_t a, + intptr_t b) { + return {d::MemoryAccessResult::kOk, a <= b}; +} inline Value Signed(d::MemoryAccessor accessor, uintptr_t u) { return {d::MemoryAccessResult::kOk, static_cast(u)}; } @@ -73,6 +82,9 @@ inline Value SmiUntag(d::MemoryAccessor accessor, uintptr_t s_t) { Smi s(s_t); return {d::MemoryAccessResult::kOk, s.value()}; } +inline Value SmiFromInt32(d::MemoryAccessor accessor, int32_t i) { + return {d::MemoryAccessResult::kOk, Smi::FromInt(i).ptr()}; +} inline Value UintPtrLessThan(d::MemoryAccessor accessor, uintptr_t a, uintptr_t b) { return {d::MemoryAccessResult::kOk, a < b}; @@ -93,6 +105,19 @@ inline Value Word32NotEqual(d::MemoryAccessor accessor, uint32_t a, uint32_t b) { return {d::MemoryAccessResult::kOk, a != b}; } +// This is used in a nested call where we cannot pass Value. +inline int31_t ConstexprIntegerLiteralToInt31(d::MemoryAccessor accessor, + const IntegerLiteral& i) { + return i.To(); +} +inline int32_t ConstexprIntegerLiteralToInt32(d::MemoryAccessor accessor, + const IntegerLiteral& i) { + return i.To(); +} +inline intptr_t ConstexprIntegerLiteralToIntptr(d::MemoryAccessor accessor, + const IntegerLiteral& i) { + return i.To(); +} } // namespace CodeStubAssembler } // namespace TorqueDebugMacroShims diff --git a/deps/v8/tools/debug_helper/gen-heap-constants.py b/deps/v8/tools/debug_helper/gen-heap-constants.py index 6eb7f3743ca2f1..3ea5be68210573 100644 --- a/deps/v8/tools/debug_helper/gen-heap-constants.py +++ b/deps/v8/tools/debug_helper/gen-heap-constants.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright 2019 the V8 project authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -31,7 +31,7 @@ def iterate_objects(target_space, camel_space_name): if space == target_space: result.append((offset, name)) for (space, offset), name in v8heapconst.KNOWN_OBJECTS.items(): - if space == target_space: + if space == target_space and (space, offset) not in v8heapconst.KNOWN_MAPS: result.append((offset, name)) out = out + '\nstd::string FindKnownObjectIn' + camel_space_name \ + '(uintptr_t offset) {\n switch (offset) {\n' @@ -40,8 +40,9 @@ def iterate_objects(target_space, camel_space_name): out = out + ' default: return "";\n }\n}\n' iterate_objects('map_space', 'MapSpace') -iterate_objects('read_only_space', 'ReadOnlySpace') iterate_objects('old_space', 'OldSpace') +iterate_objects('read_only_space', 'ReadOnlySpace') + def iterate_maps(target_space, camel_space_name): global out @@ -54,6 +55,7 @@ def iterate_maps(target_space, camel_space_name): out = out + ' default: return -1;\n }\n}\n' iterate_maps('map_space', 'MapSpace') +iterate_maps('old_space', 'OldSpace') iterate_maps('read_only_space', 'ReadOnlySpace') out = out + '\nvoid FillInUnknownHeapAddresses(' + \ diff --git a/deps/v8/tools/debug_helper/get-object-properties.cc b/deps/v8/tools/debug_helper/get-object-properties.cc index 10ef48cbbac680..43a67941ac6014 100644 --- a/deps/v8/tools/debug_helper/get-object-properties.cc +++ b/deps/v8/tools/debug_helper/get-object-properties.cc @@ -11,7 +11,7 @@ #include "src/execution/frames.h" #include "src/execution/isolate-utils.h" #include "src/objects/string-inl.h" -#include "src/security/external-pointer.h" +#include "src/sandbox/external-pointer.h" #include "src/strings/unicode-inl.h" #include "torque-generated/class-debug-readers.h" #include "torque-generated/debug-macros.h" @@ -350,7 +350,7 @@ class ReadStringVisitor : public TqObjectVisitor { ExternalPointer_t resource_data = GetOrFinish(object->GetResourceDataValue(accessor_)); #ifdef V8_COMPRESS_POINTERS - Isolate* isolate = GetIsolateForHeapSandbox( + Isolate* isolate = GetIsolateForSandbox( HeapObject::unchecked_cast(Object(heap_addresses_.any_heap_pointer))); uintptr_t data_address = static_cast(DecodeExternalPointer( isolate, resource_data, kExternalStringResourceDataTag)); diff --git a/deps/v8/tools/debug_helper/heap-constants.cc b/deps/v8/tools/debug_helper/heap-constants.cc index f62dd9b6975a9f..412308eb7f999b 100644 --- a/deps/v8/tools/debug_helper/heap-constants.cc +++ b/deps/v8/tools/debug_helper/heap-constants.cc @@ -61,6 +61,10 @@ KnownInstanceType FindKnownMapInstanceTypes( return KnownInstanceType( FindKnownMapInstanceTypeInMapSpace(offset_in_page)); } + if (containing_page == heap_addresses.old_space_first_page) { + return KnownInstanceType( + FindKnownMapInstanceTypeInOldSpace(offset_in_page)); + } if (containing_page == heap_addresses.read_only_space_first_page) { return KnownInstanceType( FindKnownMapInstanceTypeInReadOnlySpace(offset_in_page)); @@ -74,6 +78,12 @@ KnownInstanceType FindKnownMapInstanceTypes( result.types.push_back(static_cast(sub_result)); } } + if (heap_addresses.old_space_first_page == 0) { + int sub_result = FindKnownMapInstanceTypeInOldSpace(offset_in_page); + if (sub_result >= 0) { + result.types.push_back(static_cast(sub_result)); + } + } if (heap_addresses.read_only_space_first_page == 0) { int sub_result = FindKnownMapInstanceTypeInReadOnlySpace(offset_in_page); if (sub_result >= 0) { diff --git a/deps/v8/tools/debug_helper/heap-constants.h b/deps/v8/tools/debug_helper/heap-constants.h index 89620479eca2b9..9486a18d053bd3 100644 --- a/deps/v8/tools/debug_helper/heap-constants.h +++ b/deps/v8/tools/debug_helper/heap-constants.h @@ -34,6 +34,7 @@ void FillInUnknownHeapAddresses(d::HeapAddresses* heap_addresses, // Returns the instance type for the known Map, given its offset within the // first page of the space, or empty string on failure. int FindKnownMapInstanceTypeInMapSpace(uintptr_t offset); +int FindKnownMapInstanceTypeInOldSpace(uintptr_t offset); int FindKnownMapInstanceTypeInReadOnlySpace(uintptr_t offset); // ===== End of generated functions. =========================================== diff --git a/deps/v8/tools/dev/gm.py b/deps/v8/tools/dev/gm.py index 613065d5b11169..d16b4dd737add7 100755 --- a/deps/v8/tools/dev/gm.py +++ b/deps/v8/tools/dev/gm.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # Copyright 2017 the V8 project authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -21,9 +21,6 @@ not contain spaces. """ # See HELP below for additional documentation. -# Note on Python3 compatibility: gm.py itself is Python3 compatible, but -# run-tests.py, which will be executed by the same binary, is not; hence -# the hashbang line at the top of this file explicitly requires Python2. from __future__ import print_function import errno @@ -43,7 +40,8 @@ # All arches that this script understands. ARCHES = ["ia32", "x64", "arm", "arm64", "mipsel", "mips64el", "ppc", "ppc64", - "riscv64", "s390", "s390x", "android_arm", "android_arm64", "loong64"] + "riscv64", "s390", "s390x", "android_arm", "android_arm64", "loong64", + "fuchsia_x64", "fuchsia_arm64"] # Arches that get built/run when you don't specify any. DEFAULT_ARCHES = ["ia32", "x64", "arm", "arm64"] # Modes that this script understands. @@ -291,7 +289,7 @@ def GetTargetCpu(self): cpu = "x86" if self.arch == "android_arm": cpu = "arm" - elif self.arch == "android_arm64": + elif self.arch == "android_arm64" or self.arch == "fuchsia_arm64": cpu = "arm64" elif self.arch == "arm64" and _GetMachine() in ("aarch64", "arm64"): # arm64 build host: @@ -310,7 +308,7 @@ def GetTargetCpu(self): def GetV8TargetCpu(self): if self.arch == "android_arm": v8_cpu = "arm" - elif self.arch == "android_arm64": + elif self.arch == "android_arm64" or self.arch == "fuchsia_arm64": v8_cpu = "arm64" elif self.arch in ("arm", "arm64", "mipsel", "mips64el", "ppc", "ppc64", "riscv64", "s390", "s390x", "loong64"): @@ -322,6 +320,8 @@ def GetV8TargetCpu(self): def GetTargetOS(self): if self.arch in ("android_arm", "android_arm64"): return ["target_os = \"android\""] + elif self.arch in ("fuchsia_x64", "fuchsia_arm64"): + return ["target_os = \"fuchsia\""] return [] def GetSpecialCompiler(self): diff --git a/deps/v8/tools/disasm.py b/deps/v8/tools/disasm.py index a91d0dbff4d8b1..c21e7f98536604 100644 --- a/deps/v8/tools/disasm.py +++ b/deps/v8/tools/disasm.py @@ -81,7 +81,7 @@ def GetDisasmLines(filename, offset, size, arch, inplace, arch_flags=""): stdout=subprocess.PIPE, stderr=subprocess.STDOUT) out, err = process.communicate() - lines = out.split("\n") + lines = out.decode('utf-8').split("\n") header_line = 0 for i, line in enumerate(lines): if _DISASM_HEADER_RE.match(line): diff --git a/deps/v8/tools/dumpcpp.mjs b/deps/v8/tools/dumpcpp.mjs index 9459deda157fe9..d271c8c25aeb74 100644 --- a/deps/v8/tools/dumpcpp.mjs +++ b/deps/v8/tools/dumpcpp.mjs @@ -12,12 +12,13 @@ export { export class CppProcessor extends LogReader { constructor(cppEntriesProvider, timedRange, pairwiseTimedRange) { - super({}, timedRange, pairwiseTimedRange); - this.dispatchTable_ = { + super(timedRange, pairwiseTimedRange); + this.setDispatchTable({ + __proto__: null, 'shared-library': { parsers: [parseString, parseInt, parseInt, parseInt], processor: this.processSharedLibrary } - }; + }); this.cppEntriesProvider_ = cppEntriesProvider; this.codeMap_ = new CodeMap(); this.lastLogFileName_ = null; diff --git a/deps/v8/tools/gcmole/BUILD.gn b/deps/v8/tools/gcmole/BUILD.gn index 558766487d17d2..9354f24ff8a971 100644 --- a/deps/v8/tools/gcmole/BUILD.gn +++ b/deps/v8/tools/gcmole/BUILD.gn @@ -4,16 +4,18 @@ import("../../gni/v8.gni") -group("v8_run_gcmole") { +group("v8_gcmole_files") { testonly = true - + data_deps = [ + "../../:v8_dump_build_config", + "../../:v8_generated_cc_files", + ] data = [ - "GCMOLE.gn", "gcmole.py", "gcmole-test.cc", "gcmole-tools/", "run-gcmole.py", - "suspects.whitelist", + "suspects.allowlist", "ignored_files", "test-expectations.txt", @@ -36,8 +38,6 @@ group("v8_run_gcmole") { "$target_gen_dir/../../torque-generated/", ] - deps = [ "../../:run_torque" ] - if (v8_gcmole) { # This assumes gcmole tools have been fetched by a hook # into v8/tools/gcmole/gcmole_tools. diff --git a/deps/v8/tools/gcmole/GCMOLE.gn b/deps/v8/tools/gcmole/GCMOLE.gn deleted file mode 100644 index 62da0a084ba3c3..00000000000000 --- a/deps/v8/tools/gcmole/GCMOLE.gn +++ /dev/null @@ -1,6 +0,0 @@ -action("gcmole") { - sources = [ - ### gcmole(all) ### - "tools/gcmole/gcmole-test.cc", - ] -} diff --git a/deps/v8/tools/gcmole/Makefile b/deps/v8/tools/gcmole/Makefile index e1bde684a6608f..fe295341dab6c1 100644 --- a/deps/v8/tools/gcmole/Makefile +++ b/deps/v8/tools/gcmole/Makefile @@ -32,11 +32,18 @@ LLVM_BUILD_INCLUDE:=$(BUILD_ROOT)/include CLANG_SRC_INCLUDE:=$(CLANG_SRC_ROOT)/include CLANG_BUILD_INCLUDE:=$(BUILD_ROOT)/tools/clang/include +CXXFLAGS = -O3 -g3 +all: libgcmole.so +Release: libgcmole.so + +Debug: CXXFLAGS = -O1 -DDEBUG -g +Debug: libgcmole.so + libgcmole.so: gcmole.cc $(CXX) -I$(LLVM_BUILD_INCLUDE) -I$(LLVM_SRC_INCLUDE) \ - -I$(CLANG_BUILD_INCLUDE) -I$(CLANG_SRC_INCLUDE) -I. -D_DEBUG \ + -I$(CLANG_BUILD_INCLUDE) -I$(CLANG_SRC_INCLUDE) -I. ${CXXFLAGS} \ -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS \ - -D__STDC_LIMIT_MACROS -O3 -fomit-frame-pointer -fno-exceptions \ + -D__STDC_LIMIT_MACROS -fomit-frame-pointer -fno-exceptions \ -fno-rtti -fPIC -Woverloaded-virtual -Wcast-qual -fno-strict-aliasing \ -pedantic -Wno-long-long -Wall -W -Wno-unused-parameter \ -Wwrite-strings -static-libstdc++ -std=c++0x -shared -o libgcmole.so \ diff --git a/deps/v8/tools/gcmole/README b/deps/v8/tools/gcmole/README index 1d2acd3b1a0010..15828fa435381b 100644 --- a/deps/v8/tools/gcmole/README +++ b/deps/v8/tools/gcmole/README @@ -109,7 +109,7 @@ script "bootstrap.sh" mentioned above). TROUBLESHOOTING --------------------------------------------------------------- -gcmole is tighly coupled with the AST structure that Clang produces. Therefore +gcmole is tightly coupled with the AST structure that Clang produces. Therefore when upgrading to a newer Clang version, it might start producing bogus output or completely stop outputting warnings. In such occasion, one might start the debugging process by checking weather a new AST node type is introduced which diff --git a/deps/v8/tools/gcmole/bootstrap.sh b/deps/v8/tools/gcmole/bootstrap.sh index e04ef5826c6b40..f47ba6d2139deb 100755 --- a/deps/v8/tools/gcmole/bootstrap.sh +++ b/deps/v8/tools/gcmole/bootstrap.sh @@ -35,6 +35,8 @@ LLVM_RELEASE=9.0.1 +BUILD_TYPE="Release" +# BUILD_TYPE="Debug" THIS_DIR="$(readlink -f "$(dirname "${0}")")" LLVM_PROJECT_DIR="${THIS_DIR}/bootstrap/llvm" BUILD_DIR="${THIS_DIR}/bootstrap/build" @@ -99,29 +101,35 @@ if [ ! -e "${BUILD_DIR}" ]; then fi cd "${BUILD_DIR}" cmake -GNinja -DCMAKE_CXX_FLAGS="-static-libstdc++" -DLLVM_ENABLE_TERMINFO=OFF \ - -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS=clang \ + -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DLLVM_ENABLE_PROJECTS=clang \ -DLLVM_ENABLE_Z3_SOLVER=OFF "${LLVM_PROJECT_DIR}/llvm" -MACOSX_DEPLOYMENT_TARGET=10.5 ninja -j"${NUM_JOBS}" - -# Strip the clang binary. -STRIP_FLAGS= -if [ "${OS}" = "Darwin" ]; then - # See http://crbug.com/256342 - STRIP_FLAGS=-x +MACOSX_DEPLOYMENT_TARGET=10.5 ninja -j"${NUM_JOBS}" clang + +if [[ "${BUILD_TYPE}" = "Release" ]]; then + # Strip the clang binary. + STRIP_FLAGS= + if [ "${OS}" = "Darwin" ]; then + # See http://crbug.com/256342 + STRIP_FLAGS=-x + fi + strip ${STRIP_FLAGS} bin/clang fi -strip ${STRIP_FLAGS} bin/clang cd - # Build libgcmole.so make -C "${THIS_DIR}" clean make -C "${THIS_DIR}" LLVM_SRC_ROOT="${LLVM_PROJECT_DIR}/llvm" \ CLANG_SRC_ROOT="${LLVM_PROJECT_DIR}/clang" \ - BUILD_ROOT="${BUILD_DIR}" libgcmole.so + BUILD_ROOT="${BUILD_DIR}" $BUILD_TYPE set +x -echo -echo You can now run gcmole using this command: -echo -echo CLANG_BIN=\"tools/gcmole/gcmole-tools/bin\" python tools/gcmole/gcmole.py +echo '#########################################################################' +echo 'Congratulations you compiled clang and libgcmole.so' +echo +echo '# You can now run gcmole:' +echo 'tools/gcmole/gcmole.py \' +echo ' --clang-bin-dir="tools/gcmole/bootstrap/build/bin" \' +echo ' --clang-plugins-dir="tools/gcmole" \' +echo ' --v8-target-cpu=$CPU' echo diff --git a/deps/v8/tools/gcmole/gcmole-tools.tar.gz.sha1 b/deps/v8/tools/gcmole/gcmole-tools.tar.gz.sha1 index 6de7b957f367c0..70900be895cc2b 100644 --- a/deps/v8/tools/gcmole/gcmole-tools.tar.gz.sha1 +++ b/deps/v8/tools/gcmole/gcmole-tools.tar.gz.sha1 @@ -1 +1 @@ -b9c0f67013d70cabd7a53ca059304f8f51bd937b \ No newline at end of file +270aaed40a5d903a4795a2b59c33991b2885374c \ No newline at end of file diff --git a/deps/v8/tools/gcmole/gcmole.cc b/deps/v8/tools/gcmole/gcmole.cc index 3dc4baa72280e0..74208b27eec46c 100644 --- a/deps/v8/tools/gcmole/gcmole.cc +++ b/deps/v8/tools/gcmole/gcmole.cc @@ -48,6 +48,8 @@ namespace { bool g_tracing_enabled = false; bool g_dead_vars_analysis = false; +bool g_verbose = false; +bool g_print_gc_call_chain = false; #define TRACE(str) \ do { \ @@ -80,16 +82,13 @@ typedef std::map CalleesMap; static bool GetMangledName(clang::MangleContext* ctx, const clang::NamedDecl* decl, MangledName* result) { - if (!llvm::isa(decl) && - !llvm::isa(decl)) { - llvm::SmallVector output; - llvm::raw_svector_ostream out(output); - ctx->mangleName(decl, out); - *result = out.str().str(); - return true; - } - - return false; + if (llvm::isa(decl)) return false; + if (llvm::isa(decl)) return false; + llvm::SmallVector output; + llvm::raw_svector_ostream out(output); + ctx->mangleName(decl, out); + *result = out.str().str(); + return true; } @@ -217,8 +216,7 @@ struct Resolver { class CalleesPrinter : public clang::RecursiveASTVisitor { public: - explicit CalleesPrinter(clang::MangleContext* ctx) : ctx_(ctx) { - } + explicit CalleesPrinter(clang::MangleContext* ctx) : ctx_(ctx) {} virtual bool VisitCallExpr(clang::CallExpr* expr) { const clang::FunctionDecl* callee = expr->getDirectCallee(); @@ -236,17 +234,17 @@ class CalleesPrinter : public clang::RecursiveASTVisitor { } void AnalyzeFunction(const clang::FunctionDecl* f) { + if (!InV8Namespace(f)) return; MangledName name; - if (InV8Namespace(f) && GetMangledName(ctx_, f, &name)) { - const std::string& function = f->getNameAsString(); - AddCallee(name, function); - - const clang::FunctionDecl* body = NULL; - if (f->hasBody(body) && !Analyzed(name)) { - EnterScope(name); - TraverseStmt(body->getBody()); - LeaveScope(); - } + if (!GetMangledName(ctx_, f, &name)) return; + const std::string& function = f->getNameAsString(); + AddCallee(name, function); + + const clang::FunctionDecl* body = NULL; + if (f->hasBody(body) && !Analyzed(name)) { + EnterScope(name); + TraverseStmt(body->getBody()); + LeaveScope(); } } @@ -303,17 +301,18 @@ class FunctionDeclarationFinder : public clang::ASTConsumer, public clang::RecursiveASTVisitor { public: - explicit FunctionDeclarationFinder(clang::DiagnosticsEngine& d, - clang::SourceManager& sm, - const std::vector& args) - : d_(d), sm_(sm) {} + explicit FunctionDeclarationFinder( + clang::DiagnosticsEngine& diagnostics_engine, + clang::SourceManager& source_manager, + const std::vector& args) + : diagnostics_engine_(diagnostics_engine), + source_manager_(source_manager) {} virtual void HandleTranslationUnit(clang::ASTContext &ctx) { - mangle_context_ = clang::ItaniumMangleContext::create(ctx, d_); + mangle_context_ = + clang::ItaniumMangleContext::create(ctx, diagnostics_engine_); callees_printer_ = new CalleesPrinter(mangle_context_); - TraverseDecl(ctx.getTranslationUnitDecl()); - callees_printer_->PrintCallGraph(); } @@ -323,8 +322,8 @@ class FunctionDeclarationFinder } private: - clang::DiagnosticsEngine& d_; - clang::SourceManager& sm_; + clang::DiagnosticsEngine& diagnostics_engine_; + clang::SourceManager& source_manager_; clang::MangleContext* mangle_context_; CalleesPrinter* callees_printer_; @@ -333,8 +332,39 @@ class FunctionDeclarationFinder static bool gc_suspects_loaded = false; static CalleesSet gc_suspects; static CalleesSet gc_functions; -static bool whitelist_loaded = false; -static CalleesSet suspects_whitelist; + +static bool allowlist_loaded = false; +static CalleesSet suspects_allowlist; + +static bool gc_causes_loaded = false; +static std::map> gc_causes; + +static void LoadGCCauses() { + if (gc_causes_loaded) return; + std::ifstream fin("gccauses"); + std::string mangled, function; + while (!fin.eof()) { + std::getline(fin, mangled, ','); + std::getline(fin, function); + if (mangled.empty()) break; + std::string parent = mangled; + // start,nested + std::getline(fin, mangled, ','); + assert(mangled.compare("start") == 0); + std::getline(fin, function); + assert(function.compare("nested") == 0); + while (true) { + std::getline(fin, mangled, ','); + std::getline(fin, function); + if (mangled.compare("end") == 0) { + assert(function.compare("nested") == 0); + break; + } + gc_causes[parent].push_back(mangled); + } + } + gc_causes_loaded = true; +} static void LoadGCSuspects() { if (gc_suspects_loaded) return; @@ -352,55 +382,48 @@ static void LoadGCSuspects() { gc_suspects_loaded = true; } -static void LoadSuspectsWhitelist() { - if (whitelist_loaded) return; +static void LoadSuspectsAllowList() { + if (allowlist_loaded) return; - std::ifstream fin("tools/gcmole/suspects.whitelist"); + // TODO(cbruni): clean up once fully migrated + std::ifstream fin("tools/gcmole/suspects.allowlist"); std::string s; - while (fin >> s) suspects_whitelist.insert(s); + while (fin >> s) suspects_allowlist.insert(s); - whitelist_loaded = true; + allowlist_loaded = true; } // Looks for exact match of the mangled name. -static bool KnownToCauseGC(clang::MangleContext* ctx, - const clang::FunctionDecl* decl) { +static bool IsKnownToCauseGC(clang::MangleContext* ctx, + const clang::FunctionDecl* decl) { LoadGCSuspects(); - if (!InV8Namespace(decl)) return false; - - if (suspects_whitelist.find(decl->getNameAsString()) != - suspects_whitelist.end()) { + if (suspects_allowlist.find(decl->getNameAsString()) != + suspects_allowlist.end()) { return false; } - MangledName name; if (GetMangledName(ctx, decl, &name)) { return gc_suspects.find(name) != gc_suspects.end(); } - return false; } // Looks for partial match of only the function name. -static bool SuspectedToCauseGC(clang::MangleContext* ctx, - const clang::FunctionDecl* decl) { +static bool IsSuspectedToCauseGC(clang::MangleContext* ctx, + const clang::FunctionDecl* decl) { LoadGCSuspects(); - if (!InV8Namespace(decl)) return false; - - LoadSuspectsWhitelist(); - if (suspects_whitelist.find(decl->getNameAsString()) != - suspects_whitelist.end()) { + LoadSuspectsAllowList(); + if (suspects_allowlist.find(decl->getNameAsString()) != + suspects_allowlist.end()) { return false; } - if (gc_functions.find(decl->getNameAsString()) != gc_functions.end()) { TRACE_LLVM_DECL("Suspected by ", decl); return true; } - return false; } @@ -449,10 +472,9 @@ class ExprEffect { intptr_t effect_; }; - -const std::string BAD_EXPR_MSG("Possible problem with evaluation order."); -const std::string DEAD_VAR_MSG("Possibly dead variable."); - +const std::string BAD_EXPR_MSG( + "Possible problem with evaluation order with interleaved GCs."); +const std::string DEAD_VAR_MSG("Possibly stale variable due to GCs."); class Environment { public: @@ -612,22 +634,16 @@ class CallProps { ExprEffect ComputeCumulativeEffect(bool result_is_raw) { ExprEffect out = ExprEffect::NoneWithEnv(env_); - if (gc_.any()) { - out.setGC(); - } + if (gc_.any()) out.setGC(); if (raw_use_.any()) out.setRawUse(); if (result_is_raw) out.setRawDef(); return out; } bool IsSafe() { - if (!gc_.any()) { - return true; - } + if (!gc_.any()) return true; std::bitset raw = (raw_def_ | raw_use_); - if (!raw.any()) { - return true; - } + if (!raw.any()) return true; bool result = gc_.count() == 1 && !((raw ^ gc_).any()); return result; } @@ -950,13 +966,10 @@ class FunctionAnalyzer { ExprEffect Parallel(clang::Expr* parent, int n, clang::Expr** exprs, const Environment& env) { CallProps props; - for (int i = 0; i < n; ++i) { props.SetEffect(i, VisitExpr(exprs[i], env)); } - if (!props.IsSafe()) ReportUnsafe(parent, BAD_EXPR_MSG); - return props.ComputeCumulativeEffect( RepresentsRawPointerType(parent->getType())); } @@ -984,27 +997,24 @@ class FunctionAnalyzer { const clang::QualType& var_type, const std::string& var_name, const Environment& env) { - if (RepresentsRawPointerType(var_type)) { - // We currently care only about our internal pointer types and not about - // raw C++ pointers, because normally special care is taken when storing - // raw pointers to the managed heap. Furthermore, checking for raw - // pointers produces too many false positives in the dead variable - // analysis. - if (IsInternalPointerType(var_type) && !env.IsAlive(var_name) && - !HasActiveGuard() && g_dead_vars_analysis) { - ReportUnsafe(parent, DEAD_VAR_MSG); - } - return ExprEffect::RawUse(); - } - return ExprEffect::None(); + if (!g_dead_vars_analysis) return ExprEffect::None(); + if (!RepresentsRawPointerType(var_type)) return ExprEffect::None(); + // We currently care only about our internal pointer types and not about + // raw C++ pointers, because normally special care is taken when storing + // raw pointers to the managed heap. Furthermore, checking for raw + // pointers produces too many false positives in the dead variable + // analysis. + if (!IsInternalPointerType(var_type)) return ExprEffect::None(); + if (env.IsAlive(var_name)) return ExprEffect::None(); + if (HasActiveGuard()) return ExprEffect::None(); + ReportUnsafe(parent, DEAD_VAR_MSG); + return ExprEffect::RawUse(); } ExprEffect Use(const clang::Expr* parent, const clang::ValueDecl* var, const Environment& env) { - if (IsExternalVMState(var)) { - return ExprEffect::GC(); - } + if (IsExternalVMState(var)) return ExprEffect::GC(); return Use(parent, var->getType(), var->getNameAsString(), env); } @@ -1062,43 +1072,40 @@ class FunctionAnalyzer { RepresentsRawPointerType(call->getType())); clang::FunctionDecl* callee = call->getDirectCallee(); - if (callee != NULL) { - if (KnownToCauseGC(ctx_, callee)) { + if (callee == NULL) return out; + + if (IsKnownToCauseGC(ctx_, callee)) { + out.setGC(); + scopes_.back().SetGCCauseLocation( + clang::FullSourceLoc(call->getExprLoc(), sm_), callee); + } + + // Support for virtual methods that might be GC suspects. + if (memcall == NULL) return out; + clang::CXXMethodDecl* method = + llvm::dyn_cast_or_null(callee); + if (method == NULL) return out; + if (!method->isVirtual()) return out; + + clang::CXXMethodDecl* target = method->getDevirtualizedMethod( + memcall->getImplicitObjectArgument(), false); + if (target != NULL) { + if (IsKnownToCauseGC(ctx_, target)) { out.setGC(); scopes_.back().SetGCCauseLocation( - clang::FullSourceLoc(call->getExprLoc(), sm_)); + clang::FullSourceLoc(call->getExprLoc(), sm_), target); } - - // Support for virtual methods that might be GC suspects. - clang::CXXMethodDecl* method = - llvm::dyn_cast_or_null(callee); - if (method != NULL && method->isVirtual()) { - clang::CXXMemberCallExpr* memcall = - llvm::dyn_cast_or_null(call); - if (memcall != NULL) { - clang::CXXMethodDecl* target = method->getDevirtualizedMethod( - memcall->getImplicitObjectArgument(), false); - if (target != NULL) { - if (KnownToCauseGC(ctx_, target)) { - out.setGC(); - scopes_.back().SetGCCauseLocation( - clang::FullSourceLoc(call->getExprLoc(), sm_)); - } - } else { - // According to the documentation, {getDevirtualizedMethod} might - // return NULL, in which case we still want to use the partial - // match of the {method}'s name against the GC suspects in order - // to increase coverage. - if (SuspectedToCauseGC(ctx_, method)) { - out.setGC(); - scopes_.back().SetGCCauseLocation( - clang::FullSourceLoc(call->getExprLoc(), sm_)); - } - } - } + } else { + // According to the documentation, {getDevirtualizedMethod} might + // return NULL, in which case we still want to use the partial + // match of the {method}'s name against the GC suspects in order + // to increase coverage. + if (IsSuspectedToCauseGC(ctx_, method)) { + out.setGC(); + scopes_.back().SetGCCauseLocation( + clang::FullSourceLoc(call->getExprLoc(), sm_), method); } } - return out; } @@ -1185,11 +1192,9 @@ class FunctionAnalyzer { } bool changed() { - if (changed_) { - changed_ = false; - return true; - } - return false; + if (!changed_) return false; + changed_ = false; + return true; } const Environment& in() { @@ -1455,7 +1460,7 @@ class FunctionAnalyzer { } bool HasActiveGuard() { - for (auto s : scopes_) { + for (const auto s : scopes_) { if (s.IsBeforeGCCause()) return true; } return false; @@ -1466,6 +1471,36 @@ class FunctionAnalyzer { d_.Report(clang::FullSourceLoc(expr->getExprLoc(), sm_), d_.getCustomDiagID(clang::DiagnosticsEngine::Warning, "%0")) << msg; + if (scopes_.empty()) return; + GCScope scope = scopes_[0]; + if (!scope.gccause_location.isValid()) return; + d_.Report(scope.gccause_location, + d_.getCustomDiagID(clang::DiagnosticsEngine::Note, + "Call might cause unexpected GC.")); + clang::FunctionDecl* gccause_decl = scope.gccause_decl; + d_.Report( + clang::FullSourceLoc(gccause_decl->getBeginLoc(), sm_), + d_.getCustomDiagID(clang::DiagnosticsEngine::Note, "GC call here.")); + + if (!g_print_gc_call_chain) return; + // TODO(cbruni, v8::10009): print call-chain to gc with proper source + // positions. + LoadGCCauses(); + MangledName name; + if (!GetMangledName(ctx_, gccause_decl, &name)) return; + std::cout << "Potential GC call chain:\n"; + std::set stack; + while (true) { + if (!stack.insert(name).second) break; + std::cout << "\t" << name << "\n"; + auto next = gc_causes.find(name); + if (next == gc_causes.end()) break; + std::vector calls = next->second; + for (MangledName call : calls) { + name = call; + if (stack.find(call) != stack.end()) break; + } + } } @@ -1484,10 +1519,11 @@ class FunctionAnalyzer { struct GCScope { clang::FullSourceLoc guard_location; clang::FullSourceLoc gccause_location; + clang::FunctionDecl* gccause_decl; // We're only interested in guards that are declared before any further GC // causing calls (see TestGuardedDeadVarAnalysisMidFunction for example). - bool IsBeforeGCCause() { + bool IsBeforeGCCause() const { if (!guard_location.isValid()) return false; if (!gccause_location.isValid()) return true; return guard_location.isBeforeInTranslationUnitThan(gccause_location); @@ -1495,9 +1531,11 @@ class FunctionAnalyzer { // After we set the first GC cause in the scope, we don't need the later // ones. - void SetGCCauseLocation(clang::FullSourceLoc gccause_location_) { + void SetGCCauseLocation(clang::FullSourceLoc gccause_location_, + clang::FunctionDecl* decl) { if (gccause_location.isValid()) return; gccause_location = gccause_location_; + gccause_decl = decl; } }; std::vector scopes_; @@ -1513,9 +1551,8 @@ class ProblemsFinder : public clang::ASTConsumer, if (args[i] == "--dead-vars") { g_dead_vars_analysis = true; } - if (args[i] == "--verbose") { - g_tracing_enabled = true; - } + if (args[i] == "--verbose-trace") g_tracing_enabled = true; + if (args[i] == "--verbose") g_verbose = true; } } @@ -1571,7 +1608,7 @@ class ProblemsFinder : public clang::ASTConsumer, clang::ItaniumMangleContext::create(ctx, d_), object_decl, maybe_object_decl, smi_decl, no_gc_mole_decl, d_, sm_); TraverseDecl(ctx.getTranslationUnitDecl()); - } else { + } else if (g_verbose) { if (object_decl == NULL) { llvm::errs() << "Failed to resolve v8::internal::Object\n"; } @@ -1609,7 +1646,6 @@ class ProblemsFinder : public clang::ASTConsumer, FunctionAnalyzer* function_analyzer_; }; - template class Action : public clang::PluginASTAction { protected: diff --git a/deps/v8/tools/gcmole/gcmole.py b/deps/v8/tools/gcmole/gcmole.py old mode 100644 new mode 100755 index 3df0788adeff08..df1d33c3cfadab --- a/deps/v8/tools/gcmole/gcmole.py +++ b/deps/v8/tools/gcmole/gcmole.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright 2020 the V8 project authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -6,58 +6,78 @@ # This is main driver for gcmole tool. See README for more details. # Usage: CLANG_BIN=clang-bin-dir python tools/gcmole/gcmole.py [arm|arm64|ia32|x64] -# for py2/py3 compatibility -from __future__ import print_function +from multiprocessing import cpu_count +from pathlib import Path import collections import difflib -from multiprocessing import cpu_count +import json +import optparse import os import re import subprocess import sys import threading -if sys.version_info.major > 2: - import queue -else: - import Queue as queue +import queue + + +ArchCfg = collections.namedtuple( + "ArchCfg", ["name", "cpu", "triple", "arch_define", "arch_options"]) -ArchCfg = collections.namedtuple("ArchCfg", - ["triple", "arch_define", "arch_options"]) +# TODO(cbruni): use gn desc by default for platform-specific settings +OPTIONS_64BIT = [ + "-DV8_COMPRESS_POINTERS", + "-DV8_COMPRESS_POINTERS_IN_SHARED_CAGE", + "-DV8_EXTERNAL_CODE_SPACE", + "-DV8_SHORT_BUILTIN_CALLS", + "-DV8_SHARED_RO_HEAP", +] ARCHITECTURES = { "ia32": ArchCfg( + name="ia32", + cpu="x86", triple="i586-unknown-linux", arch_define="V8_TARGET_ARCH_IA32", arch_options=["-m32"], ), "arm": ArchCfg( + name="arm", + cpu="arm", triple="i586-unknown-linux", arch_define="V8_TARGET_ARCH_ARM", arch_options=["-m32"], ), + # TODO(cbruni): Use detailed settings: + # arch_options = OPTIONS_64BIT + [ "-DV8_WIN64_UNWINDING_INFO" ] "x64": ArchCfg( + name="x64", + cpu="x64", triple="x86_64-unknown-linux", arch_define="V8_TARGET_ARCH_X64", arch_options=[]), "arm64": ArchCfg( + name="arm64", + cpu="arm64", triple="x86_64-unknown-linux", arch_define="V8_TARGET_ARCH_ARM64", arch_options=[], ), } +ARCHITECTURES['x86'] = ARCHITECTURES['ia32'] -def log(format, *args): - print(format.format(*args)) +def log(format, *args, **kwargs): + mark = ("#", "=", "-", ".")[kwargs.get("level", 0)] + print(mark * 2, str(format).format(*list(map(str, args)))) -def fatal(format, *args): - log(format, *args) +def fatal(format): + log(format) sys.exit(1) @@ -65,26 +85,27 @@ def fatal(format, *args): # Clang invocation -def MakeClangCommandLine(plugin, plugin_args, arch_cfg, clang_bin_dir, - clang_plugins_dir): +def make_clang_command_line(plugin, plugin_args, options): + arch_cfg = ARCHITECTURES[options.v8_target_cpu] prefixed_plugin_args = [] if plugin_args: for arg in plugin_args: prefixed_plugin_args += [ "-Xclang", - "-plugin-arg-{}".format(plugin), + "-plugin-arg-" + plugin, "-Xclang", arg, ] - + log("Using generated files in {}", options.v8_build_dir / 'gen') + icu_src_dir = options.v8_root_dir / 'third_party/icu/source' return ([ - os.path.join(clang_bin_dir, "clang++"), - "-std=c++14", + options.clang_bin_dir / "clang++", + "-std=c++17", "-c", "-Xclang", "-load", "-Xclang", - os.path.join(clang_plugins_dir, "libgcmole.so"), + options.clang_plugins_dir / "libgcmole.so", "-Xclang", "-plugin", "-Xclang", @@ -95,41 +116,44 @@ def MakeClangCommandLine(plugin, plugin_args, arch_cfg, clang_bin_dir, "-Xclang", arch_cfg.triple, "-fno-exceptions", + "-Wno-everything", "-D", arch_cfg.arch_define, "-DENABLE_DEBUGGER_SUPPORT", - "-DV8_INTL_SUPPORT", "-DV8_ENABLE_WEBASSEMBLY", - "-I./", - "-Iinclude/", - "-Iout/build/gen", - "-Ithird_party/icu/source/common", - "-Ithird_party/icu/source/i18n", + "-DV8_GC_MOLE", + "-DV8_INTL_SUPPORT", + "-I{}".format(options.v8_root_dir), + "-I{}".format(options.v8_root_dir / 'include'), + "-I{}".format(options.v8_build_dir / 'gen'), + "-I{}".format(icu_src_dir / 'common'), + "-I{}".format(icu_src_dir / 'i18n'), ] + arch_cfg.arch_options) -def InvokeClangPluginForFile(filename, cmd_line, verbose): +def invoke_clang_plugin_for_file(filename, cmd_line, verbose): + args = cmd_line + [filename] + args = list(map(str, args)) if verbose: - print("popen ", " ".join(cmd_line + [filename])) - p = subprocess.Popen( - cmd_line + [filename], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + print("popen ", " ".join(args)) + p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate() - return p.returncode, stdout, stderr + return p.returncode, stdout.decode("utf-8"), stderr.decode("utf-8") -def InvokeClangPluginForFilesInQueue(i, input_queue, output_queue, cancel_event, - cmd_line, verbose): +def invoke_clang_plugin_for_files_in_queue(i, input_queue, output_queue, + cancel_event, cmd_line, verbose): success = False try: while not cancel_event.is_set(): filename = input_queue.get_nowait() - ret, stdout, stderr = InvokeClangPluginForFile(filename, cmd_line, - verbose) - output_queue.put_nowait((filename, ret, stdout.decode('utf-8'), stderr.decode('utf-8'))) + ret, stdout, stderr = invoke_clang_plugin_for_file( + filename, cmd_line, verbose) + output_queue.put_nowait((filename, ret, stdout, stderr)) if ret != 0: break except KeyboardInterrupt: - log("-- [{}] Interrupting", i) + log("[{}] Interrupting", i, level=1) except queue.Empty: success = True finally: @@ -138,30 +162,21 @@ def InvokeClangPluginForFilesInQueue(i, input_queue, output_queue, cancel_event, output_queue.put_nowait(success) -def InvokeClangPluginForEachFile( - filenames, - plugin, - plugin_args, - arch_cfg, - flags, - clang_bin_dir, - clang_plugins_dir, -): - cmd_line = MakeClangCommandLine(plugin, plugin_args, arch_cfg, clang_bin_dir, - clang_plugins_dir) - verbose = flags["verbose"] - if flags["sequential"]: - log("** Sequential execution.") +def invoke_clang_plugin_for_each_file(filenames, plugin, plugin_args, options): + cmd_line = make_clang_command_line(plugin, plugin_args, options) + verbose = options.verbose + if options.sequential: + log("Sequential execution.") for filename in filenames: - log("-- {}", filename) - returncode, stdout, stderr = InvokeClangPluginForFile( + log(filename, level=1) + returncode, stdout, stderr = invoke_clang_plugin_for_file( filename, cmd_line, verbose) if returncode != 0: sys.stderr.write(stderr) sys.exit(returncode) yield filename, stdout, stderr else: - log("** Parallel execution.") + log("Parallel execution.") cpus = cpu_count() input_queue = queue.Queue() output_queue = queue.Queue() @@ -175,7 +190,7 @@ def InvokeClangPluginForEachFile( for i in range(min(len(filenames), cpus)): threads.append( threading.Thread( - target=InvokeClangPluginForFilesInQueue, + target=invoke_clang_plugin_for_files_in_queue, args=(i, input_queue, output_queue, cancel_event, cmd_line, verbose))) @@ -192,7 +207,7 @@ def InvokeClangPluginForEachFile( else: break filename, returncode, stdout, stderr = output - log("-- {}", filename) + log(filename, level=2) if returncode != 0: sys.stderr.write(stderr) sys.exit(returncode) @@ -207,31 +222,30 @@ def InvokeClangPluginForEachFile( # ----------------------------------------------------------------------------- -def ParseGNFile(for_test): - result = {} +def parse_gn_file(options, for_test): if for_test: - gn_files = [("tools/gcmole/GCMOLE.gn", re.compile('"([^"]*?\.cc)"'), "")] - else: - gn_files = [ - ("BUILD.gn", re.compile('"([^"]*?\.cc)"'), ""), - ("test/cctest/BUILD.gn", re.compile('"(test-[^"]*?\.cc)"'), - "test/cctest/"), - ] - + return {"all": [options.v8_root_dir / "tools/gcmole/gcmole-test.cc"]} + result = {} + gn_files = [ + ("BUILD.gn", re.compile('"([^"]*?\.cc)"'), ""), + ("test/cctest/BUILD.gn", re.compile('"(test-[^"]*?\.cc)"'), + Path("test/cctest/")), + ] for filename, pattern, prefix in gn_files: - with open(filename) as gn_file: + path = options.v8_root_dir / filename + with open(path) as gn_file: gn = gn_file.read() for condition, sources in re.findall("### gcmole\((.*?)\) ###(.*?)\]", gn, re.MULTILINE | re.DOTALL): if condition not in result: result[condition] = [] for file in pattern.findall(sources): - result[condition].append(prefix + file) + result[condition].append(options.v8_root_dir / prefix / file) return result -def EvaluateCondition(cond, props): +def evaluate_condition(cond, props): if cond == "all": return True @@ -245,34 +259,19 @@ def EvaluateCondition(cond, props): return props[p] == v -def BuildFileList(sources, props): - ret = [] - for condition, files in sources.items(): - if EvaluateCondition(condition, props): - ret += files - return ret - - -gn_sources = ParseGNFile(for_test=False) -gn_test_sources = ParseGNFile(for_test=True) - - -def FilesForArch(arch): - return BuildFileList(gn_sources, { - "os": "linux", - "arch": arch, - "mode": "debug", - "simulator": "" - }) - - -def FilesForTest(arch): - return BuildFileList(gn_test_sources, { +def build_file_list(options, for_test): + sources = parse_gn_file(options, for_test) + props = { "os": "linux", - "arch": arch, + "arch": options.v8_target_cpu, "mode": "debug", "simulator": "" - }) + } + ret = [] + for condition, files in list(sources.items()): + if evaluate_condition(condition, props): + ret += files + return ret # ----------------------------------------------------------------------------- @@ -308,19 +307,19 @@ def FilesForTest(arch): GC_PATTERN = ",.*Collect.*Garbage" SAFEPOINT_PATTERN = ",SafepointSlowPath" -ALLOWLIST_PATTERN = "|".join("(?:%s)" % p for p in ALLOWLIST) +ALLOWLIST_PATTERN = "|".join("(?:{})".format(p) for p in ALLOWLIST) -def MergeRegexp(pattern_dict): - return re.compile("|".join( - "(?P<%s>%s)" % (key, value) for (key, value) in pattern_dict.items())) +def merge_regexp(pattern_dict): + return re.compile("|".join("(?P<{}>{})".format(key, value) + for (key, value) in list(pattern_dict.items()))) -IS_SPECIAL_WITHOUT_ALLOW_LIST = MergeRegexp({ +IS_SPECIAL_WITHOUT_ALLOW_LIST = merge_regexp({ "gc": GC_PATTERN, "safepoint": SAFEPOINT_PATTERN }) -IS_SPECIAL_WITH_ALLOW_LIST = MergeRegexp({ +IS_SPECIAL_WITH_ALLOW_LIST = merge_regexp({ "gc": GC_PATTERN, "safepoint": SAFEPOINT_PATTERN, "allow": ALLOWLIST_PATTERN @@ -329,133 +328,139 @@ def MergeRegexp(pattern_dict): class GCSuspectsCollector: - def __init__(self, flags): + def __init__(self, options): self.gc = {} - self.gc_caused = collections.defaultdict(lambda: []) + self.gc_caused = collections.defaultdict(lambda: set()) self.funcs = {} self.current_caller = None - self.allowlist = flags["allowlist"] + self.allowlist = options.allowlist self.is_special = IS_SPECIAL_WITH_ALLOW_LIST if self.allowlist else IS_SPECIAL_WITHOUT_ALLOW_LIST - def AddCause(self, name, cause): - self.gc_caused[name].append(cause) + def add_cause(self, name, cause): + self.gc_caused[name].add(cause) - def Parse(self, lines): + def parse(self, lines): for funcname in lines: if not funcname: continue if funcname[0] != "\t": - self.Resolve(funcname) + self.resolve(funcname) self.current_caller = funcname else: name = funcname[1:] - callers_for_name = self.Resolve(name) + callers_for_name = self.resolve(name) callers_for_name.add(self.current_caller) - def Resolve(self, name): + def resolve(self, name): if name not in self.funcs: self.funcs[name] = set() m = self.is_special.search(name) if m: if m.group("gc"): self.gc[name] = True - self.AddCause(name, "") + self.add_cause(name, "") elif m.group("safepoint"): self.gc[name] = True - self.AddCause(name, "") + self.add_cause(name, "") elif m.group("allow"): self.gc[name] = False return self.funcs[name] - def Propagate(self): - log("** Propagating GC information") + def propagate(self): + log("Propagating GC information") def mark(funcname, callers): for caller in callers: if caller not in self.gc: self.gc[caller] = True mark(caller, self.funcs[caller]) + self.add_cause(caller, funcname) - self.AddCause(caller, funcname) - - for funcname, callers in self.funcs.items(): + for funcname, callers in list(self.funcs.items()): if self.gc.get(funcname, False): mark(funcname, callers) -def GenerateGCSuspects(arch, files, arch_cfg, flags, clang_bin_dir, - clang_plugins_dir): +def generate_gc_suspects(files, options): # Reset the global state. - collector = GCSuspectsCollector(flags) - - log("** Building GC Suspects for {}", arch) - for filename, stdout, stderr in InvokeClangPluginForEachFile( - files, "dump-callees", [], arch_cfg, flags, clang_bin_dir, - clang_plugins_dir): - collector.Parse(stdout.splitlines()) - - collector.Propagate() - - with open("gcsuspects", "w") as out: - for name, value in collector.gc.items(): + collector = GCSuspectsCollector(options) + + log("Building GC Suspects for {}", options.v8_target_cpu) + for _, stdout, _ in invoke_clang_plugin_for_each_file(files, "dump-callees", + [], options): + collector.parse(stdout.splitlines()) + collector.propagate() + # TODO(cbruni): remove once gcmole.cc is migrated + write_gcmole_results(collector, options, options.v8_root_dir) + write_gcmole_results(collector, options, options.out_dir) + + +def write_gcmole_results(collector, options, dst): + # gcsuspects contains a list("mangled_full_name,name") of all functions that + # could cause a gc (directly or indirectly). + # + # EXAMPLE + # _ZN2v88internal4Heap16CreateApiObjectsEv,CreateApiObjects + # _ZN2v88internal4Heap17CreateInitialMapsEv,CreateInitialMaps + # ... + with open(dst / "gcsuspects", "w") as out: + for name, value in list(collector.gc.items()): if value: out.write(name + "\n") - - with open("gccauses", "w") as out: - out.write("GC = {\n") - for name, causes in collector.gc_caused.items(): - out.write(" '{}': [\n".format(name)) + # gccauses contains a map["mangled_full_name,name"] => list(inner gcsuspects) + # Where the inner gcsuspects are functions directly called in the outer + # function that can cause a gc. The format is encoded for simplified + # deserialization in gcmole.cc. + # + # EXAMPLE: + # _ZN2v88internal4Heap17CreateHeapObjectsEv,CreateHeapObjects + # start,nested + # _ZN2v88internal4Heap16CreateApiObjectsEv,CreateApiObjects + # _ZN2v88internal4Heap17CreateInitialMapsEv,CreateInitialMaps + # ... + # end,nested + # ... + with open(dst / "gccauses", "w") as out: + for name, causes in list(collector.gc_caused.items()): + out.write("{}\n".format(name)) + out.write("start,nested\n") for cause in causes: - out.write(" '{}',\n".format(cause)) - out.write(" ],\n") - out.write("}\n") - - log("** GCSuspects generated for {}", arch) + out.write("{}\n".format(cause)) + out.write("end,nested\n") + log("GCSuspects and gccauses generated for {} in '{}'", options.v8_target_cpu, + dst) # ------------------------------------------------------------------------------ # Analysis -def CheckCorrectnessForArch(arch, for_test, flags, clang_bin_dir, - clang_plugins_dir): - if for_test: - files = FilesForTest(arch) - else: - files = FilesForArch(arch) - arch_cfg = ARCHITECTURES[arch] +def check_correctness_for_arch(options, for_test): + files = build_file_list(options, for_test) - if not flags["reuse_gcsuspects"]: - GenerateGCSuspects(arch, files, arch_cfg, flags, clang_bin_dir, - clang_plugins_dir) + if not options.reuse_gcsuspects: + generate_gc_suspects(files, options) else: - log("** Reusing GCSuspects for {}", arch) + log("Reusing GCSuspects for {}", options.v8_target_cpu) processed_files = 0 errors_found = False output = "" - log( - "** Searching for evaluation order problems{} for {}", - " and dead variables" if flags["dead_vars"] else "", - arch, - ) + log("Searching for evaluation order problems " + + (' and dead variables' if options.dead_vars else '') + "for" + + options.v8_target_cpu) plugin_args = [] - if flags["dead_vars"]: + if options.dead_vars: plugin_args.append("--dead-vars") - if flags["verbose_trace"]: + if options.verbose: plugin_args.append("--verbose") - for filename, stdout, stderr in InvokeClangPluginForEachFile( - files, - "find-problems", - plugin_args, - arch_cfg, - flags, - clang_bin_dir, - clang_plugins_dir, - ): + if options.verbose_trace: + plugin_args.append("--verbose-trace") + for _, _, stderr in invoke_clang_plugin_for_each_file(files, "find-problems", + plugin_args, options): processed_files = processed_files + 1 if not errors_found: errors_found = re.search("^[^:]+:\d+:\d+: (warning|error)", stderr, @@ -465,112 +470,281 @@ def CheckCorrectnessForArch(arch, for_test, flags, clang_bin_dir, else: sys.stdout.write(stderr) - log( - "** Done processing {} files. {}", - processed_files, - "Errors found" if errors_found else "No errors found", - ) + log("Done processing {} files.", processed_files) + log("Errors found" if errors_found else "No errors found") return errors_found, output -def TestRun(flags, clang_bin_dir, clang_plugins_dir): - log("** Test Run") - errors_found, output = CheckCorrectnessForArch("x64", True, flags, - clang_bin_dir, - clang_plugins_dir) +def test_run(options): + if not options.test_run: + return True + log("Test Run") + errors_found, output = check_correctness_for_arch(options, True) if not errors_found: - log("** Test file should produce errors, but none were found. Output:") - log(output) + log("Test file should produce errors, but none were found. Output:") + print(output) return False - filename = "tools/gcmole/test-expectations.txt" - with open(filename) as exp_file: + new_file = options.out_dir / "test-expectations-gen.txt" + with open(new_file, "w") as f: + f.write(output) + log("Wrote test-results: {}", new_file) + + expected_file = options.v8_root_dir / "tools/gcmole/test-expectations.txt" + with open(expected_file) as exp_file: expectations = exp_file.read() if output != expectations: - log("** Output mismatch from running tests. Please run them manually.") - + diff_file = options.out_dir / "test_output.diff" + print("#" * 79) + log("Output mismatch from running tests.") + log("Please run gcmole manually with --test-run --verbose.") + log("Expected: " + expected_file) + log("New: " + new_file) + log("*Diff:* " + diff_file) + print("#" * 79) for line in difflib.unified_diff( expectations.splitlines(), output.splitlines(), - fromfile=filename, - tofile="output", + fromfile=str(new_file), + tofile=str(diff_file), lineterm="", ): - log("{}", line) + print(line) - log("------") - log("--- Full output ---") - log(output) - log("------") + print("#" * 79) + log("Full output") + log("Expected: " + expected_file) + log("Diff: " + diff_file) + log("*New:* " + new_file) + print("#" * 79) + print(output) + print("#" * 79) return False - log("** Tests ran successfully") + log("Tests ran successfully") return True +# ============================================================================= +def relative_parents(path, level=0): + return Path(os.path.relpath(str(path.resolve().parents[level]))) + + def main(args): - DIR = os.path.dirname(args[0]) - - clang_bin_dir = os.getenv("CLANG_BIN") - clang_plugins_dir = os.getenv("CLANG_PLUGINS") - - if not clang_bin_dir or clang_bin_dir == "": - fatal("CLANG_BIN not set") - - if not clang_plugins_dir or clang_plugins_dir == "": - clang_plugins_dir = DIR - - flags = { - #: not build gcsuspects file and reuse previously generated one. - "reuse_gcsuspects": False, - #:n't use parallel python runner. - "sequential": False, - # Print commands to console before executing them. - "verbose": True, - # Perform dead variable analysis. - "dead_vars": True, - # Enable verbose tracing from the plugin itself. - "verbose_trace": False, - # When building gcsuspects allowlist certain functions as if they can be - # causing GC. Currently used to reduce number of false positives in dead - # variables analysis. See TODO for ALLOWLIST - "allowlist": True, - } - pos_args = [] - - flag_regexp = re.compile("^--(no[-_]?)?([\w\-_]+)$") - for arg in args[1:]: - m = flag_regexp.match(arg) - if m: - no, flag = m.groups() - flag = flag.replace("-", "_") - if flag in flags: - flags[flag] = no is None - else: - fatal("Unknown flag: {}", flag) + # Print arguments for better debugging on the bots + # Get a clean parent path relative to PWD + gcmole_dir = relative_parents(Path(args[0])) + + parser = optparse.OptionParser() + archs = list(ARCHITECTURES.keys()) + parser.add_option( + "--v8-target-cpu", + type="choice", + choices=archs, + help="Tested CPU architecture. Choices: {}".format(archs), + metavar="CPU") + default_clang_bin_dir = gcmole_dir / 'gcmole-tools/bin' + parser.add_option( + "--clang-bin-dir", + metavar="DIR", + help="Build dir of the custom clang version for gcmole." + \ + "Default: env['CLANG_DIR'] or '{}'".format(default_clang_bin_dir)) + parser.add_option( + "--clang-plugins-dir", + metavar="DIR", + help="Containing dir for libgcmole.so." + "Default: env['CLANG_PLUGINS'] or '{}'".format(gcmole_dir)) + default_root_dir = relative_parents(gcmole_dir, 1) + parser.add_option( + "--v8-root-dir", + metavar="DIR", + default=default_root_dir, + help="V8 checkout directory. Default: '{}'".format(default_root_dir)) + parser.add_option( + "--v8-build-dir", + metavar="BUILD_DIR", + help="GN build dir for v8. Default: 'out/CPU.Release'. " + "Config must match cpu specified by --v8-target-cpu") + parser.add_option( + "--out-dir", + metavar="DIR", + help="Output location for the gcsuspect and gcauses file." + "Default: BUILD_DIR/gen/tools/gcmole") + parser.add_option( + "--is-bot", + action="store_true", + default=False, + help="Flag for setting build bot specific settings.") + + group = optparse.OptionGroup(parser, "GCMOLE options") + group.add_option( + "--reuse-gcsuspects", + action="store_true", + default=False, + help="Don't build gcsuspects file and reuse previously generated one.") + group.add_option( + "--sequential", + action="store_true", + default=False, + help="Don't use parallel python runner.") + group.add_option( + "--verbose", + action="store_true", + default=False, + help="Print commands to console before executing them.") + group.add_option( + "--no-dead-vars", + action="store_false", + dest="dead_vars", + default=True, + help="Don't perform dead variable analysis.") + group.add_option( + "--verbose-trace", + action="store_true", + default=False, + help="Enable verbose tracing from the plugin itself." + "This can be useful to debug finding dead variable.") + group.add_option( + "--no-allowlist", + action="store_true", + default=True, + dest="allowlist", + help="When building gcsuspects allowlist certain functions as if they can be " + "causing GC. Currently used to reduce number of false positives in dead " + "variables analysis. See TODO for ALLOWLIST in gcmole.py") + group.add_option( + "--test-run", + action="store_true", + default=False, + help="Test gcmole on tools/gcmole/gcmole-test.cc") + parser.add_option_group(group) + + (options, args) = parser.parse_args() + + if not options.v8_target_cpu: + # Backwards compatibility + if len(args[0]) > 0 and args[0] in archs: + options.v8_target_cpu = args[0] + log("Using --v8-target-cpu={}", options.v8_target_cpu) else: - pos_args.append(arg) + parser.error("Missing --v8-target-cpu option") - archs = pos_args if len(pos_args) > 0 else ["ia32", "arm", "x64", "arm64"] + options.is_bot = False + verify_and_convert_dirs(parser, options, gcmole_dir, default_clang_bin_dir) + verify_clang_plugin(parser, options) + prepare_gcmole_files(options) + verify_build_config(parser, options) any_errors_found = False - if not TestRun(flags, clang_bin_dir, clang_plugins_dir): + if not test_run(options): any_errors_found = True else: - for arch in archs: - if not ARCHITECTURES[arch]: - fatal("Unknown arch: {}", arch) - - errors_found, output = CheckCorrectnessForArch(arch, False, flags, - clang_bin_dir, - clang_plugins_dir) - any_errors_found = any_errors_found or errors_found + errors_found, output = check_correctness_for_arch(options, False) + any_errors_found = any_errors_found or errors_found sys.exit(1 if any_errors_found else 0) +def verify_and_convert_dirs(parser, options, gcmole_dir, default_clang_bin_dir): + # Verify options for setting directors and convert the input strings to Path + # objects. + options.v8_root_dir = Path(options.v8_root_dir) + + if not options.clang_bin_dir: + if os.getenv("CLANG_BIN"): + options.clang_bin_dir = Path(os.getenv("CLANG_BIN")) + options.is_bot = True + else: + options.clang_bin_dir = default_clang_bin_dir + if not (options.clang_bin_dir / 'clang++').exists(): + options.clang_bin_dir = Path(gcmole_dir, + "tools/gcmole/bootstrap/build/bin") + log("Using --clang-bin-dir={}", options.clang_bin_dir) + else: + options.clang_bin_dir = Path(options.clang_bin_dir) + + if not options.clang_plugins_dir: + if os.getenv("CLANG_PLUGINS"): + options.clang_plugins_dir = Path(os.getenv("CLANG_PLUGINS")) + else: + options.clang_plugins_dir = gcmole_dir.resolve() + log("Using --clang-plugins-dir={}", options.clang_plugins_dir) + else: + options.clang_plugins_dir = Path(options.clang_plugins_dir) + + if not options.v8_build_dir: + config = ARCHITECTURES[options.v8_target_cpu] + options.v8_build_dir = options.v8_root_dir / ('out/%s.release' % + config.name) + # Fallback for build bots. + if not options.v8_build_dir.exists() and os.getenv("CLANG_BIN"): + options.v8_build_dir = options.v8_root_dir / 'out/build' + log("Using --v8-build-dir={}", options.v8_build_dir) + else: + options.v8_build_dir = Path(options.v8_build_dir) + + if not options.out_dir: + options.out_dir = options.v8_build_dir / 'gen/tools/gcmole' + if options.v8_build_dir.exists(): + options.out_dir.mkdir(parents=True, exist_ok=True) + else: + options.out_dir = Path(options.out_dir) + + for flag in [ + "--v8-root-dir", "--v8-build-dir", "--clang-bin-dir", + "--clang-plugins-dir", "--out-dir" + ]: + dir = getattr(options, parser.get_option(flag).dest) + if not dir.is_dir(): + parser.error("{}='{}' does not exist!".format(flag, dir)) + + +def verify_clang_plugin(parser, options): + libgcmole_path = options.clang_plugins_dir / "libgcmole.so" + if not libgcmole_path.is_file(): + parser.error("'{}' does not exist. Please build gcmole first.".format( + libgcmole_path)) + clang_path = options.clang_bin_dir / "clang++" + if not clang_path.is_file(): + parser.error( + "'{}' does not exist. Please build gcmole first.".format(clang_path)) + + +def prepare_gcmole_files(options): + cmd = [ + "ninja", "-C", options.v8_build_dir, "v8_gcmole_files", + "v8_dump_build_config" + ] + cmd = list(map(str, cmd)) + log("Preparing files: {}", " ".join(cmd)) + try: + subprocess.check_call(cmd) + except: + # Ignore ninja task errors on the bots + if options.is_bot: + log("Ninja command failed, ignoring errors.") + else: + raise + + +def verify_build_config(parser, options): + if options.is_bot: + #TODO(cbruni): Fix, currently not supported on the bots + return + config_file = options.v8_build_dir / 'v8_build_config.json' + with open(config_file) as f: + config = json.load(f) + found_cpu = None + for key in ('v8_target_cpu', 'target_cpu', 'current_cpu'): + found_cpu = config.get('v8_target_cpu') + if found_cpu == options.v8_target_cpu: + return + parser.error("Build dir '{}' config doesn't match request cpu. {}: {}".format( + options.v8_build_dir, options.v8_target_cpu, found_cpu)) + + if __name__ == "__main__": main(sys.argv) diff --git a/deps/v8/tools/gcmole/package.sh b/deps/v8/tools/gcmole/package.sh index bbddd5b7726cac..ceeffc2a297c53 100755 --- a/deps/v8/tools/gcmole/package.sh +++ b/deps/v8/tools/gcmole/package.sh @@ -14,6 +14,7 @@ PACKAGE_DIR="${THIS_DIR}/gcmole-tools" PACKAGE_FILE="${THIS_DIR}/gcmole-tools.tar.gz" PACKAGE_SUM="${THIS_DIR}/gcmole-tools.tar.gz.sha1" BUILD_DIR="${THIS_DIR}/bootstrap/build" +V8_ROOT_DIR= `realpath "${THIS_DIR}/../.."` # Echo all commands set -x @@ -72,5 +73,8 @@ echo "sudo chroot \$CHROOT_DIR bash -c 'PATH=/docs/depot_tools:\$PATH; /docs/v8/ echo echo You can now run gcmole using this command: echo -echo CLANG_BIN=\"tools/gcmole/gcmole-tools/bin\" python tools/gcmole/gcmole.py +echo 'tools/gcmole/gcmole.py \' +echo ' --clang-bin-dir="tools/gcmole/gcmole-tools/bin" \' +echo ' --clang-plugins-dir="tools/gcmole/gcmole-tools" \' +echo ' --v8-target-cpu=$CPU' echo diff --git a/deps/v8/tools/gcmole/run-gcmole.py b/deps/v8/tools/gcmole/run-gcmole.py index cfcb2dd4107fa0..b3b0b68d9d2b34 100755 --- a/deps/v8/tools/gcmole/run-gcmole.py +++ b/deps/v8/tools/gcmole/run-gcmole.py @@ -1,11 +1,8 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright 2016 the V8 project authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -# for py2/py3 compatibility -from __future__ import print_function - import os import os.path import signal diff --git a/deps/v8/tools/gcmole/suspects.whitelist b/deps/v8/tools/gcmole/suspects.allowlist similarity index 100% rename from deps/v8/tools/gcmole/suspects.whitelist rename to deps/v8/tools/gcmole/suspects.allowlist diff --git a/deps/v8/tools/gdbinit b/deps/v8/tools/gdbinit index 4e26346afaa48c..3db2e42278ea33 100644 --- a/deps/v8/tools/gdbinit +++ b/deps/v8/tools/gdbinit @@ -161,16 +161,17 @@ end set disable-randomization off # Install a handler whenever the debugger stops due to a signal. It walks up the -# stack looking for V8_Dcheck and moves the frame to the one above it so it's -# immediately at the line of code that triggered the DCHECK. +# stack looking for V8_Dcheck / V8_Fatal / OS::DebugBreak frame and moves the +# frame to the one above it so it's immediately at the line of code that +# triggered the stop condition. python -def dcheck_stop_handler(event): +def v8_stop_handler(event): frame = gdb.selected_frame() select_frame = None message = None count = 0 - # limit stack scanning since they're usually shallow and otherwise stack - # overflows can be very slow. + # Limit stack scanning since the frames we look for are near the top anyway, + # and otherwise stack overflows can be very slow. while frame is not None and count < 7: count += 1 # If we are in a frame created by gdb (e.g. for `(gdb) call foo()`), gdb @@ -186,6 +187,8 @@ def dcheck_stop_handler(event): break if frame.name() is not None and frame.name().startswith('V8_Fatal'): select_frame = frame.older() + if frame.name() == 'v8::base::OS::DebugBreak': + select_frame = frame.older() frame = frame.older() if select_frame is not None: @@ -194,7 +197,7 @@ def dcheck_stop_handler(event): if message: print('DCHECK error: {}'.format(message)) -gdb.events.stop.connect(dcheck_stop_handler) +gdb.events.stop.connect(v8_stop_handler) end # Code imported from chromium/src/tools/gdb/gdbinit diff --git a/deps/v8/tools/gen-keywords-gen-h.py b/deps/v8/tools/gen-keywords-gen-h.py index 02750dc109620e..97c91ee289488c 100755 --- a/deps/v8/tools/gen-keywords-gen-h.py +++ b/deps/v8/tools/gen-keywords-gen-h.py @@ -83,7 +83,8 @@ def trim_and_dcheck_char_table(out): def use_isinrange(out): # Our IsInRange method is more efficient than checking for min/max length return checked_sub(r'if \(len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH\)', - r'if (IsInRange(len, MIN_WORD_LENGTH, MAX_WORD_LENGTH))', + r'if (base::IsInRange(len, MIN_WORD_LENGTH, ' + + r'MAX_WORD_LENGTH))', out) diff --git a/deps/v8/tools/gen-postmortem-metadata.py b/deps/v8/tools/gen-postmortem-metadata.py index 298af332d9d3c0..9fae92417e580b 100644 --- a/deps/v8/tools/gen-postmortem-metadata.py +++ b/deps/v8/tools/gen-postmortem-metadata.py @@ -337,7 +337,7 @@ */ #include "src/init/v8.h" -#include "src/codegen/register-arch.h" +#include "src/codegen/register.h" #include "src/execution/frames.h" #include "src/execution/frames-inl.h" /* for architecture-specific frame constants */ #include "src/objects/contexts.h" @@ -359,7 +359,7 @@ #undef FRAME_CONST -''' % sys.argv[0]; +''' % sys.argv[0] footer = ''' } @@ -440,12 +440,12 @@ def load_objects_from_file(objfilename, checktypes): continue; if (in_torque_insttype and (not line or line.isspace())): - in_torque_insttype = False - continue + in_torque_insttype = False + continue if (in_torque_fulldef and (not line or line.isspace())): - in_torque_fulldef = False - continue + in_torque_fulldef = False + continue pre = line.strip() line = re.sub('// .*', '', line.strip()); @@ -497,7 +497,7 @@ def load_objects_from_file(objfilename, checktypes): for entry in entries: entry = entry.strip() if not entry: - continue + continue start = entry.find('('); end = entry.find(')', start); rest = entry[start + 1: end]; diff --git a/deps/v8/tools/generate-header-include-checks.py b/deps/v8/tools/generate-header-include-checks.py index 42c118c9d5ece0..2d6f218e1161b5 100755 --- a/deps/v8/tools/generate-header-include-checks.py +++ b/deps/v8/tools/generate-header-include-checks.py @@ -37,14 +37,17 @@ 'src/trap-handler/trap-handler-simulator.h', ] AUTO_EXCLUDE_PATTERNS = [ - 'src/base/atomicops_internals_.*', - # TODO(petermarshall): Enable once Perfetto is built by default. - 'src/libplatform/tracing/perfetto*', + 'src/base/atomicops_internals_.*', + # TODO(petermarshall): Enable once Perfetto is built by default. + 'src/libplatform/tracing/perfetto*', + # TODO(v8:7700): Enable once Maglev is built by default. + 'src/maglev/.*', ] + [ - # platform-specific headers - '\\b{}\\b'.format(p) for p in - ('win', 'win32', 'ia32', 'x64', 'arm', 'arm64', 'mips', 'mips64', 's390', - 'ppc', 'riscv64', 'loong64')] + # platform-specific headers + '\\b{}\\b'.format(p) + for p in ('win', 'win32', 'ia32', 'x64', 'arm', 'arm64', 'mips', 'mips64', + 's390', 'ppc', 'riscv64', 'loong64') +] args = None def parse_args(): diff --git a/deps/v8/tools/get_landmines.py b/deps/v8/tools/get_landmines.py index bf8efa595e4ebd..a2ace649f662c8 100755 --- a/deps/v8/tools/get_landmines.py +++ b/deps/v8/tools/get_landmines.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright 2014 the V8 project authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -8,9 +8,6 @@ (or a list of 'landmines'). """ -# for py2/py3 compatibility -from __future__ import print_function - import os import sys diff --git a/deps/v8/tools/grokdump.py b/deps/v8/tools/grokdump.py index 368580f0c3ae01..1e10b36f8bed06 100755 --- a/deps/v8/tools/grokdump.py +++ b/deps/v8/tools/grokdump.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Copyright 2012 the V8 project authors. All rights reserved. # Redistribution and use in source and binary forms, with or without @@ -30,12 +30,9 @@ # flake8: noqa # https://bugs.chromium.org/p/v8/issues/detail?id=8784 -# for py2/py3 compatibility -from __future__ import print_function - -import BaseHTTPServer +import http.server as http_server import bisect -import cgi +import html import cmd import codecs import ctypes @@ -46,11 +43,10 @@ import optparse import os import re -import StringIO +import io import sys import types -import urllib -import urlparse +import urllib.parse import v8heapconst import webbrowser @@ -168,7 +164,7 @@ def dump_region(reader, start, size, location): print("%s - %s" % (reader.FormatIntPtr(start), reader.FormatIntPtr(start + size))) print(start + size + 1); - for i in range(0, size, reader.PointerSize()): + for i in range(0, size, reader.MachinePointerSize()): slot = start + i maybe_address = reader.ReadUIntPtr(slot) heap_object = heap.FindObject(maybe_address) @@ -609,12 +605,14 @@ def __cmp__(self, other): def Covers(self, addr): return (self.start <= addr) and (addr < self.end) + class MinidumpReader(object): """Minidump (.dmp) reader.""" _HEADER_MAGIC = 0x504d444d def __init__(self, options, minidump_name): + self._reset() self.minidump_name = minidump_name if sys.platform == 'win32': self.minidump_file = open(minidump_name, "a+") @@ -626,11 +624,19 @@ def __init__(self, options, minidump_name): if self.header.signature != MinidumpReader._HEADER_MAGIC: print("Warning: Unsupported minidump header magic!", file=sys.stderr) DebugPrint(self.header) - directories = [] offset = self.header.stream_directories_rva + directories = [] for _ in range(self.header.stream_count): directories.append(MINIDUMP_DIRECTORY.Read(self.minidump, offset)) offset += MINIDUMP_DIRECTORY.size + + self.symdir = options.symdir + self._ReadArchitecture(directories) + self._ReadDirectories(directories) + self._FindObjdump(options) + + def _reset(self): + self.header = None self.arch = None self.exception = None self.exception_context = None @@ -639,13 +645,9 @@ def __init__(self, options, minidump_name): self.module_list = None self.thread_map = {} - self.symdir = options.symdir self.modules_with_symbols = [] self.symbols = [] - self._ReadArchitecture(directories) - self._ReadDirectories(directories) - self._FindObjdump(options) def _ReadArchitecture(self, directories): # Find MDRawSystemInfo stream and determine arch. @@ -710,7 +712,7 @@ def _ReadDirectories(self, directories): def _FindObjdump(self, options): if options.objdump: - objdump_bin = options.objdump + objdump_bin = options.objdump else: objdump_bin = self._FindThirdPartyObjdump() if not objdump_bin or not os.path.exists(objdump_bin): @@ -722,29 +724,29 @@ def _FindObjdump(self, options): disasm.OBJDUMP_BIN = objdump_bin def _FindThirdPartyObjdump(self): - # Try to find the platform specific objdump - third_party_dir = os.path.join( - os.path.dirname(os.path.dirname(__file__)), 'third_party') - objdumps = [] - for root, dirs, files in os.walk(third_party_dir): - for file in files: - if file.endswith("objdump"): - objdumps.append(os.path.join(root, file)) - if self.arch == MD_CPU_ARCHITECTURE_ARM: - platform_filter = 'arm-linux' - elif self.arch == MD_CPU_ARCHITECTURE_ARM64: - platform_filter = 'aarch64' - else: - # use default otherwise - return None - print(("# Looking for platform specific (%s) objdump in " - "third_party directory.") % platform_filter) - objdumps = filter(lambda file: platform_filter in file >= 0, objdumps) - if len(objdumps) == 0: - print("# Could not find platform specific objdump in third_party.") - print("# Make sure you installed the correct SDK.") - return None - return objdumps[0] + # Try to find the platform specific objdump + third_party_dir = os.path.join( + os.path.dirname(os.path.dirname(__file__)), 'third_party') + objdumps = [] + for root, dirs, files in os.walk(third_party_dir): + for file in files: + if file.endswith("objdump"): + objdumps.append(os.path.join(root, file)) + if self.arch == MD_CPU_ARCHITECTURE_ARM: + platform_filter = 'arm-linux' + elif self.arch == MD_CPU_ARCHITECTURE_ARM64: + platform_filter = 'aarch64' + else: + # use default otherwise + return None + print(("# Looking for platform specific (%s) objdump in " + "third_party directory.") % platform_filter) + objdumps = list(filter(lambda file: platform_filter in file >= 0, objdumps)) + if len(objdumps) == 0: + print("# Could not find platform specific objdump in third_party.") + print("# Make sure you installed the correct SDK.") + return None + return objdumps[0] def ContextDescriptor(self): if self.arch == MD_CPU_ARCHITECTURE_X86: @@ -765,7 +767,7 @@ def IsValidAddress(self, address): return self.FindLocation(address) is not None def IsAlignedAddress(self, address): - return (address % self.PointerSize()) == 0 + return (address % self.MachinePointerSize()) == 0 def IsExceptionStackAddress(self, address): if not self.IsAlignedAddress(address): return False @@ -804,11 +806,29 @@ def Is64(self): return (self.arch == MD_CPU_ARCHITECTURE_ARM64 or self.arch == MD_CPU_ARCHITECTURE_AMD64) + def IsPointerCompressed(self): + # Assume all 64-bit builds are pointer compressed. + return self.Is64() + + def Is32BitTagged(self): + return not self.Is64() or self.IsPointerCompressed() + + def ReadTagged(self, address): + if self.Is32BitTagged(): + return self.ReadU32(address) + return self.ReadU64(address) + def ReadUIntPtr(self, address): if self.Is64(): return self.ReadU64(address) return self.ReadU32(address) + def ReadSized(self, address, size): + if size == 8: + return self.ReadU64(address) + assert (size == 4) + return self.ReadU32(address) + def ReadBytes(self, address, size): location = self.FindLocation(address) return self.minidump[location:location + size] @@ -819,8 +839,10 @@ def _ReadWord(self, location): return ctypes.c_uint32.from_buffer(self.minidump, location).value def ReadAsciiPtr(self, address): - ascii_content = [c if c >= '\x20' and c < '\x7f' else '.' - for c in self.ReadBytes(address, self.PointerSize())] + ascii_content = [ + chr(c) if c >= 0x20 and c < 0x7f else '.' + for c in self.ReadBytes(address, self.MachinePointerSize()) + ] return ''.join(ascii_content) def ReadAsciiString(self, address): @@ -908,7 +930,7 @@ def ForEachMemoryRegion(self, cb): def FindWord(self, word, alignment=0): def search_inside_region(reader, start, size, location): location = (location + alignment) & ~alignment - for i in range(size - self.PointerSize()): + for i in range(size - self.MachinePointerSize()): loc = location + i if reader._ReadWord(loc) == word: slot = start + (loc - location) @@ -920,7 +942,7 @@ def FindWordList(self, word): aligned_res = [] unaligned_res = [] def search_inside_region(reader, start, size, location): - for i in range(size - self.PointerSize()): + for i in range(size - self.MachinePointerSize()): loc = location + i if reader._ReadWord(loc) == word: slot = start + (loc - location) @@ -974,6 +996,7 @@ def CountUndefinedInstructions(lines): def Dispose(self): + self._reset() self.minidump.close() self.minidump_file.close() @@ -1023,11 +1046,21 @@ def FormatIntPtr(self, value): return "%016x" % value return "%08x" % value - def PointerSize(self): + def FormatTagged(self, value): + if self.Is64() and not self.IsPointerCompressed(): + return "%016x" % value + return "%08x" % value + + def MachinePointerSize(self): if self.Is64(): return 8 return 4 + def TaggedPointerSize(self): + if self.IsPointerCompressed(): + return 4 + return self.MachinePointerSize() + def Register(self, name): return self.exception_context.__getattribute__(name) @@ -1173,11 +1206,11 @@ def __str__(self): instance_type) def ObjectField(self, offset): - field_value = self.heap.reader.ReadUIntPtr(self.address + offset) + field_value = self.heap.reader.ReadTagged(self.address + offset) return self.heap.FindObjectOrSmi(field_value) def SmiField(self, offset): - field_value = self.heap.reader.ReadUIntPtr(self.address + offset) + field_value = self.heap.reader.ReadTagged(self.address + offset) if self.heap.IsSmi(field_value): return self.heap.SmiUntag(field_value) return None @@ -1189,7 +1222,7 @@ def Decode(self, offset, size, value): # Instance Sizes def InstanceSizesOffset(self): - return self.heap.PointerSize() + return self.heap.TaggedPointerSize() def InstanceSizeOffset(self): return self.InstanceSizesOffset() @@ -1224,28 +1257,29 @@ def BitField3Offset(self): return self.InstanceAttributesOffset() + self.heap.IntSize() def PrototypeOffset(self): - return self.BitField3Offset() + self.heap.PointerSize() + return self.BitField3Offset() + self.heap.TaggedPointerSize() def ConstructorOrBackPointerOffset(self): - return self.PrototypeOffset() + self.heap.PointerSize() + return self.PrototypeOffset() + self.heap.TaggedPointerSize() def TransitionsOrPrototypeInfoOffset(self): - return self.ConstructorOrBackPointerOffset() + self.heap.PointerSize() + return self.ConstructorOrBackPointerOffset() + self.heap.TaggedPointerSize() def DescriptorsOffset(self): - return self.TransitionsOrPrototypeInfoOffset() + self.heap.PointerSize() + return (self.TransitionsOrPrototypeInfoOffset() + + self.heap.TaggedPointerSize()) def CodeCacheOffset(self): - return self.DescriptorsOffset() + self.heap.PointerSize() + return self.DescriptorsOffset() + self.heap.TaggedPointerSize() def DependentCodeOffset(self): - return self.CodeCacheOffset() + self.heap.PointerSize() + return self.CodeCacheOffset() + self.heap.TaggedPointerSize() def ReadByte(self, offset): return self.heap.reader.ReadU8(self.address + offset) - def ReadWord(self, offset): - return self.heap.reader.ReadUIntPtr(self.address + offset) + def ReadSlot(self, offset): + return self.heap.reader.ReadTagged(self.address + offset) def Print(self, p): p.Print("Map(%08x)" % (self.address)) @@ -1264,7 +1298,7 @@ def Print(self, p): p.Print(" - kind: %s" % (self.Decode(3, 5, bitfield2))) - bitfield3 = self.ReadWord(self.BitField3Offset()) + bitfield3 = self.ReadSlot(self.BitField3Offset()) p.Print( " - EnumLength: %d NumberOfOwnDescriptors: %d OwnsDescriptors: %s" % ( @@ -1299,7 +1333,7 @@ def __init__(self, heap, map, address): class String(HeapObject): def LengthOffset(self): # First word after the map is the hash, the second is the length. - return self.heap.PointerSize() * 2 + return self.heap.TaggedPointerSize() * 2 def __init__(self, heap, map, address): HeapObject.__init__(self, heap, map, address) @@ -1317,7 +1351,7 @@ def __str__(self): class SeqString(String): def CharsOffset(self): - return self.heap.PointerSize() * 3 + return self.heap.TaggedPointerSize() * 3 def __init__(self, heap, map, address): String.__init__(self, heap, map, address) @@ -1360,10 +1394,10 @@ def GetChars(self): class ConsString(String): def LeftOffset(self): - return self.heap.PointerSize() * 3 + return self.heap.TaggedPointerSize() * 3 def RightOffset(self): - return self.heap.PointerSize() * 4 + return self.heap.TaggedPointerSize() * 4 def __init__(self, heap, map, address): String.__init__(self, heap, map, address) @@ -1390,13 +1424,13 @@ class Oddball(HeapObject): ] def ToStringOffset(self): - return self.heap.PointerSize() + return self.heap.TaggedPointerSize() def ToNumberOffset(self): - return self.ToStringOffset() + self.heap.PointerSize() + return self.ToStringOffset() + self.heap.TaggedPointerSize() def KindOffset(self): - return self.ToNumberOffset() + self.heap.PointerSize() + return self.ToNumberOffset() + self.heap.TaggedPointerSize() def __init__(self, heap, map, address): HeapObject.__init__(self, heap, map, address) @@ -1418,13 +1452,13 @@ def __str__(self): class FixedArray(HeapObject): def LengthOffset(self): - return self.heap.PointerSize() + return self.heap.TaggedPointerSize() def ElementsOffset(self): - return self.heap.PointerSize() * 2 + return self.heap.TaggedPointerSize() * 2 def MemberOffset(self, i): - return self.ElementsOffset() + self.heap.PointerSize() * i + return self.ElementsOffset() + self.heap.TaggedPointerSize() * i def Get(self, i): return self.ObjectField(self.MemberOffset(i)) @@ -1561,10 +1595,10 @@ def Print(self, p): class JSFunction(HeapObject): def CodeEntryOffset(self): - return 3 * self.heap.PointerSize() + return 3 * self.heap.TaggedPointerSize() def SharedOffset(self): - return 5 * self.heap.PointerSize() + return 5 * self.heap.TaggedPointerSize() def __init__(self, heap, map, address): HeapObject.__init__(self, heap, map, address) @@ -1611,19 +1645,19 @@ def _GetSource(self): class SharedFunctionInfo(HeapObject): def CodeOffset(self): - return 2 * self.heap.PointerSize() + return 2 * self.heap.TaggedPointerSize() def ScriptOffset(self): - return 7 * self.heap.PointerSize() + return 7 * self.heap.TaggedPointerSize() def InferredNameOffset(self): - return 9 * self.heap.PointerSize() + return 9 * self.heap.TaggedPointerSize() def EndPositionOffset(self): - return 12 * self.heap.PointerSize() + 4 * self.heap.IntSize() + return 12 * self.heap.TaggedPointerSize() + 4 * self.heap.IntSize() def StartPositionAndTypeOffset(self): - return 12 * self.heap.PointerSize() + 5 * self.heap.IntSize() + return 12 * self.heap.TaggedPointerSize() + 5 * self.heap.IntSize() def __init__(self, heap, map, address): HeapObject.__init__(self, heap, map, address) @@ -1631,7 +1665,7 @@ def __init__(self, heap, map, address): self.code = self.ObjectField(self.CodeOffset()) self.script = self.ObjectField(self.ScriptOffset()) self.inferred_name = self.ObjectField(self.InferredNameOffset()) - if heap.PointerSize() == 8: + if heap.TaggedPointerSize() == 8: start_position_and_type = \ heap.reader.ReadU32(self.StartPositionAndTypeOffset()) self.start_position = start_position_and_type >> 2 @@ -1653,10 +1687,10 @@ def __init__(self, heap, map, address): class Script(HeapObject): def SourceOffset(self): - return self.heap.PointerSize() + return self.heap.TaggedPointerSize() def NameOffset(self): - return self.SourceOffset() + self.heap.PointerSize() + return self.SourceOffset() + self.heap.TaggedPointerSize() def __init__(self, heap, map, address): HeapObject.__init__(self, heap, map, address) @@ -1666,10 +1700,10 @@ def __init__(self, heap, map, address): class CodeCache(HeapObject): def DefaultCacheOffset(self): - return self.heap.PointerSize() + return self.heap.TaggedPointerSize() def NormalTypeCacheOffset(self): - return self.DefaultCacheOffset() + self.heap.PointerSize() + return self.DefaultCacheOffset() + self.heap.TaggedPointerSize() def __init__(self, heap, map, address): HeapObject.__init__(self, heap, map, address) @@ -1689,12 +1723,12 @@ class Code(HeapObject): CODE_ALIGNMENT_MASK = (1 << 5) - 1 def InstructionSizeOffset(self): - return self.heap.PointerSize() + return self.heap.TaggedPointerSize() @staticmethod def HeaderSize(heap): - return (heap.PointerSize() + heap.IntSize() + \ - 4 * heap.PointerSize() + 3 * heap.IntSize() + \ + return (heap.TaggedPointerSize() + heap.IntSize() + \ + 4 * heap.TaggedPointerSize() + 3 * heap.IntSize() + \ Code.CODE_ALIGNMENT_MASK) & ~Code.CODE_ALIGNMENT_MASK def __init__(self, heap, map, address): @@ -1763,7 +1797,7 @@ def FindObject(self, tagged_address): if not self.IsTaggedObjectAddress(tagged_address): return None address = tagged_address - 1 if not self.reader.IsValidAddress(address): return None - map_tagged_address = self.reader.ReadUIntPtr(address) + map_tagged_address = self.reader.ReadTagged(address) if tagged_address == map_tagged_address: # Meta map? meta_map = Map(self, None, address) @@ -1796,11 +1830,17 @@ def FindMapAddress(self, tagged_address): def IntSize(self): return 4 - def PointerSize(self): - return self.reader.PointerSize() + def MachinePointerSize(self): + return self.reader.MachinePointerSize() + + def TaggedPointerSize(self): + return self.reader.TaggedPointerSize() + + def IsPointerCompressed(self): + return self.reader.IsPointerCompressed() def ObjectAlignmentMask(self): - return self.PointerSize() - 1 + return self.TaggedPointerSize() - 1 def IsTaggedObjectAddress(self, address): return (address & self.ObjectAlignmentMask()) == 1 @@ -1829,13 +1869,14 @@ def IsTaggedAddress(self, address): return (address & self.ObjectAlignmentMask()) == 1 def IsSmi(self, tagged_address): - if self.reader.Is64(): + if self.reader.Is64() and not self.reader.IsPointerCompressed(): return (tagged_address & 0xFFFFFFFF) == 0 return not self.IsTaggedAddress(tagged_address) def SmiUntag(self, tagged_address): - if self.reader.Is64(): return tagged_address >> 32 - return tagged_address >> 1 + if self.reader.Is64() and not self.reader.IsPointerCompressed(): + return tagged_address >> 32 + return (tagged_address >> 1) & 0xFFFFFFFF def AddressTypeMarker(self, address): if not self.reader.IsValidAddress(address): return " " @@ -1858,7 +1899,7 @@ def RelativeOffset(self, slot, address): if self.IsTaggedObjectAddress(address): address -= 1 if not self.reader.IsValidAlignedAddress(address): return None - offset = (address - slot) / self.PointerSize() + offset = (address - slot) / self.MachinePointerSize() lower_limit = -32 upper_limit = 128 @@ -1873,12 +1914,12 @@ def RelativeOffset(self, slot, address): def FindObjectPointers(self, start=0, end=0): objects = set() def find_object_in_region(reader, start, size, location): - for slot in range(start, start+size, self.reader.PointerSize()): + for slot in range(start, start + size, self.reader.TaggedPointerSize()): if not self.reader.IsValidAddress(slot): break # Collect only tagged pointers (object) to tagged pointers (map) - tagged_address = self.reader.ReadUIntPtr(slot) + tagged_address = self.reader.ReadTagged(slot) if not self.IsValidTaggedObjectAddress(tagged_address): continue - map_address = self.reader.ReadUIntPtr(tagged_address - 1) + map_address = self.reader.ReadTagged(tagged_address - 1) if not self.IsTaggedMapAddress(map_address): continue objects.add(tagged_address) @@ -1951,10 +1992,12 @@ def color_addresses(self): exception_thread.stack.memory.data_size frame_pointer = self.reader.ExceptionFP() self.styles[frame_pointer] = "frame" - for slot in range(stack_top, stack_bottom, self.reader.PointerSize()): + for slot in range(stack_top, stack_bottom, + self.reader.MachinePointerSize()): # stack address self.styles[slot] = "sa" - for slot in range(stack_top, stack_bottom, self.reader.PointerSize()): + for slot in range(stack_top, stack_bottom, + self.reader.MachinePointerSize()): maybe_address = self.reader.ReadUIntPtr(slot) # stack value self.styles[maybe_address] = "sv" @@ -2026,7 +2069,7 @@ def IsFrameMarker(self, slot, address): # Frame markers only occur directly after a frame pointer and only on the # stack. if not self.reader.IsExceptionStackAddress(slot): return False - next_slot = slot + self.reader.PointerSize() + next_slot = slot + self.reader.MachinePointerSize() if not self.reader.IsValidAddress(next_slot): return False next_address = self.reader.ReadUIntPtr(next_slot) return self.reader.IsExceptionStackAddress(next_address) @@ -2058,7 +2101,7 @@ def SenseObject(self, address, slot=None): if found_obj: return found_obj address = tagged_address - 1 if self.reader.IsValidAddress(address): - map_tagged_address = self.reader.ReadUIntPtr(address) + map_tagged_address = self.reader.ReadTagged(address) map = self.SenseMap(map_tagged_address) if map is None: return None instance_type_name = INSTANCE_TYPES.get(map.instance_type) @@ -2118,7 +2161,7 @@ def PrintStackTraceMessage(self, start=None, print_message=True): Returns the first address where the normal stack starts again. """ # Only look at the first 1k words on the stack - ptr_size = self.reader.PointerSize() + ptr_size = self.reader.MachinePointerSize() if start is None: start = self.reader.ExceptionSP() if not self.reader.IsValidAddress(start): return start end = start + ptr_size * 1024 * 4 @@ -2140,7 +2183,7 @@ def PrintStackTraceMessage(self, start=None, print_message=True): print_message) def TryExtractStackTrace(self, slot, start, end, print_message): - ptr_size = self.reader.PointerSize() + ptr_size = self.reader.MachinePointerSize() assert self.reader.ReadUIntPtr(slot) & 0xFFFFFFFF == STACK_TRACE_MARKER end_marker = STACK_TRACE_MARKER + 1; header_size = 10 @@ -2163,7 +2206,7 @@ def TryExtractStackTrace(self, slot, start, end, print_message): return stack_start def FindPtr(self, expected_value, start, end): - ptr_size = self.reader.PointerSize() + ptr_size = self.reader.MachinePointerSize() for slot in range(start, end, ptr_size): if not self.reader.IsValidAddress(slot): return None value = self.reader.ReadUIntPtr(slot) @@ -2171,7 +2214,7 @@ def FindPtr(self, expected_value, start, end): return None def TryExtractErrorMessage(self, slot, start, end, print_message): - ptr_size = self.reader.PointerSize() + ptr_size = self.reader.MachinePointerSize() end_marker = ERROR_MESSAGE_MARKER + 1; header_size = 1 end_search = start + 1024 + (header_size * ptr_size); @@ -2186,7 +2229,7 @@ def TryExtractErrorMessage(self, slot, start, end, print_message): def TryExtractOldStyleStackTrace(self, message_slot, start, end, print_message): - ptr_size = self.reader.PointerSize() + ptr_size = self.reader.MachinePointerSize() if message_slot == 0: """ On Mac we don't always get proper magic markers, so just try printing @@ -2225,7 +2268,7 @@ def FormatStackTrace(self, message, print_message): print(" Use `dsa` to print the message with annotated addresses.") print("") return - ptr_size = self.reader.PointerSize() + ptr_size = self.reader.MachinePointerSize() # Annotate all addresses in the dumped message prog = re.compile("[0-9a-fA-F]{%s}" % ptr_size*2) addresses = list(set(prog.findall(message))) @@ -2252,7 +2295,7 @@ def TryInferFramePointer(self, slot, address): def TryInferContext(self, address): if self.context: return - ptr_size = self.reader.PointerSize() + ptr_size = self.reader.MachinePointerSize() possible_context = dict() count = 0 while self.reader.IsExceptionStackAddress(address): @@ -2266,7 +2309,7 @@ def TryInferContext(self, address): count += 1 if count <= 5 or len(possible_context) == 0: return # Find entry with highest count - possible_context = possible_context.items() + possible_context = list(possible_context.items()) possible_context.sort(key=lambda pair: pair[1]) address,count = possible_context[-1] if count <= 4: return @@ -2287,7 +2330,7 @@ def InterpretMemory(self, start, end): in_oom_dump_area = False is_stack = self.reader.IsExceptionStackAddress(start) free_space_end = 0 - ptr_size = self.reader.PointerSize() + ptr_size = self.reader.TaggedPointerSize() for slot in range(start, end, ptr_size): if not self.reader.IsValidAddress(slot): @@ -2309,7 +2352,7 @@ def InterpretMemory(self, start, end): if isinstance(heap_object, KnownMap) and \ heap_object.known_name == "FreeSpaceMap": # The free-space length is is stored as a Smi in the next slot. - length = self.reader.ReadUIntPtr(slot + ptr_size) + length = self.reader.ReadTagged(slot + ptr_size) if self.heap.IsSmi(length): length = self.heap.SmiUntag(length) free_space_end = slot + length - ptr_size @@ -2575,11 +2618,11 @@ def InterpretMemory(self, start, end): class WebParameterError(Exception): - def __init__(self, message): - Exception.__init__(self, message) + pass -class InspectionWebHandler(BaseHTTPServer.BaseHTTPRequestHandler): +class InspectionWebHandler(http_server.BaseHTTPRequestHandler): + def formatter(self, query_components): name = query_components.get("dump", [None])[0] return self.server.get_dump_formatter(name) @@ -2593,40 +2636,39 @@ def send_success_html_headers(self): self.end_headers() return + def write(self, string): + self.wfile.write(string.encode('utf-8')) + def do_GET(self): try: - parsedurl = urlparse.urlparse(self.path) - query_components = urlparse.parse_qs(parsedurl.query) + parsedurl = urllib.parse.urlparse(self.path) + query_components = urllib.parse.parse_qs(parsedurl.query) + out_buffer = io.StringIO() if parsedurl.path == "/dumps.html": self.send_success_html_headers() - out_buffer = StringIO.StringIO() self.server.output_dumps(out_buffer) - self.wfile.write(out_buffer.getvalue()) + self.write(out_buffer.getvalue()) elif parsedurl.path == "/summary.html": self.send_success_html_headers() - out_buffer = StringIO.StringIO() self.formatter(query_components).output_summary(out_buffer) - self.wfile.write(out_buffer.getvalue()) + self.write(out_buffer.getvalue()) elif parsedurl.path == "/info.html": self.send_success_html_headers() - out_buffer = StringIO.StringIO() self.formatter(query_components).output_info(out_buffer) - self.wfile.write(out_buffer.getvalue()) + self.write(out_buffer.getvalue()) elif parsedurl.path == "/modules.html": self.send_success_html_headers() - out_buffer = StringIO.StringIO() self.formatter(query_components).output_modules(out_buffer) - self.wfile.write(out_buffer.getvalue()) + self.write(out_buffer.getvalue()) elif parsedurl.path == "/search.html" or parsedurl.path == "/s": address = query_components.get("val", []) if len(address) != 1: self.send_error(404, "Invalid params") return self.send_success_html_headers() - out_buffer = StringIO.StringIO() self.formatter(query_components).output_search_res( out_buffer, address[0]) - self.wfile.write(out_buffer.getvalue()) + self.write(out_buffer.getvalue()) elif parsedurl.path == "/disasm.html": address = query_components.get("val", []) exact = query_components.get("exact", ["on"]) @@ -2634,19 +2676,17 @@ def do_GET(self): self.send_error(404, "Invalid params") return self.send_success_html_headers() - out_buffer = StringIO.StringIO() self.formatter(query_components).output_disasm( out_buffer, address[0], exact[0]) - self.wfile.write(out_buffer.getvalue()) + self.write(out_buffer.getvalue()) elif parsedurl.path == "/data.html": address = query_components.get("val", []) datakind = query_components.get("type", ["address"]) if len(address) == 1 and len(datakind) == 1: self.send_success_html_headers() - out_buffer = StringIO.StringIO() self.formatter(query_components).output_data( out_buffer, address[0], datakind[0]) - self.wfile.write(out_buffer.getvalue()) + self.write(out_buffer.getvalue()) else: self.send_error(404,'Invalid params') elif parsedurl.path == "/setdumpdesc": @@ -2657,7 +2697,7 @@ def do_GET(self): description = description[0] if self.server.set_dump_desc(name, description): self.send_success_html_headers() - self.wfile.write("OK") + self.write("OK") return self.send_error(404,'Invalid params') elif parsedurl.path == "/setcomment": @@ -2668,7 +2708,7 @@ def do_GET(self): comment = comment[0] self.formatter(query_components).set_comment(address, comment) self.send_success_html_headers() - self.wfile.write("OK") + self.write("OK") else: self.send_error(404,'Invalid params') elif parsedurl.path == "/setpageaddress": @@ -2679,7 +2719,7 @@ def do_GET(self): address = address[0] self.formatter(query_components).set_page_address(kind, address) self.send_success_html_headers() - self.wfile.write("OK") + self.write("OK") else: self.send_error(404,'Invalid params') else: @@ -2701,7 +2741,7 @@ class InspectionWebFormatter(object): def __init__(self, switches, minidump_name, http_server): self.dumpfilename = os.path.split(minidump_name)[1] - self.encfilename = urllib.urlencode({ 'dump' : self.dumpfilename }) + self.encfilename = urllib.parse.urlencode({'dump': self.dumpfilename}) self.reader = MinidumpReader(switches, minidump_name) self.server = http_server @@ -2711,7 +2751,8 @@ def __init__(self, switches, minidump_name, http_server): stack_bottom = exception_thread.stack.start + \ exception_thread.stack.memory.data_size stack_map = {self.reader.ExceptionIP(): -1} - for slot in range(stack_top, stack_bottom, self.reader.PointerSize()): + for slot in range(stack_top, stack_bottom, + self.reader.MachinePointerSize()): maybe_address = self.reader.ReadUIntPtr(slot) if not maybe_address in stack_map: stack_map[maybe_address] = slot @@ -2757,10 +2798,23 @@ def format_address(self, maybeaddress, straddress = None): return ("%s" % (style_class, self.encfilename, straddress, straddress)) + def format_onheap_address(self, size, maybeaddress, uncompressed): + if maybeaddress is None: + return "not in dump" + else: + straddress = "0x" + self.reader.FormatTagged(maybeaddress) + struncompressed = "0x" + self.reader.FormatIntPtr(uncompressed) + style_class = "" + if not self.reader.IsValidAddress(maybeaddress): + style_class = "class=nd" + return ("%s" % + (style_class, self.encfilename, struncompressed, straddress)) + def output_header(self, f): - f.write(WEB_HEADER % - { "query_dump" : self.encfilename, - "dump_name" : cgi.escape(self.dumpfilename) }) + f.write(WEB_HEADER % { + "query_dump": self.encfilename, + "dump_name": html.escape(self.dumpfilename) + }) def output_footer(self, f): f.write(WEB_FOOTER) @@ -2779,7 +2833,8 @@ def output_summary(self, f): stack_bottom = min(exception_thread.stack.start + \ exception_thread.stack.memory.data_size, stack_top + self.MAX_CONTEXT_STACK) - self.output_words(f, stack_top - 16, stack_bottom, stack_top, "Stack") + self.output_words(f, stack_top - 16, stack_bottom, stack_top, "Stack", + self.heap.MachinePointerSize()) f.write('') self.output_footer(f) @@ -2889,7 +2944,7 @@ def align_up(self, a, size): def format_object(self, address): heap_object = self.padawan.SenseObject(address) - return cgi.escape(str(heap_object or "")) + return html.escape(str(heap_object or "")) def output_data(self, f, straddress, datakind): try: @@ -2900,7 +2955,11 @@ def output_data(self, f, straddress, datakind): return region = self.reader.FindRegion(address) if datakind == "address": - self.output_words(f, region[0], region[0] + region[1], address, "Dump") + self.output_words(f, region[0], region[0] + region[1], address, "Dump", + self.heap.MachinePointerSize()) + if datakind == "tagged": + self.output_words(f, region[0], region[0] + region[1], address, + "Tagged Dump", self.heap.TaggedPointerSize()) elif datakind == "ascii": self.output_ascii(f, region[0], region[0] + region[1], address) self.output_footer(f) @@ -2909,14 +2968,13 @@ def output_data(self, f, straddress, datakind): f.write("

Unrecognized address format \"%s\".

" % straddress) return - def output_words(self, f, start_address, end_address, - highlight_address, desc): + def output_words(self, f, start_address, end_address, highlight_address, desc, + size): region = self.reader.FindRegion(highlight_address) if region is None: f.write("

Address 0x%x not found in the dump.

" % (highlight_address)) return - size = self.heap.PointerSize() start_address = self.align_down(start_address, size) low = self.align_down(region[0], size) high = self.align_up(region[0] + region[1], size) @@ -2943,6 +3001,7 @@ def output_words(self, f, start_address, end_address, slot = start_address + j heap_object = "" maybe_address = None + maybe_uncompressed_address = None end_region = region[0] + region[1] if slot < region[0] or slot + size > end_region: straddress = "0x" @@ -2954,10 +3013,20 @@ def output_words(self, f, start_address, end_address, for i in range(slot, region[0]): straddress += "??" else: - maybe_address = self.reader.ReadUIntPtr(slot) - straddress = self.format_address(maybe_address) - if maybe_address: - heap_object = self.format_object(maybe_address) + maybe_address = self.reader.ReadSized(slot, size) + if size == self.reader.MachinePointerSize(): + maybe_uncompressed_address = maybe_address + else: + maybe_uncompressed_address = (slot & (0xFFFFFF << 32)) | ( + maybe_address & 0xFFFFFF) + + if size == self.reader.TaggedPointerSize(): + straddress = self.format_onheap_address(size, maybe_address, + maybe_uncompressed_address) + if maybe_address: + heap_object = self.format_object(maybe_address) + else: + straddress = self.format_address(maybe_address) address_fmt = "%s 
") - if maybe_address != None: - self.output_comment_box( - f, "sv-" + self.reader.FormatIntPtr(slot), maybe_address) + if maybe_uncompressed_address != None: + self.output_comment_box(f, "sv-" + self.reader.FormatIntPtr(slot), + maybe_uncompressed_address) f.write("%s
%s%s   ") f.write(datetime.datetime.fromtimestamp(mtime)) f.write(" r Relayout graph
nShow graph with selected nodes for next phase
bShow graph with selected nodes for previous phase
a Select all nodes
* Other Versions + * [18.x](CHANGELOG_V18.md) * [17.x](CHANGELOG_V17.md) * [16.x](CHANGELOG_V16.md) * [15.x](CHANGELOG_V15.md) diff --git a/doc/changelogs/CHANGELOG_V010.md b/doc/changelogs/CHANGELOG_V010.md index 97038ffea4e7fc..4aebd7733ca9db 100644 --- a/doc/changelogs/CHANGELOG_V010.md +++ b/doc/changelogs/CHANGELOG_V010.md @@ -64,6 +64,7 @@ * Other Versions + * [18.x](CHANGELOG_V18.md) * [17.x](CHANGELOG_V17.md) * [16.x](CHANGELOG_V16.md) * [15.x](CHANGELOG_V15.md) diff --git a/doc/changelogs/CHANGELOG_V012.md b/doc/changelogs/CHANGELOG_V012.md index e61577e7793e90..10a08fcba023e7 100644 --- a/doc/changelogs/CHANGELOG_V012.md +++ b/doc/changelogs/CHANGELOG_V012.md @@ -32,6 +32,7 @@ * Other Versions + * [18.x](CHANGELOG_V18.md) * [17.x](CHANGELOG_V17.md) * [16.x](CHANGELOG_V16.md) * [15.x](CHANGELOG_V15.md) diff --git a/doc/changelogs/CHANGELOG_V10.md b/doc/changelogs/CHANGELOG_V10.md index 48a71cb9eec308..5dc28207c26fd8 100644 --- a/doc/changelogs/CHANGELOG_V10.md +++ b/doc/changelogs/CHANGELOG_V10.md @@ -58,6 +58,7 @@ * Other Versions + * [18.x](CHANGELOG_V18.md) * [17.x](CHANGELOG_V17.md) * [16.x](CHANGELOG_V16.md) * [15.x](CHANGELOG_V15.md) diff --git a/doc/changelogs/CHANGELOG_V11.md b/doc/changelogs/CHANGELOG_V11.md index 95c7e50580fe93..c1018094f1acc4 100644 --- a/doc/changelogs/CHANGELOG_V11.md +++ b/doc/changelogs/CHANGELOG_V11.md @@ -30,6 +30,7 @@ * Other Versions + * [18.x](CHANGELOG_V18.md) * [17.x](CHANGELOG_V17.md) * [16.x](CHANGELOG_V16.md) * [15.x](CHANGELOG_V15.md) diff --git a/doc/changelogs/CHANGELOG_V12.md b/doc/changelogs/CHANGELOG_V12.md index 0bf4f9f1122819..63b2f650acbb2a 100644 --- a/doc/changelogs/CHANGELOG_V12.md +++ b/doc/changelogs/CHANGELOG_V12.md @@ -9,6 +9,8 @@ +12.22.12
+12.22.11
12.22.10
12.22.9
12.22.8
@@ -65,6 +67,7 @@ * Other Versions + * [18.x](CHANGELOG_V18.md) * [17.x](CHANGELOG_V17.md) * [16.x](CHANGELOG_V16.md) * [15.x](CHANGELOG_V15.md) @@ -83,6 +86,65 @@ * [io.js](CHANGELOG_IOJS.md) * [Archive](CHANGELOG_ARCHIVE.md) + + +## 2022-04-05, Version 12.22.12 'Erbium' (LTS), @richardlau + +### Notable Changes + +This is planned to be the final Node.js 12 release. Node.js 12 will +reach End-of-Life status on 30 April 2022, after which it will no +receive updates. You are strongly advised to migrate your applications +to Node.js 16 or 14 (both of which are Long Term Support (LTS) releases) +to continue to receive future security updates beyond 30 April 2022. + +This release fixes a shutdown crash in Node-API (formerly N-API) and a +potential stack overflow when using `vm.runInNewContext()`. + +The list of GPG keys used to sign releases and instructions on how to +fetch the keys for verifying binaries has been synchronized with the +main branch. + +### Commits + +* \[[`1193290f3f`](https://github.com/nodejs/node/commit/1193290f3f)] - **deps**: V8: cherry-pick cc9a8a37445e (devsnek) [#42065](https://github.com/nodejs/node/pull/42065) +* \[[`333eda8d03`](https://github.com/nodejs/node/commit/333eda8d03)] - **doc**: add a note about possible missing lines to readline.asyncIterator (Igor Mikhalev) [#34675](https://github.com/nodejs/node/pull/34675) +* \[[`518a49c0c6`](https://github.com/nodejs/node/commit/518a49c0c6)] - **doc**: use openpgp.org for keyserver examples (Nick Schonning) [#39227](https://github.com/nodejs/node/pull/39227) +* \[[`11aef2ad03`](https://github.com/nodejs/node/commit/11aef2ad03)] - **doc**: update release key for Danielle Adams (Danielle Adams) [#36793](https://github.com/nodejs/node/pull/36793) +* \[[`a9c38f1003`](https://github.com/nodejs/node/commit/a9c38f1003)] - **doc**: add release key for Danielle Adams (Danielle Adams) [#35545](https://github.com/nodejs/node/pull/35545) +* \[[`a35f553889`](https://github.com/nodejs/node/commit/a35f553889)] - **doc**: add release key for Bryan English (Bryan English) [#42102](https://github.com/nodejs/node/pull/42102) +* \[[`5f104e3218`](https://github.com/nodejs/node/commit/5f104e3218)] - **node-api**: cctest on v8impl::Reference (legendecas) [#38970](https://github.com/nodejs/node/pull/38970) +* \[[`e23c04f0dc`](https://github.com/nodejs/node/commit/e23c04f0dc)] - **node-api**: avoid SecondPassCallback crash (Michael Dawson) [#38899](https://github.com/nodejs/node/pull/38899) +* \[[`a7224c9559`](https://github.com/nodejs/node/commit/a7224c9559)] - **node-api**: fix shutdown crashes (Michael Dawson) [#38492](https://github.com/nodejs/node/pull/38492) +* \[[`81b4dc88f1`](https://github.com/nodejs/node/commit/81b4dc88f1)] - **node-api**: make reference weak parameter an indirect link to references (Chengzhong Wu) [#38000](https://github.com/nodejs/node/pull/38000) +* \[[`2aa9ca1ea9`](https://github.com/nodejs/node/commit/2aa9ca1ea9)] - **node-api**: fix crash in finalization (Michael Dawson) [#37876](https://github.com/nodejs/node/pull/37876) +* \[[`a2f4206415`](https://github.com/nodejs/node/commit/a2f4206415)] - **node-api**: stop ref gc during environment teardown (Gabriel Schulhof) [#37616](https://github.com/nodejs/node/pull/37616) +* \[[`171bb66ccc`](https://github.com/nodejs/node/commit/171bb66ccc)] - **node-api**: force env shutdown deferring behavior (Gabriel Schulhof) [#37303](https://github.com/nodejs/node/pull/37303) +* \[[`e707514c80`](https://github.com/nodejs/node/commit/e707514c80)] - **src**: fix finalization crash (James M Snell) [#38250](https://github.com/nodejs/node/pull/38250) + + + +## 2022-03-17, Version 12.22.11 'Erbium' (LTS), @richardlau + +This is a security release. + +### Notable changes + +Update to OpenSSL 1.1.1n, which addresses the following vulnerability: + +* Infinite loop in `BN_mod_sqrt()` reachable when parsing certificates (High)(CVE-2022-0778) + More details are available at + +Fix for building Node.js 12.x with Visual Studio 2019 to allow us to continue to +run CI tests. + +### Commits + +* \[[`e3e5bf11ba`](https://github.com/nodejs/node/commit/e3e5bf11ba)] - **build**: pin Windows GitHub runner to windows-2019 (Richard Lau) [#42349](https://github.com/nodejs/node/pull/42349) +* \[[`f41e7771bf`](https://github.com/nodejs/node/commit/f41e7771bf)] - **build**: fix detection of Visual Studio 2019 (Richard Lau) [#42349](https://github.com/nodejs/node/pull/42349) +* \[[`c372ec207d`](https://github.com/nodejs/node/commit/c372ec207d)] - **deps**: update archs files for OpenSSL-1.1.n (Richard Lau) [#42348](https://github.com/nodejs/node/pull/42348) +* \[[`d574a1dccb`](https://github.com/nodejs/node/commit/d574a1dccb)] - **deps**: upgrade openssl sources to 1.1.1n (Richard Lau) [#42348](https://github.com/nodejs/node/pull/42348) + ## 2022-02-01, Version 12.22.10 'Erbium' (LTS), @ruyadorno diff --git a/doc/changelogs/CHANGELOG_V13.md b/doc/changelogs/CHANGELOG_V13.md index f759ed07b8beea..e2060a11dee28a 100644 --- a/doc/changelogs/CHANGELOG_V13.md +++ b/doc/changelogs/CHANGELOG_V13.md @@ -30,6 +30,7 @@ * Other Versions + * [18.x](CHANGELOG_V18.md) * [17.x](CHANGELOG_V17.md) * [16.x](CHANGELOG_V16.md) * [15.x](CHANGELOG_V15.md) diff --git a/doc/changelogs/CHANGELOG_V14.md b/doc/changelogs/CHANGELOG_V14.md index 264a3e2ff40ce8..5a08ab5b31c2ed 100644 --- a/doc/changelogs/CHANGELOG_V14.md +++ b/doc/changelogs/CHANGELOG_V14.md @@ -9,6 +9,7 @@ +14.19.1
14.19.0
14.18.3
14.18.2
@@ -53,6 +54,7 @@ * Other Versions + * [18.x](CHANGELOG_V18.md) * [17.x](CHANGELOG_V17.md) * [16.x](CHANGELOG_V16.md) * [15.x](CHANGELOG_V15.md) @@ -71,6 +73,25 @@ * [io.js](CHANGELOG_IOJS.md) * [Archive](CHANGELOG_ARCHIVE.md) + + +## 2022-03-17, Version 14.19.1 'Fermium' (LTS), @richardlau + +This is a security release. + +### Notable Changes + +Update to OpenSSL 1.1.1n, which addresses the following vulnerability: + +* Infinite loop in `BN_mod_sqrt()` reachable when parsing certificates (High)(CVE-2022-0778) + More details are available at + +### Commits + +* \[[`b5c52e337e`](https://github.com/nodejs/node/commit/b5c52e337e)] - **build**: pin Windows GitHub runner to windows-2019 (Richard Lau) [#42350](https://github.com/nodejs/node/pull/42350) +* \[[`3b1a0b24f0`](https://github.com/nodejs/node/commit/3b1a0b24f0)] - **deps**: update archs files for OpenSSL-1.1.1n (Richard Lau) [#42347](https://github.com/nodejs/node/pull/42347) +* \[[`c83dd99e0b`](https://github.com/nodejs/node/commit/c83dd99e0b)] - **deps**: upgrade openssl sources to 1.1.1n (Richard Lau) [#42347](https://github.com/nodejs/node/pull/42347) + ## 2022-02-01, Version 14.19.0 'Fermium' (LTS), @richardlau diff --git a/doc/changelogs/CHANGELOG_V15.md b/doc/changelogs/CHANGELOG_V15.md index 0acdea3ba80043..10d08ab75fee24 100644 --- a/doc/changelogs/CHANGELOG_V15.md +++ b/doc/changelogs/CHANGELOG_V15.md @@ -31,6 +31,7 @@ * Other Versions + * [18.x](CHANGELOG_V18.md) * [17.x](CHANGELOG_V17.md) * [16.x](CHANGELOG_V16.md) * [14.x](CHANGELOG_V14.md) diff --git a/doc/changelogs/CHANGELOG_V16.md b/doc/changelogs/CHANGELOG_V16.md index 64a1eba1166a43..ddf095c26261f8 100644 --- a/doc/changelogs/CHANGELOG_V16.md +++ b/doc/changelogs/CHANGELOG_V16.md @@ -9,6 +9,8 @@ +16.14.2
+16.14.1
16.14.0
16.13.2
16.13.1
@@ -39,6 +41,7 @@ * Other Versions + * [18.x](CHANGELOG_V18.md) * [17.x](CHANGELOG_V17.md) * [15.x](CHANGELOG_V15.md) * [14.x](CHANGELOG_V14.md) @@ -57,6 +60,204 @@ * [io.js](CHANGELOG_IOJS.md) * [Archive](CHANGELOG_ARCHIVE.md) + + +## 2022-03-17, Version 16.14.2 'Gallium' (LTS), @richardlau + +This is a security release. + +### Notable Changes + +Update to OpenSSL 1.1.1n, which addresses the following vulnerability: + +* Infinite loop in `BN_mod_sqrt()` reachable when parsing certificates (High)(CVE-2022-0778) + More details are available at + +### Commits + +* \[[`3924618c74`](https://github.com/nodejs/node/commit/3924618c74)] - **deps**: update archs files for OpenSSL-1.1.1 (Hassaan Pasha) [#42352](https://github.com/nodejs/node/pull/42352) +* \[[`7a6a870d58`](https://github.com/nodejs/node/commit/7a6a870d58)] - **deps**: upgrade openssl sources to OpenSSL\_1\_1\_1n (Hassaan Pasha) [#42352](https://github.com/nodejs/node/pull/42352) +* \[[`c533b430f4`](https://github.com/nodejs/node/commit/c533b430f4)] - **test**: fix tests affected by OpenSSL update (Michael Dawson) [#42352](https://github.com/nodejs/node/pull/42352) + + + +## 2022-03-15, Version 16.14.1 'Gallium' (LTS), @danielleadams + +### Notable changes + +* **doc**: + * add release key for Bryan English (Bryan English) [#42102](https://github.com/nodejs/node/pull/42102) + +### Commits + +* \[[`2a24e763d5`](https://github.com/nodejs/node/commit/2a24e763d5)] - **async\_hooks**: fix imports in context example (Yash Ladha) [#39229](https://github.com/nodejs/node/pull/39229) +* \[[`c4a296f59b`](https://github.com/nodejs/node/commit/c4a296f59b)] - **benchmark**: enable no-empty ESLint rule (Rich Trott) [#41831](https://github.com/nodejs/node/pull/41831) +* \[[`abe2eb9fc0`](https://github.com/nodejs/node/commit/abe2eb9fc0)] - **benchmark**: avoid input param manipulation (Jithil P Ponnan) [#41741](https://github.com/nodejs/node/pull/41741) +* \[[`2c566a9830`](https://github.com/nodejs/node/commit/2c566a9830)] - **benchmark**: use Object.hasOwn() instead of hasOwnProperty() (Rich Trott) [#41769](https://github.com/nodejs/node/pull/41769) +* \[[`b77e72ab12`](https://github.com/nodejs/node/commit/b77e72ab12)] - **benchmark**: replace hasOwnProperty() with Object.hasOwn() (Rich Trott) [#41724](https://github.com/nodejs/node/pull/41724) +* \[[`ec72cb4019`](https://github.com/nodejs/node/commit/ec72cb4019)] - **benchmark**: remove unreachable code from crypto/hash-stream-creation (Rich Trott) [#41535](https://github.com/nodejs/node/pull/41535) +* \[[`14bb6f97f0`](https://github.com/nodejs/node/commit/14bb6f97f0)] - **buffer**: fix atob/btoa no-arg case (Benjamin Gruenbaum) [#41478](https://github.com/nodejs/node/pull/41478) +* \[[`79e2ab2a67`](https://github.com/nodejs/node/commit/79e2ab2a67)] - **build**: remove windows-2022 from v16.x actions (Danielle Adams) [#42299](https://github.com/nodejs/node/pull/42299) +* \[[`2893b4c85a`](https://github.com/nodejs/node/commit/2893b4c85a)] - **build**: check if python is a executable program (himself65) [#36696](https://github.com/nodejs/node/pull/36696) +* \[[`5e4fc04821`](https://github.com/nodejs/node/commit/5e4fc04821)] - **build**: enable zoslib installation on z/OS (alexcfyung) [#41493](https://github.com/nodejs/node/pull/41493) +* \[[`1e3c9ebaae`](https://github.com/nodejs/node/commit/1e3c9ebaae)] - **build**: fix libuv builds for android aarch64 (Darshan Sen) [#41555](https://github.com/nodejs/node/pull/41555) +* \[[`46f714f92a`](https://github.com/nodejs/node/commit/46f714f92a)] - **crypto**: check return code from EVP\_DigestUpdate (Michael Dawson) [#41800](https://github.com/nodejs/node/pull/41800) +* \[[`33abbf9f21`](https://github.com/nodejs/node/commit/33abbf9f21)] - **crypto**: fix `webcrypto.subtle` signature (Antoine du Hamel) [#41761](https://github.com/nodejs/node/pull/41761) +* \[[`faceae486b`](https://github.com/nodejs/node/commit/faceae486b)] - **crypto**: revise variables for const use instead of let (Rich Trott) [#41614](https://github.com/nodejs/node/pull/41614) +* \[[`fe0f9dc611`](https://github.com/nodejs/node/commit/fe0f9dc611)] - **crypto**: remove wildcard options for checkEmail (Tobias Nießen) [#41599](https://github.com/nodejs/node/pull/41599) +* \[[`188c3ab918`](https://github.com/nodejs/node/commit/188c3ab918)] - **crypto**: adjust types for getRandomValues (LiviaMedeiros) [#41481](https://github.com/nodejs/node/pull/41481) +* \[[`1ef28f1a3d`](https://github.com/nodejs/node/commit/1ef28f1a3d)] - **crypto**: remove checkIP options argument (Tobias Nießen) [#41571](https://github.com/nodejs/node/pull/41571) +* \[[`74c0464572`](https://github.com/nodejs/node/commit/74c0464572)] - **deps**: upgrade npm to 8.5.0 (npm-robot) [#41925](https://github.com/nodejs/node/pull/41925) +* \[[`b5783288d1`](https://github.com/nodejs/node/commit/b5783288d1)] - **deps**: upgrade npm to 8.4.1 (npm-robot) [#41836](https://github.com/nodejs/node/pull/41836) +* \[[`2b7c4b4afe`](https://github.com/nodejs/node/commit/2b7c4b4afe)] - **deps**: upgrade npm to 8.3.2 (npm team) [#41621](https://github.com/nodejs/node/pull/41621) +* \[[`906247933c`](https://github.com/nodejs/node/commit/906247933c)] - **dgram**: remove unreachable connectState assign (Rongjian Zhang) [#38590](https://github.com/nodejs/node/pull/38590) +* \[[`330c1bc518`](https://github.com/nodejs/node/commit/330c1bc518)] - **doc**: add comments to empty blocks in worker\_threads text (Rich Trott) [#41831](https://github.com/nodejs/node/pull/41831) +* \[[`125ed0c6b0`](https://github.com/nodejs/node/commit/125ed0c6b0)] - **doc**: remove empty block from console.timeEnd() example (Rich Trott) [#41831](https://github.com/nodejs/node/pull/41831) +* \[[`34d6f8e793`](https://github.com/nodejs/node/commit/34d6f8e793)] - **doc**: use the same case as the section heading (Mestery) [#41876](https://github.com/nodejs/node/pull/41876) +* \[[`fd28d252fa`](https://github.com/nodejs/node/commit/fd28d252fa)] - **doc**: use Oxford comma in crypto docs (Tobias Nießen) [#41875](https://github.com/nodejs/node/pull/41875) +* \[[`bf99ef8b57`](https://github.com/nodejs/node/commit/bf99ef8b57)] - **doc**: use sentence case in readme introduction (Mestery) [#41874](https://github.com/nodejs/node/pull/41874) +* \[[`b15d9c2cc6`](https://github.com/nodejs/node/commit/b15d9c2cc6)] - **doc**: add missing space before hyphen (Mestery) [#41873](https://github.com/nodejs/node/pull/41873) +* \[[`77685d5ab0`](https://github.com/nodejs/node/commit/77685d5ab0)] - **doc**: add stream pipelining note on Http usage (Rafael Silva) [#41796](https://github.com/nodejs/node/pull/41796) +* \[[`c7bae97755`](https://github.com/nodejs/node/commit/c7bae97755)] - **doc**: improve SSL\_OP\_PRIORITIZE\_CHACHA description (Tobias Nießen) [#41866](https://github.com/nodejs/node/pull/41866) +* \[[`5cd38a4ff5`](https://github.com/nodejs/node/commit/5cd38a4ff5)] - **doc**: add missing commas in cluster docs (Tobias Nießen) [#41865](https://github.com/nodejs/node/pull/41865) +* \[[`cf6b5e0e33`](https://github.com/nodejs/node/commit/cf6b5e0e33)] - **doc**: add history information for Corepack (Antoine du Hamel) [#41813](https://github.com/nodejs/node/pull/41813) +* \[[`c742a1cc4d`](https://github.com/nodejs/node/commit/c742a1cc4d)] - **doc**: feature management proposal (Michael Dawson) [#41420](https://github.com/nodejs/node/pull/41420) +* \[[`3f000a2627`](https://github.com/nodejs/node/commit/3f000a2627)] - **doc**: add overhead hints for heap snapshot generation (Gerhard Stöbich) [#41822](https://github.com/nodejs/node/pull/41822) +* \[[`42c0a8353e`](https://github.com/nodejs/node/commit/42c0a8353e)] - **doc**: fix X509 CA acronym capitalization (Tobias Nießen) [#41841](https://github.com/nodejs/node/pull/41841) +* \[[`f5b0a3be12`](https://github.com/nodejs/node/commit/f5b0a3be12)] - **doc**: use sentence case for X509 error codes header (Tobias Nießen) [#41829](https://github.com/nodejs/node/pull/41829) +* \[[`20d8fd1a83`](https://github.com/nodejs/node/commit/20d8fd1a83)] - **doc**: add initial version of maintaining-http.md (Michael Dawson) [#41798](https://github.com/nodejs/node/pull/41798) +* \[[`077fcee008`](https://github.com/nodejs/node/commit/077fcee008)] - **doc**: add registry numbers for Electron 19 and 20 (Keeley Hammond) [#41814](https://github.com/nodejs/node/pull/41814) +* \[[`44b6927179`](https://github.com/nodejs/node/commit/44b6927179)] - **doc**: add note about resource type in async\_hooks (Tony Gorez) [#41797](https://github.com/nodejs/node/pull/41797) +* \[[`1be701c9ca`](https://github.com/nodejs/node/commit/1be701c9ca)] - **doc**: use example.com for examples (Ateş Göral) [#41827](https://github.com/nodejs/node/pull/41827) +* \[[`4660c1fa7b`](https://github.com/nodejs/node/commit/4660c1fa7b)] - **doc**: align tls port types with net port types (Tobias Nießen) [#41799](https://github.com/nodejs/node/pull/41799) +* \[[`5cd8bdc4d7`](https://github.com/nodejs/node/commit/5cd8bdc4d7)] - **doc**: use UDPv4/UDPv6 consistently with TCPv4/TCPv6 (Tobias Nießen) [#41824](https://github.com/nodejs/node/pull/41824) +* \[[`3ef05a0216`](https://github.com/nodejs/node/commit/3ef05a0216)] - **doc**: improve wording surrounding TLS 1.3 ciphers (Tobias Nießen) [#41778](https://github.com/nodejs/node/pull/41778) +* \[[`51d955368e`](https://github.com/nodejs/node/commit/51d955368e)] - **doc**: add format-md step to release guide (Danielle Adams) [#41809](https://github.com/nodejs/node/pull/41809) +* \[[`8f00e5dcf7`](https://github.com/nodejs/node/commit/8f00e5dcf7)] - **doc**: add v16 changelog link to iojs changelog (Danielle Adams) [#41808](https://github.com/nodejs/node/pull/41808) +* \[[`4f194f3094`](https://github.com/nodejs/node/commit/4f194f3094)] - **doc**: add security-steward rotation information (Michael Dawson) [#41707](https://github.com/nodejs/node/pull/41707) +* \[[`14ea8fcba8`](https://github.com/nodejs/node/commit/14ea8fcba8)] - **doc**: use Object.hasOwn() in util doc (Rich Trott) [#41780](https://github.com/nodejs/node/pull/41780) +* \[[`9f77692491`](https://github.com/nodejs/node/commit/9f77692491)] - **doc**: remove section on "recent" ECDH changes (Tobias Nießen) [#41773](https://github.com/nodejs/node/pull/41773) +* \[[`211a3c4c4c`](https://github.com/nodejs/node/commit/211a3c4c4c)] - **doc**: clarify that import also uses main (Ben McCann) [#41720](https://github.com/nodejs/node/pull/41720) +* \[[`20d9c4a2c5`](https://github.com/nodejs/node/commit/20d9c4a2c5)] - **doc**: update modules.md wording (Tobias Hernstig) [#41728](https://github.com/nodejs/node/pull/41728) +* \[[`e209f53ba2`](https://github.com/nodejs/node/commit/e209f53ba2)] - **doc**: update Mesteery email (Mestery) [#41683](https://github.com/nodejs/node/pull/41683) +* \[[`db1ce43173`](https://github.com/nodejs/node/commit/db1ce43173)] - **doc**: avoid incomplete sentence in cluster docs (Tobias Nießen) [#41701](https://github.com/nodejs/node/pull/41701) +* \[[`ee79e53821`](https://github.com/nodejs/node/commit/ee79e53821)] - **doc**: fix typo in contributing guides (Yoshiki Kurihara) [#41723](https://github.com/nodejs/node/pull/41723) +* \[[`9616fd5913`](https://github.com/nodejs/node/commit/9616fd5913)] - **doc**: improve docs to give descriptive info for the platform property (Harshil jain) [#41650](https://github.com/nodejs/node/pull/41650) +* \[[`4d8ee8e3cd`](https://github.com/nodejs/node/commit/4d8ee8e3cd)] - **doc**: fix link to npm documentation (Antoine du Hamel) [#41712](https://github.com/nodejs/node/pull/41712) +* \[[`018ec32535`](https://github.com/nodejs/node/commit/018ec32535)] - **doc**: clarify treatment of non-string base in URL() (Rich Trott) [#41685](https://github.com/nodejs/node/pull/41685) +* \[[`92e6cf03fe`](https://github.com/nodejs/node/commit/92e6cf03fe)] - **doc**: fix typo in `technical-priorities.md` (Akhil Marsonya) [#41694](https://github.com/nodejs/node/pull/41694) +* \[[`071fef50e5`](https://github.com/nodejs/node/commit/071fef50e5)] - **doc**: remove unadvisable cluster example (Tobias Nießen) [#41668](https://github.com/nodejs/node/pull/41668) +* \[[`b63fb0ffb8`](https://github.com/nodejs/node/commit/b63fb0ffb8)] - **doc**: document flow for supporting type generation (Michael Dawson) [#41464](https://github.com/nodejs/node/pull/41464) +* \[[`364811aa8a`](https://github.com/nodejs/node/commit/364811aa8a)] - **doc**: clarify parameter for napi\_get\_cb\_info (Michael Dawson) [#41635](https://github.com/nodejs/node/pull/41635) +* \[[`1bd286e978`](https://github.com/nodejs/node/commit/1bd286e978)] - **doc**: revise url.resolve() text (Rich Trott) [#41661](https://github.com/nodejs/node/pull/41661) +* \[[`59f95fe4dc`](https://github.com/nodejs/node/commit/59f95fe4dc)] - **doc**: clarify treatment of non-string argument to new URL() (Rich Trott) [#41658](https://github.com/nodejs/node/pull/41658) +* \[[`3e93cc392e`](https://github.com/nodejs/node/commit/3e93cc392e)] - **doc**: fix documentation for `MODULE_NOT_FOUND` and `ERR_MODULE_NOT_FOUND` (Antoine du Hamel) [#41645](https://github.com/nodejs/node/pull/41645) +* \[[`b9d1cb7f8a`](https://github.com/nodejs/node/commit/b9d1cb7f8a)] - **doc**: improve TLS/SSL introduction (Tobias Nießen) [#41649](https://github.com/nodejs/node/pull/41649) +* \[[`5d9c83e2e9`](https://github.com/nodejs/node/commit/5d9c83e2e9)] - **doc**: modernize and simplify cluster example (Tobias Nießen) [#41626](https://github.com/nodejs/node/pull/41626) +* \[[`d5efecd64d`](https://github.com/nodejs/node/commit/d5efecd64d)] - **doc**: simplify readline/stdin text (Rich Trott) [#41583](https://github.com/nodejs/node/pull/41583) +* \[[`931be52589`](https://github.com/nodejs/node/commit/931be52589)] - **doc**: suggest worker threads in cluster docs (Tobias Nießen) [#41616](https://github.com/nodejs/node/pull/41616) +* \[[`b2a4614a0d`](https://github.com/nodejs/node/commit/b2a4614a0d)] - **doc**: add 16 and 17 to previous versions (Antoine du Hamel) [#41646](https://github.com/nodejs/node/pull/41646) +* \[[`5f0a017a02`](https://github.com/nodejs/node/commit/5f0a017a02)] - **doc**: improve `'hex'` Buffer decoding description and examples (Giora Guttsait) [#41598](https://github.com/nodejs/node/pull/41598) +* \[[`0805068add`](https://github.com/nodejs/node/commit/0805068add)] - **doc**: add note for handling signal events in trace events (Gabriel Trujillo) [#41438](https://github.com/nodejs/node/pull/41438) +* \[[`0388b9afc3`](https://github.com/nodejs/node/commit/0388b9afc3)] - **doc**: demonstrate dangers of `buffer.slice()` (Shalvah) [#41628](https://github.com/nodejs/node/pull/41628) +* \[[`3cdd1d634b`](https://github.com/nodejs/node/commit/3cdd1d634b)] - **doc**: add missing word in cluster.workers details (Tobias Nießen) [#41624](https://github.com/nodejs/node/pull/41624) +* \[[`5d94bc676e`](https://github.com/nodejs/node/commit/5d94bc676e)] - **doc**: fix async\_hooks example in api docs (Akhil Marsonya) [#41609](https://github.com/nodejs/node/pull/41609) +* \[[`39f52e1130`](https://github.com/nodejs/node/commit/39f52e1130)] - **doc**: fix deprecated alias description in cluster (Tobias Nießen) [#41618](https://github.com/nodejs/node/pull/41618) +* \[[`55714cc777`](https://github.com/nodejs/node/commit/55714cc777)] - **doc**: update timingSafeEqual error case (Alex Agranov) [#41507](https://github.com/nodejs/node/pull/41507) +* \[[`9f8e442dc7`](https://github.com/nodejs/node/commit/9f8e442dc7)] - **doc**: simplify util.TextDecoder example (Rich Trott) [#41574](https://github.com/nodejs/node/pull/41574) +* \[[`57dc5956b1`](https://github.com/nodejs/node/commit/57dc5956b1)] - **doc**: move Mesteery to collaborators (Tobias Nießen) [#41597](https://github.com/nodejs/node/pull/41597) +* \[[`10320c2965`](https://github.com/nodejs/node/commit/10320c2965)] - **doc**: fix cjs example code for process.arch (Job) [#41593](https://github.com/nodejs/node/pull/41593) +* \[[`f33e831fe3`](https://github.com/nodejs/node/commit/f33e831fe3)] - **doc**: remove redunant `await` calls from stream docs (Giora Guttsait) [#41592](https://github.com/nodejs/node/pull/41592) +* \[[`1cf74beb57`](https://github.com/nodejs/node/commit/1cf74beb57)] - **doc**: make contributing info more discoverable (Michael Dawson) [#41408](https://github.com/nodejs/node/pull/41408) +* \[[`214cf17db9`](https://github.com/nodejs/node/commit/214cf17db9)] - **doc**: recommend package exports instead of requiring folders (Antoine du Hamel) [#41381](https://github.com/nodejs/node/pull/41381) +* \[[`5c387a0d75`](https://github.com/nodejs/node/commit/5c387a0d75)] - **doc**: edit async\_context context loss text (Rich Trott) [#41550](https://github.com/nodejs/node/pull/41550) +* \[[`01283f6b25`](https://github.com/nodejs/node/commit/01283f6b25)] - **doc**: use sentence case for Web Crypto headers (Tobias Nießen) [#41577](https://github.com/nodejs/node/pull/41577) +* \[[`6b6d0c4914`](https://github.com/nodejs/node/commit/6b6d0c4914)] - **doc**: make Web Crypto example spec compliant (Tobias Nießen) [#41556](https://github.com/nodejs/node/pull/41556) +* \[[`8772d332d7`](https://github.com/nodejs/node/commit/8772d332d7)] - **doc**: do not reference SSL when discussing SNI (Tobias Nießen) [#41549](https://github.com/nodejs/node/pull/41549) +* \[[`82042d0094`](https://github.com/nodejs/node/commit/82042d0094)] - **doc**: fix typos in esm.md (Yu) [#41499](https://github.com/nodejs/node/pull/41499) +* \[[`ff0069dc3e`](https://github.com/nodejs/node/commit/ff0069dc3e)] - **doc**: adjust assignment in condition in stream doc (Rich Trott) [#41510](https://github.com/nodejs/node/pull/41510) +* \[[`1128b1c216`](https://github.com/nodejs/node/commit/1128b1c216)] - **doc**: improve Web Crypto headings related to ECC (Tobias Nießen) [#41542](https://github.com/nodejs/node/pull/41542) +* \[[`a6758d12e3`](https://github.com/nodejs/node/commit/a6758d12e3)] - **doc**: clarify module system selection (Antoine du Hamel) [#41383](https://github.com/nodejs/node/pull/41383) +* \[[`db17a529a8`](https://github.com/nodejs/node/commit/db17a529a8)] - **doc**: add release key for Bryan English (Bryan English) [#42102](https://github.com/nodejs/node/pull/42102) +* \[[`f2ca172a08`](https://github.com/nodejs/node/commit/f2ca172a08)] - **doc**: remove statement about (EC)DHE performance (Tobias Nießen) [#41528](https://github.com/nodejs/node/pull/41528) +* \[[`227dea8dc1`](https://github.com/nodejs/node/commit/227dea8dc1)] - **domain**: pass opts to `EventEmitter.init` (Chen Gang) [#41414](https://github.com/nodejs/node/pull/41414) +* \[[`bd717064b0`](https://github.com/nodejs/node/commit/bd717064b0)] - **esm**: improve validation of resolved URLs (Jacob Smith) [#41446](https://github.com/nodejs/node/pull/41446) +* \[[`e747ef5e45`](https://github.com/nodejs/node/commit/e747ef5e45)] - **http2**: fix pseudo-headers order (ofir) [#41735](https://github.com/nodejs/node/pull/41735) +* \[[`2efe9cbd01`](https://github.com/nodejs/node/commit/2efe9cbd01)] - **http2**: fix no response event on continue request (ofirbarak) [#41739](https://github.com/nodejs/node/pull/41739) +* \[[`7bf2be51b3`](https://github.com/nodejs/node/commit/7bf2be51b3)] - **http2**: fix memory leak on nghttp2 hd threshold (Rafael Silva) [#41502](https://github.com/nodejs/node/pull/41502) +* \[[`acd8768802`](https://github.com/nodejs/node/commit/acd8768802)] - **lib**: add comments to empty catch statements (Rich Trott) [#41831](https://github.com/nodejs/node/pull/41831) +* \[[`c90bb7cd93`](https://github.com/nodejs/node/commit/c90bb7cd93)] - **lib**: refactor to use `validateObject()` validator (Mohammed Keyvanzadeh) [#41845](https://github.com/nodejs/node/pull/41845) +* \[[`c93a9af82b`](https://github.com/nodejs/node/commit/c93a9af82b)] - **lib**: refactor source map stack trace prepare (Mohammed Keyvanzadeh) [#41698](https://github.com/nodejs/node/pull/41698) +* \[[`0f3287dc44`](https://github.com/nodejs/node/commit/0f3287dc44)] - **lib**: fix consistency of methods that emit warnings (Yoshiki Kurihara) [#41249](https://github.com/nodejs/node/pull/41249) +* \[[`7ee3cdf60a`](https://github.com/nodejs/node/commit/7ee3cdf60a)] - **lib**: remove erroneous JSDoc entry (Rich Trott) [#41604](https://github.com/nodejs/node/pull/41604) +* \[[`70f6554403`](https://github.com/nodejs/node/commit/70f6554403)] - **meta**: update AUTHORS (Node.js GitHub Bot) [#41868](https://github.com/nodejs/node/pull/41868) +* \[[`a44a8ff767`](https://github.com/nodejs/node/commit/a44a8ff767)] - **meta**: update AUTHORS (Node.js GitHub Bot) [#41763](https://github.com/nodejs/node/pull/41763) +* \[[`ba0ba7c4b2`](https://github.com/nodejs/node/commit/ba0ba7c4b2)] - **meta**: update mailmap/AUTHORS info for existing collaborator (Rich Trott) [#41750](https://github.com/nodejs/node/pull/41750) +* \[[`30e3327b46`](https://github.com/nodejs/node/commit/30e3327b46)] - **meta**: adjust mailmap/AUTHORS to reflect README change (Rich Trott) [#41751](https://github.com/nodejs/node/pull/41751) +* \[[`6d268fd32e`](https://github.com/nodejs/node/commit/6d268fd32e)] - **meta**: update AUTHORS (Node.js GitHub Bot) [#41659](https://github.com/nodejs/node/pull/41659) +* \[[`18e6316bf1`](https://github.com/nodejs/node/commit/18e6316bf1)] - **meta**: update AUTHORS (Node.js GitHub Bot) [#41548](https://github.com/nodejs/node/pull/41548) +* \[[`e1e059a698`](https://github.com/nodejs/node/commit/e1e059a698)] - **perf\_hooks**: remove useless calls in Histogram (Michael Dawson) [#41579](https://github.com/nodejs/node/pull/41579) +* \[[`08b3bd2fc5`](https://github.com/nodejs/node/commit/08b3bd2fc5)] - **policy**: revise manifest.js to avoid empty blocks (Rich Trott) [#41831](https://github.com/nodejs/node/pull/41831) +* \[[`33f3391a8f`](https://github.com/nodejs/node/commit/33f3391a8f)] - **policy**: check for null instead of falsy in loop (Rich Trott) [#41614](https://github.com/nodejs/node/pull/41614) +* \[[`b8b8e0bce6`](https://github.com/nodejs/node/commit/b8b8e0bce6)] - **policy**: replace entries with keys (Mohammed Keyvanzadeh) [#41482](https://github.com/nodejs/node/pull/41482) +* \[[`ee61bc74b7`](https://github.com/nodejs/node/commit/ee61bc74b7)] - **process**: unhandledRejection support more errors (Benjamin Gruenbaum) [#41682](https://github.com/nodejs/node/pull/41682) +* \[[`f066246729`](https://github.com/nodejs/node/commit/f066246729)] - **process**: check for null instead of falsy in while loop (Rich Trott) [#41614](https://github.com/nodejs/node/pull/41614) +* \[[`77cb604c0d`](https://github.com/nodejs/node/commit/77cb604c0d)] - **process**: use validateString validator (Mohammed Keyvanzadeh) [#41595](https://github.com/nodejs/node/pull/41595) +* \[[`76281f9a81`](https://github.com/nodejs/node/commit/76281f9a81)] - **process**: ignore asyncId 0 in exception handler (Anatoli Papirovski) [#41424](https://github.com/nodejs/node/pull/41424) +* \[[`dacffd3e9c`](https://github.com/nodejs/node/commit/dacffd3e9c)] - **repl**: check for precise values rather than falsy in loops (Rich Trott) [#41614](https://github.com/nodejs/node/pull/41614) +* \[[`5e595683ce`](https://github.com/nodejs/node/commit/5e595683ce)] - **src**: slightly simplify URLHost::ToString (Anna Henningsen) [#41747](https://github.com/nodejs/node/pull/41747) +* \[[`206c370d03`](https://github.com/nodejs/node/commit/206c370d03)] - **src**: slightly simplify V8CoverageConnection::GetFilename (Anna Henningsen) [#41748](https://github.com/nodejs/node/pull/41748) +* \[[`1cfc63ebe3`](https://github.com/nodejs/node/commit/1cfc63ebe3)] - **src**: fix typo in js\_native\_api\_v8.cc (Caio Agiani) [#41764](https://github.com/nodejs/node/pull/41764) +* \[[`aebd82ea7c`](https://github.com/nodejs/node/commit/aebd82ea7c)] - **stream**: remove empty block (Rich Trott) [#41831](https://github.com/nodejs/node/pull/41831) +* \[[`46ed078607`](https://github.com/nodejs/node/commit/46ed078607)] - **stream**: resume stream on drain (Robert Nagy) [#41848](https://github.com/nodejs/node/pull/41848) +* \[[`363c760c85`](https://github.com/nodejs/node/commit/363c760c85)] - **stream**: check for null instead of falsy in loops (Rich Trott) [#41614](https://github.com/nodejs/node/pull/41614) +* \[[`4f4fec4b22`](https://github.com/nodejs/node/commit/4f4fec4b22)] - **stream**: rename unknown primordial (Mohammed Keyvanzadeh) [#40622](https://github.com/nodejs/node/pull/40622) +* \[[`1425e75093`](https://github.com/nodejs/node/commit/1425e75093)] - **stream**: avoid function call where possible (Rich Trott) [#41534](https://github.com/nodejs/node/pull/41534) +* \[[`ecb52636a4`](https://github.com/nodejs/node/commit/ecb52636a4)] - **test**: renew certificates for specific test (Luigi Pinca) [#42342](https://github.com/nodejs/node/pull/42342) +* \[[`c8e59cbf9e`](https://github.com/nodejs/node/commit/c8e59cbf9e)] - **test**: enable no-empty ESLint rule (Rich Trott) [#41831](https://github.com/nodejs/node/pull/41831) +* \[[`20ec77688f`](https://github.com/nodejs/node/commit/20ec77688f)] - **test**: remove eslint-disable comments from fixtures (Rich Trott) [#41859](https://github.com/nodejs/node/pull/41859) +* \[[`a8e41837cc`](https://github.com/nodejs/node/commit/a8e41837cc)] - **test**: remove test-worker-memory flaky designation (Rich Trott) [#41867](https://github.com/nodejs/node/pull/41867) +* \[[`673c1fd5ae`](https://github.com/nodejs/node/commit/673c1fd5ae)] - **test**: avoid using Object.prototype methods directly on objects (Rich Trott) [#41801](https://github.com/nodejs/node/pull/41801) +* \[[`3690d3402d`](https://github.com/nodejs/node/commit/3690d3402d)] - **test**: exclude ibm i tests until we resolve (Michael Dawson) [#41812](https://github.com/nodejs/node/pull/41812) +* \[[`1f65620543`](https://github.com/nodejs/node/commit/1f65620543)] - **test**: make worker-take-heapsnapshot non-flaky (Michael Dawson) [#41684](https://github.com/nodejs/node/pull/41684) +* \[[`badab79527`](https://github.com/nodejs/node/commit/badab79527)] - **test**: mark test-fs-rmdir-recursive flaky on win (Michael Dawson) [#41533](https://github.com/nodejs/node/pull/41533) +* \[[`951d299aee`](https://github.com/nodejs/node/commit/951d299aee)] - **test**: make fs watch test more stable (Benjamin Gruenbaum) [#41715](https://github.com/nodejs/node/pull/41715) +* \[[`acea61ba8e`](https://github.com/nodejs/node/commit/acea61ba8e)] - **test**: fix typo in MessageChannel test (Tobias Nießen) [#41746](https://github.com/nodejs/node/pull/41746) +* \[[`081989b6b7`](https://github.com/nodejs/node/commit/081989b6b7)] - **test**: replace commented out expectations with tests (Darshan Sen) [#41667](https://github.com/nodejs/node/pull/41667) +* \[[`639130e635`](https://github.com/nodejs/node/commit/639130e635)] - **test**: use Object.hasOwn() where applicable (Rich Trott) [#41664](https://github.com/nodejs/node/pull/41664) +* \[[`cb362a3748`](https://github.com/nodejs/node/commit/cb362a3748)] - **test**: remove unneeded test statement (Rich Trott) [#41663](https://github.com/nodejs/node/pull/41663) +* \[[`2b87f9784f`](https://github.com/nodejs/node/commit/2b87f9784f)] - **test**: remove error allowance in debugger test (Jithil P Ponnan) [#41640](https://github.com/nodejs/node/pull/41640) +* \[[`55fce66af6`](https://github.com/nodejs/node/commit/55fce66af6)] - **test**: simplify test-gc-http-client (Luigi Pinca) [#41620](https://github.com/nodejs/node/pull/41620) +* \[[`b06c33b14b`](https://github.com/nodejs/node/commit/b06c33b14b)] - **test**: prepare tests for no-cond-assign ESLint rule (Rich Trott) [#41614](https://github.com/nodejs/node/pull/41614) +* \[[`950648db48`](https://github.com/nodejs/node/commit/950648db48)] - **test**: move test-gc-http-client-onerror to sequential (Luigi Pinca) [#41619](https://github.com/nodejs/node/pull/41619) +* \[[`1d3ef115ca`](https://github.com/nodejs/node/commit/1d3ef115ca)] - **test**: improve test coverage of internal/worker/io (Yoshiki Kurihara) [#41511](https://github.com/nodejs/node/pull/41511) +* \[[`122eb51c98`](https://github.com/nodejs/node/commit/122eb51c98)] - **test**: add DataView test entry for whatwg (Mohammed Keyvanzadeh) [#40622](https://github.com/nodejs/node/pull/40622) +* \[[`2c813d825f`](https://github.com/nodejs/node/commit/2c813d825f)] - **test**: improve util-format code coverage (Rich Trott) [#41572](https://github.com/nodejs/node/pull/41572) +* \[[`fab831a3fe`](https://github.com/nodejs/node/commit/fab831a3fe)] - **test**: fix typo in test\_js\_native\_api\_v8 (Tobias Nießen) [#41584](https://github.com/nodejs/node/pull/41584) +* \[[`9e7cfbbcd9`](https://github.com/nodejs/node/commit/9e7cfbbcd9)] - **test**: add missing await in fs-rm/fs-rmdir tests (Benjamin Coe) [#41545](https://github.com/nodejs/node/pull/41545) +* \[[`a8558ecfcf`](https://github.com/nodejs/node/commit/a8558ecfcf)] - **test**: add coverage for util.inspect() (Rich Trott) [#41527](https://github.com/nodejs/node/pull/41527) +* \[[`23fc205586`](https://github.com/nodejs/node/commit/23fc205586)] - **test**: avoid deep comparisons with literals (Tobias Nießen) [#40634](https://github.com/nodejs/node/pull/40634) +* \[[`63a67f8dad`](https://github.com/nodejs/node/commit/63a67f8dad)] - **timers**: check for nullish instead of falsy in loops (Rich Trott) [#41614](https://github.com/nodejs/node/pull/41614) +* \[[`788e77cb37`](https://github.com/nodejs/node/commit/788e77cb37)] - **tools**: enable no-empty ESLint rule (Rich Trott) [#41831](https://github.com/nodejs/node/pull/41831) +* \[[`10e6c70d14`](https://github.com/nodejs/node/commit/10e6c70d14)] - **tools**: update lint-md-dependencies to rollup\@2.67.0 (Node.js GitHub Bot) [#41737](https://github.com/nodejs/node/pull/41737) +* \[[`20cdf78fd8`](https://github.com/nodejs/node/commit/20cdf78fd8)] - **tools**: update doc to rehype-stringify\@9.0.3 (Node.js GitHub Bot) [#41854](https://github.com/nodejs/node/pull/41854) +* \[[`2eabfdd066`](https://github.com/nodejs/node/commit/2eabfdd066)] - **tools**: update eslint to 8.8.0 (Node.js GitHub Bot) [#41738](https://github.com/nodejs/node/pull/41738) +* \[[`9d23a27268`](https://github.com/nodejs/node/commit/9d23a27268)] - **tools**: use Set instead of { \[key]: true } object (Tobias Nießen) [#41695](https://github.com/nodejs/node/pull/41695) +* \[[`7e4d455fe4`](https://github.com/nodejs/node/commit/7e4d455fe4)] - **tools**: add compile\_commands to ignore file (Yash Ladha) [#41580](https://github.com/nodejs/node/pull/41580) +* \[[`1cbdc984fb`](https://github.com/nodejs/node/commit/1cbdc984fb)] - **tools**: use Set instead of { \[key]: true } object (Tobias Nießen) [#41675](https://github.com/nodejs/node/pull/41675) +* \[[`dc854c4f38`](https://github.com/nodejs/node/commit/dc854c4f38)] - **tools**: fix typo in `tools/code_cache/README.md` (Tobias Nießen) [#41657](https://github.com/nodejs/node/pull/41657) +* \[[`b17aa25f12`](https://github.com/nodejs/node/commit/b17aa25f12)] - **tools**: enable no-cond-assign-ESLint rule (Rich Trott) [#41614](https://github.com/nodejs/node/pull/41614) +* \[[`9601b8ddd6`](https://github.com/nodejs/node/commit/9601b8ddd6)] - **tools**: update lint-md-dependencies to rollup\@2.65.0 (Node.js GitHub Bot) [#41638](https://github.com/nodejs/node/pull/41638) +* \[[`cdbe291e5b`](https://github.com/nodejs/node/commit/cdbe291e5b)] - **tools**: increase maximum line length to 120 characters (Rich Trott) [#41586](https://github.com/nodejs/node/pull/41586) +* \[[`7cbc472ed5`](https://github.com/nodejs/node/commit/7cbc472ed5)] - **tools**: add missing `.PHONY` and `.NOTPARALLEL` targets in `Makefile` (Antoine du Hamel) [#41515](https://github.com/nodejs/node/pull/41515) +* \[[`6fccd66b34`](https://github.com/nodejs/node/commit/6fccd66b34)] - **tools**: update lint-md-dependencies (Node.js GitHub Bot) [#41440](https://github.com/nodejs/node/pull/41440) +* \[[`3163bd1ea0`](https://github.com/nodejs/node/commit/3163bd1ea0)] - **tools**: bump eslint from 8.6.0 to 8.7.0 (Rich Trott) [#41570](https://github.com/nodejs/node/pull/41570) +* \[[`e439f32a4b`](https://github.com/nodejs/node/commit/e439f32a4b)] - **tools**: update doc to highlight.js\@11.4.0 to-vfile\@7.2.3 (Node.js GitHub Bot) [#41441](https://github.com/nodejs/node/pull/41441) +* \[[`66120564b2`](https://github.com/nodejs/node/commit/66120564b2)] - **tools,test**: enable no-prototype-builtins (Rich Trott) [#41801](https://github.com/nodejs/node/pull/41801) +* \[[`4aee98b03c`](https://github.com/nodejs/node/commit/4aee98b03c)] - **util**: use hasOwnProperty() primordial (Rich Trott) [#41692](https://github.com/nodejs/node/pull/41692) +* \[[`8218bab51d`](https://github.com/nodejs/node/commit/8218bab51d)] - **util**: remove unused fast path in internal debuglog (Rich Trott) [#41605](https://github.com/nodejs/node/pull/41605) +* \[[`a4ad26d4dc`](https://github.com/nodejs/node/commit/a4ad26d4dc)] - **util**: check for null instead of flasy in loop (Rich Trott) [#41614](https://github.com/nodejs/node/pull/41614) + ## 2022-02-08, Version 16.14.0 'Gallium' (LTS), @danielleadams @@ -2247,6 +2448,7 @@ Contributed by Michaël Zasso - [#37587](https://github.com/nodejs/node/pull/375 * **deps**: update llhttp to 6.0.0 (Fedor Indutny) [#38277](https://github.com/nodejs/node/pull/38277) * **deps**: upgrade npm to 7.10.0 (Ruy Adorno) [#38254](https://github.com/nodejs/node/pull/38254) * **(SEMVER-MINOR)** **http**: add http.ClientRequest.getRawHeaderNames() (simov) [#37660](https://github.com/nodejs/node/pull/37660) +* **(SEMVER-MAJOR)** **http**: use `autoDestroy: true` in incoming message (Daniele Belardi) [#33035](https://github.com/nodejs/node/pull/33035) * **(SEMVER-MAJOR)** **lib,src**: update cluster to use Parent (Michael Dawson) [#36478](https://github.com/nodejs/node/pull/36478) * **(SEMVER-MINOR)** **module**: add support for `node:`‑prefixed `require(…)` calls (ExE Boss) [#37246](https://github.com/nodejs/node/pull/37246) * **(SEMVER-MINOR)** **perf\_hooks**: add histogram option to timerify (James M Snell) [#37475](https://github.com/nodejs/node/pull/37475) diff --git a/doc/changelogs/CHANGELOG_V17.md b/doc/changelogs/CHANGELOG_V17.md index 4e47510a205b19..25f5f267b4a8ed 100644 --- a/doc/changelogs/CHANGELOG_V17.md +++ b/doc/changelogs/CHANGELOG_V17.md @@ -8,6 +8,11 @@ +17.9.0
+17.8.0
+17.7.2
+17.7.1
+17.7.0
17.6.0
17.5.0
17.4.0
@@ -22,6 +27,7 @@ * Other Versions + * [18.x](CHANGELOG_V18.md) * [16.x](CHANGELOG_V16.md) * [15.x](CHANGELOG_V15.md) * [14.x](CHANGELOG_V14.md) @@ -40,6 +46,338 @@ * [io.js](CHANGELOG_IOJS.md) * [Archive](CHANGELOG_ARCHIVE.md) + + +## 2022-04-07, Version 17.9.0 (Current), @BethGriggs prepared by @juanarbol + +### Notable Changes + +* \[[`7124f91cbf`](https://github.com/nodejs/node/commit/7124f91cbf)] - **(SEMVER-MINOR)** **crypto**: make authTagLength optional for CC20P1305 (Tobias Nießen) [#42427](https://github.com/nodejs/node/pull/42427) +* \[[`30dc6dd3fb`](https://github.com/nodejs/node/commit/30dc6dd3fb)] - **deps**: update undici to 4.16.0 (Node.js GitHub Bot) [#42414](https://github.com/nodejs/node/pull/42414) +* \[[`f0fc2744a5`](https://github.com/nodejs/node/commit/f0fc2744a5)] - **doc**: add @meixg to collaborators (Xuguang Mei) [#42576](https://github.com/nodejs/node/pull/42576) + +### Commits + +* \[[`bb71433334`](https://github.com/nodejs/node/commit/bb71433334)] - **async\_hooks**: remove destroyed symbol on Promises (Gerhard Stöbich) [#42402](https://github.com/nodejs/node/pull/42402) +* \[[`b48a6cb3f9`](https://github.com/nodejs/node/commit/b48a6cb3f9)] - **bootstrap**: reset process.\_exit and process.exitCode in pre-execution (Joyee Cheung) [#42466](https://github.com/nodejs/node/pull/42466) +* \[[`b89f038537`](https://github.com/nodejs/node/commit/b89f038537)] - **bootstrap**: run inspector and event loop in snapshot builder (Joyee Cheung) [#42466](https://github.com/nodejs/node/pull/42466) +* \[[`177558600e`](https://github.com/nodejs/node/commit/177558600e)] - **bootstrap**: make I/O streams work with user-land snapshot (Joyee Cheung) [#42466](https://github.com/nodejs/node/pull/42466) +* \[[`e3683cb34d`](https://github.com/nodejs/node/commit/e3683cb34d)] - **bootstrap**: refresh options in pre-execution (Joyee Cheung) [#42466](https://github.com/nodejs/node/pull/42466) +* \[[`d302d2f0d2`](https://github.com/nodejs/node/commit/d302d2f0d2)] - **bootstrap**: use SnapshotData to pass snapshot data around (Joyee Cheung) [#42360](https://github.com/nodejs/node/pull/42360) +* \[[`eb3dfc00f0`](https://github.com/nodejs/node/commit/eb3dfc00f0)] - **buffer**: improve Blob constructor error message when passing a string (Xuguang Mei) [#42338](https://github.com/nodejs/node/pull/42338) +* \[[`f45d5537c1`](https://github.com/nodejs/node/commit/f45d5537c1)] - **buffer**: fix `atob` input validation (Antoine du Hamel) [#42539](https://github.com/nodejs/node/pull/42539) +* \[[`fb6a5ba8d7`](https://github.com/nodejs/node/commit/fb6a5ba8d7)] - **build**: remove precompiled header and debug information for host builds (Niyas Sait) [#42538](https://github.com/nodejs/node/pull/42538) +* \[[`1f7d2e800c`](https://github.com/nodejs/node/commit/1f7d2e800c)] - **build**: windows/arm64 native compilation support (Niyas Sait) [#42408](https://github.com/nodejs/node/pull/42408) +* \[[`d9a1d7866c`](https://github.com/nodejs/node/commit/d9a1d7866c)] - **build**: consolidate JS and md linting GitHub Actions (Rich Trott) [#42572](https://github.com/nodejs/node/pull/42572) +* \[[`ecb5be845d`](https://github.com/nodejs/node/commit/ecb5be845d)] - **build**: set stale action back to running nightly (Michael Dawson) [#42549](https://github.com/nodejs/node/pull/42549) +* \[[`f9fb7f6d96`](https://github.com/nodejs/node/commit/f9fb7f6d96)] - **build**: add --node-snapshot-main configure option (Joyee Cheung) [#42466](https://github.com/nodejs/node/pull/42466) +* \[[`c6808f088b`](https://github.com/nodejs/node/commit/c6808f088b)] - **build**: bump actions/checkout (Eliaz Bobadilla) [#42460](https://github.com/nodejs/node/pull/42460) +* \[[`9a54acb7c6`](https://github.com/nodejs/node/commit/9a54acb7c6)] - **child\_process**: add env contents types in JSDoc (Rich Trott) [#42494](https://github.com/nodejs/node/pull/42494) +* \[[`a2f07380ea`](https://github.com/nodejs/node/commit/a2f07380ea)] - **crypto**: do not add undefined hash in webcrypto normalizeAlgorithm (Filip Skokan) [#42559](https://github.com/nodejs/node/pull/42559) +* \[[`9b4bd7d031`](https://github.com/nodejs/node/commit/9b4bd7d031)] - **crypto**: cleanup webcrypto jwk code (Filip Skokan) [#42562](https://github.com/nodejs/node/pull/42562) +* \[[`541a1328b0`](https://github.com/nodejs/node/commit/541a1328b0)] - **crypto**: fix webcrypto derive key lengths (Filip Skokan) [#42542](https://github.com/nodejs/node/pull/42542) +* \[[`7124f91cbf`](https://github.com/nodejs/node/commit/7124f91cbf)] - **(SEMVER-MINOR)** **crypto**: make authTagLength optional for CC20P1305 (Tobias Nießen) [#42427](https://github.com/nodejs/node/pull/42427) +* \[[`30dc6dd3fb`](https://github.com/nodejs/node/commit/30dc6dd3fb)] - **deps**: update undici to 4.16.0 (Node.js GitHub Bot) [#42414](https://github.com/nodejs/node/pull/42414) +* \[[`6e56924274`](https://github.com/nodejs/node/commit/6e56924274)] - **doc**: simplify Http2Stream encoding text (Rich Trott) [#42597](https://github.com/nodejs/node/pull/42597) +* \[[`261672b1da`](https://github.com/nodejs/node/commit/261672b1da)] - **doc**: remove obsolete stream API selection text (Rich Trott) [#42586](https://github.com/nodejs/node/pull/42586) +* \[[`beffed1880`](https://github.com/nodejs/node/commit/beffed1880)] - **doc**: remove faulty justification for 128-bit AES (Tobias Nießen) [#42578](https://github.com/nodejs/node/pull/42578) +* \[[`71f4a39086`](https://github.com/nodejs/node/commit/71f4a39086)] - **doc**: fix documentation of `FileHandle.prototype.appendFile` (Antoine du Hamel) [#42588](https://github.com/nodejs/node/pull/42588) +* \[[`c83ea22f7c`](https://github.com/nodejs/node/commit/c83ea22f7c)] - **doc**: change "OCSP Request" to "OCSP request" (Tobias Nießen) [#42582](https://github.com/nodejs/node/pull/42582) +* \[[`71ab0dea35`](https://github.com/nodejs/node/commit/71ab0dea35)] - **doc**: aes webcrypto unwrap is not a node-specific extensions (Filip Skokan) [#42561](https://github.com/nodejs/node/pull/42561) +* \[[`1c614184da`](https://github.com/nodejs/node/commit/1c614184da)] - **doc**: simplify recommendations in process.md (Rich Trott) [#42556](https://github.com/nodejs/node/pull/42556) +* \[[`c036800ddc`](https://github.com/nodejs/node/commit/c036800ddc)] - **doc**: clarify recommendations in stream.md (Rich Trott) [#42555](https://github.com/nodejs/node/pull/42555) +* \[[`dcf0abf8c7`](https://github.com/nodejs/node/commit/dcf0abf8c7)] - **doc**: simplify recommendation in webcrypto.md (Rich Trott) [#42554](https://github.com/nodejs/node/pull/42554) +* \[[`8333fa063b`](https://github.com/nodejs/node/commit/8333fa063b)] - **doc**: update DEP0102 text (Rich Trott) [#42553](https://github.com/nodejs/node/pull/42553) +* \[[`8b08bff682`](https://github.com/nodejs/node/commit/8b08bff682)] - **doc**: remove util.promisify() content in readline.md (Rich Trott) [#42552](https://github.com/nodejs/node/pull/42552) +* \[[`94492424ba`](https://github.com/nodejs/node/commit/94492424ba)] - **doc**: add introduction sentence for CJS (Antoine du Hamel) [#42491](https://github.com/nodejs/node/pull/42491) +* \[[`f0fc2744a5`](https://github.com/nodejs/node/commit/f0fc2744a5)] - **doc**: add @meixg to collaborators (Xuguang Mei) [#42576](https://github.com/nodejs/node/pull/42576) +* \[[`d935fef594`](https://github.com/nodejs/node/commit/d935fef594)] - **doc**: consolidate CI sections (Rich Trott) [#42534](https://github.com/nodejs/node/pull/42534) +* \[[`fd45df314b`](https://github.com/nodejs/node/commit/fd45df314b)] - **doc**: document breaking change in `http.IncomingMessage` `'close'` event (Paolo Insogna) [#42521](https://github.com/nodejs/node/pull/42521) +* \[[`53584fa750`](https://github.com/nodejs/node/commit/53584fa750)] - **doc**: remove extraneous comma (Rich Trott) [#42548](https://github.com/nodejs/node/pull/42548) +* \[[`b819af6509`](https://github.com/nodejs/node/commit/b819af6509)] - **doc**: guide towards x509.fingerprint256 (Tobias Nießen) [#42516](https://github.com/nodejs/node/pull/42516) +* \[[`f2355e41ed`](https://github.com/nodejs/node/commit/f2355e41ed)] - **doc**: fix internal link in collaborator-guide.md (Daeyeon Jeong) [#42551](https://github.com/nodejs/node/pull/42551) +* \[[`ffc6776996`](https://github.com/nodejs/node/commit/ffc6776996)] - **doc**: add suggestion for OpenSSL only sec releases (Michael Dawson) [#42456](https://github.com/nodejs/node/pull/42456) +* \[[`1454c0297d`](https://github.com/nodejs/node/commit/1454c0297d)] - **doc**: fix comment text in async\_hooks example (Rich Trott) [#42499](https://github.com/nodejs/node/pull/42499) +* \[[`b9ab9867f4`](https://github.com/nodejs/node/commit/b9ab9867f4)] - **doc**: add `stability` class to legacy status description (Daniel Roe) [#42525](https://github.com/nodejs/node/pull/42525) +* \[[`6c13988d53`](https://github.com/nodejs/node/commit/6c13988d53)] - **doc**: suggest checkHost in checkServerIdentity docs (Tobias Nießen) [#42495](https://github.com/nodejs/node/pull/42495) +* \[[`28665a9dd6`](https://github.com/nodejs/node/commit/28665a9dd6)] - **doc**: update security release onboarding (Joe Sepi) [#42333](https://github.com/nodejs/node/pull/42333) +* \[[`d335addf0c`](https://github.com/nodejs/node/commit/d335addf0c)] - **doc**: fix question promise API example (Xuguang Mei) [#42465](https://github.com/nodejs/node/pull/42465) +* \[[`7cf9febcb4`](https://github.com/nodejs/node/commit/7cf9febcb4)] - **doc**: remove comma splice in events.md (Rich Trott) [#42484](https://github.com/nodejs/node/pull/42484) +* \[[`3c3684d9f1`](https://github.com/nodejs/node/commit/3c3684d9f1)] - **doc**: clarify napi\_finalize behavior (Alba Mendez) [#42461](https://github.com/nodejs/node/pull/42461) +* \[[`334cc1936b`](https://github.com/nodejs/node/commit/334cc1936b)] - **doc**: expand history for conditional exports changes in v12 (Greg Poole) [#42339](https://github.com/nodejs/node/pull/42339) +* \[[`fb146f9eaf`](https://github.com/nodejs/node/commit/fb146f9eaf)] - **doc**: change comma-splice to two sentences (Rich Trott) [#42455](https://github.com/nodejs/node/pull/42455) +* \[[`ce4b823946`](https://github.com/nodejs/node/commit/ce4b823946)] - **doc**: add link to section (Rich Trott) [#42428](https://github.com/nodejs/node/pull/42428) +* \[[`5869275479`](https://github.com/nodejs/node/commit/5869275479)] - **doc**: fix typo in async\_context.md (Anupama Codippily) [#42444](https://github.com/nodejs/node/pull/42444) +* \[[`48bd9fa2c7`](https://github.com/nodejs/node/commit/48bd9fa2c7)] - **doc**: add `trace_gc` to diagnostic tooling support document (Tony Gorez) [#42346](https://github.com/nodejs/node/pull/42346) +* \[[`00f693b6b1`](https://github.com/nodejs/node/commit/00f693b6b1)] - **doc**: make header smaller and dropdown click-driven when JS is on (Paolo Insogna) [#42165](https://github.com/nodejs/node/pull/42165) +* \[[`abbb23620a`](https://github.com/nodejs/node/commit/abbb23620a)] - **doc**: standardize typography for \_semantic versioning\_ (Rich Trott) [#42401](https://github.com/nodejs/node/pull/42401) +* \[[`e763e575c6`](https://github.com/nodejs/node/commit/e763e575c6)] - **doc**: unify import order in CCM example (Tobias Nießen) [#42394](https://github.com/nodejs/node/pull/42394) +* \[[`10d638a735`](https://github.com/nodejs/node/commit/10d638a735)] - **doc**: update property name (Rich Trott) [#42398](https://github.com/nodejs/node/pull/42398) +* \[[`5589a448b7`](https://github.com/nodejs/node/commit/5589a448b7)] - **doc,test**: clarify ChaCha20-Poly1305 usage (Tobias Nießen) [#42323](https://github.com/nodejs/node/pull/42323) +* \[[`902776e674`](https://github.com/nodejs/node/commit/902776e674)] - **esm**: emit experimental warnings in common place (Jacob Smith) [#42314](https://github.com/nodejs/node/pull/42314) +* \[[`8009cb0a78`](https://github.com/nodejs/node/commit/8009cb0a78)] - **fs**: fix write methods param validation and docs (Livia Medeiros) [#42631](https://github.com/nodejs/node/pull/42631) +* \[[`a9dc3a92d9`](https://github.com/nodejs/node/commit/a9dc3a92d9)] - **lib**: prepare files for no-var lint rule (Rich Trott) [#42573](https://github.com/nodejs/node/pull/42573) +* \[[`3306fee824`](https://github.com/nodejs/node/commit/3306fee824)] - **lib**: source maps filter null prefix (Fabian Cook) [#42522](https://github.com/nodejs/node/pull/42522) +* \[[`3bac969655`](https://github.com/nodejs/node/commit/3bac969655)] - **lib**: improve the coverage of the validator (mawaregetsuka) [#42443](https://github.com/nodejs/node/pull/42443) +* \[[`b74de21cc3`](https://github.com/nodejs/node/commit/b74de21cc3)] - **lib**: update JSDoc for linting (Rich Trott) [#42489](https://github.com/nodejs/node/pull/42489) +* \[[`7766bf954f`](https://github.com/nodejs/node/commit/7766bf954f)] - **meta**: update .mailmap and AUTHORS (Rich Trott) [#42602](https://github.com/nodejs/node/pull/42602) +* \[[`93ffc5535a`](https://github.com/nodejs/node/commit/93ffc5535a)] - **meta**: move one or more collaborators to emeritus (Node.js GitHub Bot) [#42500](https://github.com/nodejs/node/pull/42500) +* \[[`256509056d`](https://github.com/nodejs/node/commit/256509056d)] - **meta**: update AUTHORS (Node.js GitHub Bot) [#42585](https://github.com/nodejs/node/pull/42585) +* \[[`41c2a32390`](https://github.com/nodejs/node/commit/41c2a32390)] - **meta**: update AUTHORS (Node.js GitHub Bot) [#42488](https://github.com/nodejs/node/pull/42488) +* \[[`b71a8107c0`](https://github.com/nodejs/node/commit/b71a8107c0)] - **net,dns**: trace tcp connection and dns by perf\_hooks (theanarkh) [#42390](https://github.com/nodejs/node/pull/42390) +* \[[`f9f3b6e45d`](https://github.com/nodejs/node/commit/f9f3b6e45d)] - **node-api**: format Node-API related code (Vladimir Morozov) [#42396](https://github.com/nodejs/node/pull/42396) +* \[[`0bd9d9e24f`](https://github.com/nodejs/node/commit/0bd9d9e24f)] - **os**: avoid unnecessary usage of var (Mohammed Keyvanzadeh) [#42563](https://github.com/nodejs/node/pull/42563) +* \[[`e798e26dfd`](https://github.com/nodejs/node/commit/e798e26dfd)] - **src**: add proper mutexes for accessing FIPS state (Anna Henningsen) [#42278](https://github.com/nodejs/node/pull/42278) +* \[[`a1fe0d2222`](https://github.com/nodejs/node/commit/a1fe0d2222)] - **src**: fix typo in InspectorIoDelegate constructor (Kohei Ueno) [#42520](https://github.com/nodejs/node/pull/42520) +* \[[`0c54f3637b`](https://github.com/nodejs/node/commit/0c54f3637b)] - **src**: remove unnecessary static qualifier in crypto\_dh.cc (Darshan Sen) [#42492](https://github.com/nodejs/node/pull/42492) +* \[[`2e6a66d7d7`](https://github.com/nodejs/node/commit/2e6a66d7d7)] - **src**: address 3 useless call coverity warnings (Michael Dawson) [#42426](https://github.com/nodejs/node/pull/42426) +* \[[`ce9d840079`](https://github.com/nodejs/node/commit/ce9d840079)] - **src**: properly report exceptions from AddressToJS() (Darshan Sen) [#42054](https://github.com/nodejs/node/pull/42054) +* \[[`c6a558c61b`](https://github.com/nodejs/node/commit/c6a558c61b)] - **src**: suppress false coverity warning (Michael Dawson) [#42284](https://github.com/nodejs/node/pull/42284) +* \[[`878148c266`](https://github.com/nodejs/node/commit/878148c266)] - **src**: refactor IsSupportedAuthenticatedMode (Tobias Nießen) [#42368](https://github.com/nodejs/node/pull/42368) +* \[[`970483ffd3`](https://github.com/nodejs/node/commit/970483ffd3)] - **src,crypto**: handle empty maybe correctly in crypto\_dh.cc (Darshan Sen) [#42492](https://github.com/nodejs/node/pull/42492) +* \[[`a348f8ac1a`](https://github.com/nodejs/node/commit/a348f8ac1a)] - **src,crypto**: remove uses of AllocatedBuffer from crypto\_dh.cc (Darshan Sen) [#42492](https://github.com/nodejs/node/pull/42492) +* \[[`fb25ba435c`](https://github.com/nodejs/node/commit/fb25ba435c)] - **test**: improve lib/internal/readline/promises.js coverage (MURAKAMI Masahiko) [#42420](https://github.com/nodejs/node/pull/42420) +* \[[`4cbb1ea910`](https://github.com/nodejs/node/commit/4cbb1ea910)] - **test**: remove hack for `atob` and `btoa` WPT tests (Antoine du Hamel) [#42540](https://github.com/nodejs/node/pull/42540) +* \[[`f41a4780d5`](https://github.com/nodejs/node/commit/f41a4780d5)] - **test**: pass data into napi\_create\_external (Joyee Cheung) [#42532](https://github.com/nodejs/node/pull/42532) +* \[[`90554572b5`](https://github.com/nodejs/node/commit/90554572b5)] - **test**: improve `FileHandle.prototype.write` coverage (Antoine du Hamel) [#42541](https://github.com/nodejs/node/pull/42541) +* \[[`797994e4c0`](https://github.com/nodejs/node/commit/797994e4c0)] - **test**: add test for exception handlings in debugger (Kohei Ueno) [#42327](https://github.com/nodejs/node/pull/42327) +* \[[`8c9b5e9a36`](https://github.com/nodejs/node/commit/8c9b5e9a36)] - **test**: fix typo in common/wpt.js (Ikko Ashimine) [#42567](https://github.com/nodejs/node/pull/42567) +* \[[`2f682091cd`](https://github.com/nodejs/node/commit/2f682091cd)] - **test**: fix typos in test/parallel (Daeyeon Jeong) [#42502](https://github.com/nodejs/node/pull/42502) +* \[[`52d1c8d6d9`](https://github.com/nodejs/node/commit/52d1c8d6d9)] - **test**: add trace-gc flag test (Tony Gorez) [#42471](https://github.com/nodejs/node/pull/42471) +* \[[`19c933c79a`](https://github.com/nodejs/node/commit/19c933c79a)] - **test,fs**: add fs.rm() tests for .git directories (Darshan Sen) [#42410](https://github.com/nodejs/node/pull/42410) +* \[[`d64c4fb94d`](https://github.com/nodejs/node/commit/d64c4fb94d)] - **tools**: enable no-var ESLint rule for lib (Rich Trott) [#42573](https://github.com/nodejs/node/pull/42573) +* \[[`a9f2636d12`](https://github.com/nodejs/node/commit/a9f2636d12)] - **tools**: fixed bug causing JSON format to be broken (mawaregetsuka) [#41565](https://github.com/nodejs/node/pull/41565) +* \[[`bff9cae16a`](https://github.com/nodejs/node/commit/bff9cae16a)] - **tools**: update GHA actions version (Antoine du Hamel) [#42498](https://github.com/nodejs/node/pull/42498) +* \[[`c6bfb225cd`](https://github.com/nodejs/node/commit/c6bfb225cd)] - **tools**: update eslint to 8.12.0 (Node.js GitHub Bot) [#42489](https://github.com/nodejs/node/pull/42489) +* \[[`464e57ffc7`](https://github.com/nodejs/node/commit/464e57ffc7)] - **tools**: update lint-md-dependencies to vfile-reporter\@7.0.4 (Node.js GitHub Bot) [#42487](https://github.com/nodejs/node/pull/42487) +* \[[`13dd8e73df`](https://github.com/nodejs/node/commit/13dd8e73df)] - **tools**: refloat 7 Node.js patches to cpplint.py (Rich Trott) [#42416](https://github.com/nodejs/node/pull/42416) +* \[[`3a1b0e5b87`](https://github.com/nodejs/node/commit/3a1b0e5b87)] - **tools**: bump cpplint to 1.6.0 (Rich Trott) [#42416](https://github.com/nodejs/node/pull/42416) +* \[[`9344a06d9c`](https://github.com/nodejs/node/commit/9344a06d9c)] - **tools**: fix skip PR if CI is still running (Xuguang Mei) [#42377](https://github.com/nodejs/node/pull/42377) + + + +## 2022-03-22, Version 17.8.0 (Current), @bengl + +### Notable Changes + +* \[[`3bd0078457`](https://github.com/nodejs/node/commit/3bd0078457)] - **doc**: add @ShogunPanda to collaborators (Shogun) [#42362](https://github.com/nodejs/node/pull/42362) +* \[[`23354673be`](https://github.com/nodejs/node/commit/23354673be)] - **doc**: deprecate string coercion in `fs.write`, `fs.writeFileSync` (Livia Medeiros) [#42149](https://github.com/nodejs/node/pull/42149) +* \[[`da42ffb85e`](https://github.com/nodejs/node/commit/da42ffb85e)] - **(SEMVER-MINOR)** **http**: trace http client by perf\_hooks (theanarkh) [#42345](https://github.com/nodejs/node/pull/42345) +* \[[`84fd6e54b0`](https://github.com/nodejs/node/commit/84fd6e54b0)] - **deps**: upgrade npm to 8.5.5 (npm team) [#42382](https://github.com/nodejs/node/pull/42382) +* \[[`b60262ee9f`](https://github.com/nodejs/node/commit/b60262ee9f)] - **deps**: update undici to 4.15.1 (Michaël Zasso) [#42246](https://github.com/nodejs/node/pull/42246) + +### Commits + +* \[[`1796f035c7`](https://github.com/nodejs/node/commit/1796f035c7)] - **build**: rename tools workflow and add undici to it (Michaël Zasso) [#42246](https://github.com/nodejs/node/pull/42246) +* \[[`f27bcec2ea`](https://github.com/nodejs/node/commit/f27bcec2ea)] - **build**: use ccache in make-v8.sh on ppc64le and s390x (Richard Lau) [#42204](https://github.com/nodejs/node/pull/42204) +* \[[`f48c3baf5a`](https://github.com/nodejs/node/commit/f48c3baf5a)] - **crypto**: fix auth tag length error when mode != GCM (Tobias Nießen) [#42383](https://github.com/nodejs/node/pull/42383) +* \[[`1d0468f749`](https://github.com/nodejs/node/commit/1d0468f749)] - **crypto**: fix fingerprint string size calculation (Tobias Nießen) [#42175](https://github.com/nodejs/node/pull/42175) +* \[[`a4632a3dc2`](https://github.com/nodejs/node/commit/a4632a3dc2)] - **crypto**: add CHECKs to remaining BIO\_s\_mem allocs (Tobias Nießen) [#42155](https://github.com/nodejs/node/pull/42155) +* \[[`3b55946452`](https://github.com/nodejs/node/commit/3b55946452)] - **debugger**: correct typo in inspect\_repl.js (Kohei Ueno) [#42267](https://github.com/nodejs/node/pull/42267) +* \[[`84fd6e54b0`](https://github.com/nodejs/node/commit/84fd6e54b0)] - **deps**: upgrade npm to 8.5.5 (npm team) [#42382](https://github.com/nodejs/node/pull/42382) +* \[[`f2178fcc1a`](https://github.com/nodejs/node/commit/f2178fcc1a)] - **deps**: cares: cherry-pick b5a3d96 (bradh352) [#42216](https://github.com/nodejs/node/pull/42216) +* \[[`063ff08cb1`](https://github.com/nodejs/node/commit/063ff08cb1)] - **deps**: V8: cherry-pick c6f6626deb14 (Lu Yahan) [#42240](https://github.com/nodejs/node/pull/42240) +* \[[`b60262ee9f`](https://github.com/nodejs/node/commit/b60262ee9f)] - **deps**: update undici to 4.15.1 (Michaël Zasso) [#42246](https://github.com/nodejs/node/pull/42246) +* \[[`70c0758308`](https://github.com/nodejs/node/commit/70c0758308)] - **deps**: upgrade npm to 8.5.3 (npm team) [#42205](https://github.com/nodejs/node/pull/42205) +* \[[`fd51e78963`](https://github.com/nodejs/node/commit/fd51e78963)] - **doc**: fix version history for `net.Socket` and `net.Server` (Antoine du Hamel) [#42268](https://github.com/nodejs/node/pull/42268) +* \[[`db83c4d6dc`](https://github.com/nodejs/node/commit/db83c4d6dc)] - **doc**: improve README.md usability (Rich Trott) [#42378](https://github.com/nodejs/node/pull/42378) +* \[[`88d3401329`](https://github.com/nodejs/node/commit/88d3401329)] - **doc**: add that chacha20-poly1305 is IETF version (Tobias Nießen) [#42370](https://github.com/nodejs/node/pull/42370) +* \[[`04a7c0061b`](https://github.com/nodejs/node/commit/04a7c0061b)] - **doc**: update instructions for openssl updates (Michael Dawson) [#42353](https://github.com/nodejs/node/pull/42353) +* \[[`78b858dd4b`](https://github.com/nodejs/node/commit/78b858dd4b)] - **doc**: document goal to have examples (Michael Dawson) [#42274](https://github.com/nodejs/node/pull/42274) +* \[[`a5e42f0113`](https://github.com/nodejs/node/commit/a5e42f0113)] - **doc**: fix Embedder's Guide link to V8 official docs (Aroyan) [#42373](https://github.com/nodejs/node/pull/42373) +* \[[`6c265e7243`](https://github.com/nodejs/node/commit/6c265e7243)] - **doc**: remove unneeded lint disable comment (Rich Trott) [#42374](https://github.com/nodejs/node/pull/42374) +* \[[`46d3d23e64`](https://github.com/nodejs/node/commit/46d3d23e64)] - **doc**: revise async\_hooks docs (Rich Trott) [#42337](https://github.com/nodejs/node/pull/42337) +* \[[`3bd0078457`](https://github.com/nodejs/node/commit/3bd0078457)] - **doc**: add @ShogunPanda to collaborators (Shogun) [#42362](https://github.com/nodejs/node/pull/42362) +* \[[`e7e8eb9f03`](https://github.com/nodejs/node/commit/e7e8eb9f03)] - **doc**: update base branch name for `nodejs/nodejs.org` (Danielle Adams) [#42355](https://github.com/nodejs/node/pull/42355) +* \[[`fd7e4ab654`](https://github.com/nodejs/node/commit/fd7e4ab654)] - **doc**: fix async iterable pipeline signal examples (Randall Leeds) [#42258](https://github.com/nodejs/node/pull/42258) +* \[[`96dc591b55`](https://github.com/nodejs/node/commit/96dc591b55)] - **doc**: clarify path search in `child_process.spawn` (Damjan Cvetko) [#41418](https://github.com/nodejs/node/pull/41418) +* \[[`72dd50016a`](https://github.com/nodejs/node/commit/72dd50016a)] - **doc**: clarify the meaning of legacy status (Darshan Sen) [#42269](https://github.com/nodejs/node/pull/42269) +* \[[`8b99099063`](https://github.com/nodejs/node/commit/8b99099063)] - **doc**: improve pipe description (Mikael Finstad) [#42295](https://github.com/nodejs/node/pull/42295) +* \[[`701dc14fdf`](https://github.com/nodejs/node/commit/701dc14fdf)] - **doc**: remove outdated timeout.unref content (Xuguang Mei) [#42241](https://github.com/nodejs/node/pull/42241) +* \[[`23354673be`](https://github.com/nodejs/node/commit/23354673be)] - **doc**: deprecate string coercion in `fs.write`, `fs.writeFileSync` (Livia Medeiros) [#42149](https://github.com/nodejs/node/pull/42149) +* \[[`f3c6c00963`](https://github.com/nodejs/node/commit/f3c6c00963)] - **doc**: remove refs to old OpenSSL list-\* commands (Tobias Nießen) [#42235](https://github.com/nodejs/node/pull/42235) +* \[[`19851f8d2d`](https://github.com/nodejs/node/commit/19851f8d2d)] - **doc**: readline `'line'` event emits final line (Matt Probert) [#42214](https://github.com/nodejs/node/pull/42214) +* \[[`e55283b978`](https://github.com/nodejs/node/commit/e55283b978)] - **esm**: make extension-less errors in type:module actionable (Bradley Farias) [#42301](https://github.com/nodejs/node/pull/42301) +* \[[`e17db8f0fa`](https://github.com/nodejs/node/commit/e17db8f0fa)] - **esm**: improve typings and code coverage (Bradley Farias) [#42305](https://github.com/nodejs/node/pull/42305) +* \[[`4829a1047f`](https://github.com/nodejs/node/commit/4829a1047f)] - **esm**: add runtime warning for specifier resolution flag (Geoffrey Booth) [#42252](https://github.com/nodejs/node/pull/42252) +* \[[`da42ffb85e`](https://github.com/nodejs/node/commit/da42ffb85e)] - **(SEMVER-MINOR)** **http**: trace http client by perf\_hooks (theanarkh) [#42345](https://github.com/nodejs/node/pull/42345) +* \[[`88dee3c6b5`](https://github.com/nodejs/node/commit/88dee3c6b5)] - **http2**: fix potential integer overflow (Michael Dawson) [#42248](https://github.com/nodejs/node/pull/42248) +* \[[`1fe0b69c31`](https://github.com/nodejs/node/commit/1fe0b69c31)] - **lib**: refactor to use primordials in `lib/assert.js` (Akhil Marsonya) [#41702](https://github.com/nodejs/node/pull/41702) +* \[[`69a3792540`](https://github.com/nodejs/node/commit/69a3792540)] - **lib**: fix AsyncResource.bind not using 'this' from the caller by default (Roch Devost) [#42177](https://github.com/nodejs/node/pull/42177) +* \[[`1c87ce6a32`](https://github.com/nodejs/node/commit/1c87ce6a32)] - **meta**: update AUTHORS (Node.js GitHub Bot) [#42404](https://github.com/nodejs/node/pull/42404) +* \[[`e7b8d83acd`](https://github.com/nodejs/node/commit/e7b8d83acd)] - **meta**: update AUTHORS (Node.js GitHub Bot) [#42317](https://github.com/nodejs/node/pull/42317) +* \[[`7fc4b9f08d`](https://github.com/nodejs/node/commit/7fc4b9f08d)] - **meta**: add dependencies label to label-pr-config (Mestery) [#42129](https://github.com/nodejs/node/pull/42129) +* \[[`e96042442b`](https://github.com/nodejs/node/commit/e96042442b)] - **src**: convert hex2bin() into a regular function (Darshan Sen) [#42321](https://github.com/nodejs/node/pull/42321) +* \[[`21198c1407`](https://github.com/nodejs/node/commit/21198c1407)] - **src**: fix coverity warnings in node\_file.cc (Michael Dawson) [#42272](https://github.com/nodejs/node/pull/42272) +* \[[`846b074075`](https://github.com/nodejs/node/commit/846b074075)] - **src**: check EC\_POINT\_get\_affine\_coordinates result (Tobias Nießen) [#42304](https://github.com/nodejs/node/pull/42304) +* \[[`8b84e68cbd`](https://github.com/nodejs/node/commit/8b84e68cbd)] - **src**: simplify bound check in ParseArrayIndex (Tobias Nießen) [#42306](https://github.com/nodejs/node/pull/42306) +* \[[`9500e5862e`](https://github.com/nodejs/node/commit/9500e5862e)] - **src**: avoid returning invalid value from hex2bin (Tobias Nießen) [#42307](https://github.com/nodejs/node/pull/42307) +* \[[`08e2d8ab86`](https://github.com/nodejs/node/commit/08e2d8ab86)] - **src**: check return value of HMAC\_Final (Tobias Nießen) [#42303](https://github.com/nodejs/node/pull/42303) +* \[[`9fc4b9b04e`](https://github.com/nodejs/node/commit/9fc4b9b04e)] - **src**: include internal/options in the snapshot (Joyee Cheung) [#42203](https://github.com/nodejs/node/pull/42203) +* \[[`e43aa30982`](https://github.com/nodejs/node/commit/e43aa30982)] - **src**: remove redundant buffer size check (Tobias Nießen) [#42257](https://github.com/nodejs/node/pull/42257) +* \[[`d06e92dba0`](https://github.com/nodejs/node/commit/d06e92dba0)] - **src**: perform minor cleanups on zlib code (Anna Henningsen) [#42247](https://github.com/nodejs/node/pull/42247) +* \[[`9af908305d`](https://github.com/nodejs/node/commit/9af908305d)] - **src**: use `emplace_back` instead of `push_back` (Yash Ladha) [#42159](https://github.com/nodejs/node/pull/42159) +* \[[`62d9a7f5db`](https://github.com/nodejs/node/commit/62d9a7f5db)] - **src**: fix unchecked return warning from coverity (Michael Dawson) [#42176](https://github.com/nodejs/node/pull/42176) +* \[[`58763d7f9d`](https://github.com/nodejs/node/commit/58763d7f9d)] - **src,crypto**: avoid tristate Maybe\ in ExportJWKEcKey() (Darshan Sen) [#42223](https://github.com/nodejs/node/pull/42223) +* \[[`5367002bc8`](https://github.com/nodejs/node/commit/5367002bc8)] - **stream**: do cleanup when iterator is destroyed (Khoo Hao Yit) [#42320](https://github.com/nodejs/node/pull/42320) +* \[[`3492a0eb1e`](https://github.com/nodejs/node/commit/3492a0eb1e)] - **string\_decoder**: fix crash when calling \_\_proto\_\_.write() (Darshan Sen) [#42062](https://github.com/nodejs/node/pull/42062) +* \[[`d9a5c2b284`](https://github.com/nodejs/node/commit/d9a5c2b284)] - **test**: give slow tests more time on Rasberry PIs (Michael Dawson) [#42380](https://github.com/nodejs/node/pull/42380) +* \[[`b82bac09ff`](https://github.com/nodejs/node/commit/b82bac09ff)] - **test**: improve https\_renew\_cert.sh script (Tobias Nießen) [#42343](https://github.com/nodejs/node/pull/42343) +* \[[`dfdce7c182`](https://github.com/nodejs/node/commit/dfdce7c182)] - **test**: improve \_http\_incoming.js coverage (Yoshiki Kurihara) [#42211](https://github.com/nodejs/node/pull/42211) +* \[[`4941791f29`](https://github.com/nodejs/node/commit/4941791f29)] - **test**: improve \_http\_outgoing coverage (Yoshiki Kurihara) [#42213](https://github.com/nodejs/node/pull/42213) +* \[[`94e5eaa7e9`](https://github.com/nodejs/node/commit/94e5eaa7e9)] - **test**: add test case for reverted 17.7 regression (Rich Trott) [#42283](https://github.com/nodejs/node/pull/42283) +* \[[`a4aa9eb97f`](https://github.com/nodejs/node/commit/a4aa9eb97f)] - **test**: use global webcrypto for WPT tests (Antoine du Hamel) [#42236](https://github.com/nodejs/node/pull/42236) +* \[[`26d4a2d489`](https://github.com/nodejs/node/commit/26d4a2d489)] - **test,crypto**: add and update empty passphrase regression tests (Darshan Sen) [#42319](https://github.com/nodejs/node/pull/42319) +* \[[`4fd2aff42e`](https://github.com/nodejs/node/commit/4fd2aff42e)] - **tools**: make update-undici script executable (Michaël Zasso) [#42406](https://github.com/nodejs/node/pull/42406) +* \[[`38e7681ac7`](https://github.com/nodejs/node/commit/38e7681ac7)] - **tools**: update lint-md-dependencies to rollup\@2.70.1 (Node.js GitHub Bot) [#42403](https://github.com/nodejs/node/pull/42403) +* \[[`b7a4b4b1fd`](https://github.com/nodejs/node/commit/b7a4b4b1fd)] - **tools**: update doc to highlight.js\@11.5.0 unified\@10.1.2 (Node.js GitHub Bot) [#42315](https://github.com/nodejs/node/pull/42315) +* \[[`30ea1889d5`](https://github.com/nodejs/node/commit/30ea1889d5)] - **tools**: update lint-md-dependencies to rollup\@2.70.0 unified\@10.1.2 (Node.js GitHub Bot) [#42316](https://github.com/nodejs/node/pull/42316) +* \[[`eb0e1a1147`](https://github.com/nodejs/node/commit/eb0e1a1147)] - **tools**: update eslint to 8.11.0 (Node.js GitHub Bot) [#42318](https://github.com/nodejs/node/pull/42318) +* \[[`e95426fd3a`](https://github.com/nodejs/node/commit/e95426fd3a)] - **tools**: fix web streams API links (Brian White) [#42153](https://github.com/nodejs/node/pull/42153) +* \[[`fe01940f35`](https://github.com/nodejs/node/commit/fe01940f35)] - **url**: preserve null char in WHATWG URL errors (Rich Trott) [#42263](https://github.com/nodejs/node/pull/42263) +* \[[`b89f4d5c17`](https://github.com/nodejs/node/commit/b89f4d5c17)] - **url**: trim leading and trailing C0 control chars (Rich Trott) [#42196](https://github.com/nodejs/node/pull/42196) +* \[[`229fb40edc`](https://github.com/nodejs/node/commit/229fb40edc)] - **worker**: do not send message if port is closing (Rich Trott) [#42357](https://github.com/nodejs/node/pull/42357) + + + +## 2022-03-17, Version 17.7.2 (Current), @richardlau + +This is a security release. + +### Notable Changes + +Update to OpenSSL 3.0.2, which addresses the following vulnerability: + +* Infinite loop in `BN_mod_sqrt()` reachable when parsing certificates (High)(CVE-2022-0778) + More details are available at + +### Commits + +* \[[`55e293e05f`](https://github.com/nodejs/node/commit/55e293e05f)] - **deps**: update archs files for quictls/openssl-3.0.2+quic (Hassaan Pasha) [#42356](https://github.com/nodejs/node/pull/42356) +* \[[`b8d090603d`](https://github.com/nodejs/node/commit/b8d090603d)] - **deps**: upgrade openssl sources to quictls/openssl-3.0.2+quic (Hassaan Pasha) [#42356](https://github.com/nodejs/node/pull/42356) +* \[[`c8b6d92af0`](https://github.com/nodejs/node/commit/c8b6d92af0)] - **test**: fix tests affected by OpenSSL update (Michael Dawson) [#42356](https://github.com/nodejs/node/pull/42356) +* \[[`457e31ea09`](https://github.com/nodejs/node/commit/457e31ea09)] - **test**: renew certificates for specific test (Luigi Pinca) [#42342](https://github.com/nodejs/node/pull/42342) + + + +## 2022-03-10, Version 17.7.1 (Current), @BethGriggs prepared by @sxa + +### Notable Changes + +#### Fixed regression in url.resolve() + +This release fixes an issue introduced in Node.js v17.7.0 with some URLs +that contain `@`. This issue affected yarn 1. This version reverts the +change that introduced the regression. + +### Commits + +* \[[`96a9e00fb3`](https://github.com/nodejs/node/commit/96a9e00fb3)] - **url**: revert fix url.parse() for `@hostname` (Antoine du Hamel) [#42280](https://github.com/nodejs/node/pull/42280) + + + +## 2022-03-09, Version 17.7.0 (Current), @BethGriggs prepared by @sxa + +### Notable Changes + +* \[[`2b354223d4`](https://github.com/nodejs/node/commit/2b354223d4)] - **(SEMVER-MINOR)** **crypto**: add KeyObject.prototype.equals method (Filip Skokan) [#42093](https://github.com/nodejs/node/pull/42093) +* \[[`a2926c477a`](https://github.com/nodejs/node/commit/a2926c477a)] - **(SEMVER-MINOR)** **net**: add new options to `net.Socket` and `net.Server` (Paolo Insogna) [#41310](https://github.com/nodejs/node/pull/41310) +* \[[`86248f1178`](https://github.com/nodejs/node/commit/86248f1178)] - **(SEMVER-MINOR)** **src**: allow preventing InitializeInspector in env (Shelley Vohr) [#35025](https://github.com/nodejs/node/pull/35025) +* \[[`a4969d5c37`](https://github.com/nodejs/node/commit/a4969d5c37)] - **doc**: add release key for Bryan English (Bryan English) [#42102](https://github.com/nodejs/node/pull/42102) + +### Dependency Updates + +* \[[`0b8efea182`](https://github.com/nodejs/node/commit/0b8efea182)] - **deps**: update nghttp2 to 1.47.0 (Yash Ladha) [#42127](https://github.com/nodejs/node/pull/42127) +* \[[`ccacf99e5c`](https://github.com/nodejs/node/commit/ccacf99e5c)] - **deps**: upgrade npm to 8.5.2 (npm team) [#42122](https://github.com/nodejs/node/pull/42122) + +### New Collaborators + +* \[[`3df001fa93`](https://github.com/nodejs/node/commit/3df001fa93)] - **doc**: add JakobJingleheimer to collaborators list (Jacob Smith) [#42185](https://github.com/nodejs/node/pull/42185) +* \[[`0ce00ca5e1`](https://github.com/nodejs/node/commit/0ce00ca5e1)] - **doc**: move bnoordhuis back to collaborators (Ben Noordhuis) [#42064](https://github.com/nodejs/node/pull/42064) + +### Commits + +* \[[`d05758f79f`](https://github.com/nodejs/node/commit/d05758f79f)] - **buffer**: improve blob read performance (Xuguang Mei) [#42117](https://github.com/nodejs/node/pull/42117) +* \[[`0bbb44741b`](https://github.com/nodejs/node/commit/0bbb44741b)] - **build**: drop shortened URL from lint-commit-message (Richard Lau) [#42168](https://github.com/nodejs/node/pull/42168) +* \[[`4e9fac6539`](https://github.com/nodejs/node/commit/4e9fac6539)] - **build**: fix usage of input in feature action (Michael Dawson) [#42150](https://github.com/nodejs/node/pull/42150) +* \[[`853cbd99b8`](https://github.com/nodejs/node/commit/853cbd99b8)] - **build**: increase max ops for stale feature action (Michael Dawson) [#42130](https://github.com/nodejs/node/pull/42130) +* \[[`3fc3f521d6`](https://github.com/nodejs/node/commit/3fc3f521d6)] - **build**: add corepack to the auto-updated dependencies (Maël Nison) [#42090](https://github.com/nodejs/node/pull/42090) +* \[[`ec4c0de6d1`](https://github.com/nodejs/node/commit/ec4c0de6d1)] - **build**: last test of the stale feature action (Michael Dawson) [#42085](https://github.com/nodejs/node/pull/42085) +* \[[`03ebca86d9`](https://github.com/nodejs/node/commit/03ebca86d9)] - **build**: update feature close action for testing (Michael Dawson) [#42082](https://github.com/nodejs/node/pull/42082) +* \[[`c9ea6a9261`](https://github.com/nodejs/node/commit/c9ea6a9261)] - **crypto**: validate `this` value for `webcrypto.getRandomValues` (Antoine du Hamel) [#41760](https://github.com/nodejs/node/pull/41760) +* \[[`2b354223d4`](https://github.com/nodejs/node/commit/2b354223d4)] - **(SEMVER-MINOR)** **crypto**: add KeyObject.prototype.equals method (Filip Skokan) [#42093](https://github.com/nodejs/node/pull/42093) +* \[[`288f627c46`](https://github.com/nodejs/node/commit/288f627c46)] - **crypto**: clarify `require("crypto").getRandomValues` is Node.js specific (Antoine du Hamel) [#41782](https://github.com/nodejs/node/pull/41782) +* \[[`0b8efea182`](https://github.com/nodejs/node/commit/0b8efea182)] - **deps**: update nghttp2 to 1.47.0 (Yash Ladha) [#42127](https://github.com/nodejs/node/pull/42127) +* \[[`ccacf99e5c`](https://github.com/nodejs/node/commit/ccacf99e5c)] - **deps**: upgrade npm to 8.5.2 (npm team) [#42122](https://github.com/nodejs/node/pull/42122) +* \[[`1359f60338`](https://github.com/nodejs/node/commit/1359f60338)] - **deps**: V8: cherry-pick 77d515484864 (Lu Yahan) [#42067](https://github.com/nodejs/node/pull/42067) +* \[[`769e2a486f`](https://github.com/nodejs/node/commit/769e2a486f)] - **deps**: V8: cherry-pick b66334313c8b (Lu Yahan) [#42067](https://github.com/nodejs/node/pull/42067) +* \[[`fc7d429516`](https://github.com/nodejs/node/commit/fc7d429516)] - **doc**: update stale feature messages (Michael Dawson) [#42217](https://github.com/nodejs/node/pull/42217) +* \[[`6183749861`](https://github.com/nodejs/node/commit/6183749861)] - **doc**: remove erroneous comma in cluster explainer (Tobias Nießen) [#42238](https://github.com/nodejs/node/pull/42238) +* \[[`555da9b658`](https://github.com/nodejs/node/commit/555da9b658)] - **doc**: remove "considered" for clarity (Rich Trott) [#42218](https://github.com/nodejs/node/pull/42218) +* \[[`05c3ff5e76`](https://github.com/nodejs/node/commit/05c3ff5e76)] - **doc**: clarify that some modules don't work when compiled without ssl (Antoine du Hamel) [#42198](https://github.com/nodejs/node/pull/42198) +* \[[`a6c1abf7e1`](https://github.com/nodejs/node/commit/a6c1abf7e1)] - **doc**: add note about nghttp2 hd pair size (Rafael Silva) [#42172](https://github.com/nodejs/node/pull/42172) +* \[[`04d2c74c8a`](https://github.com/nodejs/node/commit/04d2c74c8a)] - **doc**: use parenthesis instead of em dash (Antoine du Hamel) [#42202](https://github.com/nodejs/node/pull/42202) +* \[[`6a74fa91bb`](https://github.com/nodejs/node/commit/6a74fa91bb)] - **doc**: add next-10 to strategic initiatives (Michael Dawson) [#42167](https://github.com/nodejs/node/pull/42167) +* \[[`ee027391e7`](https://github.com/nodejs/node/commit/ee027391e7)] - **doc**: add missing single-quotes to `http.OutgoingMessage` (Juan José Arboleda) [#42162](https://github.com/nodejs/node/pull/42162) +* \[[`84859c4029`](https://github.com/nodejs/node/commit/84859c4029)] - **doc**: fix typos (apeltop) [#42146](https://github.com/nodejs/node/pull/42146) +* \[[`3df001fa93`](https://github.com/nodejs/node/commit/3df001fa93)] - **doc**: add JakobJingleheimer to collaborators list (Jacob Smith) [#42185](https://github.com/nodejs/node/pull/42185) +* \[[`ce86fc3006`](https://github.com/nodejs/node/commit/ce86fc3006)] - **doc**: remove reference to obsolete security program (Rich Trott) [#42144](https://github.com/nodejs/node/pull/42144) +* \[[`5d010bcde2`](https://github.com/nodejs/node/commit/5d010bcde2)] - **doc**: remove repeated a word (apeltop) [#42138](https://github.com/nodejs/node/pull/42138) +* \[[`a32ec983c6`](https://github.com/nodejs/node/commit/a32ec983c6)] - **doc**: make building with ninja more discoverable (Balakrishna Avulapati) [#41840](https://github.com/nodejs/node/pull/41840) +* \[[`26fe61b6ad`](https://github.com/nodejs/node/commit/26fe61b6ad)] - **doc**: document change to IncomingMessage.headers enumerability (Arnold Zokas) [#42095](https://github.com/nodejs/node/pull/42095) +* \[[`eb622a0761`](https://github.com/nodejs/node/commit/eb622a0761)] - **doc**: add meixg to triagers (Xuguang Mei) [#42066](https://github.com/nodejs/node/pull/42066) +* \[[`bd04fc89da`](https://github.com/nodejs/node/commit/bd04fc89da)] - **doc**: clarify persistent ref behavior (Michael Dawson) [#42035](https://github.com/nodejs/node/pull/42035) +* \[[`0ce00ca5e1`](https://github.com/nodejs/node/commit/0ce00ca5e1)] - **doc**: move bnoordhuis back to collaborators (Ben Noordhuis) [#42064](https://github.com/nodejs/node/pull/42064) +* \[[`8b531dadb1`](https://github.com/nodejs/node/commit/8b531dadb1)] - **doc**: clarify supported versus enabled TLS ciphers (Tobias Nießen) [#42063](https://github.com/nodejs/node/pull/42063) +* \[[`3789d668f7`](https://github.com/nodejs/node/commit/3789d668f7)] - **doc**: add missing api entries on performance (legendecas) [#42018](https://github.com/nodejs/node/pull/42018) +* \[[`a4969d5c37`](https://github.com/nodejs/node/commit/a4969d5c37)] - **doc**: add release key for Bryan English (Bryan English) [#42102](https://github.com/nodejs/node/pull/42102) +* \[[`8b94ea6e1c`](https://github.com/nodejs/node/commit/8b94ea6e1c)] - **doc**,tools: improve navigability of API docs (Paolo Insogna) [#41404](https://github.com/nodejs/node/pull/41404) +* \[[`2e1231b831`](https://github.com/nodejs/node/commit/2e1231b831)] - **errors**: do not access .stack in debug (Benjamin Coe) [#42096](https://github.com/nodejs/node/pull/42096) +* \[[`8dd4878850`](https://github.com/nodejs/node/commit/8dd4878850)] - **esm**: fix base URL for network imports (Bradley Farias) [#42131](https://github.com/nodejs/node/pull/42131) +* \[[`2bc136d3cb`](https://github.com/nodejs/node/commit/2bc136d3cb)] - **esm**: fix relative imports for https (Bradley Farias) [#42119](https://github.com/nodejs/node/pull/42119) +* \[[`576c1aea8e`](https://github.com/nodejs/node/commit/576c1aea8e)] - **fs**: adjust default `length` for `fs.readSync` and fsPromises/`read` (Livia Medeiros) [#42128](https://github.com/nodejs/node/pull/42128) +* \[[`041373696f`](https://github.com/nodejs/node/commit/041373696f)] - **http**: add default argument for Agent.prototype.getName (小菜) [#41906](https://github.com/nodejs/node/pull/41906) +* \[[`1de80872e8`](https://github.com/nodejs/node/commit/1de80872e8)] - **http2**: add edge case to GOAWAY request (Rafael Silva) [#42190](https://github.com/nodejs/node/pull/42190) +* \[[`9bc7a954fd`](https://github.com/nodejs/node/commit/9bc7a954fd)] - **http2**: close stream and session on frameError (Rafael Silva) [#42147](https://github.com/nodejs/node/pull/42147) +* \[[`384872fdbd`](https://github.com/nodejs/node/commit/384872fdbd)] - **lib**: clean after the cancel algorithm throw error (Chen Gang) [#41366](https://github.com/nodejs/node/pull/41366) +* \[[`f7ea75fd0a`](https://github.com/nodejs/node/commit/f7ea75fd0a)] - **lib**: add legacy built-in functions to primordials (Antoine du Hamel) [#42049](https://github.com/nodejs/node/pull/42049) +* \[[`7f1c83e674`](https://github.com/nodejs/node/commit/7f1c83e674)] - **loader**: fix esm resolve for symlink file (Xuguang Mei) [#42197](https://github.com/nodejs/node/pull/42197) +* \[[`5b23e67ad5`](https://github.com/nodejs/node/commit/5b23e67ad5)] - **meta**: update AUTHORS (Node.js GitHub Bot) [#42227](https://github.com/nodejs/node/pull/42227) +* \[[`cad7dde9af`](https://github.com/nodejs/node/commit/cad7dde9af)] - **meta**: update AUTHORS (Node.js GitHub Bot) [#42142](https://github.com/nodejs/node/pull/42142) +* \[[`7ed2c19cbb`](https://github.com/nodejs/node/commit/7ed2c19cbb)] - **meta**: correct link to feature request document (Simen Bekkhus) [#42092](https://github.com/nodejs/node/pull/42092) +* \[[`e0448da9a9`](https://github.com/nodejs/node/commit/e0448da9a9)] - **meta**: move one or more collaborators to emeritus (Node.js GitHub Bot) [#42068](https://github.com/nodejs/node/pull/42068) +* \[[`e3347dbaa8`](https://github.com/nodejs/node/commit/e3347dbaa8)] - **meta**: remove collaborator (Rich Trott) [#42073](https://github.com/nodejs/node/pull/42073) +* \[[`96510b3411`](https://github.com/nodejs/node/commit/96510b3411)] - **module**: prefer async/await in https imports (Benjamin Gruenbaum) [#41950](https://github.com/nodejs/node/pull/41950) +* \[[`a2926c477a`](https://github.com/nodejs/node/commit/a2926c477a)] - **(SEMVER-MINOR)** **net**: add new options to `net.Socket` and `net.Server` (Paolo Insogna) [#41310](https://github.com/nodejs/node/pull/41310) +* \[[`f107f8bf40`](https://github.com/nodejs/node/commit/f107f8bf40)] - **node**-api: fix typo in `node_api.cc` (Austin Kelleher) [#42110](https://github.com/nodejs/node/pull/42110) +* \[[`c72c3f4bb1`](https://github.com/nodejs/node/commit/c72c3f4bb1)] - **perf\_hooks**: do not return all entries with getEntriesBy\[Name|Type] (Xuguang Mei) [#42104](https://github.com/nodejs/node/pull/42104) +* \[[`7c49785348`](https://github.com/nodejs/node/commit/7c49785348)] - **process**: fix named report export (madflow) [#41861](https://github.com/nodejs/node/pull/41861) +* \[[`d6b1a4a235`](https://github.com/nodejs/node/commit/d6b1a4a235)] - **repl**: remove preview when press escape (meixg) [#42053](https://github.com/nodejs/node/pull/42053) +* \[[`98b1be0ec2`](https://github.com/nodejs/node/commit/98b1be0ec2)] - **src**: return proper URLs from node\_api\_get\_module\_file\_name (Anna Henningsen) [#41758](https://github.com/nodejs/node/pull/41758) +* \[[`0abc20b6cf`](https://github.com/nodejs/node/commit/0abc20b6cf)] - **src**: skip revoke\_data\_object if uuid is not found (Xuguang Mei) [#42212](https://github.com/nodejs/node/pull/42212) +* \[[`ce409279d8`](https://github.com/nodejs/node/commit/ce409279d8)] - **src**: remove dead code in AddFingerprintDigest (Tobias Nießen) [#42145](https://github.com/nodejs/node/pull/42145) +* \[[`4c9f2b5d83`](https://github.com/nodejs/node/commit/4c9f2b5d83)] - **src**: combine GetCurveASN1Name and GetCurveNistName (Tobias Nießen) [#42118](https://github.com/nodejs/node/pull/42118) +* \[[`f0558d88fb`](https://github.com/nodejs/node/commit/f0558d88fb)] - **src**: simplify TLSWrap::SetSession (Tobias Nießen) [#42087](https://github.com/nodejs/node/pull/42087) +* \[[`97f5ceda84`](https://github.com/nodejs/node/commit/97f5ceda84)] - **src**: prefer bool over int in crypto\_common (Tobias Nießen) [#42097](https://github.com/nodejs/node/pull/42097) +* \[[`382ffdade0`](https://github.com/nodejs/node/commit/382ffdade0)] - **src**: simplify arg type of AddFingerprintDigest (Tobias Nießen) [#42101](https://github.com/nodejs/node/pull/42101) +* \[[`07de4ed641`](https://github.com/nodejs/node/commit/07de4ed641)] - **src**: do not ignore return value of BIO\_reset (Tobias Nießen) [#42103](https://github.com/nodejs/node/pull/42103) +* \[[`3366618ce2`](https://github.com/nodejs/node/commit/3366618ce2)] - **src**: simplify GetExponentString (Tobias Nießen) [#42121](https://github.com/nodejs/node/pull/42121) +* \[[`86248f1178`](https://github.com/nodejs/node/commit/86248f1178)] - **(SEMVER-MINOR)** src: allow preventing InitializeInspector in env (Shelley Vohr) [#35025](https://github.com/nodejs/node/pull/35025) +* \[[`06e5c0ee39`](https://github.com/nodejs/node/commit/06e5c0ee39)] - **stream**: use .chunk when calling adapters's writev (Xuguang Mei) [#42161](https://github.com/nodejs/node/pull/42161) +* \[[`53338fe65c`](https://github.com/nodejs/node/commit/53338fe65c)] - **stream**: allow returning null from pipeline tail (Robert Nagy) [#42078](https://github.com/nodejs/node/pull/42078) +* \[[`8431fb90dc`](https://github.com/nodejs/node/commit/8431fb90dc)] - **stream**: port more test262 tests (Benjamin Gruenbaum) [#41974](https://github.com/nodejs/node/pull/41974) +* \[[`0be3c61ae9`](https://github.com/nodejs/node/commit/0be3c61ae9)] - **test**: cover 32-bit sizes in generatePrime (Tobias Nießen) [#42207](https://github.com/nodejs/node/pull/42207) +* \[[`ceb47d13ff`](https://github.com/nodejs/node/commit/ceb47d13ff)] - **test**: fix test-process-env-tz.js by using RegExp (Khaidi Chu) [#42113](https://github.com/nodejs/node/pull/42113) +* \[[`f21fbeec2e`](https://github.com/nodejs/node/commit/f21fbeec2e)] - **test**: update V8 trace events test expectations (Nikolaos Papaspyrou) [#42120](https://github.com/nodejs/node/pull/42120) +* \[[`d0f68a398e`](https://github.com/nodejs/node/commit/d0f68a398e)] - **test**: deflake test-common-expect-warning (Luigi Pinca) [#42046](https://github.com/nodejs/node/pull/42046) +* \[[`482b2205b2`](https://github.com/nodejs/node/commit/482b2205b2)] - **test**: validate `EventEmitterAsyncResource` methods throw on invalid this (Yoshiki Kurihara) [#42041](https://github.com/nodejs/node/pull/42041) +* \[[`99301469ed`](https://github.com/nodejs/node/commit/99301469ed)] - **test**: increase Fibonacci argument to 40 (Rich Trott) [#42055](https://github.com/nodejs/node/pull/42055) +* \[[`c01134ed27`](https://github.com/nodejs/node/commit/c01134ed27)] - **tools**: update lint-md-dependencies to rollup\@2.69.1 (Node.js GitHub Bot) [#42226](https://github.com/nodejs/node/pull/42226) +* \[[`41b6d9e95a`](https://github.com/nodejs/node/commit/41b6d9e95a)] - **tools**: update lint-md rollup dependencies (Node.js GitHub Bot) [#42141](https://github.com/nodejs/node/pull/42141) +* \[[`28f636422e`](https://github.com/nodejs/node/commit/28f636422e)] - **tools**: update eslint to 8.10.0 (Node.js GitHub Bot) [#42143](https://github.com/nodejs/node/pull/42143) +* \[[`9aeda47d9c`](https://github.com/nodejs/node/commit/9aeda47d9c)] - **url**: fix url.parse() for @hostname (Rich Trott) [#42136](https://github.com/nodejs/node/pull/42136) +* \[[`ecb5980e2f`](https://github.com/nodejs/node/commit/ecb5980e2f)] - **url, src**: modify one `special_back_slash` (Khaidi Chu) [#42112](https://github.com/nodejs/node/pull/42112) + ## 2022-02-22, Version 17.6.0 (Current), @BethGriggs prepared by @bengl diff --git a/doc/changelogs/CHANGELOG_V18.md b/doc/changelogs/CHANGELOG_V18.md new file mode 100644 index 00000000000000..39cd97afac6192 --- /dev/null +++ b/doc/changelogs/CHANGELOG_V18.md @@ -0,0 +1,306 @@ +# Node.js 18 ChangeLog + + + + + + + + + + +
Current
+18.0.0
+
+ +* Other Versions + * [17.x](CHANGELOG_V17.md) + * [16.x](CHANGELOG_V16.md) + * [15.x](CHANGELOG_V15.md) + * [14.x](CHANGELOG_V14.md) + * [13.x](CHANGELOG_V13.md) + * [12.x](CHANGELOG_V12.md) + * [11.x](CHANGELOG_V11.md) + * [10.x](CHANGELOG_V10.md) + * [9.x](CHANGELOG_V9.md) + * [8.x](CHANGELOG_V8.md) + * [7.x](CHANGELOG_V7.md) + * [6.x](CHANGELOG_V6.md) + * [5.x](CHANGELOG_V5.md) + * [4.x](CHANGELOG_V4.md) + * [0.12.x](CHANGELOG_V012.md) + * [0.10.x](CHANGELOG_V010.md) + * [io.js](CHANGELOG_IOJS.md) + * [Archive](CHANGELOG_ARCHIVE.md) + + + +## 2022-04-19, Version 18.0.0 (Current), @BethGriggs + +Node.js 18 is here! Highlights include the update of the V8 JavaScript engine to 10.1, global fetch enabled by default, and a core test runner module. + +Initially, Node.js 18 will replace Node.js 17 as our ‘Current’ release line. As per the release schedule, Node.js 18 will be the ‘Current’ release for the next 6 months and then promoted to Long-term Support (LTS) in October 2022. Once promoted to long-term support the release will be designated the codename ‘Hydrogen’. Node.js 18 will be supported until April 2025. + +### Notable Changes + +#### Deprecations and Removals + +* **(SEMVER-MAJOR)** **fs**: runtime deprecate string coercion in `fs.write`, `fs.writeFileSync` (Livia Medeiros) [#42607](https://github.com/nodejs/node/pull/42607) +* **(SEMVER-MAJOR)** **dns**: remove `dns.lookup` and `dnsPromises.lookup` options type coercion (Antoine du Hamel) [#41431](https://github.com/nodejs/node/pull/41431) +* **(SEMVER-MAJOR)** **process**: runtime deprecate multipleResolves (Benjamin Gruenbaum) [#41896](https://github.com/nodejs/node/pull/41896) +* **(SEMVER-MAJOR)** **stream**: remove thenable support (Robert Nagy) [#40773](https://github.com/nodejs/node/pull/40773) +* **(SEMVER-MAJOR)** **tls**: move tls.parseCertString to end-of-life (Tobias Nießen) [#41479](https://github.com/nodejs/node/pull/41479) + +#### fetch (experimental) + +An experimental fetch API is available on the global scope by default. The implementation is based upon [undici](https://undici.nodejs.org/#/), an HTTP/1.1 client written for Node.js by contributors to the project. + +```mjs +const res = await fetch('https://nodejs.org/api/documentation.json'); +if (res.ok) { + const data = await res.json(); + console.log(data); +} +``` + +Through this addition, the following globals are made available: `fetch`, `FormData`, `Headers`, `Request`, `Response`. + +Disable this API with the `--no-experimental-fetch` command-line flag. + +Contributed by Michaël Zasso in [#41811](https://github.com/nodejs/node/pull/41811). + +#### HTTP Timeouts + +`server.headersTimeout` which limits the amount of time the parser will wait to receive the complete HTTP headers is now set to `60000` (60 seconds) by default. + +`server.requestTimeout` which sets the timeout value in milliseconds for receiving the entire request from the client is now set to `300000` (5 minutes) by default. + +If these timeouts expire, the server responds with status 408 without forwarding the request to the request listener and then closes the connection. + +Both timeouts must be set to a non-zero value to protect against potential Denial-of-Service attacks in case the server is deployed without a reverse proxy in front. + +Contributed by Paolo Insogna in [#41263](https://github.com/nodejs/node/pull/41263). + +#### Test Runner module (experimental) + +The `node:test` module facilitates the creation of JavaScript tests that report results in TAP format. To access it: + +`import test from 'node:test';` + +This module is only available under the `node:` scheme. + +The following is an example implementation of a parent test with two subtests: + +```js +test('top level test', async (t) => { + await t.test('subtest 1', (t) => { + assert.strictEqual(1, 1); + }); + + await t.test('subtest 2', (t) => { + assert.strictEqual(2, 2); + }); +}); +``` + +Read more in . + +Contributed by Colin Ihrig in [#42325](https://github.com/nodejs/node/pull/42325). + +#### Toolchain and Compiler Upgrades + +* Prebuilt binaries for Linux are now built on Red Hat Enterprise Linux (RHEL) 8 and are compatible with Linux distributions based on glibc 2.28 or later, for example, Debian 10, RHEL 8, Ubuntu 20.04. +* Prebuilt binaries for macOS now require macOS 10.15 or later. +* For AIX the minimum supported architecture has been raised from Power 7 to Power 8. + +Prebuilt binaries for 32-bit Windows will initially not be available due to issues building the V8 dependency in Node.js. We hope to restore 32-bit Windows binaries for Node.js 18 with a future V8 update. + +Node.js does not support running on operating systems that are no longer supported by their vendor. For operating systems where their vendor has planned to end support earlier than April 2025, such as Windows 8.1 (January 2023) and Windows Server 2012 R2 (October 2023), support for Node.js 18 will end at the earlier date. + +Full details about the supported toolchains and compilers are documented in the Node.js [BUILDING.md](https://github.com/nodejs/node/blob/v18.x/BUILDING.md#supported-platforms) file. + +Contributed by Richard Lau in [#42292](https://github.com/nodejs/node/pull/42292), [#42604](https://github.com/nodejs/node/pull/42604) and [#42659](https://github.com/nodejs/node/pull/42659),and Michaël Zasso in [#42105](https://github.com/nodejs/node/pull/42105) and [#42666](https://github.com/nodejs/node/pull/42666). + +#### V8 10.1 + +The V8 engine is updated to version 10.1, which is part of Chromium 101. Compared to the version included in Node.js 17.9.0, the following new features are included: + +* The [`findLast` and `findLastIndex` array methods](https://v8.dev/features/finding-in-arrays). +* Improvements to the [`Intl.Locale` API](https://v8.dev/blog/v8-release-99#intl.locale-extensions). +* The [`Intl.supportedValuesOf` function](https://v8.dev/blog/v8-release-99#intl-enumeration). +* Improved performance of [class fields](https://bugs.chromium.org/p/v8/issues/detail?id=9888) and [private class methods](https://bugs.chromium.org/p/v8/issues/detail?id=10793) (the initialization of them is now as fast as ordinary property stores). + +The data format returned by the serialization API (`v8.serialize(value)`) has changed, and cannot be deserialized by earlier versions of Node.js. On the other hand, it is still possible to deserialize the previous format, as the API is backwards-compatible. + +Contributed by Michaël Zasso in . + +#### Web Streams API (experimental) + +Node.js now exposes the experimental implementation of the [Web Streams API](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) on the global scope. This means the following APIs are now globally available: + +* `ReadableStream`, `ReadableStreamDefaultReader`, `ReadableStreamBYOBReader`, `ReadableStreamBYOBRequest`, `ReadableByteStreamController`, `ReadableStreamDefaultController`, `TransformStream`, `TransformStreamDefaultController`, `WritableStream`, `WritableStreamDefaultWriter`, `WritableStreamDefaultController`, `ByteLengthQueuingStrategy`, `CountQueuingStrategy`, `TextEncoderStream`, `TextDecoderStream`, `CompressionStream`, `DecompressionStream`. + +Contributed James Snell in , and Antoine du Hamel in . + +#### Other Notable Changes + +* **(SEMVER-MAJOR)** **buffer**: expose Blob as a global (James M Snell) [#41270](https://github.com/nodejs/node/pull/41270) +* **(SEMVER-MAJOR)** **child\_process**: improve argument validation (Rich Trott) [#41305](https://github.com/nodejs/node/pull/41305) +* **doc**: add RafaelGSS to collaborators (RafaelGSS) [#42718](https://github.com/nodejs/node/pull/42718) +* **(SEMVER-MAJOR)** **http**: make TCP noDelay enabled by default (Paolo Insogna) [#42163](https://github.com/nodejs/node/pull/42163) +* **(SEMVER-MAJOR)** **net**: make `server.address()` return an integer for `family` (Antoine du Hamel) [#41431](https://github.com/nodejs/node/pull/41431) +* **(SEMVER-MAJOR)** **worker**: expose BroadcastChannel as a global (James M Snell) [#41271](https://github.com/nodejs/node/pull/41271) +* **(SEMVER-MAJOR)** **worker**: graduate BroadcastChannel to supported (James M Snell) [#41271](https://github.com/nodejs/node/pull/41271) + +### Semver-Major Commits + +* \[[`dab8ab2837`](https://github.com/nodejs/node/commit/dab8ab2837)] - **(SEMVER-MAJOR)** **assert,util**: compare RegExp.lastIndex while using deep equal checks (Ruben Bridgewater) [#41020](https://github.com/nodejs/node/pull/41020) +* \[[`cff14bcaef`](https://github.com/nodejs/node/commit/cff14bcaef)] - **(SEMVER-MAJOR)** **buffer**: refactor `byteLength` to remove outdated optimizations (Rongjian Zhang) [#38545](https://github.com/nodejs/node/pull/38545) +* \[[`cea76dbf33`](https://github.com/nodejs/node/commit/cea76dbf33)] - **(SEMVER-MAJOR)** **buffer**: expose Blob as a global (James M Snell) [#41270](https://github.com/nodejs/node/pull/41270) +* \[[`99c18f4786`](https://github.com/nodejs/node/commit/99c18f4786)] - **(SEMVER-MAJOR)** **buffer**: graduate Blob from experimental (James M Snell) [#41270](https://github.com/nodejs/node/pull/41270) +* \[[`35d72bf4ec`](https://github.com/nodejs/node/commit/35d72bf4ec)] - **(SEMVER-MAJOR)** **build**: make x86 Windows support temporarily experimental (Michaël Zasso) [#42666](https://github.com/nodejs/node/pull/42666) +* \[[`1134d8faf8`](https://github.com/nodejs/node/commit/1134d8faf8)] - **(SEMVER-MAJOR)** **build**: bump macOS deployment target to 10.15 (Richard Lau) [#42292](https://github.com/nodejs/node/pull/42292) +* \[[`27eb91d378`](https://github.com/nodejs/node/commit/27eb91d378)] - **(SEMVER-MAJOR)** **build**: downgrade Windows 8.1 and server 2012 R2 to experimental (Michaël Zasso) [#42105](https://github.com/nodejs/node/pull/42105) +* \[[`26c973d4b3`](https://github.com/nodejs/node/commit/26c973d4b3)] - **(SEMVER-MAJOR)** **child\_process**: improve argument validation (Rich Trott) [#41305](https://github.com/nodejs/node/pull/41305) +* \[[`38007df999`](https://github.com/nodejs/node/commit/38007df999)] - **(SEMVER-MAJOR)** **cluster**: make `kill` to be just `process.kill` (Bar Admoni) [#34312](https://github.com/nodejs/node/pull/34312) +* \[[`aed18dfe59`](https://github.com/nodejs/node/commit/aed18dfe59)] - **(SEMVER-MAJOR)** **crypto**: cleanup validation (Mohammed Keyvanzadeh) [#39841](https://github.com/nodejs/node/pull/39841) +* \[[`e1fb6ae02f`](https://github.com/nodejs/node/commit/e1fb6ae02f)] - **(SEMVER-MAJOR)** **crypto**: prettify othername in PrintGeneralName (Tobias Nießen) [#42123](https://github.com/nodejs/node/pull/42123) +* \[[`36fb79030e`](https://github.com/nodejs/node/commit/36fb79030e)] - **(SEMVER-MAJOR)** **crypto**: fix X509Certificate toLegacyObject (Tobias Nießen) [#42124](https://github.com/nodejs/node/pull/42124) +* \[[`563b2ed000`](https://github.com/nodejs/node/commit/563b2ed000)] - **(SEMVER-MAJOR)** **crypto**: use RFC2253 format in PrintGeneralName (Tobias Nießen) [#42002](https://github.com/nodejs/node/pull/42002) +* \[[`18365d8ee6`](https://github.com/nodejs/node/commit/18365d8ee6)] - **(SEMVER-MAJOR)** **crypto**: change default check(Host|Email) behavior (Tobias Nießen) [#41600](https://github.com/nodejs/node/pull/41600) +* \[[`58f3fdcccd`](https://github.com/nodejs/node/commit/58f3fdcccd)] - **(SEMVER-MAJOR)** **deps**: V8: cherry-pick semver-major commits from 10.2 (Michaël Zasso) [#42657](https://github.com/nodejs/node/pull/42657) +* \[[`fd4f80ce54`](https://github.com/nodejs/node/commit/fd4f80ce54)] - **(SEMVER-MAJOR)** **deps**: update V8 to 10.1.124.6 (Michaël Zasso) [#42657](https://github.com/nodejs/node/pull/42657) +* \[[`974ab4060f`](https://github.com/nodejs/node/commit/974ab4060f)] - **(SEMVER-MAJOR)** **deps**: update V8 to 9.8.177.9 (Michaël Zasso) [#41610](https://github.com/nodejs/node/pull/41610) +* \[[`270253c4e2`](https://github.com/nodejs/node/commit/270253c4e2)] - **(SEMVER-MAJOR)** **deps**: update V8 to 9.7.106.18 (Michaël Zasso) [#40907](https://github.com/nodejs/node/pull/40907) +* \[[`08773e3c04`](https://github.com/nodejs/node/commit/08773e3c04)] - **(SEMVER-MAJOR)** **dns**: remove `dns.lookup` and `dnsPromises.lookup` options type coercion (Antoine du Hamel) [#41431](https://github.com/nodejs/node/pull/41431) +* \[[`3671cc0432`](https://github.com/nodejs/node/commit/3671cc0432)] - **(SEMVER-MAJOR)** **doc**: update minimum glibc requirements for Linux (Richard Lau) [#42659](https://github.com/nodejs/node/pull/42659) +* \[[`646e057680`](https://github.com/nodejs/node/commit/646e057680)] - **(SEMVER-MAJOR)** **doc**: update AIX minimum supported arch (Richard Lau) [#42604](https://github.com/nodejs/node/pull/42604) +* \[[`0bac5478eb`](https://github.com/nodejs/node/commit/0bac5478eb)] - **(SEMVER-MAJOR)** **fs**: runtime deprecate string coercion in `fs.write`, `fs.writeFileSync` (Livia Medeiros) [#42607](https://github.com/nodejs/node/pull/42607) +* \[[`3caa2c1a00`](https://github.com/nodejs/node/commit/3caa2c1a00)] - **(SEMVER-MAJOR)** **http**: refactor headersTimeout and requestTimeout logic (Paolo Insogna) [#41263](https://github.com/nodejs/node/pull/41263) +* \[[`eacd45656a`](https://github.com/nodejs/node/commit/eacd45656a)] - **(SEMVER-MAJOR)** **http**: make TCP noDelay enabled by default (Paolo Insogna) [#42163](https://github.com/nodejs/node/pull/42163) +* \[[`4944ad0b9e`](https://github.com/nodejs/node/commit/4944ad0b9e)] - **(SEMVER-MAJOR)** **lib**: enable fetch by default (Michaël Zasso) [#41811](https://github.com/nodejs/node/pull/41811) +* \[[`8c4b8b201a`](https://github.com/nodejs/node/commit/8c4b8b201a)] - **(SEMVER-MAJOR)** **lib**: replace validator and error (Mohammed Keyvanzadeh) [#41678](https://github.com/nodejs/node/pull/41678) +* \[[`3c4ee5267a`](https://github.com/nodejs/node/commit/3c4ee5267a)] - **(SEMVER-MAJOR)** **module,repl**: support 'node:'-only core modules (Colin Ihrig) [#42325](https://github.com/nodejs/node/pull/42325) +* \[[`3a26db9697`](https://github.com/nodejs/node/commit/3a26db9697)] - **(SEMVER-MAJOR)** **net**: make `server.address()` return an integer for `family` (Antoine du Hamel) [#41431](https://github.com/nodejs/node/pull/41431) +* \[[`e6a7300a10`](https://github.com/nodejs/node/commit/e6a7300a10)] - **(SEMVER-MAJOR)** **process**: disallow some uses of Object.defineProperty() on process.env (Himself65) [#28006](https://github.com/nodejs/node/pull/28006) +* \[[`60b8e79599`](https://github.com/nodejs/node/commit/60b8e79599)] - **(SEMVER-MAJOR)** **process**: runtime deprecate multipleResolves (Benjamin Gruenbaum) [#41896](https://github.com/nodejs/node/pull/41896) +* \[[`d36b60e69a`](https://github.com/nodejs/node/commit/d36b60e69a)] - **(SEMVER-MAJOR)** **readline**: fix question still called after closed (Xuguang Mei) [#42464](https://github.com/nodejs/node/pull/42464) +* \[[`58e645de63`](https://github.com/nodejs/node/commit/58e645de63)] - **(SEMVER-MAJOR)** **stream**: remove thenable support (Robert Nagy) [#40773](https://github.com/nodejs/node/pull/40773) +* \[[`560cbc5849`](https://github.com/nodejs/node/commit/560cbc5849)] - **(SEMVER-MAJOR)** **stream**: expose web streams globals, remove runtime experimental warning (Antoine du Hamel) [#42225](https://github.com/nodejs/node/pull/42225) +* \[[`9fb7ac3bbd`](https://github.com/nodejs/node/commit/9fb7ac3bbd)] - **(SEMVER-MAJOR)** **stream**: need to cleanup event listeners if last stream is readable (Xuguang Mei) [#41954](https://github.com/nodejs/node/pull/41954) +* \[[`ceaa299958`](https://github.com/nodejs/node/commit/ceaa299958)] - **(SEMVER-MAJOR)** **stream**: revert revert `map` spec compliance (Benjamin Gruenbaum) [#41933](https://github.com/nodejs/node/pull/41933) +* \[[`fe7ca085a7`](https://github.com/nodejs/node/commit/fe7ca085a7)] - **(SEMVER-MAJOR)** **stream**: throw invalid arg type from End Of Stream (Jithil P Ponnan) [#41766](https://github.com/nodejs/node/pull/41766) +* \[[`48e784043d`](https://github.com/nodejs/node/commit/48e784043d)] - **(SEMVER-MAJOR)** **stream**: don't emit finish after destroy (Robert Nagy) [#40852](https://github.com/nodejs/node/pull/40852) +* \[[`f2170253b6`](https://github.com/nodejs/node/commit/f2170253b6)] - **(SEMVER-MAJOR)** **stream**: add errored and closed props (Robert Nagy) [#40696](https://github.com/nodejs/node/pull/40696) +* \[[`432d1b50e0`](https://github.com/nodejs/node/commit/432d1b50e0)] - **(SEMVER-MAJOR)** **test**: add initial test module (Colin Ihrig) [#42325](https://github.com/nodejs/node/pull/42325) +* \[[`92567283f4`](https://github.com/nodejs/node/commit/92567283f4)] - **(SEMVER-MAJOR)** **timers**: refactor internal classes to ES2015 syntax (Rabbit) [#37408](https://github.com/nodejs/node/pull/37408) +* \[[`65910c0d6c`](https://github.com/nodejs/node/commit/65910c0d6c)] - **(SEMVER-MAJOR)** **tls**: represent registeredID numerically always (Tobias Nießen) [#41561](https://github.com/nodejs/node/pull/41561) +* \[[`807c7e14f4`](https://github.com/nodejs/node/commit/807c7e14f4)] - **(SEMVER-MAJOR)** **tls**: move tls.parseCertString to end-of-life (Tobias Nießen) [#41479](https://github.com/nodejs/node/pull/41479) +* \[[`f524306077`](https://github.com/nodejs/node/commit/f524306077)] - **(SEMVER-MAJOR)** **url**: throw on NULL in IPv6 hostname (Rich Trott) [#42313](https://github.com/nodejs/node/pull/42313) +* \[[`0187bc5cdc`](https://github.com/nodejs/node/commit/0187bc5cdc)] - **(SEMVER-MAJOR)** **v8**: make v8.writeHeapSnapshot() error codes consistent (Darshan Sen) [#42577](https://github.com/nodejs/node/pull/42577) +* \[[`74b9baa426`](https://github.com/nodejs/node/commit/74b9baa426)] - **(SEMVER-MAJOR)** **v8**: make writeHeapSnapshot throw if fopen fails (Antonio Román) [#41373](https://github.com/nodejs/node/pull/41373) +* \[[`ce4d3adf50`](https://github.com/nodejs/node/commit/ce4d3adf50)] - **(SEMVER-MAJOR)** **worker**: expose BroadcastChannel as a global (James M Snell) [#41271](https://github.com/nodejs/node/pull/41271) +* \[[`6486a304d3`](https://github.com/nodejs/node/commit/6486a304d3)] - **(SEMVER-MAJOR)** **worker**: graduate BroadcastChannel to supported (James M Snell) [#41271](https://github.com/nodejs/node/pull/41271) + +### Semver-Minor Commits + +* \[[`415726b8c4`](https://github.com/nodejs/node/commit/415726b8c4)] - **(SEMVER-MINOR)** **stream**: add writableAborted (Robert Nagy) [#40802](https://github.com/nodejs/node/pull/40802) +* \[[`54819f08e0`](https://github.com/nodejs/node/commit/54819f08e0)] - **(SEMVER-MINOR)** **test\_runner**: support 'only' tests (Colin Ihrig) [#42514](https://github.com/nodejs/node/pull/42514) + +### Semver-Patch Commits + +* \[[`7533d08b94`](https://github.com/nodejs/node/commit/7533d08b94)] - **buffer**: fix `atob` input validation (Austin Kelleher) [#42662](https://github.com/nodejs/node/pull/42662) +* \[[`96673bcb96`](https://github.com/nodejs/node/commit/96673bcb96)] - **build**: run clang-format on CI (Darshan Sen) [#42681](https://github.com/nodejs/node/pull/42681) +* \[[`d5462e4558`](https://github.com/nodejs/node/commit/d5462e4558)] - **build**: reset embedder string to "-node.0" (Michaël Zasso) [#42657](https://github.com/nodejs/node/pull/42657) +* \[[`aa52873887`](https://github.com/nodejs/node/commit/aa52873887)] - **build**: add configure option --v8-enable-short-builtin-calls (daomingq) [#42109](https://github.com/nodejs/node/pull/42109) +* \[[`7ee8a7a463`](https://github.com/nodejs/node/commit/7ee8a7a463)] - **build**: reset embedder string to "-node.0" (Michaël Zasso) [#41610](https://github.com/nodejs/node/pull/41610) +* \[[`a189dee52a`](https://github.com/nodejs/node/commit/a189dee52a)] - **build**: reset embedder string to "-node.0" (Michaël Zasso) [#40907](https://github.com/nodejs/node/pull/40907) +* \[[`e8697cfe38`](https://github.com/nodejs/node/commit/e8697cfe38)] - **crypto**: improve prime size argument validation (Tobias Nießen) [#42234](https://github.com/nodejs/node/pull/42234) +* \[[`a9c0689786`](https://github.com/nodejs/node/commit/a9c0689786)] - **crypto**: fix return type prob reported by coverity (Michael Dawson) [#42135](https://github.com/nodejs/node/pull/42135) +* \[[`e938515b41`](https://github.com/nodejs/node/commit/e938515b41)] - **deps**: patch V8 to 10.1.124.8 (Michaël Zasso) [#42730](https://github.com/nodejs/node/pull/42730) +* \[[`eba7d2db7f`](https://github.com/nodejs/node/commit/eba7d2db7f)] - **deps**: V8: cherry-pick ad21d212fc14 (Michaël Zasso) [#42657](https://github.com/nodejs/node/pull/42657) +* \[[`004137e269`](https://github.com/nodejs/node/commit/004137e269)] - **deps**: V8: cherry-pick 4c29cf1b7885 (Michaël Zasso) [#42657](https://github.com/nodejs/node/pull/42657) +* \[[`a052c03033`](https://github.com/nodejs/node/commit/a052c03033)] - **deps**: V8: cherry-pick ca2a787a0b49 (Michaël Zasso) [#42657](https://github.com/nodejs/node/pull/42657) +* \[[`01cea9a8d8`](https://github.com/nodejs/node/commit/01cea9a8d8)] - **deps**: V8: cherry-pick a2cae2180a7a (Michaël Zasso) [#42657](https://github.com/nodejs/node/pull/42657) +* \[[`d9d26b08ef`](https://github.com/nodejs/node/commit/d9d26b08ef)] - **deps**: V8: cherry-pick 87ce4f5d98a5 (Michaël Zasso) [#42657](https://github.com/nodejs/node/pull/42657) +* \[[`64a6328505`](https://github.com/nodejs/node/commit/64a6328505)] - **deps**: make V8 compilable with older glibc (Michaël Zasso) [#42657](https://github.com/nodejs/node/pull/42657) +* \[[`fde59217b9`](https://github.com/nodejs/node/commit/fde59217b9)] - **deps**: V8: fix v8-cppgc.h for MSVC (Jiawen Geng) [#42657](https://github.com/nodejs/node/pull/42657) +* \[[`cdcc82cced`](https://github.com/nodejs/node/commit/cdcc82cced)] - **deps**: silence V8's warning on CompileFunction (Michaël Zasso) [#40907](https://github.com/nodejs/node/pull/40907) +* \[[`2f51e121da`](https://github.com/nodejs/node/commit/2f51e121da)] - **deps**: update Acorn to v8.7.0 (Michaël Zasso) [#42667](https://github.com/nodejs/node/pull/42667) +* \[[`6d4b01774b`](https://github.com/nodejs/node/commit/6d4b01774b)] - **deps**: update ICU to 71.1 (Michaël Zasso) [#42655](https://github.com/nodejs/node/pull/42655) +* \[[`2d84620f86`](https://github.com/nodejs/node/commit/2d84620f86)] - **deps**: upgrade npm to 8.6.0 (npm team) [#42550](https://github.com/nodejs/node/pull/42550) +* \[[`c7ac11fa25`](https://github.com/nodejs/node/commit/c7ac11fa25)] - **deps**: update undici to 5.0.0 (Node.js GitHub Bot) [#42583](https://github.com/nodejs/node/pull/42583) +* \[[`468fffdf66`](https://github.com/nodejs/node/commit/468fffdf66)] - **deps**: V8: cherry-pick 50d5fb7a457c (Michaël Zasso) [#41610](https://github.com/nodejs/node/pull/41610) +* \[[`48708be57b`](https://github.com/nodejs/node/commit/48708be57b)] - **deps**: V8: cherry-pick 79a9d2eb3477 (Michaël Zasso) [#41610](https://github.com/nodejs/node/pull/41610) +* \[[`3c8782f70e`](https://github.com/nodejs/node/commit/3c8782f70e)] - **deps**: silence V8's warning on CompileFunction (Michaël Zasso) [#40907](https://github.com/nodejs/node/pull/40907) +* \[[`9318408c49`](https://github.com/nodejs/node/commit/9318408c49)] - **deps**: silence V8's warning on CompileFunction (Michaël Zasso) [#40907](https://github.com/nodejs/node/pull/40907) +* \[[`e23e345b6c`](https://github.com/nodejs/node/commit/e23e345b6c)] - **deps**: V8: cherry-pick 80bbbb143c24 (Michaël Zasso) [#40907](https://github.com/nodejs/node/pull/40907) +* \[[`696ce7df26`](https://github.com/nodejs/node/commit/696ce7df26)] - **deps**: V8: cherry-pick 1cc12b278e22 (Michaël Zasso) [#40907](https://github.com/nodejs/node/pull/40907) +* \[[`aa88e5e4b9`](https://github.com/nodejs/node/commit/aa88e5e4b9)] - **doc**: revise data imports and node: imports sections (Rich Trott) [#42734](https://github.com/nodejs/node/pull/42734) +* \[[`a058cefe29`](https://github.com/nodejs/node/commit/a058cefe29)] - **doc**: fix ESM JSON/data URL import example (Rich Trott) [#42728](https://github.com/nodejs/node/pull/42728) +* \[[`e61b62b9d4`](https://github.com/nodejs/node/commit/e61b62b9d4)] - **doc**: improve doc for http.ServerResponse inheritance (Luigi Pinca) [#42693](https://github.com/nodejs/node/pull/42693) +* \[[`6669b3857f`](https://github.com/nodejs/node/commit/6669b3857f)] - **doc**: add RafaelGSS to collaborators (RafaelGSS) [#42718](https://github.com/nodejs/node/pull/42718) +* \[[`f825341bab`](https://github.com/nodejs/node/commit/f825341bab)] - **doc**: add NodeEdKeyGenParams to CryptoKey.algorithm (Tobias Nießen) [#42629](https://github.com/nodejs/node/pull/42629) +* \[[`d4d78361f2`](https://github.com/nodejs/node/commit/d4d78361f2)] - **doc**: fix the example for embedders (Momtchil Momtchev) [#42671](https://github.com/nodejs/node/pull/42671) +* \[[`6706be1cdb`](https://github.com/nodejs/node/commit/6706be1cdb)] - **doc**: change AES-GCM IV recommendation in WebCrypto (Tobias Nießen) [#42611](https://github.com/nodejs/node/pull/42611) +* \[[`4508c8caa4`](https://github.com/nodejs/node/commit/4508c8caa4)] - **doc**: fix `added:` info for some methods (Luigi Pinca) [#42661](https://github.com/nodejs/node/pull/42661) +* \[[`951dbc045a`](https://github.com/nodejs/node/commit/951dbc045a)] - **doc**: remove unneeded new in Buffer example (Niklas Mischkulnig) [#42682](https://github.com/nodejs/node/pull/42682) +* \[[`65e838071b`](https://github.com/nodejs/node/commit/65e838071b)] - **doc**: mark worker.id as integer in cluster docs (Tobias Nießen) [#42684](https://github.com/nodejs/node/pull/42684) +* \[[`a82713cbb6`](https://github.com/nodejs/node/commit/a82713cbb6)] - **doc**: recommend `fh.createWriteStream` for fsPromises methods (Antoine du Hamel) [#42653](https://github.com/nodejs/node/pull/42653) +* \[[`13ad8d4e09`](https://github.com/nodejs/node/commit/13ad8d4e09)] - **doc**: fix outgoingMessage.removeHeader() signature (Luigi Pinca) [#42652](https://github.com/nodejs/node/pull/42652) +* \[[`a0461255c0`](https://github.com/nodejs/node/commit/a0461255c0)] - **doc**: mark tlsSocket.authorized as boolean property (Tobias Nießen) [#42647](https://github.com/nodejs/node/pull/42647) +* \[[`3ac7f86c2b`](https://github.com/nodejs/node/commit/3ac7f86c2b)] - **doc**: add missing punctuation in Web Streams doc (Tobias Nießen) [#42672](https://github.com/nodejs/node/pull/42672) +* \[[`b98386c977`](https://github.com/nodejs/node/commit/b98386c977)] - **doc**: add missing article in session ticket section (Tobias Nießen) [#42632](https://github.com/nodejs/node/pull/42632) +* \[[`a113468383`](https://github.com/nodejs/node/commit/a113468383)] - **doc**: link to dynamic import function (Tobias Nießen) [#42634](https://github.com/nodejs/node/pull/42634) +* \[[`dfc2dc8b65`](https://github.com/nodejs/node/commit/dfc2dc8b65)] - **doc**: add note about header values encoding (Shogun) [#42624](https://github.com/nodejs/node/pull/42624) +* \[[`ec5a359ffd`](https://github.com/nodejs/node/commit/ec5a359ffd)] - **doc**: add missing word in rootCertificates section (Tobias Nießen) [#42633](https://github.com/nodejs/node/pull/42633) +* \[[`c08a361f70`](https://github.com/nodejs/node/commit/c08a361f70)] - **doc**: add history entries for DEP0162 on `fs.md` (Antoine du Hamel) [#42608](https://github.com/nodejs/node/pull/42608) +* \[[`4fade6acb4`](https://github.com/nodejs/node/commit/4fade6acb4)] - **doc**: fix brackets position (Livia Medeiros) [#42649](https://github.com/nodejs/node/pull/42649) +* \[[`8055c7ba5d`](https://github.com/nodejs/node/commit/8055c7ba5d)] - **doc**: copyedit corepack.md (Rich Trott) [#42620](https://github.com/nodejs/node/pull/42620) +* \[[`85a65c3260`](https://github.com/nodejs/node/commit/85a65c3260)] - **doc**: delete chakra tt from diagnostic tooling support tiers (Tony Gorez) [#42627](https://github.com/nodejs/node/pull/42627) +* \[[`63bb6dcf0f`](https://github.com/nodejs/node/commit/63bb6dcf0f)] - **doc**: align links in table to top (nikoladev) [#41396](https://github.com/nodejs/node/pull/41396) +* \[[`28d8614add`](https://github.com/nodejs/node/commit/28d8614add)] - **http**: document that ClientRequest inherits from OutgoingMessage (K.C.Ashish Kumar) [#42642](https://github.com/nodejs/node/pull/42642) +* \[[`c37fdacb34`](https://github.com/nodejs/node/commit/c37fdacb34)] - **lib**: use class fields in observe.js (Joyee Cheung) [#42361](https://github.com/nodejs/node/pull/42361) +* \[[`ea0668a27e`](https://github.com/nodejs/node/commit/ea0668a27e)] - **lib**: use class fields in Event and EventTarget (Joyee Cheung) [#42361](https://github.com/nodejs/node/pull/42361) +* \[[`eb7b89c829`](https://github.com/nodejs/node/commit/eb7b89c829)] - **lib**: update class fields TODO in abort\_controller.js (Joyee Cheung) [#42361](https://github.com/nodejs/node/pull/42361) +* \[[`d835b1f1c1`](https://github.com/nodejs/node/commit/d835b1f1c1)] - **meta**: update AUTHORS (Node.js GitHub Bot) [#42677](https://github.com/nodejs/node/pull/42677) +* \[[`29492496e8`](https://github.com/nodejs/node/commit/29492496e8)] - **meta**: move one or more collaborators to emeritus (Node.js GitHub Bot) [#42599](https://github.com/nodejs/node/pull/42599) +* \[[`93c4dc5e5a`](https://github.com/nodejs/node/commit/93c4dc5e5a)] - **module**: ensure 'node:'-only modules can access node\_modules (Colin Ihrig) [#42430](https://github.com/nodejs/node/pull/42430) +* \[[`3a26db9697`](https://github.com/nodejs/node/commit/3a26db9697)] - **net**: make `server.address()` return an integer for `family` (Antoine du Hamel) [#41431](https://github.com/nodejs/node/pull/41431) +* \[[`44fdf953ba`](https://github.com/nodejs/node/commit/44fdf953ba)] - **node-api,src**: fix module registration in MSVC C++ (Vladimir Morozov) [#42459](https://github.com/nodejs/node/pull/42459) +* \[[`3026ca0bf2`](https://github.com/nodejs/node/commit/3026ca0bf2)] - **src**: fix coverity report (Michael Dawson) [#42663](https://github.com/nodejs/node/pull/42663) +* \[[`01fd048c6e`](https://github.com/nodejs/node/commit/01fd048c6e)] - **src**: update NODE\_MODULE\_VERSION to 108 (Michaël Zasso) [#42657](https://github.com/nodejs/node/pull/42657) +* \[[`75a71dc7ae`](https://github.com/nodejs/node/commit/75a71dc7ae)] - **src**: fix alphabetically sorted binding list (Tobias Nießen) [#42687](https://github.com/nodejs/node/pull/42687) +* \[[`457567f72c`](https://github.com/nodejs/node/commit/457567f72c)] - **src**: include crypto in the bootstrap snapshot (Joyee Cheung) [#42203](https://github.com/nodejs/node/pull/42203) +* \[[`aa7dc808f5`](https://github.com/nodejs/node/commit/aa7dc808f5)] - **src**: update ImportModuleDynamically (Camillo Bruni) [#41610](https://github.com/nodejs/node/pull/41610) +* \[[`fa0439e66c`](https://github.com/nodejs/node/commit/fa0439e66c)] - **src**: update NODE\_MODULE\_VERSION to 105 (Michaël Zasso) [#41610](https://github.com/nodejs/node/pull/41610) +* \[[`6ec1664dc8`](https://github.com/nodejs/node/commit/6ec1664dc8)] - **src**: update NODE\_MODULE\_VERSION to 104 (Michaël Zasso) [#40907](https://github.com/nodejs/node/pull/40907) +* \[[`a706342368`](https://github.com/nodejs/node/commit/a706342368)] - **src**: add kNoBrowserGlobals flag for Environment (Cheng Zhao) [#40532](https://github.com/nodejs/node/pull/40532) +* \[[`0c57a37dd0`](https://github.com/nodejs/node/commit/0c57a37dd0)] - **src,crypto**: remove uses of AllocatedBuffer from crypto\_tls.cc (Darshan Sen) [#42589](https://github.com/nodejs/node/pull/42589) +* \[[`be01185844`](https://github.com/nodejs/node/commit/be01185844)] - **src,inspector**: fix empty MaybeLocal crash (Darshan Sen) [#42409](https://github.com/nodejs/node/pull/42409) +* \[[`340b770d3f`](https://github.com/nodejs/node/commit/340b770d3f)] - **stream**: unify writableErrored and readableErrored (Robert Nagy) [#40799](https://github.com/nodejs/node/pull/40799) +* \[[`19064bec34`](https://github.com/nodejs/node/commit/19064bec34)] - **test**: delete test/pummel/test-repl-empty-maybelocal-crash.js (Darshan Sen) [#42720](https://github.com/nodejs/node/pull/42720) +* \[[`9d6af7d1fe`](https://github.com/nodejs/node/commit/9d6af7d1fe)] - **test**: improve `internal/url.js` coverage (Yoshiki Kurihara) [#42650](https://github.com/nodejs/node/pull/42650) +* \[[`d49df5ca8d`](https://github.com/nodejs/node/commit/d49df5ca8d)] - **test**: adapt message tests for V8 10.2 (Michaël Zasso) [#42657](https://github.com/nodejs/node/pull/42657) +* \[[`c6b4e9604f`](https://github.com/nodejs/node/commit/c6b4e9604f)] - **test**: adapt test-worker-debug for V8 10.0 (Michaël Zasso) [#42657](https://github.com/nodejs/node/pull/42657) +* \[[`0854fce8bc`](https://github.com/nodejs/node/commit/0854fce8bc)] - **test**: adapt test-v8-serdes for V8 9.9 (Michaël Zasso) [#42657](https://github.com/nodejs/node/pull/42657) +* \[[`73d53fe9f5`](https://github.com/nodejs/node/commit/73d53fe9f5)] - **test**: only skip slow tests on Raspberry Pi devices (Richard Lau) [#42645](https://github.com/nodejs/node/pull/42645) +* \[[`db7fa9f4b7`](https://github.com/nodejs/node/commit/db7fa9f4b7)] - **test**: allow numeric string for lookupService test (Daeyeon Jeong) [#42596](https://github.com/nodejs/node/pull/42596) +* \[[`0525a147b2`](https://github.com/nodejs/node/commit/0525a147b2)] - **test**: remove an unnecessary `undefined` in wpt (Khaidi Chu) [#41470](https://github.com/nodejs/node/pull/41470) +* \[[`bb762c5bd0`](https://github.com/nodejs/node/commit/bb762c5bd0)] - **test**: simplify test-http-write-callbacks.js (Tobias Nießen) [#42628](https://github.com/nodejs/node/pull/42628) +* \[[`1600869eb7`](https://github.com/nodejs/node/commit/1600869eb7)] - **test**: fix comments in test files (Daeyeon Jeong) [#42536](https://github.com/nodejs/node/pull/42536) +* \[[`82181bb9b8`](https://github.com/nodejs/node/commit/82181bb9b8)] - **test**: fix failure in test/sequential/test-heapdump.js (Darshan Sen) [#41772](https://github.com/nodejs/node/pull/41772) +* \[[`ba5b5acaf1`](https://github.com/nodejs/node/commit/ba5b5acaf1)] - **test**: improve `worker_threads ` coverage (Erick Wendel) [#41818](https://github.com/nodejs/node/pull/41818) +* \[[`f076c36335`](https://github.com/nodejs/node/commit/f076c36335)] - **tools**: update clang-format 1.6.0 to 1.7.0 (Rich Trott) [#42724](https://github.com/nodejs/node/pull/42724) +* \[[`45162bf9e7`](https://github.com/nodejs/node/commit/45162bf9e7)] - **tools**: update clang-format from 1.2.3 to 1.6.0 (Rich Trott) [#42685](https://github.com/nodejs/node/pull/42685) +* \[[`40bc08089d`](https://github.com/nodejs/node/commit/40bc08089d)] - **tools**: update V8 gypfiles for 10.1 (Michaël Zasso) [#42657](https://github.com/nodejs/node/pull/42657) +* \[[`09513cd1a3`](https://github.com/nodejs/node/commit/09513cd1a3)] - **tools**: update eslint to 8.13.0 (Node.js GitHub Bot) [#42678](https://github.com/nodejs/node/pull/42678) +* \[[`b99bb57416`](https://github.com/nodejs/node/commit/b99bb57416)] - **tools**: update gyp-next to v0.12.1 (Michaël Zasso) [#42625](https://github.com/nodejs/node/pull/42625) +* \[[`2468db1f53`](https://github.com/nodejs/node/commit/2468db1f53)] - **tools**: update lint-md-dependencies to @rollup/plugin-commonjs\@21.0.3 (Node.js GitHub Bot) [#42584](https://github.com/nodejs/node/pull/42584) +* \[[`8a3f28a05c`](https://github.com/nodejs/node/commit/8a3f28a05c)] - **tools**: add v8-embedder-state-scope.h to distributed headers (Michaël Zasso) [#41610](https://github.com/nodejs/node/pull/41610) +* \[[`30c4e1d952`](https://github.com/nodejs/node/commit/30c4e1d952)] - **tools**: update V8 gypfiles for 9.8 (Michaël Zasso) [#41610](https://github.com/nodejs/node/pull/41610) +* \[[`1ad44094a2`](https://github.com/nodejs/node/commit/1ad44094a2)] - **tools**: update V8 gypfiles for 9.7 (Michaël Zasso) [#40907](https://github.com/nodejs/node/pull/40907) +* \[[`86b77f7d0f`](https://github.com/nodejs/node/commit/86b77f7d0f)] - **tools,doc**: use V8::DisposePlatform (Michaël Zasso) [#41610](https://github.com/nodejs/node/pull/41610) +* \[[`62e62757b3`](https://github.com/nodejs/node/commit/62e62757b3)] - **tools,test**: fix V8 initialization order (Camillo Bruni) [#42657](https://github.com/nodejs/node/pull/42657) +* \[[`0187bc5cdc`](https://github.com/nodejs/node/commit/0187bc5cdc)] - **v8**: make v8.writeHeapSnapshot() error codes consistent (Darshan Sen) [#42577](https://github.com/nodejs/node/pull/42577) +* \[[`74b9baa426`](https://github.com/nodejs/node/commit/74b9baa426)] - **v8**: make writeHeapSnapshot throw if fopen fails (Antonio Román) [#41373](https://github.com/nodejs/node/pull/41373) diff --git a/doc/changelogs/CHANGELOG_V4.md b/doc/changelogs/CHANGELOG_V4.md index 8dac45fb1eee1f..947c9a9fdcb858 100644 --- a/doc/changelogs/CHANGELOG_V4.md +++ b/doc/changelogs/CHANGELOG_V4.md @@ -56,6 +56,7 @@ * Other Versions + * [18.x](CHANGELOG_V18.md) * [17.x](CHANGELOG_V17.md) * [16.x](CHANGELOG_V16.md) * [15.x](CHANGELOG_V15.md) diff --git a/doc/changelogs/CHANGELOG_V5.md b/doc/changelogs/CHANGELOG_V5.md index b711a0624551df..a80a0ee7e5beae 100644 --- a/doc/changelogs/CHANGELOG_V5.md +++ b/doc/changelogs/CHANGELOG_V5.md @@ -32,6 +32,7 @@ * Other Versions + * [18.x](CHANGELOG_V18.md) * [17.x](CHANGELOG_V17.md) * [16.x](CHANGELOG_V16.md) * [15.x](CHANGELOG_V15.md) diff --git a/doc/changelogs/CHANGELOG_V6.md b/doc/changelogs/CHANGELOG_V6.md index 0c5e1ee0d62b9b..d0372a3baa64ad 100644 --- a/doc/changelogs/CHANGELOG_V6.md +++ b/doc/changelogs/CHANGELOG_V6.md @@ -61,6 +61,7 @@ * Other Versions + * [18.x](CHANGELOG_V18.md) * [17.x](CHANGELOG_V17.md) * [16.x](CHANGELOG_V16.md) * [15.x](CHANGELOG_V15.md) diff --git a/doc/changelogs/CHANGELOG_V7.md b/doc/changelogs/CHANGELOG_V7.md index 120179523a8883..b698044eb6b775 100644 --- a/doc/changelogs/CHANGELOG_V7.md +++ b/doc/changelogs/CHANGELOG_V7.md @@ -30,6 +30,7 @@ * Other Versions + * [18.x](CHANGELOG_V18.md) * [17.x](CHANGELOG_V17.md) * [16.x](CHANGELOG_V16.md) * [15.x](CHANGELOG_V15.md) diff --git a/doc/changelogs/CHANGELOG_V8.md b/doc/changelogs/CHANGELOG_V8.md index 8c0b1c5479d3cb..611fcbf032dd1a 100644 --- a/doc/changelogs/CHANGELOG_V8.md +++ b/doc/changelogs/CHANGELOG_V8.md @@ -52,6 +52,7 @@ * Other Versions + * [18.x](CHANGELOG_V18.md) * [17.x](CHANGELOG_V17.md) * [16.x](CHANGELOG_V16.md) * [15.x](CHANGELOG_V15.md) diff --git a/doc/changelogs/CHANGELOG_V9.md b/doc/changelogs/CHANGELOG_V9.md index 8373b13a524592..0407b28d5e0874 100644 --- a/doc/changelogs/CHANGELOG_V9.md +++ b/doc/changelogs/CHANGELOG_V9.md @@ -31,6 +31,7 @@ * Other Versions + * [18.x](CHANGELOG_V18.md) * [17.x](CHANGELOG_V17.md) * [16.x](CHANGELOG_V16.md) * [15.x](CHANGELOG_V15.md) diff --git a/doc/contributing/collaborator-guide.md b/doc/contributing/collaborator-guide.md index 77e88f4a4571f6..4550c2e7d1565c 100644 --- a/doc/contributing/collaborator-guide.md +++ b/doc/contributing/collaborator-guide.md @@ -22,7 +22,7 @@ * [Unintended breaking changes](#unintended-breaking-changes) * [Reverting commits](#reverting-commits) * [Introducing new modules](#introducing-new-modules) - * [Additions to Node-API](#additions-to-n-api) + * [Additions to Node-API](#additions-to-node-api) * [Deprecations](#deprecations) * [Involving the TSC](#involving-the-tsc) * [Landing pull requests](#landing-pull-requests) diff --git a/doc/contributing/diagnostic-tooling-support-tiers.md b/doc/contributing/diagnostic-tooling-support-tiers.md index 556c87e6a95493..dbfbf56024e948 100644 --- a/doc/contributing/diagnostic-tooling-support-tiers.md +++ b/doc/contributing/diagnostic-tooling-support-tiers.md @@ -9,7 +9,7 @@ The Node.js project has assessed the tools and the APIs which support those tools. Each of the tools and APIs has been put into one of the following tiers. -* Tier 1 - Must always be working(CI tests passing) for all +* Tier 1 - Must always be working (CI tests passing) for all Current and LTS Node.js releases. A release will not be shipped if the test suite for the tool/API is not green. To be considered for inclusion in this tier it must have a good test suite and that test suite and a job @@ -29,7 +29,7 @@ the following tiers. its dependencies; and * The tool must be open source. -* Tier 2 - Must be working(CI tests passing) for all +* Tier 2 - Must be working (CI tests passing) for all LTS releases. An LTS release will not be shipped if the test suite for the tool/API is not green. To be considered for inclusion in this tier it must have a good test suite and that test suite and a job @@ -125,8 +125,6 @@ The tools are currently assigned to Tiers as follows: | Tool Type | Tool/API Name | Regular Testing in Node.js CI | Integrated with Node.js | Target Tier | | --------- | ------------------------- | ----------------------------- | ----------------------- | ----------- | | FFDC | node-report | No | No | 1 | -| Memory | mdb\_V8 | No | No | 4 | -| Memory | node-heapdump | No | No | 2 | | Memory | V8 heap profiler | No | Yes | 1 | | Memory | V8 sampling heap profiler | No | Yes | 1 | | AsyncFlow | Async Hooks (API) | ? | Yes | 1 | @@ -134,8 +132,8 @@ The tools are currently assigned to Tiers as follows: | Debugger | Command line Debug Client | ? | Yes | 1 | | Debugger | llnode | ? | No | 2 | | Debugger | Chrome Dev tools | ? | No | 3 | -| Debugger | Chakracore - time-travel | No | Data source only | too early | | Tracing | trace\_events (API) | No | Yes | 1 | +| Tracing | trace\_gc | No | Yes | 1 | | Tracing | DTrace | No | Partial | 3 | | Tracing | LTTng | No | Removed? | N/A | | Tracing | ETW | No | Partial | 3 | diff --git a/doc/contributing/feature-request-management.md b/doc/contributing/feature-request-management.md index 45774869cd1738..4cd4f00836130d 100644 --- a/doc/contributing/feature-request-management.md +++ b/doc/contributing/feature-request-management.md @@ -65,11 +65,11 @@ to the issue: ```markdown There has been no activity on this feature request for 5 months and it is unlikely to be implemented. -It will be closed after 6 months after the last non-automated comment unless there is renewed -discussion or collaborators advocating that it be kept -open. For more information on how the project manages -feature requests please consult the -[feature request management document](https://github.com/nodejs/node/blob/HEAD/doc/guides/feature-request-management.md). +It will be closed 6 months after the last non-automated comment. + +For more information on how the project manages +feature requests, please consult the +[feature request management document](https://github.com/nodejs/node/blob/HEAD/doc/contributing/feature-request-management.md). ``` If there is no additional activity/discussion on the @@ -79,11 +79,10 @@ closed: ```markdown There has been no activity on this feature request -and it is being closed. We value your input -and if you feel closing this issue was not the -right thing to do, please re-open it. +and it is being closed. If you feel closing this issue is not the +right thing to do, please leave a comment. For more information on how the project manages -feature requests please consult the -[feature request management document](https://github.com/nodejs/node/blob/HEAD/doc/guides/feature-request-management.md). +feature requests, please consult the +[feature request management document](https://github.com/nodejs/node/blob/HEAD/doc/contributing/feature-request-management.md). ``` diff --git a/doc/contributing/investigating-native-memory-leaks.md b/doc/contributing/investigating-native-memory-leaks.md index f9345b092737fa..17389c6bc91a7e 100644 --- a/doc/contributing/investigating-native-memory-leaks.md +++ b/doc/contributing/investigating-native-memory-leaks.md @@ -101,7 +101,7 @@ Leaks can be introduced in native addons and the following is a simple example leak based on the "Hello world" addon from [node-addon-examples](https://github.com/nodejs/node-addon-examples). -In this example, a loop which allocates approximately 1 MB of memory and never +In this example, a loop which allocates approximately 1 MiB of memory and never frees it has been added: ```cpp diff --git a/doc/contributing/maintaining-openssl.md b/doc/contributing/maintaining-openssl.md index 25a58e2f440cc5..9228b9c6bb107c 100644 --- a/doc/contributing/maintaining-openssl.md +++ b/doc/contributing/maintaining-openssl.md @@ -3,13 +3,15 @@ This document describes how to update `deps/openssl/`. If you need to provide updates across all active release lines you will -currently need to generate three PRs as follows: +currently need to generate four PRs as follows: * a PR for master which is generated following the instructions - below. + below for OpenSSL 3.x.x. +* a PR for 16.x following the instructions in the v16.x-staging version + of this guide. * a PR for 14.x following the instructions in the v14.x-staging version of this guide. -* a PR which uses the same commit from the second PR to apply the +* a PR which uses the same commit from the third PR to apply the updates to the openssl source code, with a new commit generated by following steps 2 onwards on the 12.x line. This is necessary because the configuration files have embedded timestamps @@ -25,7 +27,7 @@ Details on the fork, as well as the latest sources, can be found at . Branches are used per OpenSSL version (for instance, -. +). ## Requirements @@ -90,7 +92,7 @@ This updates all sources in deps/openssl/openssl by: $ git commit openssl ``` -### OpenSSL 3.0.0 +### OpenSSL 3.x.x ```console % git clone https://github.com/quictls/openssl @@ -104,14 +106,15 @@ This updates all sources in deps/openssl/openssl by: ``` ```text -deps: upgrade openssl sources to quictls/openssl-3.0.0-alpha-16 +deps: upgrade openssl sources to quictls/openssl-3.0.2 This updates all sources in deps/openssl/openssl by: $ git clone git@github.com:quictls/openssl.git $ cd openssl + $ git checkout openssl-3.0.2+quic $ cd ../node/deps/openssl $ rm -rf openssl - $ cp -R ../openssl openssl + $ cp -R ../../../openssl openssl $ rm -rf openssl/.git* openssl/.travis* $ git add --all openssl $ git commit openssl @@ -178,7 +181,7 @@ to the relevant value): $ git commit ``` -### OpenSSL 3.0.0 +### OpenSSL 3.0.x ```text deps: update archs files for quictls/openssl-3.0.0-alpha-16 diff --git a/doc/contributing/maintaining-web-assembly.md b/doc/contributing/maintaining-web-assembly.md new file mode 100644 index 00000000000000..9d8d8626ba9c84 --- /dev/null +++ b/doc/contributing/maintaining-web-assembly.md @@ -0,0 +1,97 @@ +# Maintaining WebAssembly + +Support for [WebAssembly](https://webassembly.org/) +has been identified as one of the +[top technical priorities](https://github.com/nodejs/node/blob/master/doc/contributing/technical-priorities.md#webassembly) +for the future success of Node.js. + +This document provides an overview of our high-level strategy for +supporting WebAssembly and information about our current implementation +as a starting point for contributors. + +## High-level approach + +The key elements of our WebAssembly strategy include: + +* Up-to-date core WebAssembly support +* Support for high-level APIs +* Making it easy to load WebAssembly +* Making sure the core Node.js APIs are compatible with WebAssembly + and can be called in an efficient manner from WebAssembly + +### Up-to-date core WebAssembly support + +Node.js gets its core WebAssembly support through V8. We don't need +to do anything specific to support this, all we have to do is keep +the version of V8 as up-to-date as possible. + +### Key API support + +As a runtime, Node.js must implement a number of APIs in addition +to the core WebAssembly support in order to be a good choice to run +WebAssembly. The project has currently identified these additional +APIs as important: + +* WebAssembly System Interface (WASI). This provides the ability for + WebAssembly to interact with the outside world. Node.js currently + has an implementation (see below for more details). +* WebAssembly streaming APIs - As the Node.js implementation of + [WebStreams](https://nodejs.org/api/webstreams.html) matures, + implementing the embedder APIs to enable streaming with WebAssembly + will be important. +* [WebAssembly Component Model](https://github.com/WebAssembly/component-model/). + This API is still in the definition stage but the project should + keep track of its development as a way to simplify native code + integration. + +### Making it as easy as possible to load WASM + +The most important thing we can do on this front is to either find and +reference resources or provide resources on how to: + +* Compile your WebAssembly code (outside of Node.js) and integrate that + into an npm workflow. +* Load and run WebAssembly code in your Node.js application. + +It is also important to support and track the ongoing work in ESM to enable +loading of WebAssembly with ESM. + +### Making sure the core Node.js APIs are compatible with WebAssembly + +Use cases for which Node.js will be a good runtime will include code +both in JavaScript and compiled into WebAssembly. It is important +that Node.js APIs are able to be called from WebAssembly in +an efficient manner without extra buffer copies. We need to: + +* Review APIs and identify those that can be called often from + WebAssembly. +* Where appropriate, make additions to identified APIs to allow + a pre-existing buffer to be passed in order to avoid copies. + +## Current implementation and assets + +### WebAssembly System Interface (WASI) + +The Node.js WASI implementation is maintained in the +[uvwasi](https://github.com/nodejs/uvwasi) repository in the +Node.js GitHub organization. As needed, an updated copy +is vendored into the Node.js deps in +[deps/uvwasi](https://github.com/nodejs/node/tree/master/deps/uvwasi). + +To update the copy of uvwasi in the Node.js deps: + +* Copy over the contents of `include` and `src` to the corresponding + directories. +* Check if any additional files have been added and need to be added + to the `sources` list in `deps/uvwasi/uvwasi.gyp`. + +In addition to the code from uvwasi, Node.js includes bindings and +APIs that allow WebAssembly to be run with WASI support from Node.js. +The documentation for this API is in +[WebAssembly System Interface (WASI)](https://nodejs.org/api/wasi.html). + +The implementation of the bindings and the public API is in: + +* [src/node\_wasi.h](https://github.com/nodejs/node/blob/master/src/node_wasi.h) +* [src/node\_wasi.cc](https://github.com/nodejs/node/blob/master/src/node_wasi.cc) +* [lib/wasi.js](https://github.com/nodejs/node/blob/master/lib/wasi.js) diff --git a/doc/contributing/pull-requests.md b/doc/contributing/pull-requests.md index 00868c4bfc91d9..7a9e1a051e7b50 100644 --- a/doc/contributing/pull-requests.md +++ b/doc/contributing/pull-requests.md @@ -27,7 +27,6 @@ * [Notes](#notes) * [Commit squashing](#commit-squashing) * [Getting approvals for your pull request](#getting-approvals-for-your-pull-request) - * [CI testing](#ci-testing) * [Waiting until the pull request gets landed](#waiting-until-the-pull-request-gets-landed) * [Check out the collaborator guide](#check-out-the-collaborator-guide) * [Appendix: subsystems](#appendix-subsystems) @@ -244,6 +243,10 @@ test suite. To run the tests (including code linting) on Unix / macOS: ./configure && make -j4 test ``` +We can speed up the builds by using [Ninja](https://ninja-build.org/). For more +information, see +[Building Node.js with Ninja](building-node-with-ninja.md). + And on Windows: ```text @@ -433,7 +436,7 @@ check with the contributor to see if they intend to continue the work before checking if they would mind if you took it over (especially if it just has nits left). When doing so, it is courteous to give the original contributor credit for the work they started (either by preserving their name and email -address in the commit log, or by using an `Author:` meta-data tag in the +address) in the commit log, or by using an `Author:` meta-data tag in the commit. ### Approving a change @@ -507,17 +510,18 @@ feedback. All pull requests that contain changes to code must be run through continuous integration (CI) testing at [https://ci.nodejs.org/][]. -Only Node.js core collaborators with commit rights to the `nodejs/node` -repository may start a CI testing run. The specific details of how to do -this are included in the new collaborator [Onboarding guide][]. +Only Node.js core collaborators and triagers can start a CI testing run. The +specific details of how to do this are included in the new collaborator +[Onboarding guide][]. Usually, a collaborator or triager will start a CI +test run for you as approvals for the pull request come in. +If not, you can ask a collaborator or triager to start a CI run. Ideally, the code change will pass ("be green") on all platform configurations -supported by Node.js (there are over 30 platform configurations currently). -This means that all tests pass and there are no linting errors. In reality, -however, it is not uncommon for the CI infrastructure itself to fail on -specific platforms or for so-called "flaky" tests to fail ("be red"). It is -vital to visually inspect the results of all failed ("red") tests to determine -whether the failure was caused by the changes in the pull request. +supported by Node.js. This means that all tests pass and there are no linting +errors. In reality, however, it is not uncommon for the CI infrastructure itself +to fail on specific platforms or for so-called "flaky" tests to fail ("be red"). +It is vital to visually inspect the results of all failed ("red") tests to +determine whether the failure was caused by the changes in the pull request. ## Notes @@ -549,16 +553,6 @@ After you push new changes to your branch, you need to get approval for these new changes again, even if GitHub shows "Approved" because the reviewers have hit the buttons before. -### CI testing - -Every pull request needs to be tested -to make sure that it works on the platforms that Node.js -supports. This is done by running the code through the CI system. - -Only a collaborator can start a CI run. Usually one of them will do it -for you as approvals for the pull request come in. -If not, you can ask a collaborator to start a CI run. - ### Waiting until the pull request gets landed A pull request needs to stay open for at least 48 hours from when it is @@ -587,7 +581,7 @@ You can find the full list of supported subsystems in the More than one subsystem may be valid for any particular issue or pull request. [Building guide]: ../../BUILDING.md -[CI (Continuous Integration) test run]: #ci-testing +[CI (Continuous Integration) test run]: #continuous-integration-testing [Code of Conduct]: https://github.com/nodejs/admin/blob/HEAD/CODE_OF_CONDUCT.md [Onboarding guide]: ../../onboarding.md [approved]: #getting-approvals-for-your-pull-request diff --git a/doc/contributing/releases.md b/doc/contributing/releases.md index 10bf234088a1a4..44a2eba83f1394 100644 --- a/doc/contributing/releases.md +++ b/doc/contributing/releases.md @@ -692,8 +692,9 @@ This script will use the promoted builds and changelog to generate the post. Run Refs: ``` -* Changes to `master` on the [nodejs.org repository][] will trigger a new build - of nodejs.org so your changes should appear a few minutes after pushing. +* Changes to the base branch, `main`, on the [nodejs.org repository][] will + trigger a new build of nodejs.org so your changes should appear a few minutes + after pushing. ### 18. Create the release on GitHub diff --git a/doc/contributing/security-release-process.md b/doc/contributing/security-release-process.md index 6aee4655ad75b8..1fe257181b6fb8 100644 --- a/doc/contributing/security-release-process.md +++ b/doc/contributing/security-release-process.md @@ -74,6 +74,17 @@ The current security stewards are documented in the main Node.js (Re-PR the pre-approved branch from nodejs-private/nodejs.org-private to nodejs/nodejs.org) + If the security release will only contain an OpenSSL update consider + adding the following to the pre-release announcement: + + ```text + Since this security release will only include updates for OpenSSL, if you're using + a Node.js version which is part of a distribution which uses a system + installed OpenSSL, this Node.js security update might not concern you. You may + instead need to update your system OpenSSL libraries, please check the + security announcements for the distribution. + ``` + * [ ] Pre-release announcement [email][]: _**LINK TO EMAIL**_ * Subject: `Node.js security updates for all active release lines, Month Year` * Body: diff --git a/doc/contributing/security-steward-on-off-boarding.md b/doc/contributing/security-steward-on-off-boarding.md index 19c058f1696bc8..a689e6f33c03c8 100644 --- a/doc/contributing/security-steward-on-off-boarding.md +++ b/doc/contributing/security-steward-on-off-boarding.md @@ -6,6 +6,7 @@ to the project and not to use/disclose to their employer. * Add them to the security-stewards team in the GitHub nodejs-private organization. +* Add them to the [public website team](https://github.com/orgs/nodejs/teams/website). * Ensure they have 2FA enabled in H1. * Add them to the standard team in H1 using this [page](https://hackerone.com/nodejs/team_members). @@ -16,6 +17,7 @@ * Remove them from security-stewards team in the GitHub nodejs-private organization. +* Remove them from public website team * Unless they have access for another reason, remove them from the standard team in H1 using this [page](https://hackerone.com/nodejs/team_members). diff --git a/doc/contributing/static-analysis.md b/doc/contributing/static-analysis.md index 6c4a03883c7caf..46ee0c3649f873 100644 --- a/doc/contributing/static-analysis.md +++ b/doc/contributing/static-analysis.md @@ -13,4 +13,4 @@ titled `Please add me to coverity`. A member of the build WG with admin access will verify that the requestor is an existing collaborator as listed in the [collaborators section](https://github.com/nodejs/node#collaborators) on the nodejs/node project repo. Once validated the requestor will added -to to the coverity project. +to the coverity project. diff --git a/doc/contributing/strategic-initiatives.md b/doc/contributing/strategic-initiatives.md index 8bcd8800b7ee2b..b4c22eef27072d 100644 --- a/doc/contributing/strategic-initiatives.md +++ b/doc/contributing/strategic-initiatives.md @@ -6,13 +6,13 @@ agenda to ensure they are active and have the support they need. ## Current initiatives -| Initiative | Champion | Links | -| ------------------------- | --------------------------- | -------------------------------------------------------------------------------------------- | -| Core Promise APIs | [Antoine du Hamel][aduh95] | | -| Future of Build Toolchain | [Mary Marchini][mmarchini] | , | -| QUIC / HTTP3 | [James M Snell][jasnell] | | -| Startup performance | [Joyee Cheung][joyeecheung] | | -| V8 Currency | [Michaël Zasso][targos] | | +| Initiative | Champion | Links | +| ------------------- | --------------------------- | --------------------------------------------- | +| Core Promise APIs | [Antoine du Hamel][aduh95] | | +| QUIC / HTTP3 | [James M Snell][jasnell] | | +| Startup performance | [Joyee Cheung][joyeecheung] | | +| V8 Currency | [Michaël Zasso][targos] | | +| Next-10 | [Michael Dawson][mhdawson] | |
List of completed initiatives @@ -38,5 +38,5 @@ agenda to ensure they are active and have the support they need. [aduh95]: https://github.com/aduh95 [jasnell]: https://github.com/jasnell [joyeecheung]: https://github.com/joyeecheung -[mmarchini]: https://github.com/mmarchini +[mhdawson]: https://github.com/mhdawson [targos]: https://github.com/targos diff --git a/doc/contributing/writing-and-running-benchmarks.md b/doc/contributing/writing-and-running-benchmarks.md index a5c52eafb8a3d7..e61b5ea898c7de 100644 --- a/doc/contributing/writing-and-running-benchmarks.md +++ b/doc/contributing/writing-and-running-benchmarks.md @@ -254,7 +254,7 @@ run `node benchmark/compare.js`. As an example on how to check for a possible performance improvement, the [#5134](https://github.com/nodejs/node/pull/5134) pull request will be used as an example. This pull request _claims_ to improve the performance of the -`string_decoder` module. +`node:string_decoder` module. First build two versions of Node.js, one from the master branch (here called `./node-master`) and another with the pull request applied (here called @@ -479,7 +479,7 @@ the code inside the `main` function if it's more than just declaration. ```js 'use strict'; const common = require('../common.js'); -const { SlowBuffer } = require('buffer'); +const { SlowBuffer } = require('node:buffer'); const configs = { // Number of operations, specified here so they show up in the report. @@ -539,7 +539,7 @@ const bench = common.createBenchmark(main, { }); function main(conf) { - const http = require('http'); + const http = require('node:http'); const len = conf.kb * 1024; const chunk = Buffer.alloc(len, 'x'); const server = http.createServer((req, res) => { diff --git a/doc/contributing/writing-tests.md b/doc/contributing/writing-tests.md index 9884408af8553a..6241cb68c9624f 100644 --- a/doc/contributing/writing-tests.md +++ b/doc/contributing/writing-tests.md @@ -37,8 +37,8 @@ const fixtures = require('../common/fixtures'); // 3 // This test ensures that the http-parser can handle UTF-8 characters // 5 // in the http header. // 6 -const assert = require('assert'); // 8 -const http = require('http'); // 9 +const assert = require('node:assert'); // 8 +const http = require('node:http'); // 9 const server = http.createServer(common.mustCall((req, res) => { // 11 res.end('ok'); // 12 @@ -95,13 +95,13 @@ designed to test. ### **Lines 8-9** ```js -const assert = require('assert'); -const http = require('http'); +const assert = require('node:assert'); +const http = require('node:http'); ``` -The test checks functionality in the `http` module. +The test checks functionality in the `node:http` module. -Most tests use the `assert` module to confirm expectations of the test. +Most tests use the `node:assert` module to confirm expectations of the test. The require statements are sorted in [ASCII][] order (digits, upper @@ -173,8 +173,8 @@ explain this with a real test from the test suite. ```js 'use strict'; require('../common'); -const assert = require('assert'); -const http = require('http'); +const assert = require('node:assert'); +const http = require('node:http'); let request = 0; let listening = 0; @@ -207,7 +207,7 @@ This test could be greatly simplified by using `common.mustCall` like this: ```js 'use strict'; const common = require('../common'); -const http = require('http'); +const http = require('node:http'); const server = http.createServer(common.mustCall((req, res) => { res.end(); @@ -256,8 +256,8 @@ Node.js automatically crashes - and hence, the test fails - in the case of an ```js const common = require('../common'); -const assert = require('assert'); -const fs = require('fs').promises; +const assert = require('node:assert'); +const fs = require('node:fs').promises; // Wrap the `onFulfilled` handler in `common.mustCall()`. fs.readFile('test-file').then( @@ -280,8 +280,8 @@ A test that would require `internal/freelist` could start like this: // Flags: --expose-internals require('../common'); -const assert = require('assert'); -const freelist = require('internal/freelist'); +const assert = require('node:assert'); +const freelist = require('node:internal/freelist'); ``` In specific scenarios it may be useful to get a hold of `primordials` or @@ -291,7 +291,8 @@ In specific scenarios it may be useful to get a hold of `primordials` or node --expose-internals -r internal/test/binding lib/fs.js ``` -This only works if you preload `internal/test/binding` by command line flag. +This only works if you preload `node:internal/test/binding` by command line +flag. ### Assertions diff --git a/doc/node.1 b/doc/node.1 index 27b3365140cc57..2003bc8ca79127 100644 --- a/doc/node.1 +++ b/doc/node.1 @@ -270,7 +270,7 @@ This flag is inherited from V8 and is subject to change upstream. It may disappear in a non-semver-major release. . .It Fl -max-http-header-size Ns = Ns Ar size -Specify the maximum size of HTTP headers in bytes. Defaults to 16 KB. +Specify the maximum size of HTTP headers in bytes. Defaults to 16 KiB. . .It Fl -napi-modules This option is a no-op. @@ -381,6 +381,13 @@ the secure heap. The default is 0. The value must be a power of two. .It Fl -secure-heap-min Ns = Ns Ar n Specify the minimum allocation from the OpenSSL secure heap. The default is 2. The value must be a power of two. . +.It Fl -test +Starts the Node.js command line test runner. +. +.It Fl -test-only +Configures the test runner to only execute top level tests that have the `only` +option set. +. .It Fl -throw-deprecation Throw errors for deprecations. . diff --git a/doc/template.html b/doc/template.html index c8c78dce59b64a..86ba3c9581e004 100644 --- a/doc/template.html +++ b/doc/template.html @@ -9,6 +9,7 @@ +
@@ -22,7 +23,7 @@
-
+

Node.js __VERSION__ documentation


@@ -63,41 +75,5 @@

Node.js __VERSION__ documentation

- diff --git a/lib/.eslintrc.yaml b/lib/.eslintrc.yaml index bb9edf87e1fb13..c1635e4cf080af 100644 --- a/lib/.eslintrc.yaml +++ b/lib/.eslintrc.yaml @@ -43,6 +43,14 @@ rules: message: Use `const { BroadcastChannel } = require('internal/worker/io');` instead of the global. - name: Buffer message: Use `const { Buffer } = require('buffer');` instead of the global. + - name: ByteLengthQueuingStrategy + message: Use `const { ByteLengthQueuingStrategy } = require('internal/webstreams/queuingstrategies')` instead of the global. + - name: CompressionStream + message: Use `const { CompressionStream } = require('internal/webstreams/compression')` instead of the global. + - name: CountQueuingStrategy + message: Use `const { CountQueuingStrategy } = require('internal/webstreams/queuingstrategies')` instead of the global. + - name: DecompressionStream + message: Use `const { DecompressionStream } = require('internal/webstreams/compression')` instead of the global. - name: DOMException message: Use lazy function `const { lazyDOMException } = require('internal/util');` instead of the global. - name: Event @@ -63,6 +71,18 @@ rules: message: Use `const { MessageEvent } = require('internal/worker/io');` instead of the global. - name: MessagePort message: Use `const { MessagePort } = require('internal/worker/io');` instead of the global. + - name: ReadableStream + message: Use `const { ReadableStream } = require('internal/webstreams/readablestream')` instead of the global. + - name: ReadableStreamDefaultReader + message: Use `const { ReadableStreamDefaultReader } = require('internal/webstreams/readablestream')` instead of the global. + - name: ReadableStreamBYOBReader + message: Use `const { ReadableStreamBYOBReader } = require('internal/webstreams/readablestream')` instead of the global. + - name: ReadableStreamBYOBRequest + message: Use `const { ReadableStreamBYOBRequest } = require('internal/webstreams/readablestream')` instead of the global. + - name: ReadableByteStreamController + message: Use `const { ReadableByteStreamController } = require('internal/webstreams/readablestream')` instead of the global. + - name: ReadableStreamDefaultController + message: Use `const { ReadableStreamDefaultController } = require('internal/webstreams/readablestream')` instead of the global. - name: Request message: Use `const { Request } = require('internal/deps/undici/undici');` instead of the global. - name: Response @@ -73,8 +93,16 @@ rules: message: Use `const { SharedArrayBuffer } = globalThis;` instead of the global. - name: TextDecoder message: Use `const { TextDecoder } = require('internal/encoding');` instead of the global. + - name: TextDecoderStream + message: Use `const { TextDecoderStream } = require('internal/webstreams/encoding')` instead of the global. - name: TextEncoder message: Use `const { TextEncoder } = require('internal/encoding');` instead of the global. + - name: TextEncoderStream + message: Use `const { TextEncoderStream } = require('internal/webstreams/encoding')` instead of the global. + - name: TransformStream + message: Use `const { TransformStream } = require('internal/webstreams/transformstream')` instead of the global. + - name: TransformStreamDefaultController + message: Use `const { TransformStreamDefaultController } = require('internal/webstreams/transformstream')` instead of the global. - name: URL message: Use `const { URL } = require('internal/url');` instead of the global. - name: URLSearchParams @@ -83,6 +111,12 @@ rules: # disabled with --jitless CLI flag. - name: WebAssembly message: Use `const { WebAssembly } = globalThis;` instead of the global. + - name: WritableStream + message: Use `const { WritableStream } = require('internal/webstreams/writablestream')` instead of the global. + - name: WritableStreamDefaultWriter + message: Use `const { WritableStreamDefaultWriter } = require('internal/webstreams/writablestream')` instead of the global. + - name: WritableStreamDefaultController + message: Use `const { WritableStreamDefaultController } = require('internal/webstreams/writablestream')` instead of the global. - name: atob message: Use `const { atob } = require('buffer');` instead of the global. - name: btoa diff --git a/lib/_http_agent.js b/lib/_http_agent.js index 9c15875762dd47..ff2047d4ea81bb 100644 --- a/lib/_http_agent.js +++ b/lib/_http_agent.js @@ -101,6 +101,9 @@ function Agent(options) { this.options = { __proto__: null, ...options }; + if (this.options.noDelay === undefined) + this.options.noDelay = true; + // Don't confuse net and make it think that we're connecting to a pipe this.options.path = null; this.requests = ObjectCreate(null); @@ -217,7 +220,7 @@ Agent.defaultMaxSockets = Infinity; Agent.prototype.createConnection = net.createConnection; // Get the key for a given set of request options -Agent.prototype.getName = function getName(options) { +Agent.prototype.getName = function getName(options = {}) { let name = options.host || 'localhost'; name += ':'; diff --git a/lib/_http_client.js b/lib/_http_client.js index 59c4cac89e9d9d..a4f7a255a99671 100644 --- a/lib/_http_client.js +++ b/lib/_http_client.js @@ -57,7 +57,7 @@ const Agent = require('_http_agent'); const { Buffer } = require('buffer'); const { defaultTriggerAsyncIdScope } = require('internal/async_hooks'); const { URL, urlToHttpOptions, searchParamsSymbol } = require('internal/url'); -const { kOutHeaders, kNeedDrain } = require('internal/http'); +const { kOutHeaders, kNeedDrain, emitStatistics } = require('internal/http'); const { connResetException, codes } = require('internal/errors'); const { ERR_HTTP_HEADERS_SENT, @@ -75,6 +75,12 @@ const { DTRACE_HTTP_CLIENT_RESPONSE } = require('internal/dtrace'); +const { + hasObserver, +} = require('internal/perf/observe'); + +const kClientRequestStatistics = Symbol('ClientRequestStatistics'); + const { addAbortSignal, finished } = require('stream'); let debug = require('internal/util/debuglog').debuglog('http', (fn) => { @@ -337,6 +343,12 @@ ObjectSetPrototypeOf(ClientRequest, OutgoingMessage); ClientRequest.prototype._finish = function _finish() { DTRACE_HTTP_CLIENT_REQUEST(this, this.socket); FunctionPrototypeCall(OutgoingMessage.prototype._finish, this); + if (hasObserver('http')) { + this[kClientRequestStatistics] = { + startTime: process.hrtime(), + type: 'HttpClient', + }; + } }; ClientRequest.prototype._implicitHeader = function _implicitHeader() { @@ -604,6 +616,7 @@ function parserOnIncomingClient(res, shouldKeepAlive) { } DTRACE_HTTP_CLIENT_RESPONSE(socket, req); + emitStatistics(req[kClientRequestStatistics]); req.res = res; res.req = req; @@ -722,8 +735,7 @@ function tickOnSocket(req, socket) { parser.initialize(HTTPParser.RESPONSE, new HTTPClientAsyncResource('HTTPINCOMINGMESSAGE', req), req.maxHeaderSize || 0, - lenient ? kLenientAll : kLenientNone, - 0); + lenient ? kLenientAll : kLenientNone); parser.socket = socket; parser.outgoing = req; req.parser = parser; diff --git a/lib/_http_common.js b/lib/_http_common.js index 796deeff055767..2055ca205b84a3 100644 --- a/lib/_http_common.js +++ b/lib/_http_common.js @@ -40,12 +40,7 @@ const { readStop } = incoming; -let debug = require('internal/util/debuglog').debuglog('http', (fn) => { - debug = fn; -}); - const kIncomingMessage = Symbol('IncomingMessage'); -const kRequestTimeout = Symbol('RequestTimeout'); const kOnMessageBegin = HTTPParser.kOnMessageBegin | 0; const kOnHeaders = HTTPParser.kOnHeaders | 0; const kOnHeadersComplete = HTTPParser.kOnHeadersComplete | 0; @@ -102,12 +97,6 @@ function parserOnHeadersComplete(versionMajor, versionMinor, headers, method, incoming.url = url; incoming.upgrade = upgrade; - if (socket) { - debug('requestTimeout timer moved to req'); - incoming[kRequestTimeout] = incoming.socket[kRequestTimeout]; - incoming.socket[kRequestTimeout] = undefined; - } - let n = headers.length; // If parser.maxHeaderPairs <= 0 assume that there's no limit. @@ -273,7 +262,6 @@ module.exports = { methods, parsers, kIncomingMessage, - kRequestTimeout, HTTPParser, isLenient, prepareError, diff --git a/lib/_http_server.js b/lib/_http_server.js index 18127f0d19b3d5..84be32c78c4075 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -27,6 +27,7 @@ const { ObjectKeys, ObjectSetPrototypeOf, RegExpPrototypeTest, + ReflectApply, Symbol, SymbolFor, } = primordials; @@ -40,12 +41,12 @@ const { continueExpression, chunkExpression, kIncomingMessage, - kRequestTimeout, HTTPParser, isLenient, _checkInvalidHeaderChar: checkInvalidHeaderChar, prepareError, } = require('_http_common'); +const { ConnectionsList } = internalBinding('http_parser'); const { OutgoingMessage } = require('_http_outgoing'); const { kOutHeaders, @@ -79,8 +80,7 @@ const { DTRACE_HTTP_SERVER_REQUEST, DTRACE_HTTP_SERVER_RESPONSE } = require('internal/dtrace'); -const { setTimeout, clearTimeout } = require('timers'); - +const { setInterval, clearInterval } = require('timers'); let debug = require('internal/util/debuglog').debuglog('http', (fn) => { debug = fn; }); @@ -162,11 +162,12 @@ const STATUS_CODES = { 511: 'Network Authentication Required' // RFC 6585 6 }; -const kOnMessageBegin = HTTPParser.kOnMessageBegin | 0; const kOnExecute = HTTPParser.kOnExecute | 0; const kOnTimeout = HTTPParser.kOnTimeout | 0; const kLenientAll = HTTPParser.kLenientAll | 0; const kLenientNone = HTTPParser.kLenientNone | 0; +const kConnections = Symbol('http.server.connections'); +const kConnectionsCheckingInterval = Symbol('http.server.connectionsCheckingInterval'); class HTTPServerAsyncResource { constructor(type, socket) { @@ -193,7 +194,8 @@ function ServerResponse(req) { if (hasObserver('http')) { this[kServerResponseStatistics] = { - startTime: process.hrtime() + startTime: process.hrtime(), + type: 'HttpRequest', }; } } @@ -363,6 +365,54 @@ function storeHTTPOptions(options) { if (insecureHTTPParser !== undefined) validateBoolean(insecureHTTPParser, 'options.insecureHTTPParser'); this.insecureHTTPParser = insecureHTTPParser; + + if (options.noDelay === undefined) + options.noDelay = true; + + const requestTimeout = options.requestTimeout; + if (requestTimeout !== undefined) { + validateInteger(requestTimeout, 'requestTimeout', 0); + this.requestTimeout = requestTimeout; + } else { + this.requestTimeout = 300_000; // 5 minutes + } + + const headersTimeout = options.headersTimeout; + if (headersTimeout !== undefined) { + validateInteger(headersTimeout, 'headersTimeout', 0); + this.headersTimeout = headersTimeout; + } else { + this.headersTimeout = 60_000; // 60 seconds + } + + if (this.requestTimeout > 0 && this.headersTimeout > 0 && this.headersTimeout >= this.requestTimeout) { + throw new codes.ERR_OUT_OF_RANGE('headersTimeout', '>= requestTimeout', headersTimeout); + } + + const keepAliveTimeout = options.keepAliveTimeout; + if (keepAliveTimeout !== undefined) { + validateInteger(keepAliveTimeout, 'keepAliveTimeout', 0); + this.keepAliveTimeout = keepAliveTimeout; + } else { + this.keepAliveTimeout = 5_000; // 5 seconds; + } + + const connectionsCheckingInterval = options.connectionsCheckingInterval; + if (connectionsCheckingInterval !== undefined) { + validateInteger(connectionsCheckingInterval, 'connectionsCheckingInterval', 0); + this.connectionsCheckingInterval = connectionsCheckingInterval; + } else { + this.connectionsCheckingInterval = 30_000; // 30 seconds + } +} + +function setupConnectionsTracking(server) { + // Start connection handling + server[kConnections] = new ConnectionsList(); + if (server.headersTimeout > 0 || server.requestTimeout > 0) { + server[kConnectionsCheckingInterval] = + setInterval(checkConnections.bind(server), server.connectionsCheckingInterval).unref(); + } } function Server(options, requestListener) { @@ -396,15 +446,17 @@ function Server(options, requestListener) { this.on('connection', connectionListener); this.timeout = 0; - this.keepAliveTimeout = 5000; this.maxHeadersCount = null; this.maxRequestsPerSocket = 0; - this.headersTimeout = 60 * 1000; // 60 seconds - this.requestTimeout = 0; + setupConnectionsTracking(this); } ObjectSetPrototypeOf(Server.prototype, net.Server.prototype); ObjectSetPrototypeOf(Server, net.Server); +Server.prototype.close = function() { + clearInterval(this[kConnectionsCheckingInterval]); + ReflectApply(net.Server.prototype.close, this, arguments); +}; Server.prototype.setTimeout = function setTimeout(msecs, callback) { this.timeout = msecs; @@ -436,6 +488,18 @@ Server.prototype[EE.captureRejectionSymbol] = function(err, event, ...args) { } }; +function checkConnections() { + const expired = this[kConnections].expired(this.headersTimeout, this.requestTimeout); + + for (let i = 0; i < expired.length; i++) { + const socket = expired[i].socket; + + if (socket) { + onRequestTimeout(socket); + } + } +} + function connectionListener(socket) { defaultTriggerAsyncIdScope( getOrSetAsyncId(socket), connectionListenerInternal, this, socket @@ -469,7 +533,7 @@ function connectionListenerInternal(server, socket) { new HTTPServerAsyncResource('HTTPINCOMINGMESSAGE', socket), server.maxHeaderSize || 0, lenient ? kLenientAll : kLenientNone, - server.headersTimeout || 0, + server[kConnections], ); parser.socket = socket; socket.parser = parser; @@ -535,17 +599,6 @@ function connectionListenerInternal(server, socket) { onParserTimeout.bind(undefined, server, socket); - // When receiving new requests on the same socket (pipelining or keep alive) - // make sure the requestTimeout is active. - parser[kOnMessageBegin] = - setRequestTimeout.bind(undefined, - server, socket); - - // This protects from DOS attack where an attacker establish the connection - // without sending any data on applications where server.timeout is left to - // the default value of zero. - setRequestTimeout(server, socket); - socket._paused = false; } @@ -628,7 +681,6 @@ function socketOnData(server, socket, parser, state, d) { } function onRequestTimeout(socket) { - socket[kRequestTimeout] = undefined; // socketOnError has additional logic and will call socket.destroy(err). socketOnError.call(socket, new ERR_HTTP_REQUEST_TIMEOUT()); } @@ -723,9 +775,6 @@ function onParserExecuteCommon(server, socket, parser, state, ret, d) { socket.readableFlowing = null; - // Clear the requestTimeout after upgrading the connection. - clearRequestTimeout(req); - server.emit(eventName, req, socket, bodyHead); } else { // Got CONNECT method, but have no handler. @@ -734,11 +783,6 @@ function onParserExecuteCommon(server, socket, parser, state, ret, d) { } else if (parser.incoming && parser.incoming.method === 'PRI') { debug('SERVER got PRI request'); socket.destroy(); - } else { - // When receiving new requests on the same socket (pipelining or keep alive) - // make sure the requestTimeout is active. - parser[kOnMessageBegin] = - setRequestTimeout.bind(undefined, server, socket); } if (socket._paused && socket.parser) { @@ -761,32 +805,6 @@ function clearIncoming(req) { } } -function setRequestTimeout(server, socket) { - // Set the request timeout handler. - if ( - !socket[kRequestTimeout] && - server.requestTimeout && server.requestTimeout > 0 - ) { - debug('requestTimeout timer set'); - socket[kRequestTimeout] = - setTimeout(onRequestTimeout, server.requestTimeout, socket).unref(); - } -} - -function clearRequestTimeout(req) { - if (!req) { - req = this; - } - - if (!req[kRequestTimeout]) { - return; - } - - debug('requestTimeout timer cleared'); - clearTimeout(req[kRequestTimeout]); - req[kRequestTimeout] = undefined; -} - function resOnFinish(req, res, socket, state, server) { if (onResponseFinishChannel.hasSubscribers) { onResponseFinishChannel.publish({ @@ -810,14 +828,6 @@ function resOnFinish(req, res, socket, state, server) { if (!req._consuming && !req._readableState.resumeScheduled) req._dump(); - // Make sure the requestTimeout is cleared before finishing. - // This might occur if the application has sent a response - // without consuming the request body, which would have already - // cleared the timer. - // clearRequestTimeout can be executed even if the timer is not active, - // so this is safe. - clearRequestTimeout(req); - res.detachSocket(socket); clearIncoming(req); process.nextTick(emitCloseNT, res); @@ -951,7 +961,6 @@ function parserOnIncoming(server, socket, state, req, keepAlive) { } if (!handled) { - req.on('end', clearRequestTimeout); server.emit('request', req, res); } @@ -1023,6 +1032,7 @@ module.exports = { STATUS_CODES, Server, ServerResponse, + setupConnectionsTracking, storeHTTPOptions, _connectionListener: connectionListener, kServerResponse diff --git a/lib/assert.js b/lib/assert.js index 9a684cbe5fd6bf..2c7cf369a87af2 100644 --- a/lib/assert.js +++ b/lib/assert.js @@ -213,7 +213,7 @@ function getCode(fd, line, column) { let lines = 0; // Prevent blocking the event loop by limiting the maximum amount of // data that may be read. - let maxReads = 32; // bytesPerRead * maxReads = 512 kb + let maxReads = 32; // bytesPerRead * maxReads = 512 KiB const bytesPerRead = 16384; // Use a single buffer up front that is reused until the call site is found. let buffer = Buffer.allocUnsafe(bytesPerRead); @@ -965,10 +965,10 @@ assert.ifError = function ifError(err) { // This will remove any duplicated frames from the error frames taken // from within `ifError` and add the original error frames to the newly // created ones. - const origStackStart = origStack.indexOf('\n at'); + const origStackStart = StringPrototypeIndexOf(origStack, '\n at'); if (origStackStart !== -1) { const originalFrames = StringPrototypeSplit( - origStack.slice(origStackStart + 1), + StringPrototypeSlice(origStack, origStackStart + 1), '\n' ); // Filter all frames existing in err.stack. diff --git a/lib/async_hooks.js b/lib/async_hooks.js index af5a37b749d94b..6cc2052474d9c2 100644 --- a/lib/async_hooks.js +++ b/lib/async_hooks.js @@ -5,6 +5,7 @@ const { ArrayPrototypeIndexOf, ArrayPrototypePush, ArrayPrototypeSplice, + ArrayPrototypeUnshift, FunctionPrototypeBind, NumberIsSafeInteger, ObjectDefineProperties, @@ -223,15 +224,19 @@ class AsyncResource { return this[trigger_async_id_symbol]; } - bind(fn, thisArg = this) { + bind(fn, thisArg) { validateFunction(fn, 'fn'); - const ret = - FunctionPrototypeBind( - this.runInAsyncScope, - this, - fn, - thisArg); - ObjectDefineProperties(ret, { + let bound; + if (thisArg === undefined) { + const resource = this; + bound = function(...args) { + ArrayPrototypeUnshift(args, fn, this); + return ReflectApply(resource.runInAsyncScope, resource, args); + }; + } else { + bound = FunctionPrototypeBind(this.runInAsyncScope, this, fn, thisArg); + } + ObjectDefineProperties(bound, { 'length': { configurable: true, enumerable: false, @@ -245,7 +250,7 @@ class AsyncResource { writable: true, } }); - return ret; + return bound; } static bind(fn, type, thisArg) { diff --git a/lib/buffer.js b/lib/buffer.js index 57d6cddbaa2e6b..bdf01156824180 100644 --- a/lib/buffer.js +++ b/lib/buffer.js @@ -23,8 +23,10 @@ const { Array, + ArrayFrom, ArrayIsArray, ArrayPrototypeForEach, + ArrayPrototypeIndexOf, MathFloor, MathMin, MathTrunc, @@ -738,17 +740,16 @@ function byteLength(string, encoding) { } const len = string.length; - const mustMatch = (arguments.length > 2 && arguments[2] === true); - if (!mustMatch && len === 0) + if (len === 0) return 0; - if (!encoding) - return (mustMatch ? -1 : byteLengthUtf8(string)); - - const ops = getEncodingOps(encoding); - if (ops === undefined) - return (mustMatch ? -1 : byteLengthUtf8(string)); - return ops.byteLength(string); + if (encoding) { + const ops = getEncodingOps(encoding); + if (ops) { + return ops.byteLength(string); + } + } + return byteLengthUtf8(string); } Buffer.byteLength = byteLength; @@ -1231,8 +1232,25 @@ function btoa(input) { return buf.toString('base64'); } -const kBase64Digits = - 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; +// Refs: https://infra.spec.whatwg.org/#forgiving-base64-decode +const kForgivingBase64AllowedChars = [ + // ASCII whitespace + // Refs: https://infra.spec.whatwg.org/#ascii-whitespace + 0x09, 0x0A, 0x0C, 0x0D, 0x20, + + // Uppercase letters + ...ArrayFrom({ length: 26 }, (_, i) => StringPrototypeCharCodeAt('A') + i), + + // Lowercase letters + ...ArrayFrom({ length: 26 }, (_, i) => StringPrototypeCharCodeAt('a') + i), + + // Decimal digits + ...ArrayFrom({ length: 10 }, (_, i) => StringPrototypeCharCodeAt('0') + i), + + 0x2B, // + + 0x2F, // / + 0x3D, // = +]; function atob(input) { // The implementation here has not been performance optimized in any way and @@ -1241,11 +1259,31 @@ function atob(input) { if (arguments.length === 0) { throw new ERR_MISSING_ARGS('input'); } + input = `${input}`; + let nonAsciiWhitespaceCharCount = 0; + for (let n = 0; n < input.length; n++) { - if (!kBase64Digits.includes(input[n])) + const index = ArrayPrototypeIndexOf( + kForgivingBase64AllowedChars, + StringPrototypeCharCodeAt(input, n)); + + if (index > 4) { + // The first 5 elements of `kForgivingBase64AllowedChars` are + // ASCII whitespace char codes. + nonAsciiWhitespaceCharCount++; + } else if (index === -1) { throw lazyDOMException('Invalid character', 'InvalidCharacterError'); + } + } + + // See #3 - https://infra.spec.whatwg.org/#forgiving-base64 + if (nonAsciiWhitespaceCharCount % 4 === 1) { + throw lazyDOMException( + 'The string to be decoded is not correctly encoded.', + 'InvalidCharacterError'); } + return Buffer.from(input, 'base64').toString('latin1'); } diff --git a/lib/child_process.js b/lib/child_process.js index 29fece552567a9..4b5da300b6295d 100644 --- a/lib/child_process.js +++ b/lib/child_process.js @@ -98,7 +98,7 @@ const MAX_BUFFER = 1024 * 1024; * @param {{ * cwd?: string; * detached?: boolean; - * env?: Object; + * env?: Record; * execPath?: string; * execArgv?: string[]; * gid?: number; @@ -199,7 +199,7 @@ function normalizeExecArgs(command, options, callback) { * @param {string} command * @param {{ * cmd?: string; - * env?: Object; + * env?: Record; * encoding?: string; * shell?: string; * signal?: AbortSignal; @@ -253,7 +253,7 @@ ObjectDefineProperty(exec, promisify.custom, { * @param {string[]} [args] * @param {{ * cwd?: string; - * env?: Object; + * env?: Record; * encoding?: string; * timeout?: number; * maxBuffer?: number; @@ -662,7 +662,7 @@ function abortChildProcess(child, killSignal) { * @param {string[]} [args] * @param {{ * cwd?: string; - * env?: Object; + * env?: Record; * argv0?: string; * stdio?: Array | string; * detached?: boolean; @@ -735,7 +735,7 @@ function spawn(file, args, options) { * input?: string | Buffer | TypedArray | DataView; * argv0?: string; * stdio?: string | Array; - * env?: Object; + * env?: Record; * uid?: number; * gid?: number; * timeout?: number; @@ -827,7 +827,7 @@ function checkExecSyncError(ret, args, cmd) { * cwd?: string; * input?: string | Buffer | TypedArray | DataView; * stdio?: string | Array; - * env?: Object; + * env?: Record; * uid?: number; * gid?: number; * timeout?: number; @@ -864,7 +864,7 @@ function execFileSync(command, args, options) { * cwd?: string; * input?: string | Buffer | TypedArray | DataView; * stdio?: string | Array; - * env?: Object; + * env?: Record; * shell?: string; * uid?: number; * gid?: number; diff --git a/lib/crypto.js b/lib/crypto.js index a839f5fe27b029..77de7cda9d08a2 100644 --- a/lib/crypto.js +++ b/lib/crypto.js @@ -40,8 +40,6 @@ const { } = require('internal/errors').codes; const constants = internalBinding('constants').crypto; const { getOptionValue } = require('internal/options'); -const pendingDeprecation = getOptionValue('--pending-deprecation'); -const fipsForced = getOptionValue('--force-fips'); const { getFipsCrypto, setFipsCrypto, @@ -221,8 +219,8 @@ module.exports = { sign: signOneShot, setEngine, timingSafeEqual, - getFips: fipsForced ? getFipsForced : getFipsCrypto, - setFips: fipsForced ? setFipsForced : setFipsCrypto, + getFips, + setFips, verify: verifyOneShot, // Classes @@ -243,13 +241,17 @@ module.exports = { secureHeapUsed, }; -function setFipsForced(val) { - if (val) return; - throw new ERR_CRYPTO_FIPS_FORCED(); +function getFips() { + return getOptionValue('--force-fips') ? 1 : getFipsCrypto(); } -function getFipsForced() { - return 1; +function setFips(val) { + if (getOptionValue('--force-fips')) { + if (val) return; + throw new ERR_CRYPTO_FIPS_FORCED(); + } else { + setFipsCrypto(val); + } } function getRandomValues(array) { @@ -257,9 +259,69 @@ function getRandomValues(array) { } ObjectDefineProperty(constants, 'defaultCipherList', { - value: getOptionValue('--tls-cipher-list') + get() { + const value = getOptionValue('--tls-cipher-list'); + ObjectDefineProperty(this, 'defaultCipherList', { + writable: true, + configurable: true, + enumerable: true, + value + }); + return value; + }, + set(val) { + ObjectDefineProperty(this, 'defaultCipherList', { + writable: true, + configurable: true, + enumerable: true, + value: val + }); + }, + configurable: true, + enumerable: true, }); +function getRandomBytesAlias(key) { + return { + enumerable: false, + configurable: true, + get() { + let value; + if (getOptionValue('--pending-deprecation')) { + value = deprecate( + randomBytes, + `crypto.${key} is deprecated.`, + 'DEP0115'); + } else { + value = randomBytes; + } + ObjectDefineProperty( + this, + key, + { + enumerable: false, + configurable: true, + writable: true, + value: value + } + ); + return value; + }, + set(value) { + ObjectDefineProperty( + this, + key, + { + enumerable: true, + configurable: true, + writable: true, + value + } + ); + } + }; +} + ObjectDefineProperties(module.exports, { createCipher: { enumerable: false, @@ -273,8 +335,8 @@ ObjectDefineProperties(module.exports, { }, // crypto.fips is deprecated. DEP0093. Use crypto.getFips()/crypto.setFips() fips: { - get: fipsForced ? getFipsForced : getFipsCrypto, - set: fipsForced ? setFipsForced : setFipsCrypto + get: getFips, + set: setFips, }, DEFAULT_ENCODING: { enumerable: false, @@ -313,29 +375,7 @@ ObjectDefineProperties(module.exports, { // Aliases for randomBytes are deprecated. // The ecosystem needs those to exist for backwards compatibility. - prng: { - enumerable: false, - configurable: true, - writable: true, - value: pendingDeprecation ? - deprecate(randomBytes, 'crypto.prng is deprecated.', 'DEP0115') : - randomBytes - }, - pseudoRandomBytes: { - enumerable: false, - configurable: true, - writable: true, - value: pendingDeprecation ? - deprecate(randomBytes, - 'crypto.pseudoRandomBytes is deprecated.', 'DEP0115') : - randomBytes - }, - rng: { - enumerable: false, - configurable: true, - writable: true, - value: pendingDeprecation ? - deprecate(randomBytes, 'crypto.rng is deprecated.', 'DEP0115') : - randomBytes - } + prng: getRandomBytesAlias('prng'), + pseudoRandomBytes: getRandomBytesAlias('pseudoRandomBytes'), + rng: getRandomBytesAlias('rng') }); diff --git a/lib/dgram.js b/lib/dgram.js index 221afcf5bb0c84..5dbc2f22dab658 100644 --- a/lib/dgram.js +++ b/lib/dgram.js @@ -159,7 +159,7 @@ function startListening(socket) { const state = socket[kStateSymbol]; state.handle.onmessage = onMessage; - // Todo: handle errors + state.handle.onerror = onError; state.handle.recvStart(); state.receiving = true; state.bindState = BIND_STATE_BOUND; @@ -923,6 +923,12 @@ function onMessage(nread, handle, buf, rinfo) { } +function onError(nread, handle, error) { + const self = handle[owner_symbol]; + return self.emit('error', error); +} + + Socket.prototype.ref = function() { const handle = this[kStateSymbol].handle; diff --git a/lib/dns.js b/lib/dns.js index d795e8e09045c4..af5416c62478cc 100644 --- a/lib/dns.js +++ b/lib/dns.js @@ -27,6 +27,7 @@ const { ObjectDefineProperties, ObjectDefineProperty, ReflectApply, + Symbol, } = primordials; const cares = internalBinding('cares_wrap'); @@ -41,7 +42,6 @@ const { Resolver, validateHints, emitInvalidHostnameWarning, - emitTypeCoercionDeprecationWarning, getDefaultVerbatim, setDefaultResultOrder, } = require('internal/dns/utils'); @@ -51,10 +51,12 @@ const { ERR_MISSING_ARGS, } = errors.codes; const { + validateBoolean, validateFunction, + validateNumber, + validateOneOf, validatePort, validateString, - validateOneOf, } = require('internal/validators'); const { @@ -63,6 +65,15 @@ const { QueryReqWrap, } = cares; +const kPerfHooksDnsLookupContext = Symbol('kPerfHooksDnsLookupContext'); +const kPerfHooksDnsLookupServiceContext = Symbol('kPerfHooksDnsLookupServiceContext'); +const kPerfHooksDnsLookupResolveContext = Symbol('kPerfHooksDnsLookupResolveContext'); + +const { + startPerf, + stopPerf, +} = require('internal/perf/observe'); + const dnsException = errors.dnsException; let promises = null; // Lazy loaded @@ -72,6 +83,7 @@ function onlookup(err, addresses) { return this.callback(dnsException(err, 'getaddrinfo', this.hostname)); } this.callback(null, addresses[0], this.family || isIP(addresses[0])); + stopPerf(this, kPerfHooksDnsLookupContext); } @@ -90,14 +102,16 @@ function onlookupall(err, addresses) { } this.callback(null, addresses); + stopPerf(this, kPerfHooksDnsLookupContext); } // Easy DNS A/AAAA look up // lookup(hostname, [options,] callback) +const validFamilies = [0, 4, 6]; function lookup(hostname, options, callback) { let hints = 0; - let family = -1; + let family = 0; let all = false; let verbatim = getDefaultVerbatim(); @@ -109,39 +123,36 @@ function lookup(hostname, options, callback) { if (typeof options === 'function') { callback = options; family = 0; + } else if (typeof options === 'number') { + validateFunction(callback, 'callback'); + + validateOneOf(options, 'family', validFamilies, true); + family = options; + } else if (options !== undefined && typeof options !== 'object') { + validateFunction(arguments.length === 2 ? options : callback, 'callback'); + throw new ERR_INVALID_ARG_TYPE('options', ['integer', 'object'], options); } else { validateFunction(callback, 'callback'); - if (options !== null && typeof options === 'object') { - if (options.hints != null && typeof options.hints !== 'number') { - emitTypeCoercionDeprecationWarning(); - } + if (options?.hints != null) { + validateNumber(options.hints, 'options.hints'); hints = options.hints >>> 0; - if (options.family != null && typeof options.family !== 'number') { - emitTypeCoercionDeprecationWarning(); - } - family = options.family >>> 0; - if (options.all != null && typeof options.all !== 'boolean') { - emitTypeCoercionDeprecationWarning(); - } - all = options.all === true; - if (typeof options.verbatim === 'boolean') { - verbatim = options.verbatim === true; - } else if (options.verbatim != null) { - emitTypeCoercionDeprecationWarning(); - } - validateHints(hints); - } else { - if (options != null && typeof options !== 'number') { - emitTypeCoercionDeprecationWarning(); - } - family = options >>> 0; + } + if (options?.family != null) { + validateOneOf(options.family, 'options.family', validFamilies, true); + family = options.family; + } + if (options?.all != null) { + validateBoolean(options.all, 'options.all'); + all = options.all; + } + if (options?.verbatim != null) { + validateBoolean(options.verbatim, 'options.verbatim'); + verbatim = options.verbatim; } } - validateOneOf(family, 'family', [0, 4, 6]); - if (!hostname) { emitInvalidHostnameWarning(hostname); if (all) { @@ -176,6 +187,13 @@ function lookup(hostname, options, callback) { process.nextTick(callback, dnsException(err, 'getaddrinfo', hostname)); return {}; } + const detail = { + hostname, + family, + hints, + verbatim, + }; + startPerf(req, kPerfHooksDnsLookupContext, { type: 'dns', name: 'lookup', detail }); return req; } @@ -188,6 +206,7 @@ function onlookupservice(err, hostname, service) { return this.callback(dnsException(err, 'getnameinfo', this.hostname)); this.callback(null, hostname, service); + stopPerf(this, kPerfHooksDnsLookupServiceContext); } @@ -212,6 +231,14 @@ function lookupService(address, port, callback) { const err = cares.getnameinfo(req, address, port); if (err) throw dnsException(err, 'getnameinfo', address); + startPerf(req, kPerfHooksDnsLookupServiceContext, { + type: 'dns', + name: 'lookupService', + detail: { + host: address, + port + } + }); return req; } @@ -226,8 +253,10 @@ function onresolve(err, result, ttls) { if (err) this.callback(dnsException(err, this.bindingName, this.hostname)); - else + else { this.callback(null, result); + stopPerf(this, kPerfHooksDnsLookupResolveContext); + } } function resolver(bindingName) { @@ -249,6 +278,14 @@ function resolver(bindingName) { req.ttl = !!(options && options.ttl); const err = this._handle[bindingName](req, toASCII(name)); if (err) throw dnsException(err, bindingName, name); + startPerf(req, kPerfHooksDnsLookupResolveContext, { + type: 'dns', + name: bindingName, + detail: { + host: name, + ttl: req.ttl + } + }); return req; } ObjectDefineProperty(query, 'name', { value: bindingName }); diff --git a/lib/fs.js b/lib/fs.js index b8a5cefa1b0aa8..7b10c3d9f43302 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -116,6 +116,7 @@ const { validateRmOptionsSync, validateRmdirOptions, validateStringAfterArrayBufferView, + validatePrimitiveStringAfterArrayBufferView, warnOnNonPortableTemplate } = require('internal/fs/utils'); const { @@ -162,6 +163,11 @@ const isWindows = process.platform === 'win32'; const isOSX = process.platform === 'darwin'; +const showStringCoercionDeprecation = internalUtil.deprecate( + () => {}, + 'Implicit coercion of objects with own toString property is deprecated.', + 'DEP0162' +); function showTruncateDeprecation() { if (truncateWarn) { process.emitWarning( @@ -604,7 +610,7 @@ function read(fd, buffer, offset, length, position, callback) { if (arguments.length <= 3) { // Assume fs.read(fd, options, callback) - let options = {}; + let options = ObjectCreate(null); if (arguments.length < 3) { // This is fs.read(fd, callback) // buffer will be the callback @@ -621,7 +627,7 @@ function read(fd, buffer, offset, length, position, callback) { buffer = Buffer.alloc(16384), offset = 0, length = buffer.byteLength - offset, - position + position = null } = options); } @@ -686,10 +692,14 @@ function readSync(fd, buffer, offset, length, position) { validateBuffer(buffer); if (arguments.length <= 3) { - // Assume fs.read(fd, buffer, options) - const options = offset || {}; + // Assume fs.readSync(fd, buffer, options) + const options = offset || ObjectCreate(null); - ({ offset = 0, length = buffer.byteLength, position } = options); + ({ + offset = 0, + length = buffer.byteLength - offset, + position = null + } = options); } if (offset == null) { @@ -821,6 +831,9 @@ function write(fd, buffer, offset, length, position, callback) { } validateStringAfterArrayBufferView(buffer, 'buffer'); + if (typeof buffer !== 'string') { + showStringCoercionDeprecation(); + } if (typeof position !== 'function') { if (typeof offset === 'function') { @@ -848,7 +861,7 @@ ObjectDefineProperty(write, internalUtil.customPromisifyArgs, * Synchronously writes `buffer` to the * specified `fd` (file descriptor). * @param {number} fd - * @param {Buffer | TypedArray | DataView | string | object} buffer + * @param {Buffer | TypedArray | DataView | string} buffer * @param {number} [offset] * @param {number} [length] * @param {number} [position] @@ -872,7 +885,7 @@ function writeSync(fd, buffer, offset, length, position) { result = binding.writeBuffer(fd, buffer, offset, length, position, undefined, ctx); } else { - validateStringAfterArrayBufferView(buffer, 'buffer'); + validatePrimitiveStringAfterArrayBufferView(buffer, 'buffer'); validateEncoding(buffer, length); if (offset === undefined) @@ -2116,6 +2129,9 @@ function writeFile(path, data, options, callback) { if (!isArrayBufferView(data)) { validateStringAfterArrayBufferView(data, 'data'); + if (typeof data !== 'string') { + showStringCoercionDeprecation(); + } data = Buffer.from(String(data), options.encoding || 'utf8'); } @@ -2156,6 +2172,9 @@ function writeFileSync(path, data, options) { if (!isArrayBufferView(data)) { validateStringAfterArrayBufferView(data, 'data'); + if (typeof data !== 'string') { + showStringCoercionDeprecation(); + } data = Buffer.from(String(data), options.encoding || 'utf8'); } @@ -2860,7 +2879,7 @@ function lazyLoadStreams() { /** * Creates a readable stream with a default `highWaterMark` - * of 64 kb. + * of 64 KiB. * @param {string | Buffer | URL} path * @param {string | { * flags?: string; @@ -2872,7 +2891,7 @@ function lazyLoadStreams() { * start: number; * end?: number; * highWaterMark?: number; - * fs?: Object | null; + * fs?: object | null; * }} [options] * @returns {ReadStream} */ @@ -2892,7 +2911,7 @@ function createReadStream(path, options) { * autoClose?: boolean; * emitClose?: boolean; * start: number; - * fs?: Object | null; + * fs?: object | null; * }} [options] * @returns {WriteStream} */ diff --git a/lib/https.js b/lib/https.js index 695a9020994852..74ce93baaafe35 100644 --- a/lib/https.js +++ b/lib/https.js @@ -40,6 +40,7 @@ const tls = require('tls'); const { Agent: HttpAgent } = require('_http_agent'); const { Server: HttpServer, + setupConnectionsTracking, storeHTTPOptions, _connectionListener, } = require('_http_server'); @@ -80,10 +81,8 @@ function Server(opts, requestListener) { }); this.timeout = 0; - this.keepAliveTimeout = 5000; this.maxHeadersCount = null; - this.headersTimeout = 60 * 1000; // 60 seconds - this.requestTimeout = 0; + setupConnectionsTracking(this); } ObjectSetPrototypeOf(Server.prototype, tls.Server.prototype); ObjectSetPrototypeOf(Server, tls.Server); @@ -203,7 +202,7 @@ Agent.prototype.createConnection = createConnection; * }} [options] * @returns {string} */ -Agent.prototype.getName = function getName(options) { +Agent.prototype.getName = function getName(options = {}) { let name = FunctionPrototypeCall(HttpAgent.prototype.getName, this, options); name += ':'; @@ -362,7 +361,7 @@ function request(...args) { * createConnection?: Function; * defaultPort?: number; * family?: number; - * headers?: Object; + * headers?: object; * hints?: number; * host?: string; * hostname?: string; diff --git a/lib/internal/abort_controller.js b/lib/internal/abort_controller.js index e0c0d63899972c..7e89e43cc7cead 100644 --- a/lib/internal/abort_controller.js +++ b/lib/internal/abort_controller.js @@ -291,9 +291,8 @@ function abortSignal(signal, reason) { signal.dispatchEvent(event); } -// TODO(joyeecheung): V8 snapshot does not support instance member -// initializers for now: -// https://bugs.chromium.org/p/v8/issues/detail?id=10704 +// TODO(joyeecheung): use private fields and we'll get invalid access +// validation from V8 instead of throwing ERR_INVALID_THIS ourselves. const kSignal = Symbol('signal'); function validateAbortController(obj) { diff --git a/lib/internal/async_hooks.js b/lib/internal/async_hooks.js index f15fe5cc99b5c4..107aab227e4637 100644 --- a/lib/internal/async_hooks.js +++ b/lib/internal/async_hooks.js @@ -196,6 +196,7 @@ function emitInitNative(asyncId, type, triggerAsyncId, resource) { try { // Using var here instead of let because "for (var ...)" is faster than let. // Refs: https://github.com/nodejs/node/pull/30380#issuecomment-552948364 + // eslint-disable-next-line no-var for (var i = 0; i < active_hooks.array.length; i++) { if (typeof active_hooks.array[i][init_symbol] === 'function') { active_hooks.array[i][init_symbol]( @@ -228,6 +229,7 @@ function emitHook(symbol, asyncId) { try { // Using var here instead of let because "for (var ...)" is faster than let. // Refs: https://github.com/nodejs/node/pull/30380#issuecomment-552948364 + // eslint-disable-next-line no-var for (var i = 0; i < active_hooks.array.length; i++) { if (typeof active_hooks.array[i][symbol] === 'function') { active_hooks.array[i][symbol](asyncId); @@ -326,14 +328,10 @@ function promiseInitHookWithDestroyTracking(promise, parent) { destroyTracking(promise, parent); } -const destroyedSymbol = Symbol('destroyed'); - function destroyTracking(promise, parent) { trackPromise(promise, parent); const asyncId = promise[async_id_symbol]; - const destroyed = { destroyed: false }; - promise[destroyedSymbol] = destroyed; - registerDestroyHook(promise, asyncId, destroyed); + registerDestroyHook(promise, asyncId); } function promiseBeforeHook(promise) { diff --git a/lib/internal/blob.js b/lib/internal/blob.js index 3a45d58d7c179e..8fee1d91f79448 100644 --- a/lib/internal/blob.js +++ b/lib/internal/blob.js @@ -64,6 +64,7 @@ const { const kHandle = Symbol('kHandle'); const kState = Symbol('kState'); +const kIndex = Symbol('kIndex'); const kType = Symbol('kType'); const kLength = Symbol('kLength'); const kArrayBufferPromise = Symbol('kArrayBufferPromise'); @@ -87,6 +88,7 @@ function lazyURL(id) { } function lazyReadableStream(options) { + // eslint-disable-next-line no-global-assign ReadableStream ??= require('internal/webstreams/readablestream').ReadableStream; return new ReadableStream(options); @@ -136,7 +138,7 @@ class Blob { if (sources === null || typeof sources[SymbolIterator] !== 'function' || typeof sources === 'string') { - throw new ERR_INVALID_ARG_TYPE('sources', 'Iterable', sources); + throw new ERR_INVALID_ARG_TYPE('sources', 'a sequence', sources); } validateObject(options, 'options'); let { @@ -323,17 +325,17 @@ class Blob { return new lazyReadableStream({ async start() { this[kState] = await self.arrayBuffer(); + this[kIndex] = 0; }, pull(controller) { - if (this[kState].byteLength <= kMaxChunkSize) { - controller.enqueue(new Uint8Array(this[kState])); + if (this[kState].byteLength - this[kIndex] <= kMaxChunkSize) { + controller.enqueue(new Uint8Array(this[kState], this[kIndex])); controller.close(); this[kState] = undefined; } else { - const slice = this[kState].slice(0, kMaxChunkSize); - this[kState] = this[kState].slice(kMaxChunkSize); - controller.enqueue(new Uint8Array(slice)); + controller.enqueue(new Uint8Array(this[kState], this[kIndex], kMaxChunkSize)); + this[kIndex] += kMaxChunkSize; } } }); diff --git a/lib/internal/bootstrap/browser.js b/lib/internal/bootstrap/browser.js index 68dac3b1ca4b8c..49d69b939687ca 100644 --- a/lib/internal/bootstrap/browser.js +++ b/lib/internal/bootstrap/browser.js @@ -116,3 +116,57 @@ function defineReplacableAttribute(target, name, value) { value, }); } + +// Web Streams API +const { + TransformStream, + TransformStreamDefaultController, +} = require('internal/webstreams/transformstream'); + +const { + WritableStream, + WritableStreamDefaultController, + WritableStreamDefaultWriter, +} = require('internal/webstreams/writablestream'); + +const { + ReadableStream, + ReadableStreamDefaultReader, + ReadableStreamBYOBReader, + ReadableStreamBYOBRequest, + ReadableByteStreamController, + ReadableStreamDefaultController, +} = require('internal/webstreams/readablestream'); + +const { + ByteLengthQueuingStrategy, + CountQueuingStrategy, +} = require('internal/webstreams/queuingstrategies'); + +const { + TextEncoderStream, + TextDecoderStream, +} = require('internal/webstreams/encoding'); + +const { + CompressionStream, + DecompressionStream, +} = require('internal/webstreams/compression'); + +exposeInterface(globalThis, 'ReadableStream', ReadableStream); +exposeInterface(globalThis, 'ReadableStreamDefaultReader', ReadableStreamDefaultReader); +exposeInterface(globalThis, 'ReadableStreamBYOBReader', ReadableStreamBYOBReader); +exposeInterface(globalThis, 'ReadableStreamBYOBRequest', ReadableStreamBYOBRequest); +exposeInterface(globalThis, 'ReadableByteStreamController', ReadableByteStreamController); +exposeInterface(globalThis, 'ReadableStreamDefaultController', ReadableStreamDefaultController); +exposeInterface(globalThis, 'TransformStream', TransformStream); +exposeInterface(globalThis, 'TransformStreamDefaultController', TransformStreamDefaultController); +exposeInterface(globalThis, 'WritableStream', WritableStream); +exposeInterface(globalThis, 'WritableStreamDefaultWriter', WritableStreamDefaultWriter); +exposeInterface(globalThis, 'WritableStreamDefaultController', WritableStreamDefaultController); +exposeInterface(globalThis, 'ByteLengthQueuingStrategy', ByteLengthQueuingStrategy); +exposeInterface(globalThis, 'CountQueuingStrategy', CountQueuingStrategy); +exposeInterface(globalThis, 'TextEncoderStream', TextEncoderStream); +exposeInterface(globalThis, 'TextDecoderStream', TextDecoderStream); +exposeInterface(globalThis, 'CompressionStream', CompressionStream); +exposeInterface(globalThis, 'DecompressionStream', DecompressionStream); diff --git a/lib/internal/bootstrap/loaders.js b/lib/internal/bootstrap/loaders.js index d6869ef6334496..bc2a83034256d6 100644 --- a/lib/internal/bootstrap/loaders.js +++ b/lib/internal/bootstrap/loaders.js @@ -44,6 +44,7 @@ /* global process, getLinkedBinding, getInternalBinding, primordials */ const { + ArrayFrom, ArrayPrototypeMap, ArrayPrototypePush, ArrayPrototypeSlice, @@ -119,6 +120,11 @@ const legacyWrapperList = new SafeSet([ 'util', ]); +// Modules that can only be imported via the node: scheme. +const schemelessBlockList = new SafeSet([ + 'test', +]); + // Set up process.binding() and process._linkedBinding(). { const bindingObj = ObjectCreate(null); @@ -242,6 +248,16 @@ class NativeModule { return mod && mod.canBeRequiredByUsers; } + // Determine if a core module can be loaded without the node: prefix. This + // function does not validate if the module actually exists. + static canBeRequiredWithoutScheme(id) { + return !schemelessBlockList.has(id); + } + + static getSchemeOnlyModuleNames() { + return ArrayFrom(schemelessBlockList); + } + // Used by user-land module loaders to compile and load builtins. compileForPublicLoader() { if (!this.canBeRequiredByUsers) { diff --git a/lib/internal/bootstrap/node.js b/lib/internal/bootstrap/node.js index 239f5258620c04..1fc59fdeffd510 100644 --- a/lib/internal/bootstrap/node.js +++ b/lib/internal/bootstrap/node.js @@ -338,6 +338,10 @@ require('fs'); require('v8'); require('vm'); require('url'); +require('internal/options'); +if (config.hasOpenSSL) { + require('crypto'); +} function setupPrepareStackTrace() { const { diff --git a/lib/internal/bootstrap/pre_execution.js b/lib/internal/bootstrap/pre_execution.js index 33a594d35fcbb0..0b58803e63d243 100644 --- a/lib/internal/bootstrap/pre_execution.js +++ b/lib/internal/bootstrap/pre_execution.js @@ -5,6 +5,7 @@ const { ObjectDefineProperties, ObjectDefineProperty, ObjectGetOwnPropertyDescriptor, + PromiseResolve, SafeMap, SafeWeakMap, StringPrototypeStartsWith, @@ -14,6 +15,7 @@ const { const { getOptionValue, getEmbedderOptions, + refreshOptions, } = require('internal/options'); const { reconnectZeroFillToggle } = require('internal/buffer'); const { @@ -23,10 +25,17 @@ const { } = require('internal/util'); const { Buffer } = require('buffer'); -const { ERR_MANIFEST_ASSERT_INTEGRITY } = require('internal/errors').codes; +const { + ERR_INVALID_ARG_TYPE, + ERR_MANIFEST_ASSERT_INTEGRITY, + ERR_WEBASSEMBLY_RESPONSE, +} = require('internal/errors').codes; const assert = require('internal/assert'); -function prepareMainThreadExecution(expandArgv1 = false) { +function prepareMainThreadExecution(expandArgv1 = false, + initialzeModules = true) { + refreshRuntimeOptions(); + // TODO(joyeecheung): this is also necessary for workers when they deserialize // this toggle from the snapshot. reconnectZeroFillToggle(); @@ -78,15 +87,23 @@ function prepareMainThreadExecution(expandArgv1 = false) { initializeSourceMapsHandlers(); initializeDeprecations(); initializeWASI(); + + if (!initialzeModules) { + return; + } + initializeCJSLoader(); initializeESMLoader(); - const CJSLoader = require('internal/modules/cjs/loader'); assert(!CJSLoader.hasLoadedAnyUserCJSModule); loadPreloadModules(); initializeFrozenIntrinsics(); } +function refreshRuntimeOptions() { + refreshOptions(); +} + function patchProcessObject(expandArgv1) { const binding = internalBinding('process_methods'); binding.patchProcessObject(process); @@ -95,9 +112,13 @@ function patchProcessObject(expandArgv1) { ObjectDefineProperty(process, 'argv0', { enumerable: true, - configurable: false, + // Only set it to true during snapshot building. + configurable: getOptionValue('--build-snapshot'), value: process.argv[0] }); + + process.exitCode = undefined; + process._exiting = false; process.argv[0] = process.execPath; if (expandArgv1 && process.argv[1] && @@ -111,6 +132,12 @@ function patchProcessObject(expandArgv1) { } } + // We need to initialize the global console here again with process.stdout + // and friends for snapshot deserialization. + const globalConsole = require('internal/console/global'); + const { initializeGlobalConsole } = require('internal/console/constructor'); + initializeGlobalConsole(globalConsole); + // TODO(joyeecheung): most of these should be deprecated and removed, // except some that we need to be able to mutate during run time. addReadOnlyProcessAlias('_eval', '--eval'); @@ -163,12 +190,12 @@ function setupFetch() { return undici; } - emitExperimentalWarning('The Fetch API'); undici = require('internal/deps/undici/undici'); return undici; } async function fetch(input, init = undefined) { + emitExperimentalWarning('The Fetch API'); return lazyUndici().fetch(input, init); } @@ -193,6 +220,44 @@ function setupFetch() { Request: lazyInterface('Request'), Response: lazyInterface('Response'), }); + + // The WebAssembly Web API: https://webassembly.github.io/spec/web-api + internalBinding('wasm_web_api').setImplementation((streamState, source) => { + (async () => { + const response = await PromiseResolve(source); + if (!(response instanceof lazyUndici().Response)) { + throw new ERR_INVALID_ARG_TYPE( + 'source', ['Response', 'Promise resolving to Response'], response); + } + + const contentType = response.headers.get('Content-Type'); + if (contentType !== 'application/wasm') { + throw new ERR_WEBASSEMBLY_RESPONSE( + `has unsupported MIME type '${contentType}'`); + } + + if (!response.ok) { + throw new ERR_WEBASSEMBLY_RESPONSE( + `has status code ${response.status}`); + } + + if (response.bodyUsed !== false) { + throw new ERR_WEBASSEMBLY_RESPONSE('body has already been used'); + } + + // Pass all data from the response body to the WebAssembly compiler. + for await (const chunk of response.body) { + streamState.push(chunk); + } + })().then(() => { + // No error occurred. Tell the implementation that the stream has ended. + streamState.finish(); + }, (err) => { + // An error occurred, either because the given object was not a valid + // and usable Response or because a network error occurred. + streamState.abort(err); + }); + }); } // TODO(aduh95): move this to internal/bootstrap/browser when the CLI flag is @@ -253,7 +318,7 @@ function setupStacktracePrinterOnSigint() { function initializeReport() { const { report } = require('internal/process/report'); ObjectDefineProperty(process, 'report', { - enumerable: false, + enumerable: true, configurable: true, get() { return report; @@ -556,6 +621,7 @@ function loadPreloadModules() { } module.exports = { + refreshRuntimeOptions, patchProcessObject, setupCoverageHooks, setupWarningHandler, diff --git a/lib/internal/bootstrap/switches/is_main_thread.js b/lib/internal/bootstrap/switches/is_main_thread.js index 606da95f21ca8f..b7bd79e09c4acf 100644 --- a/lib/internal/bootstrap/switches/is_main_thread.js +++ b/lib/internal/bootstrap/switches/is_main_thread.js @@ -122,15 +122,34 @@ let stdin; let stdout; let stderr; +let stdoutDestroy; +let stderrDestroy; + +function refreshStdoutOnSigWinch() { + stdout._refreshSize(); +} + +function refreshStderrOnSigWinch() { + stderr._refreshSize(); +} + function getStdout() { if (stdout) return stdout; stdout = createWritableStdioStream(1); stdout.destroySoon = stdout.destroy; // Override _destroy so that the fd is never actually closed. + stdoutDestroy = stdout._destroy; stdout._destroy = dummyDestroy; if (stdout.isTTY) { - process.on('SIGWINCH', () => stdout._refreshSize()); + process.on('SIGWINCH', refreshStdoutOnSigWinch); } + + internalBinding('mksnapshot').cleanups.push(function cleanupStdout() { + stdout._destroy = stdoutDestroy; + stdout.destroy(); + process.removeListener('SIGWINCH', refreshStdoutOnSigWinch); + stdout = undefined; + }); return stdout; } @@ -138,11 +157,18 @@ function getStderr() { if (stderr) return stderr; stderr = createWritableStdioStream(2); stderr.destroySoon = stderr.destroy; + stderrDestroy = stderr._destroy; // Override _destroy so that the fd is never actually closed. stderr._destroy = dummyDestroy; if (stderr.isTTY) { - process.on('SIGWINCH', () => stderr._refreshSize()); + process.on('SIGWINCH', refreshStderrOnSigWinch); } + internalBinding('mksnapshot').cleanups.push(function cleanupStderr() { + stderr._destroy = stderrDestroy; + stderr.destroy(); + process.removeListener('SIGWINCH', refreshStderrOnSigWinch); + stderr = undefined; + }); return stderr; } @@ -229,6 +255,10 @@ function getStdin() { } } + internalBinding('mksnapshot').cleanups.push(function cleanupStdin() { + stdin.destroy(); + stdin = undefined; + }); return stdin; } diff --git a/lib/internal/console/constructor.js b/lib/internal/console/constructor.js index 695a56164b7d84..18e66a7afee0ee 100644 --- a/lib/internal/console/constructor.js +++ b/lib/internal/console/constructor.js @@ -100,7 +100,7 @@ function Console(options /* or: stdout, stderr, ignoreErrors = true */) { // We have to test new.target here to see if this function is called // with new, because we need to define a custom instanceof to accommodate // the global console. - if (!new.target) { + if (new.target === undefined) { return ReflectConstruct(Console, arguments); } @@ -486,7 +486,7 @@ const consoleMethods = { if (tabularData === null || typeof tabularData !== 'object') return this.log(tabularData); - if (cliTable === undefined) cliTable = require('internal/cli_table'); + cliTable ??= require('internal/cli_table'); const final = (k, v) => this.log(cliTable(k, v)); const _inspect = (v) => { @@ -570,8 +570,7 @@ const consoleMethods = { } else { const keys = properties || ObjectKeys(item); for (const key of keys) { - if (map[key] === undefined) - map[key] = []; + map[key] ??= []; if ((primitive && properties) || !ObjectPrototypeHasOwnProperty(item, key)) map[key][i] = ''; @@ -669,9 +668,15 @@ Console.prototype.dirxml = Console.prototype.log; Console.prototype.error = Console.prototype.warn; Console.prototype.groupCollapsed = Console.prototype.group; +function initializeGlobalConsole(globalConsole) { + globalConsole[kBindStreamsLazy](process); + globalConsole[kBindProperties](true, 'auto'); +} + module.exports = { Console, kBindStreamsLazy, kBindProperties, + initializeGlobalConsole, formatTime // exported for tests }; diff --git a/lib/internal/console/global.js b/lib/internal/console/global.js index d6c0c24d529dcc..782a585957f746 100644 --- a/lib/internal/console/global.js +++ b/lib/internal/console/global.js @@ -21,9 +21,7 @@ const { } = primordials; const { - Console, - kBindStreamsLazy, - kBindProperties + Console } = require('internal/console/constructor'); const globalConsole = ObjectCreate({}); @@ -44,9 +42,6 @@ for (const prop of ReflectOwnKeys(Console.prototype)) { ReflectDefineProperty(globalConsole, prop, desc); } -globalConsole[kBindStreamsLazy](process); -globalConsole[kBindProperties](true, 'auto'); - // This is a legacy feature - the Console constructor is exposed on // the global console instance. globalConsole.Console = Console; diff --git a/lib/internal/crypto/dsa.js b/lib/internal/crypto/dsa.js index 12e327c6ed1765..faaf7d6b889c07 100644 --- a/lib/internal/crypto/dsa.js +++ b/lib/internal/crypto/dsa.js @@ -7,11 +7,9 @@ const { const { DSAKeyExportJob, - KeyObjectHandle, SignJob, kCryptoJobAsync, kSigEncDER, - kKeyTypePrivate, kSignJobModeSign, kSignJobModeVerify, } = internalBinding('crypto'); @@ -29,8 +27,6 @@ const { const { InternalCryptoKey, - PrivateKeyObject, - PublicKeyObject, createPrivateKey, createPublicKey, isKeyObject, @@ -45,7 +41,6 @@ const { hasAnyNotIn, jobPromise, normalizeHashName, - validateKeyOps, kKeyObject, kHandle, } = require('internal/crypto/util'); @@ -178,52 +173,6 @@ async function dsaImportKey( }); break; } - case 'jwk': { - if (keyData == null || typeof keyData !== 'object') - throw lazyDOMException('Invalid JWK keyData', 'DataError'); - - verifyAcceptableDsaKeyUse( - algorithm.name, - keyData.x !== undefined ? 'private' : 'public', - usagesSet); - - if (keyData.kty !== 'DSA') - throw lazyDOMException('Invalid key type', 'DataError'); - - if (usagesSet.size > 0 && - keyData.use !== undefined && - keyData.use !== 'sig') { - throw lazyDOMException('Invalid use type', 'DataError'); - } - - validateKeyOps(keyData.key_ops, usagesSet); - - if (keyData.ext !== undefined && - keyData.ext === false && - extractable === true) { - throw lazyDOMException('JWK is not extractable', 'DataError'); - } - - if (keyData.alg !== undefined) { - if (typeof keyData.alg !== 'string') - throw lazyDOMException('Invalid alg', 'DataError'); - const hash = - normalizeHashName(keyData.alg, normalizeHashName.kContextWebCrypto); - if (hash !== algorithm.hash.name) - throw lazyDOMException('Hash mismatch', 'DataError'); - } - - const handle = new KeyObjectHandle(); - const type = handle.initJwk(keyData); - if (type === undefined) - throw lazyDOMException('Invalid JWK keyData', 'DataError'); - - keyObject = type === kKeyTypePrivate ? - new PrivateKeyObject(handle) : - new PublicKeyObject(handle); - - break; - } default: throw lazyDOMException( `Unable to import DSA key with format ${format}`, diff --git a/lib/internal/crypto/keygen.js b/lib/internal/crypto/keygen.js index bbbe72475bd870..c6909b227d0c15 100644 --- a/lib/internal/crypto/keygen.js +++ b/lib/internal/crypto/keygen.js @@ -41,13 +41,14 @@ const { const { customPromisifyArgs } = require('internal/util'); const { - isInt32, - isUint32, validateFunction, + validateBuffer, validateString, validateInteger, validateObject, validateOneOf, + validateInt32, + validateUint32, } = require('internal/validators'); const { @@ -61,7 +62,6 @@ const { const { isArrayBufferView } = require('internal/util/types'); const { getOptionValue } = require('internal/options'); -const pendingDeprecation = getOptionValue('--pending-deprecation'); function wrapKey(key, ctor) { if (typeof key === 'string' || @@ -175,16 +175,13 @@ function createJob(mode, type, options) { { validateObject(options, 'options'); const { modulusLength } = options; - if (!isUint32(modulusLength)) - throw new ERR_INVALID_ARG_VALUE('options.modulusLength', modulusLength); + validateUint32(modulusLength, 'options.modulusLength'); let { publicExponent } = options; if (publicExponent == null) { publicExponent = 0x10001; - } else if (!isUint32(publicExponent)) { - throw new ERR_INVALID_ARG_VALUE( - 'options.publicExponent', - publicExponent); + } else { + validateUint32(publicExponent, 'options.publicExponent'); } if (type === 'rsa') { @@ -199,22 +196,23 @@ function createJob(mode, type, options) { const { hash, mgf1Hash, hashAlgorithm, mgf1HashAlgorithm, saltLength } = options; - if (saltLength !== undefined && (!isInt32(saltLength) || saltLength < 0)) - throw new ERR_INVALID_ARG_VALUE('options.saltLength', saltLength); - if (hashAlgorithm !== undefined && typeof hashAlgorithm !== 'string') - throw new ERR_INVALID_ARG_VALUE('options.hashAlgorithm', hashAlgorithm); - if (mgf1HashAlgorithm !== undefined && - typeof mgf1HashAlgorithm !== 'string') - throw new ERR_INVALID_ARG_VALUE('options.mgf1HashAlgorithm', - mgf1HashAlgorithm); + + const pendingDeprecation = getOptionValue('--pending-deprecation'); + + if (saltLength !== undefined) + validateInt32(saltLength, 'options.saltLength', 0); + if (hashAlgorithm !== undefined) + validateString(hashAlgorithm, 'options.hashAlgorithm'); + if (mgf1HashAlgorithm !== undefined) + validateString(mgf1HashAlgorithm, 'options.mgf1HashAlgorithm'); if (hash !== undefined) { pendingDeprecation && process.emitWarning( '"options.hash" is deprecated, ' + 'use "options.hashAlgorithm" instead.', 'DeprecationWarning', 'DEP0154'); - if (typeof hash !== 'string' || - (hashAlgorithm && hash !== hashAlgorithm)) { + validateString(hash, 'options.hash'); + if (hashAlgorithm && hash !== hashAlgorithm) { throw new ERR_INVALID_ARG_VALUE('options.hash', hash); } } @@ -224,8 +222,8 @@ function createJob(mode, type, options) { 'use "options.mgf1HashAlgorithm" instead.', 'DeprecationWarning', 'DEP0154'); - if (typeof mgf1Hash !== 'string' || - (mgf1HashAlgorithm && mgf1Hash !== mgf1HashAlgorithm)) { + validateString(mgf1Hash, 'options.mgf1Hash'); + if (mgf1HashAlgorithm && mgf1Hash !== mgf1HashAlgorithm) { throw new ERR_INVALID_ARG_VALUE('options.mgf1Hash', mgf1Hash); } } @@ -244,15 +242,13 @@ function createJob(mode, type, options) { { validateObject(options, 'options'); const { modulusLength } = options; - if (!isUint32(modulusLength)) - throw new ERR_INVALID_ARG_VALUE('options.modulusLength', modulusLength); + validateUint32(modulusLength, 'options.modulusLength'); let { divisorLength } = options; if (divisorLength == null) { divisorLength = -1; - } else if (!isInt32(divisorLength) || divisorLength < 0) { - throw new ERR_INVALID_ARG_VALUE('options.divisorLength', divisorLength); - } + } else + validateInt32(divisorLength, 'options.divisorLength', 0); return new DsaKeyPairGenJob( mode, @@ -264,8 +260,7 @@ function createJob(mode, type, options) { { validateObject(options, 'options'); const { namedCurve } = options; - if (typeof namedCurve !== 'string') - throw new ERR_INVALID_ARG_VALUE('options.namedCurve', namedCurve); + validateString(namedCurve, 'options.namedCurve'); let { paramEncoding } = options; if (paramEncoding == null || paramEncoding === 'named') paramEncoding = OPENSSL_EC_NAMED_CURVE; @@ -313,8 +308,8 @@ function createJob(mode, type, options) { throw new ERR_INCOMPATIBLE_OPTION_PAIR('group', 'primeLength'); if (generator != null) throw new ERR_INCOMPATIBLE_OPTION_PAIR('group', 'generator'); - if (typeof group !== 'string') - throw new ERR_INVALID_ARG_VALUE('options.group', group); + + validateString(group, 'options.group'); return new DhKeyPairGenJob(mode, group, ...encoding); } @@ -322,19 +317,17 @@ function createJob(mode, type, options) { if (prime != null) { if (primeLength != null) throw new ERR_INCOMPATIBLE_OPTION_PAIR('prime', 'primeLength'); - if (!isArrayBufferView(prime)) - throw new ERR_INVALID_ARG_VALUE('options.prime', prime); + + validateBuffer(prime, 'options.prime'); } else if (primeLength != null) { - if (!isInt32(primeLength) || primeLength < 0) - throw new ERR_INVALID_ARG_VALUE('options.primeLength', primeLength); + validateInt32(primeLength, 'options.primeLength', 0); } else { throw new ERR_MISSING_OPTION( 'At least one of the group, prime, or primeLength options'); } if (generator != null) { - if (!isInt32(generator) || generator < 0) - throw new ERR_INVALID_ARG_VALUE('options.generator', generator); + validateInt32(generator, 'options.generator', 0); } return new DhKeyPairGenJob( mode, diff --git a/lib/internal/crypto/keys.js b/lib/internal/crypto/keys.js index ea2ecc262ed665..5c9f1fd0926506 100644 --- a/lib/internal/crypto/keys.js +++ b/lib/internal/crypto/keys.js @@ -124,6 +124,16 @@ const { throw new ERR_INVALID_ARG_TYPE('key', 'CryptoKey', key); return key[kKeyObject]; } + + equals(otherKeyObject) { + if (!isKeyObject(otherKeyObject)) { + throw new ERR_INVALID_ARG_TYPE( + 'otherKeyObject', 'KeyObject', otherKeyObject); + } + + return otherKeyObject.type === this.type && + this[kHandle].equals(otherKeyObject[kHandle]); + } } class SecretKeyObject extends KeyObject { diff --git a/lib/internal/crypto/random.js b/lib/internal/crypto/random.js index fc2e19e501c405..0a889cbebf6a7d 100644 --- a/lib/internal/crypto/random.js +++ b/lib/internal/crypto/random.js @@ -43,6 +43,7 @@ const { validateNumber, validateBoolean, validateFunction, + validateInt32, validateObject, validateUint32, } = require('internal/validators'); @@ -460,7 +461,7 @@ function createRandomPrimeJob(type, size, options) { } function generatePrime(size, options, callback) { - validateUint32(size, 'size', true); + validateInt32(size, 'size', 1); if (typeof options === 'function') { callback = options; options = {}; @@ -482,7 +483,7 @@ function generatePrime(size, options, callback) { } function generatePrimeSync(size, options = {}) { - validateUint32(size, 'size', true); + validateInt32(size, 'size', 1); const job = createRandomPrimeJob(kCryptoJobSync, size, options); const { 0: err, 1: prime } = job.run(); diff --git a/lib/internal/crypto/scrypt.js b/lib/internal/crypto/scrypt.js index 0d0ac1f7261e63..a36fb6dfba5260 100644 --- a/lib/internal/crypto/scrypt.js +++ b/lib/internal/crypto/scrypt.js @@ -41,7 +41,7 @@ const defaults = { N: 16384, r: 8, p: 1, - maxmem: 32 << 20, // 32 MB, matches SCRYPT_MAX_MEM. + maxmem: 32 << 20, // 32 MiB, matches SCRYPT_MAX_MEM. }; function scrypt(password, salt, keylen, options, callback = defaults) { diff --git a/lib/internal/crypto/util.js b/lib/internal/crypto/util.js index eafcc3d9669288..854994277e5d30 100644 --- a/lib/internal/crypto/util.js +++ b/lib/internal/crypto/util.js @@ -71,7 +71,7 @@ function lazyRequire(name) { return ret; } -var defaultEncoding = 'buffer'; +let defaultEncoding = 'buffer'; function setDefaultEncoding(val) { defaultEncoding = val; @@ -206,30 +206,33 @@ function validateMaxBufferLength(data, name) { } } -function normalizeAlgorithm(algorithm, label = 'algorithm') { +function normalizeAlgorithm(algorithm) { if (algorithm != null) { if (typeof algorithm === 'string') algorithm = { name: algorithm }; if (typeof algorithm === 'object') { const { name } = algorithm; - let hash; if (typeof name !== 'string' || !ArrayPrototypeIncludes( kAlgorithmsKeys, StringPrototypeToLowerCase(name))) { throw lazyDOMException('Unrecognized name.', 'NotSupportedError'); } - if (algorithm.hash !== undefined) { - hash = normalizeAlgorithm(algorithm.hash, 'algorithm.hash'); + let { hash } = algorithm; + if (hash !== undefined) { + hash = normalizeAlgorithm(hash); if (!ArrayPrototypeIncludes(kHashTypes, hash.name)) throw lazyDOMException('Unrecognized name.', 'NotSupportedError'); } - return { + const normalized = { ...algorithm, name: kAlgorithms[StringPrototypeToLowerCase(name)], - hash, }; + if (hash) { + normalized.hash = hash; + } + return normalized; } } throw lazyDOMException('Unrecognized name.', 'NotSupportedError'); diff --git a/lib/internal/crypto/webcrypto.js b/lib/internal/crypto/webcrypto.js index 8e3822494be230..3b7e4d13d8b1c0 100644 --- a/lib/internal/crypto/webcrypto.js +++ b/lib/internal/crypto/webcrypto.js @@ -5,6 +5,8 @@ const { JSONParse, JSONStringify, ObjectDefineProperties, + ReflectApply, + ReflectConstruct, SafeSet, SymbolToStringTag, StringPrototypeRepeat, @@ -30,7 +32,9 @@ const { TextDecoder, TextEncoder } = require('internal/encoding'); const { codes: { + ERR_ILLEGAL_CONSTRUCTOR, ERR_INVALID_ARG_TYPE, + ERR_INVALID_THIS, } } = require('internal/errors'); @@ -64,16 +68,25 @@ const { } = require('internal/util'); const { - getRandomValues, + getRandomValues: _getRandomValues, randomUUID: _randomUUID, } = require('internal/crypto/random'); -const randomUUID = () => _randomUUID(); +async function digest(algorithm, data) { + if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto'); + return ReflectApply(asyncDigest, this, arguments); +} + +function randomUUID() { + if (this !== crypto) throw new ERR_INVALID_THIS('Crypto'); + return _randomUUID(); +} async function generateKey( algorithm, extractable, keyUsages) { + if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto'); algorithm = normalizeAlgorithm(algorithm); validateBoolean(extractable, 'extractable'); validateArray(keyUsages, 'keyUsages'); @@ -121,6 +134,7 @@ async function generateKey( } async function deriveBits(algorithm, baseKey, length) { + if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto'); algorithm = normalizeAlgorithm(algorithm); if (!isCryptoKey(baseKey)) throw new ERR_INVALID_ARG_TYPE('baseKey', 'CryptoKey', baseKey); @@ -151,12 +165,48 @@ async function deriveBits(algorithm, baseKey, length) { throw lazyDOMException('Unrecognized name.'); } +function getKeyLength({ name, length, hash }) { + switch (name) { + case 'AES-CTR': + case 'AES-CBC': + case 'AES-GCM': + case 'AES-KW': + if (length !== 128 && length !== 192 && length !== 256) + throw lazyDOMException('Invalid key length', 'OperationError'); + + return length; + case 'HMAC': + if (length === undefined) { + switch (hash?.name) { + case 'SHA-1': + return 160; + case 'SHA-256': + return 256; + case 'SHA-384': + return 384; + case 'SHA-512': + return 512; + } + } + + if (typeof length === 'number' && length !== 0) { + return length; + } + + throw lazyDOMException('Invalid key length', 'OperationError'); + case 'HKDF': + case 'PBKDF2': + return null; + } +} + async function deriveKey( algorithm, baseKey, derivedKeyAlgorithm, extractable, keyUsages) { + if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto'); algorithm = normalizeAlgorithm(algorithm); derivedKeyAlgorithm = normalizeAlgorithm(derivedKeyAlgorithm); if (!isCryptoKey(baseKey)) @@ -174,7 +224,7 @@ async function deriveKey( validateBoolean(extractable, 'extractable'); validateArray(keyUsages, 'keyUsages'); - const { length } = derivedKeyAlgorithm; + const length = getKeyLength(derivedKeyAlgorithm); let bits; switch (algorithm.name) { case 'ECDH': @@ -201,7 +251,11 @@ async function deriveKey( throw lazyDOMException('Unrecognized name.'); } - return importKey('raw', bits, derivedKeyAlgorithm, extractable, keyUsages); + return ReflectApply( + importKey, + this, + ['raw', bits, derivedKeyAlgorithm, extractable, keyUsages], + ); } async function exportKeySpki(key) { @@ -366,11 +420,6 @@ async function exportKeyJWK(key) { key.algorithm.hash.name, normalizeHashName.kContextJwkHmac); return jwk; - case 'NODE-DSA': - jwk.alg = normalizeHashName( - key.algorithm.hash.name, - normalizeHashName.kContextJwkDsa); - return jwk; case 'NODE-ED25519': // Fall through case 'NODE-ED448': @@ -383,6 +432,7 @@ async function exportKeyJWK(key) { } async function exportKey(format, key) { + if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto'); validateString(format, 'format'); validateOneOf(format, 'format', kExportFormats); if (!isCryptoKey(key)) @@ -464,6 +514,7 @@ async function importKey( algorithm, extractable, keyUsages) { + if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto'); validateString(format, 'format'); validateOneOf(format, 'format', kExportFormats); if (format !== 'node.keyObject' && format !== 'jwk') @@ -525,8 +576,9 @@ async function importKey( // subtle.wrapKey() is essentially a subtle.exportKey() followed // by a subtle.encrypt(). async function wrapKey(format, key, wrappingKey, algorithm) { + if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto'); algorithm = normalizeAlgorithm(algorithm); - let keyData = await exportKey(format, key); + let keyData = await ReflectApply(exportKey, this, [format, key]); if (format === 'jwk') { if (keyData == null || typeof keyData !== 'object') @@ -554,11 +606,12 @@ async function unwrapKey( unwrappedKeyAlgo, extractable, keyUsages) { + if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto'); wrappedKey = getArrayBufferOrView(wrappedKey, 'wrappedKey'); - + unwrapAlgo = normalizeAlgorithm(unwrapAlgo); let keyData = await cipherOrWrap( kWebCryptoCipherDecrypt, - normalizeAlgorithm(unwrapAlgo), + unwrapAlgo, unwrappingKey, wrappedKey, 'unwrapKey'); @@ -575,7 +628,11 @@ async function unwrapKey( } } - return importKey(format, keyData, unwrappedKeyAlgo, extractable, keyUsages); + return ReflectApply( + importKey, + this, + [format, keyData, unwrappedKeyAlgo, extractable, keyUsages], + ); } function signVerify(algorithm, key, data, signature) { @@ -622,10 +679,12 @@ function signVerify(algorithm, key, data, signature) { } async function sign(algorithm, key, data) { + if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto'); return signVerify(algorithm, key, data); } async function verify(algorithm, key, signature, data) { + if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto'); return signVerify(algorithm, key, data, signature); } @@ -675,25 +734,41 @@ async function cipherOrWrap(mode, algorithm, key, data, op) { } async function encrypt(algorithm, key, data) { + if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto'); return cipherOrWrap(kWebCryptoCipherEncrypt, algorithm, key, data, 'encrypt'); } async function decrypt(algorithm, key, data) { + if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto'); return cipherOrWrap(kWebCryptoCipherDecrypt, algorithm, key, data, 'decrypt'); } // The SubtleCrypto and Crypto classes are defined as part of the // Web Crypto API standard: https://www.w3.org/TR/WebCryptoAPI/ -class SubtleCrypto {} -const subtle = new SubtleCrypto(); +class SubtleCrypto { + constructor() { + throw new ERR_ILLEGAL_CONSTRUCTOR(); + } +} +const subtle = ReflectConstruct(function() {}, [], SubtleCrypto); class Crypto { + constructor() { + throw new ERR_ILLEGAL_CONSTRUCTOR(); + } + get subtle() { + if (this !== crypto) throw new ERR_INVALID_THIS('Crypto'); return subtle; } } -const crypto = new Crypto(); +const crypto = ReflectConstruct(function() {}, [], Crypto); + +function getRandomValues(array) { + if (this !== crypto) throw new ERR_INVALID_THIS('Crypto'); + return ReflectApply(_getRandomValues, this, arguments); +} ObjectDefineProperties( Crypto.prototype, { @@ -760,7 +835,7 @@ ObjectDefineProperties( enumerable: true, configurable: true, writable: true, - value: asyncDigest, + value: digest, }, generateKey: { enumerable: true, diff --git a/lib/internal/crypto/x509.js b/lib/internal/crypto/x509.js index e7098d17da6aac..386b41f3e4a42f 100644 --- a/lib/internal/crypto/x509.js +++ b/lib/internal/crypto/x509.js @@ -56,6 +56,8 @@ const { kHandle, } = require('internal/crypto/util'); +let lazyTranslatePeerCertificate; + const kInternalState = Symbol('kInternalState'); function isX509Certificate(value) { @@ -345,7 +347,11 @@ class X509Certificate extends JSTransferable { } toLegacyObject() { - return this[kHandle].toLegacy(); + // TODO(tniessen): do not depend on translatePeerCertificate here, return + // the correct legacy representation from the binding + lazyTranslatePeerCertificate ??= + require('_tls_common').translatePeerCertificate; + return lazyTranslatePeerCertificate(this[kHandle].toLegacy()); } } diff --git a/lib/internal/debugger/inspect_repl.js b/lib/internal/debugger/inspect_repl.js index a32099a1dbf88e..4b11023554e268 100644 --- a/lib/internal/debugger/inspect_repl.js +++ b/lib/internal/debugger/inspect_repl.js @@ -976,7 +976,7 @@ function createRepl(inspector) { let sizeWritten = 0; function onProgress({ done, total, finished }) { if (finished) { - print('Heap snaphost prepared.'); + print('Heap snapshot prepared.'); } else { print(`Heap snapshot: ${done}/${total}`, false); } diff --git a/lib/internal/dns/promises.js b/lib/internal/dns/promises.js index fcff215896ae8f..9625e9e7b9b6ea 100644 --- a/lib/internal/dns/promises.js +++ b/lib/internal/dns/promises.js @@ -5,6 +5,7 @@ const { ObjectDefineProperty, Promise, ReflectApply, + Symbol, } = primordials; const { @@ -14,7 +15,6 @@ const { validateTimeout, validateTries, emitInvalidHostnameWarning, - emitTypeCoercionDeprecationWarning, getDefaultVerbatim, } = require('internal/dns/utils'); const { codes, dnsException } = require('internal/errors'); @@ -29,15 +29,27 @@ const { QueryReqWrap } = internalBinding('cares_wrap'); const { + ERR_INVALID_ARG_TYPE, ERR_INVALID_ARG_VALUE, ERR_MISSING_ARGS, } = codes; const { + validateBoolean, + validateNumber, + validateOneOf, validatePort, validateString, - validateOneOf, } = require('internal/validators'); +const kPerfHooksDnsLookupContext = Symbol('kPerfHooksDnsLookupContext'); +const kPerfHooksDnsLookupServiceContext = Symbol('kPerfHooksDnsLookupServiceContext'); +const kPerfHooksDnsLookupResolveContext = Symbol('kPerfHooksDnsLookupResolveContext'); + +const { + startPerf, + stopPerf, +} = require('internal/perf/observe'); + function onlookup(err, addresses) { if (err) { this.reject(dnsException(err, 'getaddrinfo', this.hostname)); @@ -46,6 +58,7 @@ function onlookup(err, addresses) { const family = this.family || isIP(addresses[0]); this.resolve({ address: addresses[0], family }); + stopPerf(this, kPerfHooksDnsLookupContext); } function onlookupall(err, addresses) { @@ -56,7 +69,7 @@ function onlookupall(err, addresses) { const family = this.family; - for (var i = 0; i < addresses.length; i++) { + for (let i = 0; i < addresses.length; i++) { const address = addresses[i]; addresses[i] = { @@ -66,6 +79,7 @@ function onlookupall(err, addresses) { } this.resolve(addresses); + stopPerf(this, kPerfHooksDnsLookupContext); } function createLookupPromise(family, hostname, all, hints, verbatim) { @@ -96,50 +110,55 @@ function createLookupPromise(family, hostname, all, hints, verbatim) { if (err) { reject(dnsException(err, 'getaddrinfo', hostname)); + } else { + const detail = { + hostname, + family, + hints, + verbatim, + }; + startPerf(req, kPerfHooksDnsLookupContext, { type: 'dns', name: 'lookup', detail }); } }); } +const validFamilies = [0, 4, 6]; function lookup(hostname, options) { - var hints = 0; - var family = -1; - var all = false; - var verbatim = getDefaultVerbatim(); + let hints = 0; + let family = 0; + let all = false; + let verbatim = getDefaultVerbatim(); // Parse arguments if (hostname) { validateString(hostname, 'hostname'); } - if (options !== null && typeof options === 'object') { - if (options.hints != null && typeof options.hints !== 'number') { - emitTypeCoercionDeprecationWarning(); - } - hints = options.hints >>> 0; - if (options.family != null && typeof options.family !== 'number') { - emitTypeCoercionDeprecationWarning(); + if (typeof options === 'number') { + validateOneOf(options, 'family', validFamilies, true); + family = options; + } else if (options !== undefined && typeof options !== 'object') { + throw new ERR_INVALID_ARG_TYPE('options', ['integer', 'object'], options); + } else { + if (options?.hints != null) { + validateNumber(options.hints, 'options.hints'); + hints = options.hints >>> 0; + validateHints(hints); } - family = options.family >>> 0; - if (options.all != null && typeof options.all !== 'boolean') { - emitTypeCoercionDeprecationWarning(); + if (options?.family != null) { + validateOneOf(options.family, 'options.family', validFamilies, true); + family = options.family; } - all = options.all === true; - if (typeof options.verbatim === 'boolean') { - verbatim = options.verbatim === true; - } else if (options.verbatim != null) { - emitTypeCoercionDeprecationWarning(); + if (options?.all != null) { + validateBoolean(options.all, 'options.all'); + all = options.all; } - - validateHints(hints); - } else { - if (options != null && typeof options !== 'number') { - emitTypeCoercionDeprecationWarning(); + if (options?.verbatim != null) { + validateBoolean(options.verbatim, 'options.verbatim'); + verbatim = options.verbatim; } - family = options >>> 0; } - validateOneOf(family, 'family', [0, 4, 6], true); - return createLookupPromise(family, hostname, all, hints, verbatim); } @@ -151,6 +170,7 @@ function onlookupservice(err, hostname, service) { } this.resolve({ hostname, service }); + stopPerf(this, kPerfHooksDnsLookupServiceContext); } function createLookupServicePromise(hostname, port) { @@ -167,6 +187,15 @@ function createLookupServicePromise(hostname, port) { if (err) reject(dnsException(err, 'getnameinfo', hostname)); + else + startPerf(req, kPerfHooksDnsLookupServiceContext, { + type: 'dns', + name: 'lookupService', + detail: { + host: hostname, + port + } + }); }); } @@ -194,6 +223,7 @@ function onresolve(err, result, ttls) { result, (address, index) => ({ address, ttl: ttls[index] })); this.resolve(result); + stopPerf(this, kPerfHooksDnsLookupResolveContext); } function createResolverPromise(resolver, bindingName, hostname, ttl) { @@ -211,6 +241,16 @@ function createResolverPromise(resolver, bindingName, hostname, ttl) { if (err) reject(dnsException(err, bindingName, hostname)); + else { + startPerf(req, kPerfHooksDnsLookupResolveContext, { + type: 'dns', + name: bindingName, + detail: { + host: hostname, + ttl + } + }); + } }); } @@ -256,7 +296,7 @@ Resolver.prototype.resolveNaptr = resolveMap.NAPTR = resolver('queryNaptr'); Resolver.prototype.resolveSoa = resolveMap.SOA = resolver('querySoa'); Resolver.prototype.reverse = resolver('getHostByAddr'); Resolver.prototype.resolve = function resolve(hostname, rrtype) { - var resolver; + let resolver; if (rrtype !== undefined) { validateString(rrtype, 'rrtype'); diff --git a/lib/internal/dns/utils.js b/lib/internal/dns/utils.js index 58d3eaafcaa6c9..62606e6f7f0aaf 100644 --- a/lib/internal/dns/utils.js +++ b/lib/internal/dns/utils.js @@ -190,18 +190,6 @@ function emitInvalidHostnameWarning(hostname) { } } -let typeCoercionWarningEmitted = false; -function emitTypeCoercionDeprecationWarning() { - if (!typeCoercionWarningEmitted) { - process.emitWarning( - 'Type coercion of dns.lookup options is deprecated', - 'DeprecationWarning', - 'DEP0153' - ); - typeCoercionWarningEmitted = true; - } -} - let dnsOrder = getOptionValue('--dns-result-order') || 'verbatim'; function getDefaultVerbatim() { @@ -222,7 +210,6 @@ module.exports = { validateTries, Resolver, emitInvalidHostnameWarning, - emitTypeCoercionDeprecationWarning, getDefaultVerbatim, setDefaultResultOrder, }; diff --git a/lib/internal/errors.js b/lib/internal/errors.js index c0834aab9c070c..de4d33b93ede8a 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -175,24 +175,19 @@ let assert; let internalUtil = null; function lazyInternalUtil() { - if (!internalUtil) { - internalUtil = require('internal/util'); - } + internalUtil ??= require('internal/util'); return internalUtil; } let internalUtilInspect = null; function lazyInternalUtilInspect() { - if (!internalUtilInspect) { - internalUtilInspect = require('internal/util/inspect'); - } + internalUtilInspect ??= require('internal/util/inspect'); return internalUtilInspect; } let buffer; function lazyBuffer() { - if (buffer === undefined) - buffer = require('buffer').Buffer; + buffer ??= require('buffer').Buffer; return buffer; } @@ -207,6 +202,16 @@ function isErrorStackTraceLimitWritable() { desc.set !== undefined; } +function inspectWithNoCustomRetry(obj, options) { + const utilInspect = lazyInternalUtilInspect(); + + try { + return utilInspect.inspect(obj, options); + } catch { + return utilInspect.inspect(obj, { ...options, customInspect: false }); + } +} + // A specialized Error that includes an additional info property with // additional information about the error condition. // It has the properties present in a UVException but with a custom error @@ -411,7 +416,7 @@ function E(sym, val, def, ...otherClasses) { function getMessage(key, args, self) { const msg = messages.get(key); - if (assert === undefined) assert = require('internal/assert'); + assert ??= require('internal/assert'); if (typeof msg === 'function') { assert( @@ -439,9 +444,7 @@ function getMessage(key, args, self) { let uvBinding; function lazyUv() { - if (!uvBinding) { - uvBinding = internalBinding('uv'); - } + uvBinding ??= internalBinding('uv'); return uvBinding; } @@ -449,9 +452,7 @@ const uvUnmappedError = ['UNKNOWN', 'unknown error']; function uvErrmapGet(name) { uvBinding = lazyUv(); - if (!uvBinding.errmap) { - uvBinding.errmap = uvBinding.getErrorMap(); - } + uvBinding.errmap ??= uvBinding.getErrorMap(); return MapPrototypeGet(uvBinding.errmap, name); } @@ -578,7 +579,7 @@ const errnoException = hideStackFrames( // getSystemErrorName(err) to guard against invalid arguments from users. // This can be replaced with [ code ] = errmap.get(err) when this method // is no longer exposed to user land. - if (util === undefined) util = require('util'); + util ??= require('util'); const code = util.getSystemErrorName(err); const message = original ? `${syscall} ${code} ${original}` : `${syscall} ${code}`; @@ -612,7 +613,7 @@ const exceptionWithHostPort = hideStackFrames( // getSystemErrorName(err) to guard against invalid arguments from users. // This can be replaced with [ code ] = errmap.get(err) when this method // is no longer exposed to user land. - if (util === undefined) util = require('util'); + util ??= require('util'); const code = util.getSystemErrorName(err); let details = ''; if (port && port > 0) { @@ -862,6 +863,7 @@ module.exports = { getMessage, hideInternalStackFrames, hideStackFrames, + inspectWithNoCustomRetry, isErrorStackTraceLimitWritable, isStackOverflowError, kEnhanceStackBeforeInspector, @@ -993,7 +995,7 @@ E('ERR_FS_CP_SYMLINK_TO_SUBDIRECTORY', 'Cannot overwrite symlink in subdirectory of self', SystemError); E('ERR_FS_CP_UNKNOWN', 'Cannot copy an unknown file type', SystemError); E('ERR_FS_EISDIR', 'Path is a directory', SystemError); -E('ERR_FS_FILE_TOO_LARGE', 'File size (%s) is greater than 2 GB', RangeError); +E('ERR_FS_FILE_TOO_LARGE', 'File size (%s) is greater than 2 GiB', RangeError); E('ERR_FS_INVALID_SYMLINK_TYPE', 'Symlink type must be one of "dir", "file", or "junction". Received "%s"', Error); // Switch to TypeError. The current implementation does not seem right @@ -1227,7 +1229,7 @@ E('ERR_INVALID_ARG_TYPE', } else if (typeof actual === 'function' && actual.name) { msg += `. Received function ${actual.name}`; } else if (typeof actual === 'object') { - if (actual.constructor && actual.constructor.name) { + if (actual.constructor?.name) { msg += `. Received an instance of ${actual.constructor.name}`; } else { const inspected = lazyInternalUtilInspect() @@ -1311,7 +1313,7 @@ E('ERR_INVALID_RETURN_PROPERTY', (input, name, prop, value) => { }, TypeError); E('ERR_INVALID_RETURN_PROPERTY_VALUE', (input, name, prop, value) => { let type; - if (value && value.constructor && value.constructor.name) { + if (value?.constructor?.name) { type = `instance of ${value.constructor.name}`; } else { type = `type ${typeof value}`; @@ -1321,7 +1323,7 @@ E('ERR_INVALID_RETURN_PROPERTY_VALUE', (input, name, prop, value) => { }, TypeError); E('ERR_INVALID_RETURN_VALUE', (input, name, value) => { let type; - if (value && value.constructor && value.constructor.name) { + if (value?.constructor?.name) { type = `instance of ${value.constructor.name}`; } else { type = `type ${typeof value}`; @@ -1544,6 +1546,21 @@ E('ERR_STREAM_WRAP', 'Stream has StringDecoder set or is in objectMode', Error); E('ERR_STREAM_WRITE_AFTER_END', 'write after end', Error); E('ERR_SYNTHETIC', 'JavaScript Callstack', Error); E('ERR_SYSTEM_ERROR', 'A system error occurred', SystemError); +E('ERR_TEST_FAILURE', function(error, failureType) { + hideInternalStackFrames(this); + assert(typeof failureType === 'string', + "The 'failureType' argument must be of type string."); + + let msg = error?.message ?? error; + + if (typeof msg !== 'string') { + msg = inspectWithNoCustomRetry(msg); + } + + this.failureType = failureType; + this.cause = error; + return msg; +}, Error); E('ERR_TLS_CERT_ALTNAME_FORMAT', 'Invalid subject alternative name string', SyntaxError); E('ERR_TLS_CERT_ALTNAME_INVALID', function(reason, host, cert) { @@ -1594,9 +1611,13 @@ E('ERR_UNHANDLED_ERROR', E('ERR_UNKNOWN_BUILTIN_MODULE', 'No such built-in module: %s', Error); E('ERR_UNKNOWN_CREDENTIAL', '%s identifier does not exist: %s', Error); E('ERR_UNKNOWN_ENCODING', 'Unknown encoding: %s', TypeError); -E('ERR_UNKNOWN_FILE_EXTENSION', - 'Unknown file extension "%s" for %s', - TypeError); +E('ERR_UNKNOWN_FILE_EXTENSION', (ext, path, suggestion) => { + let msg = `Unknown file extension "${ext}" for ${path}`; + if (suggestion) { + msg += `. ${suggestion}`; + } + return msg; +}, TypeError); E('ERR_UNKNOWN_MODULE_FORMAT', 'Unknown module format: %s for URL %s', RangeError); E('ERR_UNKNOWN_SIGNAL', 'Unknown signal: %s', TypeError); @@ -1611,6 +1632,7 @@ E('ERR_UNSUPPORTED_ESM_URL_SCHEME', (url, supported) => { msg += `. Received protocol '${url.protocol}'`; return msg; }, Error); +E('ERR_USE_AFTER_CLOSE', '%s was closed', Error); // This should probably be a `TypeError`. E('ERR_VALID_PERFORMANCE_ENTRY_TYPE', @@ -1622,12 +1644,15 @@ E('ERR_VM_MODULE_CANNOT_CREATE_CACHED_DATA', 'Cached data cannot be created for a module which has been evaluated', Error); E('ERR_VM_MODULE_DIFFERENT_CONTEXT', 'Linked modules must use the same context', Error); -E('ERR_VM_MODULE_LINKING_ERRORED', - 'Linking has already failed for the provided module', Error); +E('ERR_VM_MODULE_LINK_FAILURE', function(message, cause) { + this.cause = cause; + return message; +}, Error); E('ERR_VM_MODULE_NOT_MODULE', 'Provided module is not an instance of Module', Error); E('ERR_VM_MODULE_STATUS', 'Module status %s', Error); E('ERR_WASI_ALREADY_STARTED', 'WASI instance has already started', Error); +E('ERR_WEBASSEMBLY_RESPONSE', 'WebAssembly response %s', TypeError); E('ERR_WORKER_INIT_FAILED', 'Worker initialization failure: %s', Error); E('ERR_WORKER_INVALID_EXEC_ARGV', (errors, msg = 'invalid execArgv flags') => `Initiated Worker with ${msg}: ${ArrayPrototypeJoin(errors, ', ')}`, diff --git a/lib/internal/event_target.js b/lib/internal/event_target.js index 7c539db2eb3c2a..0f9a896396f79c 100644 --- a/lib/internal/event_target.js +++ b/lib/internal/event_target.js @@ -63,16 +63,7 @@ const kTrustEvent = Symbol('kTrustEvent'); const { now } = require('internal/perf/utils'); -// TODO(joyeecheung): V8 snapshot does not support instance member -// initializers for now: -// https://bugs.chromium.org/p/v8/issues/detail?id=10704 const kType = Symbol('type'); -const kDefaultPrevented = Symbol('defaultPrevented'); -const kCancelable = Symbol('cancelable'); -const kTimestamp = Symbol('timestamp'); -const kBubbles = Symbol('bubbles'); -const kComposed = Symbol('composed'); -const kPropagationStopped = Symbol('propagationStopped'); const isTrustedSet = new SafeWeakSet(); const isTrusted = ObjectGetOwnPropertyDescriptor({ @@ -86,6 +77,13 @@ function isEvent(value) { } class Event { + #cancelable = false; + #bubbles = false; + #composed = false; + #defaultPrevented = false; + #timestamp = now(); + #propagationStopped = false; + /** * @param {string} type * @param {{ @@ -101,13 +99,11 @@ class Event { allowArray: true, allowFunction: true, nullable: true, }); const { cancelable, bubbles, composed } = { ...options }; - this[kCancelable] = !!cancelable; - this[kBubbles] = !!bubbles; - this[kComposed] = !!composed; + this.#cancelable = !!cancelable; + this.#bubbles = !!bubbles; + this.#composed = !!composed; + this[kType] = `${type}`; - this[kDefaultPrevented] = false; - this[kTimestamp] = now(); - this[kPropagationStopped] = false; if (options?.[kTrustEvent]) { isTrustedSet.add(this); } @@ -135,9 +131,9 @@ class Event { return `${name} ${inspect({ type: this[kType], - defaultPrevented: this[kDefaultPrevented], - cancelable: this[kCancelable], - timeStamp: this[kTimestamp], + defaultPrevented: this.#defaultPrevented, + cancelable: this.#cancelable, + timeStamp: this.#timestamp, }, opts)}`; } @@ -150,7 +146,7 @@ class Event { preventDefault() { if (!isEvent(this)) throw new ERR_INVALID_THIS('Event'); - this[kDefaultPrevented] = true; + this.#defaultPrevented = true; } /** @@ -195,7 +191,7 @@ class Event { get cancelable() { if (!isEvent(this)) throw new ERR_INVALID_THIS('Event'); - return this[kCancelable]; + return this.#cancelable; } /** @@ -204,7 +200,7 @@ class Event { get defaultPrevented() { if (!isEvent(this)) throw new ERR_INVALID_THIS('Event'); - return this[kCancelable] && this[kDefaultPrevented]; + return this.#cancelable && this.#defaultPrevented; } /** @@ -213,7 +209,7 @@ class Event { get timeStamp() { if (!isEvent(this)) throw new ERR_INVALID_THIS('Event'); - return this[kTimestamp]; + return this.#timestamp; } @@ -244,7 +240,7 @@ class Event { get bubbles() { if (!isEvent(this)) throw new ERR_INVALID_THIS('Event'); - return this[kBubbles]; + return this.#bubbles; } /** @@ -253,7 +249,7 @@ class Event { get composed() { if (!isEvent(this)) throw new ERR_INVALID_THIS('Event'); - return this[kComposed]; + return this.#composed; } /** @@ -271,7 +267,7 @@ class Event { get cancelBubble() { if (!isEvent(this)) throw new ERR_INVALID_THIS('Event'); - return this[kPropagationStopped]; + return this.#propagationStopped; } /** @@ -288,7 +284,7 @@ class Event { stopPropagation() { if (!isEvent(this)) throw new ERR_INVALID_THIS('Event'); - this[kPropagationStopped] = true; + this.#propagationStopped = true; } static NONE = 0; diff --git a/lib/internal/fs/promises.js b/lib/internal/fs/promises.js index fbc4de640ade3c..73c159550740b0 100644 --- a/lib/internal/fs/promises.js +++ b/lib/internal/fs/promises.js @@ -5,6 +5,7 @@ const { Error, MathMax, MathMin, + ObjectCreate, NumberIsSafeInteger, Promise, PromisePrototypeThen, @@ -64,7 +65,7 @@ const { validateOffsetLengthWrite, validateRmOptions, validateRmdirOptions, - validateStringAfterArrayBufferView, + validatePrimitiveStringAfterArrayBufferView, warnOnNonPortableTemplate, } = require('internal/fs/utils'); const { opendir } = require('internal/fs/dir'); @@ -510,18 +511,15 @@ async function open(path, flags, mode) { async function read(handle, bufferOrOptions, offset, length, position) { let buffer = bufferOrOptions; if (!isArrayBufferView(buffer)) { - if (bufferOrOptions === undefined) { - bufferOrOptions = {}; - } - if (bufferOrOptions.buffer) { - buffer = bufferOrOptions.buffer; - validateBuffer(buffer); - } else { - buffer = Buffer.alloc(16384); - } - offset = bufferOrOptions.offset || 0; - length = bufferOrOptions.length ?? buffer.byteLength; - position = bufferOrOptions.position ?? null; + bufferOrOptions ??= ObjectCreate(null); + ({ + buffer = Buffer.alloc(16384), + offset = 0, + length = buffer.byteLength - offset, + position = null + } = bufferOrOptions); + + validateBuffer(buffer); } if (offset == null) { @@ -583,7 +581,7 @@ async function write(handle, buffer, offset, length, position) { return { bytesWritten, buffer }; } - validateStringAfterArrayBufferView(buffer, 'buffer'); + validatePrimitiveStringAfterArrayBufferView(buffer, 'buffer'); validateEncoding(buffer, length); const bytesWritten = (await binding.writeString(handle.fd, buffer, offset, length, kUsePromises)) || 0; @@ -813,7 +811,7 @@ async function writeFile(path, data, options) { const flag = options.flag || 'w'; if (!isArrayBufferView(data) && !isCustomIterable(data)) { - validateStringAfterArrayBufferView(data, 'data'); + validatePrimitiveStringAfterArrayBufferView(data, 'data'); data = Buffer.from(data, options.encoding || 'utf8'); } diff --git a/lib/internal/fs/utils.js b/lib/internal/fs/utils.js index 481b5292b1d726..e570b42da88204 100644 --- a/lib/internal/fs/utils.js +++ b/lib/internal/fs/utils.js @@ -122,7 +122,7 @@ const kMaximumCopyMode = COPYFILE_EXCL | COPYFILE_FICLONE | COPYFILE_FICLONE_FORCE; -// Most platforms don't allow reads or writes >= 2 GB. +// Most platforms don't allow reads or writes >= 2 GiB. // See https://github.com/libuv/libuv/pull/1501. const kIoMaxLength = 2 ** 31 - 1; @@ -889,6 +889,16 @@ const validateStringAfterArrayBufferView = hideStackFrames((buffer, name) => { ); }); +const validatePrimitiveStringAfterArrayBufferView = hideStackFrames((buffer, name) => { + if (typeof buffer !== 'string') { + throw new ERR_INVALID_ARG_TYPE( + name, + ['string', 'Buffer', 'TypedArray', 'DataView'], + buffer + ); + } +}); + const validatePosition = hideStackFrames((position, name) => { if (typeof position === 'number') { validateInteger(position, 'position'); @@ -943,5 +953,6 @@ module.exports = { validateRmOptionsSync, validateRmdirOptions, validateStringAfterArrayBufferView, + validatePrimitiveStringAfterArrayBufferView, warnOnNonPortableTemplate }; diff --git a/lib/internal/http.js b/lib/internal/http.js index 56187a2b1cc315..375118da49f59b 100644 --- a/lib/internal/http.js +++ b/lib/internal/http.js @@ -38,7 +38,7 @@ function emitStatistics(statistics) { const startTime = statistics.startTime; const diff = process.hrtime(startTime); const entry = new InternalPerformanceEntry( - 'HttpRequest', + statistics.type, 'http', startTime[0] * 1000 + startTime[1] / 1e6, diff[0] * 1000 + diff[1] / 1e6, diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js index b7663cbb618f7d..9d8d6bc2c860fa 100644 --- a/lib/internal/http2/core.js +++ b/lib/internal/http2/core.js @@ -607,6 +607,8 @@ function onFrameError(id, type, code) { const emitter = session[kState].streams.get(id) || session; emitter[kUpdateTimer](); emitter.emit('frameError', type, code, id); + session[kState].streams.get(id).close(code); + session.close(); } function onAltSvc(stream, origin, alt) { diff --git a/lib/internal/http2/util.js b/lib/internal/http2/util.js index 38578d2a151c52..962bdd753c2717 100644 --- a/lib/internal/http2/util.js +++ b/lib/internal/http2/util.js @@ -588,7 +588,7 @@ const assertWithinRange = hideStackFrames( function toHeaderObject(headers, sensitiveHeaders) { const obj = ObjectCreate(null); - for (var n = 0; n < headers.length; n += 2) { + for (let n = 0; n < headers.length; n += 2) { const name = headers[n]; let value = headers[n + 1]; if (name === HTTP2_HEADER_STATUS) diff --git a/lib/internal/js_stream_socket.js b/lib/internal/js_stream_socket.js index faad988e820ffa..f2a0ecaa1d8a65 100644 --- a/lib/internal/js_stream_socket.js +++ b/lib/internal/js_stream_socket.js @@ -171,6 +171,7 @@ class JSStreamSocket extends Socket { this.stream.cork(); // Use `var` over `let` for performance optimization. + // eslint-disable-next-line no-var for (var i = 0; i < bufs.length; ++i) this.stream.write(bufs[i], done); this.stream.uncork(); diff --git a/lib/internal/main/mksnapshot.js b/lib/internal/main/mksnapshot.js new file mode 100644 index 00000000000000..3e1515a2d2e05e --- /dev/null +++ b/lib/internal/main/mksnapshot.js @@ -0,0 +1,142 @@ +'use strict'; + +const { + Error, + SafeSet, + SafeArrayIterator +} = primordials; + +const binding = internalBinding('mksnapshot'); +const { NativeModule } = require('internal/bootstrap/loaders'); +const { + compileSnapshotMain, +} = binding; + +const { + getOptionValue +} = require('internal/options'); + +const { + readFileSync +} = require('fs'); + +const supportedModules = new SafeSet(new SafeArrayIterator([ + // '_http_agent', + // '_http_client', + // '_http_common', + // '_http_incoming', + // '_http_outgoing', + // '_http_server', + '_stream_duplex', + '_stream_passthrough', + '_stream_readable', + '_stream_transform', + '_stream_wrap', + '_stream_writable', + // '_tls_common', + // '_tls_wrap', + 'assert', + 'assert/strict', + // 'async_hooks', + 'buffer', + // 'child_process', + // 'cluster', + 'console', + 'constants', + 'crypto', + // 'dgram', + // 'diagnostics_channel', + // 'dns', + // 'dns/promises', + // 'domain', + 'events', + 'fs', + 'fs/promises', + // 'http', + // 'http2', + // 'https', + // 'inspector', + // 'module', + // 'net', + 'os', + 'path', + 'path/posix', + 'path/win32', + // 'perf_hooks', + 'process', + 'punycode', + 'querystring', + // 'readline', + // 'repl', + 'stream', + 'stream/promises', + 'string_decoder', + 'sys', + 'timers', + 'timers/promises', + // 'tls', + // 'trace_events', + // 'tty', + 'url', + 'util', + 'util/types', + 'v8', + // 'vm', + // 'worker_threads', + // 'zlib', +])); + +const warnedModules = new SafeSet(); +function supportedInUserSnapshot(id) { + return supportedModules.has(id); +} + +function requireForUserSnapshot(id) { + if (!NativeModule.canBeRequiredByUsers(id)) { + // eslint-disable-next-line no-restricted-syntax + const err = new Error( + `Cannot find module '${id}'. ` + ); + err.code = 'MODULE_NOT_FOUND'; + throw err; + } + if (!supportedInUserSnapshot(id)) { + if (!warnedModules.has(id)) { + process.emitWarning( + `built-in module ${id} is not yet supported in user snapshots`); + warnedModules.add(id); + } + } + + return require(id); +} + +function main() { + const { + prepareMainThreadExecution + } = require('internal/bootstrap/pre_execution'); + + prepareMainThreadExecution(true, false); + process.once('beforeExit', function runCleanups() { + for (const cleanup of binding.cleanups) { + cleanup(); + } + }); + + const file = process.argv[1]; + const path = require('path'); + const filename = path.resolve(file); + const dirname = path.dirname(filename); + const source = readFileSync(file, 'utf-8'); + const snapshotMainFunction = compileSnapshotMain(filename, source); + + if (getOptionValue('--inspect-brk')) { + internalBinding('inspector').callAndPauseOnStart( + snapshotMainFunction, undefined, + requireForUserSnapshot, filename, dirname); + } else { + snapshotMainFunction(requireForUserSnapshot, filename, dirname); + } +} + +main(); diff --git a/lib/internal/main/test_runner.js b/lib/internal/main/test_runner.js new file mode 100644 index 00000000000000..71bf21782f39f3 --- /dev/null +++ b/lib/internal/main/test_runner.js @@ -0,0 +1,157 @@ +'use strict'; +const { + ArrayFrom, + ArrayPrototypeFilter, + ArrayPrototypeIncludes, + ArrayPrototypePush, + ArrayPrototypeSlice, + ArrayPrototypeSort, + Promise, + SafeSet, +} = primordials; +const { + prepareMainThreadExecution, +} = require('internal/bootstrap/pre_execution'); +const { spawn } = require('child_process'); +const { readdirSync, statSync } = require('fs'); +const console = require('internal/console/global'); +const { + codes: { + ERR_TEST_FAILURE, + }, +} = require('internal/errors'); +const test = require('internal/test_runner/harness'); +const { kSubtestsFailed } = require('internal/test_runner/test'); +const { + isSupportedFileType, + doesPathMatchFilter, +} = require('internal/test_runner/utils'); +const { basename, join, resolve } = require('path'); +const kFilterArgs = ['--test']; + +prepareMainThreadExecution(false); +markBootstrapComplete(); + +// TODO(cjihrig): Replace this with recursive readdir once it lands. +function processPath(path, testFiles, options) { + const stats = statSync(path); + + if (stats.isFile()) { + if (options.userSupplied || + (options.underTestDir && isSupportedFileType(path)) || + doesPathMatchFilter(path)) { + testFiles.add(path); + } + } else if (stats.isDirectory()) { + const name = basename(path); + + if (!options.userSupplied && name === 'node_modules') { + return; + } + + // 'test' directories get special treatment. Recursively add all .js, + // .cjs, and .mjs files in the 'test' directory. + const isTestDir = name === 'test'; + const { underTestDir } = options; + const entries = readdirSync(path); + + if (isTestDir) { + options.underTestDir = true; + } + + options.userSupplied = false; + + for (let i = 0; i < entries.length; i++) { + processPath(join(path, entries[i]), testFiles, options); + } + + options.underTestDir = underTestDir; + } +} + +function createTestFileList() { + const cwd = process.cwd(); + const hasUserSuppliedPaths = process.argv.length > 1; + const testPaths = hasUserSuppliedPaths ? + ArrayPrototypeSlice(process.argv, 1) : [cwd]; + const testFiles = new SafeSet(); + + try { + for (let i = 0; i < testPaths.length; i++) { + const absolutePath = resolve(testPaths[i]); + + processPath(absolutePath, testFiles, { userSupplied: true }); + } + } catch (err) { + if (err?.code === 'ENOENT') { + console.error(`Could not find '${err.path}'`); + process.exit(1); + } + + throw err; + } + + return ArrayPrototypeSort(ArrayFrom(testFiles)); +} + +function filterExecArgv(arg) { + return !ArrayPrototypeIncludes(kFilterArgs, arg); +} + +function runTestFile(path) { + return test(path, () => { + return new Promise((resolve, reject) => { + const args = ArrayPrototypeFilter(process.execArgv, filterExecArgv); + ArrayPrototypePush(args, path); + + const child = spawn(process.execPath, args); + // TODO(cjihrig): Implement a TAP parser to read the child's stdout + // instead of just displaying it all if the child fails. + let stdout = ''; + let stderr = ''; + let err; + + child.on('error', (error) => { + err = error; + }); + + child.stdout.setEncoding('utf8'); + child.stderr.setEncoding('utf8'); + + child.stdout.on('data', (chunk) => { + stdout += chunk; + }); + + child.stderr.on('data', (chunk) => { + stderr += chunk; + }); + + child.once('exit', (code, signal) => { + if (code !== 0 || signal !== null) { + if (!err) { + err = new ERR_TEST_FAILURE('test failed', kSubtestsFailed); + err.exitCode = code; + err.signal = signal; + err.stdout = stdout; + err.stderr = stderr; + // The stack will not be useful since the failures came from tests + // in a child process. + err.stack = undefined; + } + + return reject(err); + } + + resolve(); + }); + }); + }); +} + +(async function main() { + const testFiles = createTestFileList(); + + for (let i = 0; i < testFiles.length; i++) { + runTestFile(testFiles[i]); + } +})(); diff --git a/lib/internal/modules/cjs/helpers.js b/lib/internal/modules/cjs/helpers.js index 3d27a19a250162..3ae63b46195f75 100644 --- a/lib/internal/modules/cjs/helpers.js +++ b/lib/internal/modules/cjs/helpers.js @@ -193,6 +193,11 @@ function addBuiltinLibsToObject(object, dummyModuleName) { }); } +/** + * + * @param {string | URL} referrer + * @returns {string} + */ function normalizeReferrerURL(referrer) { if (typeof referrer === 'string' && path.isAbsolute(referrer)) { return pathToFileURL(referrer).href; diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js index d09f30f53e5b44..de919e7406b4e7 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js @@ -182,7 +182,8 @@ function Module(id = '', parent) { const builtinModules = []; for (const { 0: id, 1: mod } of NativeModule.map) { - if (mod.canBeRequiredByUsers) { + if (mod.canBeRequiredByUsers && + NativeModule.canBeRequiredWithoutScheme(id)) { ArrayPrototypePush(builtinModules, id); } } @@ -665,7 +666,8 @@ if (isWindows) { } Module._resolveLookupPaths = function(request, parent) { - if (NativeModule.canBeRequiredByUsers(request)) { + if (NativeModule.canBeRequiredByUsers(request) && + NativeModule.canBeRequiredWithoutScheme(request)) { debug('looking for %j in []', request); return null; } @@ -802,7 +804,10 @@ Module._load = function(request, parent, isMain) { } const mod = loadNativeModule(filename, request); - if (mod?.canBeRequiredByUsers) return mod.exports; + if (mod?.canBeRequiredByUsers && + NativeModule.canBeRequiredWithoutScheme(filename)) { + return mod.exports; + } // Don't call updateChildren(), Module constructor already does. const module = cachedModule || new Module(filename, parent); @@ -847,7 +852,8 @@ Module._load = function(request, parent, isMain) { Module._resolveFilename = function(request, parent, isMain, options) { if (StringPrototypeStartsWith(request, 'node:') || - NativeModule.canBeRequiredByUsers(request)) { + (NativeModule.canBeRequiredByUsers(request) && + NativeModule.canBeRequiredWithoutScheme(request))) { return request; } @@ -1017,7 +1023,8 @@ function wrapSafe(filename, content, cjsModuleInstance) { displayErrors: true, importModuleDynamically: async (specifier, _, importAssertions) => { const loader = asyncESM.esmLoader; - return loader.import(specifier, normalizeReferrerURL(filename), + return loader.import(specifier, + loader.getBaseURL(normalizeReferrerURL(filename)), importAssertions); }, }); @@ -1033,7 +1040,8 @@ function wrapSafe(filename, content, cjsModuleInstance) { filename, importModuleDynamically(specifier, _, importAssertions) { const loader = asyncESM.esmLoader; - return loader.import(specifier, normalizeReferrerURL(filename), + return loader.import(specifier, + loader.getBaseURL(normalizeReferrerURL(filename)), importAssertions); }, }); @@ -1277,7 +1285,8 @@ Module._preloadModules = function(requests) { Module.syncBuiltinESMExports = function syncBuiltinESMExports() { for (const mod of NativeModule.map.values()) { - if (mod.canBeRequiredByUsers) { + if (mod.canBeRequiredByUsers && + NativeModule.canBeRequiredWithoutScheme(mod.id)) { mod.syncExports(); } } diff --git a/lib/internal/modules/esm/fetch_module.js b/lib/internal/modules/esm/fetch_module.js index 2cbc6ea72d0934..f6c2fc8827aa73 100644 --- a/lib/internal/modules/esm/fetch_module.js +++ b/lib/internal/modules/esm/fetch_module.js @@ -1,26 +1,24 @@ 'use strict'; const { - ArrayPrototypePush, - Promise, + ObjectPrototypeHasOwnProperty, PromisePrototypeThen, - PromiseResolve, SafeMap, StringPrototypeEndsWith, StringPrototypeSlice, StringPrototypeStartsWith, } = primordials; const { - Buffer: { - concat: BufferConcat - } + Buffer: { concat: BufferConcat }, } = require('buffer'); const { ERR_NETWORK_IMPORT_DISALLOWED, ERR_NETWORK_IMPORT_BAD_RESPONSE, + ERR_MODULE_NOT_FOUND, } = require('internal/errors').codes; const { URL } = require('internal/url'); const net = require('net'); - +const { once } = require('events'); +const { compose } = require('stream'); /** * @typedef CacheEntry * @property {Promise | string} resolvedHREF @@ -32,6 +30,9 @@ const net = require('net'); * Only for GET requests, other requests would need new Map * HTTP cache semantics keep diff caches * + * It caches either the promise or the cache entry since import.meta.url needs + * the value synchronously for the response location after all redirects. + * * Maps HREF to pending cache entry * @type {Map | CacheEntry>} */ @@ -47,11 +48,11 @@ let HTTPSAgent; function HTTPSGet(url, opts) { const https = require('https'); // [1] HTTPSAgent ??= new https.Agent({ // [2] - keepAlive: true + keepAlive: true, }); return https.get(url, { agent: HTTPSAgent, - ...opts + ...opts, }); } @@ -59,11 +60,11 @@ let HTTPAgent; function HTTPGet(url, opts) { const http = require('http'); // [1] HTTPAgent ??= new http.Agent({ // [2] - keepAlive: true + keepAlive: true, }); return http.get(url, { agent: HTTPAgent, - ...opts + ...opts, }); } @@ -98,59 +99,47 @@ function fetchWithRedirects(parsed) { return existing; } const handler = parsed.protocol === 'http:' ? HTTPGet : HTTPSGet; - const result = new Promise((fulfill, reject) => { + const result = (async () => { const req = handler(parsed, { - headers: { - Accept: '*/*' - } - }) - .on('error', reject) - .on('response', (res) => { - function dispose() { - req.destroy(); - res.destroy(); - } - if (res.statusCode >= 300 && res.statusCode <= 303) { - if (res.headers.location) { - dispose(); - try { - const location = new URL(res.headers.location, parsed); - if (location.protocol !== 'http:' && - location.protocol !== 'https:') { - reject(new ERR_NETWORK_IMPORT_DISALLOWED( - res.headers.location, - parsed.href, - 'cannot redirect to non-network location')); - return; - } - return PromisePrototypeThen( - PromiseResolve(fetchWithRedirects(location)), - (entry) => { - cacheForGET.set(parsed.href, entry); - fulfill(entry); - }); - } catch (e) { - dispose(); - reject(e); - } + headers: { Accept: '*/*' }, + }); + // Note that `once` is used here to handle `error` and that it hits the + // `finally` on network error/timeout. + const { 0: res } = await once(req, 'response'); + try { + const isRedirect = res.statusCode >= 300 && res.statusCode <= 303; + const hasLocation = ObjectPrototypeHasOwnProperty(res.headers, 'location'); + if (isRedirect && hasLocation) { + const location = new URL(res.headers.location, parsed); + if (location.protocol !== 'http:' && location.protocol !== 'https:') { + throw new ERR_NETWORK_IMPORT_DISALLOWED( + res.headers.location, + parsed.href, + 'cannot redirect to non-network location' + ); } + const entry = await fetchWithRedirects(location); + cacheForGET.set(parsed.href, entry); + return entry; + } + if (res.statusCode === 404) { + const err = new ERR_MODULE_NOT_FOUND(parsed.href, null); + err.message = `Cannot find module '${parsed.href}', HTTP 404`; + throw err; } if (res.statusCode > 303 || res.statusCode < 200) { - dispose(); - reject( - new ERR_NETWORK_IMPORT_BAD_RESPONSE( - parsed.href, - 'HTTP response returned status code of ' + res.statusCode)); - return; + throw new ERR_NETWORK_IMPORT_DISALLOWED( + res.headers.location, + parsed.href, + 'cannot redirect to non-network location'); } const { headers } = res; const contentType = headers['content-type']; if (!contentType) { - dispose(); - reject(new ERR_NETWORK_IMPORT_BAD_RESPONSE( + throw new ERR_NETWORK_IMPORT_BAD_RESPONSE( parsed.href, - 'the \'Content-Type\' header is required')); - return; + "the 'Content-Type' header is required" + ); } /** * @type {CacheEntry} @@ -158,58 +147,31 @@ function fetchWithRedirects(parsed) { const entry = { resolvedHREF: parsed.href, headers: { - 'content-type': res.headers['content-type'] + 'content-type': res.headers['content-type'], }, - body: new Promise((f, r) => { - const buffers = []; - let size = 0; + body: (async () => { let bodyStream = res; - let onError; if (res.headers['content-encoding'] === 'br') { - bodyStream = createBrotliDecompress(); - onError = function onError(error) { - bodyStream.close(); - dispose(); - reject(error); - r(error); - }; - res.on('error', onError); - res.pipe(bodyStream); - } else if (res.headers['content-encoding'] === 'gzip' || - res.headers['content-encoding'] === 'deflate') { - bodyStream = createUnzip(); - onError = function onError(error) { - bodyStream.close(); - dispose(); - reject(error); - r(error); - }; - res.on('error', onError); - res.pipe(bodyStream); - } else { - onError = function onError(error) { - dispose(); - reject(error); - r(error); - }; + bodyStream = compose(res, createBrotliDecompress()); + } else if ( + res.headers['content-encoding'] === 'gzip' || + res.headers['content-encoding'] === 'deflate' + ) { + bodyStream = compose(res, createUnzip()); } - bodyStream.on('error', onError); - bodyStream.on('data', (d) => { - ArrayPrototypePush(buffers, d); - size += d.length; - }); - bodyStream.on('end', () => { - const body = entry.body = /** @type {Buffer} */( - BufferConcat(buffers, size) - ); - f(body); - }); - }), + const buffers = await bodyStream.toArray(); + const body = BufferConcat(buffers); + entry.body = body; + return body; + })(), }; cacheForGET.set(parsed.href, entry); - fulfill(entry); - }); - }); + await entry.body; + return entry; + } finally { + req.destroy(); + } + })(); cacheForGET.set(parsed.href, result); return result; } @@ -226,8 +188,10 @@ allowList.addRange('127.0.0.1', '127.255.255.255'); */ async function isLocalAddress(hostname) { try { - if (StringPrototypeStartsWith(hostname, '[') && - StringPrototypeEndsWith(hostname, ']')) { + if ( + StringPrototypeStartsWith(hostname, '[') && + StringPrototypeEndsWith(hostname, ']') + ) { hostname = StringPrototypeSlice(hostname, 1, -1); } const addr = await dnsLookup(hostname, { verbatim: true }); @@ -275,5 +239,5 @@ function fetchModule(parsed, { parentURL }) { } module.exports = { - fetchModule: fetchModule + fetchModule: fetchModule, }; diff --git a/lib/internal/modules/esm/formats.js b/lib/internal/modules/esm/formats.js index 8fbe0f38446862..f9da01402e7f62 100644 --- a/lib/internal/modules/esm/formats.js +++ b/lib/internal/modules/esm/formats.js @@ -7,8 +7,6 @@ const { getOptionValue } = require('internal/options'); const experimentalWasmModules = getOptionValue('--experimental-wasm-modules'); -const experimentalSpecifierResolution = - getOptionValue('--experimental-specifier-resolution'); const extensionFormatMap = { '__proto__': null, @@ -31,6 +29,10 @@ if (experimentalWasmModules) { extensionFormatMap['.wasm'] = legacyExtensionFormatMap['.wasm'] = 'wasm'; } +/** + * @param {string} mime + * @returns {string | null} + */ function mimeToFormat(mime) { if ( RegExpPrototypeTest( @@ -43,17 +45,7 @@ function mimeToFormat(mime) { return null; } -let experimentalSpecifierResolutionWarned = false; function getLegacyExtensionFormat(ext) { - if ( - experimentalSpecifierResolution === 'node' && - !experimentalSpecifierResolutionWarned - ) { - process.emitWarning( - 'The Node.js specifier resolution in ESM is experimental.', - 'ExperimentalWarning'); - experimentalSpecifierResolutionWarned = true; - } return legacyExtensionFormatMap[ext]; } diff --git a/lib/internal/modules/esm/get_format.js b/lib/internal/modules/esm/get_format.js index 825fbae3f5931f..950a769227c03f 100644 --- a/lib/internal/modules/esm/get_format.js +++ b/lib/internal/modules/esm/get_format.js @@ -6,8 +6,9 @@ const { ObjectPrototypeHasOwnProperty, PromisePrototypeThen, PromiseResolve, + StringPrototypeSlice, } = primordials; -const { extname } = require('path'); +const { basename, extname, relative } = require('path'); const { getOptionValue } = require('internal/options'); const { fetchModule } = require('internal/modules/esm/fetch_module'); const { @@ -20,7 +21,7 @@ const experimentalNetworkImports = getOptionValue('--experimental-network-imports'); const experimentalSpecifierResolution = getOptionValue('--experimental-specifier-resolution'); -const { getPackageType } = require('internal/modules/esm/resolve'); +const { getPackageType, getPackageScopeConfig } = require('internal/modules/esm/resolve'); const { URL, fileURLToPath } = require('internal/url'); const { ERR_UNKNOWN_FILE_EXTENSION } = require('internal/errors').codes; @@ -32,6 +33,10 @@ const protocolHandlers = ObjectAssign(ObjectCreate(null), { 'node:'() { return 'builtin'; }, }); +/** + * @param {URL} parsed + * @returns {string | null} + */ function getDataProtocolModuleFormat(parsed) { const { 1: mime } = RegExpPrototypeExec( /^([^/]+\/[^;,]+)(?:[^,]*?)(;base64)?,/, @@ -41,8 +46,15 @@ function getDataProtocolModuleFormat(parsed) { return mimeToFormat(mime); } +/** + * @param {URL} url + * @param {{parentURL: string}} context + * @param {boolean} ignoreErrors + * @returns {string} + */ function getFileProtocolModuleFormat(url, context, ignoreErrors) { - const ext = extname(url.pathname); + const filepath = fileURLToPath(url); + const ext = extname(filepath); if (ext === '.js') { return getPackageType(url) === 'module' ? 'module' : 'commonjs'; } @@ -53,12 +65,29 @@ function getFileProtocolModuleFormat(url, context, ignoreErrors) { if (experimentalSpecifierResolution !== 'node') { // Explicit undefined return indicates load hook should rerun format check if (ignoreErrors) return undefined; - throw new ERR_UNKNOWN_FILE_EXTENSION(ext, fileURLToPath(url)); + let suggestion = ''; + if (getPackageType(url) === 'module' && ext === '') { + const config = getPackageScopeConfig(url); + const fileBasename = basename(filepath); + const relativePath = StringPrototypeSlice(relative(config.pjsonPath, filepath), 1); + suggestion = 'Loading extensionless files is not supported inside of ' + + '"type":"module" package.json contexts. The package.json file ' + + `${config.pjsonPath} caused this "type":"module" context. Try ` + + `changing ${filepath} to have a file extension. Note the "bin" ` + + 'field of package.json can point to a file with an extension, for example ' + + `{"type":"module","bin":{"${fileBasename}":"${relativePath}.js"}}`; + } + throw new ERR_UNKNOWN_FILE_EXTENSION(ext, filepath, suggestion); } return getLegacyExtensionFormat(ext) ?? null; } +/** + * @param {URL} url + * @param {{parentURL: string}} context + * @returns {Promise | undefined} only works when enabled + */ function getHttpProtocolModuleFormat(url, context) { if (experimentalNetworkImports) { return PromisePrototypeThen( @@ -70,6 +99,11 @@ function getHttpProtocolModuleFormat(url, context) { } } +/** + * @param {URL | URL['href']} url + * @param {{parentURL: string}} context + * @returns {Promise | string | undefined} only works when enabled + */ function defaultGetFormatWithoutErrors(url, context) { const parsed = new URL(url); if (!ObjectPrototypeHasOwnProperty(protocolHandlers, parsed.protocol)) @@ -77,6 +111,11 @@ function defaultGetFormatWithoutErrors(url, context) { return protocolHandlers[parsed.protocol](parsed, context, true); } +/** + * @param {URL | URL['href']} url + * @param {{parentURL: string}} context + * @returns {Promise | string | undefined} only works when enabled + */ function defaultGetFormat(url, context) { const parsed = new URL(url); return ObjectPrototypeHasOwnProperty(protocolHandlers, parsed.protocol) ? diff --git a/lib/internal/modules/esm/initialize_import_meta.js b/lib/internal/modules/esm/initialize_import_meta.js index cb9fa23f966f76..f1daabbb6425aa 100644 --- a/lib/internal/modules/esm/initialize_import_meta.js +++ b/lib/internal/modules/esm/initialize_import_meta.js @@ -3,12 +3,9 @@ const { getOptionValue } = require('internal/options'); const experimentalImportMetaResolve = getOptionValue('--experimental-import-meta-resolve'); -const { fetchModule } = require('internal/modules/esm/fetch_module'); -const { URL } = require('internal/url'); const { PromisePrototypeThen, PromiseReject, - StringPrototypeStartsWith, } = primordials; const asyncESM = require('internal/process/esm_loader'); @@ -24,6 +21,10 @@ function createImportMetaResolve(defaultParentUrl) { }; } +/** + * @param {object} meta + * @param {{url: string}} context + */ function initializeImportMeta(meta, context) { let url = context.url; @@ -32,14 +33,7 @@ function initializeImportMeta(meta, context) { meta.resolve = createImportMetaResolve(url); } - if ( - StringPrototypeStartsWith(url, 'http:') || - StringPrototypeStartsWith(url, 'https:') - ) { - // The request & response have already settled, so they are in fetchModule's - // cache, in which case, fetchModule returns immediately and synchronously - url = fetchModule(new URL(url), context).resolvedHREF; - } + url = asyncESM.esmLoader.getBaseURL(url); meta.url = url; } diff --git a/lib/internal/modules/esm/loader.js b/lib/internal/modules/esm/loader.js index 61e609d9ad6c62..39f8ebca59e9df 100644 --- a/lib/internal/modules/esm/loader.js +++ b/lib/internal/modules/esm/loader.js @@ -17,11 +17,13 @@ const { RegExpPrototypeExec, SafeArrayIterator, SafeWeakMap, + StringPrototypeStartsWith, globalThis, } = primordials; const { MessageChannel } = require('internal/worker/io'); const { + ERR_INTERNAL_ASSERTION, ERR_INVALID_ARG_TYPE, ERR_INVALID_ARG_VALUE, ERR_INVALID_RETURN_PROPERTY_VALUE, @@ -29,6 +31,7 @@ const { ERR_UNKNOWN_MODULE_FORMAT } = require('internal/errors').codes; const { pathToFileURL, isURLInstance, URL } = require('internal/url'); +const { emitExperimentalWarning } = require('internal/util'); const { isAnyArrayBuffer, isArrayBufferView, @@ -47,6 +50,16 @@ const { defaultLoad } = require('internal/modules/esm/load'); const { translators } = require( 'internal/modules/esm/translators'); const { getOptionValue } = require('internal/options'); +const { + fetchModule, +} = require('internal/modules/esm/fetch_module'); + + +/** + * Prevent the specifier resolution warning from being printed twice + */ +let emittedSpecifierResolutionWarning = false; + /** * An ESMLoader instance is used as the main entry point for loading ES modules. @@ -102,6 +115,22 @@ class ESMLoader { */ translators = translators; + constructor() { + if (getOptionValue('--experimental-loader')) { + emitExperimentalWarning('Custom ESM Loaders'); + } + if (getOptionValue('--experimental-network-imports')) { + emitExperimentalWarning('Network Imports'); + } + if (getOptionValue('--experimental-specifier-resolution') === 'node' && !emittedSpecifierResolutionWarning) { + process.emitWarning( + 'The Node.js specifier resolution flag is experimental. It could change or be removed at any time.', + 'ExperimentalWarning' + ); + emittedSpecifierResolutionWarning = true; + } + } + static pluckHooks({ globalPreload, resolve, @@ -209,7 +238,9 @@ class ESMLoader { const module = new ModuleWrap(url, undefined, source, 0, 0); callbackMap.set(module, { importModuleDynamically: (specifier, { url }, importAssertions) => { - return this.import(specifier, url, importAssertions); + return this.import(specifier, + this.getBaseURL(url), + importAssertions); } }); @@ -225,6 +256,43 @@ class ESMLoader { }; } + /** + * Returns the url to use for the resolution of a given cache key url + * These are not guaranteed to be the same. + * + * In WHATWG HTTP spec for ESM the cache key is the non-I/O bound + * synchronous resolution using only string operations + * ~= resolveImportMap(new URL(specifier, importerHREF)) + * + * The url used for subsequent resolution is the response URL after + * all redirects have been resolved. + * + * https://example.com/foo redirecting to https://example.com/bar + * would have a cache key of https://example.com/foo and baseURL + * of https://example.com/bar + * + * MUST BE SYNCHRONOUS for import.meta initialization + * MUST BE CALLED AFTER receiving the url body due to I/O + * @param {string} url + * @returns {string} + */ + getBaseURL(url) { + if ( + StringPrototypeStartsWith(url, 'http:') || + StringPrototypeStartsWith(url, 'https:') + ) { + // The request & response have already settled, so they are in + // fetchModule's cache, in which case, fetchModule returns + // immediately and synchronously + url = fetchModule(new URL(url), { parentURL: url }).resolvedHREF; + // This should only occur if the module hasn't been fetched yet + if (typeof url !== 'string') { + throw new ERR_INTERNAL_ASSERTION(`Base url for module ${url} not loaded.`); + } + } + return url; + } + /** * Get a (possibly still pending) module job from the cache, * or create one and return its Promise. @@ -466,7 +534,8 @@ class ESMLoader { globalThis, // Param getBuiltin (builtinName) => { - if (NativeModule.canBeRequiredByUsers(builtinName)) { + if (NativeModule.canBeRequiredByUsers(builtinName) && + NativeModule.canBeRequiredWithoutScheme(builtinName)) { return require(builtinName); } throw new ERR_INVALID_ARG_VALUE('builtinName', builtinName); diff --git a/lib/internal/modules/esm/module_job.js b/lib/internal/modules/esm/module_job.js index fd1c6166330e76..e012eebc4ac971 100644 --- a/lib/internal/modules/esm/module_job.js +++ b/lib/internal/modules/esm/module_job.js @@ -76,7 +76,8 @@ class ModuleJob { // these `link` callbacks depending on each other. const dependencyJobs = []; const promises = this.module.link(async (specifier, assertions) => { - const jobPromise = this.loader.getModuleJob(specifier, url, assertions); + const baseURL = this.loader.getBaseURL(url); + const jobPromise = this.loader.getModuleJob(specifier, baseURL, assertions); ArrayPrototypePush(dependencyJobs, jobPromise); const job = await jobPromise; return job.modulePromise; diff --git a/lib/internal/modules/esm/resolve.js b/lib/internal/modules/esm/resolve.js index cc05536af930a4..f1298e58dc445c 100644 --- a/lib/internal/modules/esm/resolve.js +++ b/lib/internal/modules/esm/resolve.js @@ -79,10 +79,11 @@ const DEFAULT_CONDITIONS_SET = new SafeSet(DEFAULT_CONDITIONS); * @typedef {string | string[] | Record} Exports * @typedef {'module' | 'commonjs'} PackageType * @typedef {{ - * exports?: ExportConfig; - * name?: string; - * main?: string; - * type?: PackageType; + * pjsonPath: string, + * exports?: ExportConfig, + * name?: string, + * main?: string, + * type?: PackageType, * }} PackageConfig */ @@ -374,18 +375,24 @@ function finalizeResolution(resolved, base, preserveSymlinks) { resolved.pathname, 'must not include encoded "/" or "\\" characters', fileURLToPath(base)); - const path = fileURLToPath(resolved); + let path = fileURLToPath(resolved); if (getOptionValue('--experimental-specifier-resolution') === 'node') { let file = resolveExtensionsWithTryExactName(resolved); - if (file !== undefined) return file; - if (!StringPrototypeEndsWith(path, '/')) { - file = resolveDirectoryEntry(new URL(`${resolved}/`)); - if (file !== undefined) return file; - } else { - return resolveDirectoryEntry(resolved) || resolved; + + // Directory + if (file === undefined) { + file = StringPrototypeEndsWith(path, '/') ? + (resolveDirectoryEntry(resolved) || resolved) : resolveDirectoryEntry(new URL(`${resolved}/`)); + + if (file === resolved) return file; + + if (file === undefined) { + throw new ERR_MODULE_NOT_FOUND( + resolved.pathname, fileURLToPath(base), 'module'); + } } - throw new ERR_MODULE_NOT_FOUND( - resolved.pathname, fileURLToPath(base), 'module'); + + path = file; } const stats = tryStatSync(StringPrototypeEndsWith(path, '/') ? @@ -845,8 +852,10 @@ function parsePackageName(specifier, base) { * @returns {resolved: URL, format? : string} */ function packageResolve(specifier, base, conditions) { - if (NativeModule.canBeRequiredByUsers(specifier)) + if (NativeModule.canBeRequiredByUsers(specifier) && + NativeModule.canBeRequiredWithoutScheme(specifier)) { return new URL('node:' + specifier); + } const { packageName, packageSubpath, isScoped } = parsePackageName(specifier, base); @@ -1028,7 +1037,8 @@ function checkIfDisallowedImport(specifier, parsed, parsedParentURL) { return { url: parsed.href }; } - if (NativeModule.canBeRequiredByUsers(specifier)) { + if (NativeModule.canBeRequiredByUsers(specifier) && + NativeModule.canBeRequiredWithoutScheme(specifier)) { throw new ERR_NETWORK_IMPORT_DISALLOWED( specifier, parentURL, @@ -1125,7 +1135,7 @@ async function defaultResolve(specifier, context = {}, defaultResolveUnused) { ) ) ) { - return { url: specifier }; + return { url: parsed.href }; } } catch { // Ignore exception diff --git a/lib/internal/modules/esm/translators.js b/lib/internal/modules/esm/translators.js index da95703faad9fd..d7f4c7edec63d3 100644 --- a/lib/internal/modules/esm/translators.js +++ b/lib/internal/modules/esm/translators.js @@ -103,7 +103,9 @@ function errPath(url) { } async function importModuleDynamically(specifier, { url }, assertions) { - return asyncESM.esmLoader.import(specifier, url, assertions); + return asyncESM.esmLoader.import(specifier, + asyncESM.esmLoader.getBaseURL(url), + assertions); } // Strategy for loading a standard JavaScript module. diff --git a/lib/internal/options.js b/lib/internal/options.js index 01b334d4ec5614..4d92ad681a1207 100644 --- a/lib/internal/options.js +++ b/lib/internal/options.js @@ -36,6 +36,11 @@ function getEmbedderOptions() { return embedderOptions; } +function refreshOptions() { + optionsMap = undefined; + aliasesMap = undefined; +} + function getOptionValue(optionName) { const options = getCLIOptionsFromBinding(); if (optionName.startsWith('--no-')) { @@ -68,5 +73,6 @@ module.exports = { }, getOptionValue, getAllowUnauthorized, - getEmbedderOptions + getEmbedderOptions, + refreshOptions }; diff --git a/lib/internal/perf/observe.js b/lib/internal/perf/observe.js index 2f9506ed28e3eb..18fc10eb789e01 100644 --- a/lib/internal/perf/observe.js +++ b/lib/internal/perf/observe.js @@ -24,6 +24,8 @@ const { NODE_PERFORMANCE_ENTRY_TYPE_GC, NODE_PERFORMANCE_ENTRY_TYPE_HTTP2, NODE_PERFORMANCE_ENTRY_TYPE_HTTP, + NODE_PERFORMANCE_ENTRY_TYPE_NET, + NODE_PERFORMANCE_ENTRY_TYPE_DNS, }, installGarbageCollectionTracking, observerCounts, @@ -61,13 +63,9 @@ const { const { inspect } = require('util'); -const kBuffer = Symbol('kBuffer'); -const kCallback = Symbol('kCallback'); const kDispatch = Symbol('kDispatch'); -const kEntryTypes = Symbol('kEntryTypes'); const kMaybeBuffer = Symbol('kMaybeBuffer'); const kDeprecatedFields = Symbol('kDeprecatedFields'); -const kType = Symbol('kType'); const kDeprecationMessage = 'Custom PerformanceEntry accessors are deprecated. ' + @@ -79,12 +77,14 @@ const kTypeMultiple = 1; let gcTrackingInstalled = false; const kSupportedEntryTypes = ObjectFreeze([ + 'dns', 'function', 'gc', 'http', 'http2', 'mark', 'measure', + 'net', ]); // Performance timeline entry Buffers @@ -118,6 +118,8 @@ function getObserverType(type) { case 'gc': return NODE_PERFORMANCE_ENTRY_TYPE_GC; case 'http2': return NODE_PERFORMANCE_ENTRY_TYPE_HTTP2; case 'http': return NODE_PERFORMANCE_ENTRY_TYPE_HTTP; + case 'net': return NODE_PERFORMANCE_ENTRY_TYPE_NET; + case 'dns': return NODE_PERFORMANCE_ENTRY_TYPE_DNS; } } @@ -151,20 +153,22 @@ function maybeIncrementObserverCount(type) { } class PerformanceObserverEntryList { + #buffer = []; + constructor(entries) { - this[kBuffer] = ArrayPrototypeSort(entries, (first, second) => { + this.#buffer = ArrayPrototypeSort(entries, (first, second) => { return first.startTime - second.startTime; }); } getEntries() { - return ArrayPrototypeSlice(this[kBuffer]); + return ArrayPrototypeSlice(this.#buffer); } getEntriesByType(type) { type = `${type}`; return ArrayPrototypeFilter( - this[kBuffer], + this.#buffer, (entry) => entry.entryType === type); } @@ -172,11 +176,11 @@ class PerformanceObserverEntryList { name = `${name}`; if (type != null /** not nullish */) { return ArrayPrototypeFilter( - this[kBuffer], + this.#buffer, (entry) => entry.name === name && entry.entryType === type); } return ArrayPrototypeFilter( - this[kBuffer], + this.#buffer, (entry) => entry.name === name); } @@ -188,20 +192,19 @@ class PerformanceObserverEntryList { depth: options.depth == null ? null : options.depth - 1 }; - return `PerformanceObserverEntryList ${inspect(this[kBuffer], opts)}`; + return `PerformanceObserverEntryList ${inspect(this.#buffer, opts)}`; } } class PerformanceObserver { + #buffer = []; + #entryTypes = new SafeSet(); + #type; + #callback; + constructor(callback) { - // TODO(joyeecheung): V8 snapshot does not support instance member - // initializers for now: - // https://bugs.chromium.org/p/v8/issues/detail?id=10704 - this[kBuffer] = []; - this[kEntryTypes] = new SafeSet(); - this[kType] = undefined; validateFunction(callback, 'callback'); - this[kCallback] = callback; + this.#callback = callback; } observe(options = {}) { @@ -219,10 +222,10 @@ class PerformanceObserver { 'options.entryTypes can not set with ' + 'options.type together'); - switch (this[kType]) { + switch (this.#type) { case undefined: - if (entryTypes !== undefined) this[kType] = kTypeMultiple; - if (type !== undefined) this[kType] = kTypeSingle; + if (entryTypes !== undefined) this.#type = kTypeMultiple; + if (type !== undefined) this.#type = kTypeSingle; break; case kTypeSingle: if (entryTypes !== undefined) @@ -238,53 +241,53 @@ class PerformanceObserver { break; } - if (this[kType] === kTypeMultiple) { + if (this.#type === kTypeMultiple) { if (!ArrayIsArray(entryTypes)) { throw new ERR_INVALID_ARG_TYPE( 'options.entryTypes', 'string[]', entryTypes); } - maybeDecrementObserverCounts(this[kEntryTypes]); - this[kEntryTypes].clear(); + maybeDecrementObserverCounts(this.#entryTypes); + this.#entryTypes.clear(); for (let n = 0; n < entryTypes.length; n++) { if (ArrayPrototypeIncludes(kSupportedEntryTypes, entryTypes[n])) { - this[kEntryTypes].add(entryTypes[n]); + this.#entryTypes.add(entryTypes[n]); maybeIncrementObserverCount(entryTypes[n]); } } } else { if (!ArrayPrototypeIncludes(kSupportedEntryTypes, type)) return; - this[kEntryTypes].add(type); + this.#entryTypes.add(type); maybeIncrementObserverCount(type); if (buffered) { const entries = filterBufferMapByNameAndType(undefined, type); - ArrayPrototypePushApply(this[kBuffer], entries); + ArrayPrototypePushApply(this.#buffer, entries); kPending.add(this); if (kPending.size) queuePending(); } } - if (this[kEntryTypes].size) + if (this.#entryTypes.size) kObservers.add(this); else this.disconnect(); } disconnect() { - maybeDecrementObserverCounts(this[kEntryTypes]); + maybeDecrementObserverCounts(this.#entryTypes); kObservers.delete(this); kPending.delete(this); - this[kBuffer] = []; - this[kEntryTypes].clear(); - this[kType] = undefined; + this.#buffer = []; + this.#entryTypes.clear(); + this.#type = undefined; } takeRecords() { - const list = this[kBuffer]; - this[kBuffer] = []; + const list = this.#buffer; + this.#buffer = []; return list; } @@ -293,17 +296,17 @@ class PerformanceObserver { } [kMaybeBuffer](entry) { - if (!this[kEntryTypes].has(entry.entryType)) + if (!this.#entryTypes.has(entry.entryType)) return; - ArrayPrototypePush(this[kBuffer], entry); + ArrayPrototypePush(this.#buffer, entry); kPending.add(this); if (kPending.size) queuePending(); } [kDispatch]() { - this[kCallback](new PerformanceObserverEntryList(this.takeRecords()), - this); + this.#callback(new PerformanceObserverEntryList(this.takeRecords()), + this); } [kInspect](depth, options) { @@ -317,8 +320,8 @@ class PerformanceObserver { return `PerformanceObserver ${inspect({ connected: kObservers.has(this), pending: kPending.has(this), - entryTypes: ArrayFrom(this[kEntryTypes]), - buffer: this[kBuffer], + entryTypes: ArrayFrom(this.#entryTypes), + buffer: this.#buffer, }, opts)}`; } } @@ -443,6 +446,32 @@ function hasObserver(type) { return observerCounts[observerType] > 0; } + +function startPerf(target, key, context = {}) { + if (hasObserver(context.type)) { + target[key] = { + ...context, + startTime: process.hrtime(), + }; + } +} + +function stopPerf(target, key, context = {}) { + const ctx = target[key]; + if (ctx && hasObserver(ctx.type)) { + const startTime = ctx.startTime; + const diff = process.hrtime(startTime); + const entry = new InternalPerformanceEntry( + ctx.name, + ctx.type, + startTime[0] * 1000 + startTime[1] / 1e6, + diff[0] * 1000 + diff[1] / 1e6, + { ...ctx.detail, ...context.detail }, + ); + enqueue(entry); + } +} + module.exports = { PerformanceObserver, PerformanceObserverEntryList, @@ -450,4 +479,6 @@ module.exports = { hasObserver, clearEntriesFromBuffer, filterBufferMapByNameAndType, + startPerf, + stopPerf, }; diff --git a/lib/internal/perf/performance.js b/lib/internal/perf/performance.js index 38dac0ee32397c..20603fa382e702 100644 --- a/lib/internal/perf/performance.js +++ b/lib/internal/perf/performance.js @@ -9,6 +9,7 @@ const { const { codes: { ERR_ILLEGAL_CONSTRUCTOR, + ERR_MISSING_ARGS } } = require('internal/errors'); @@ -86,16 +87,18 @@ function getEntries() { } function getEntriesByName(name) { - if (name !== undefined) { - name = `${name}`; + if (arguments.length === 0) { + throw new ERR_MISSING_ARGS('name'); } + name = `${name}`; return filterBufferMapByNameAndType(name, undefined); } function getEntriesByType(type) { - if (type !== undefined) { - type = `${type}`; + if (arguments.length === 0) { + throw new ERR_MISSING_ARGS('type'); } + type = `${type}`; return filterBufferMapByNameAndType(undefined, type); } diff --git a/lib/internal/process/esm_loader.js b/lib/internal/process/esm_loader.js index 73385a85b4e106..20021134ce40f3 100644 --- a/lib/internal/process/esm_loader.js +++ b/lib/internal/process/esm_loader.js @@ -55,9 +55,6 @@ async function initializeLoader() { if (!customLoaders.length) return; - const { emitExperimentalWarning } = require('internal/util'); - emitExperimentalWarning('--experimental-loader'); - let cwd; try { cwd = process.cwd() + '/'; diff --git a/lib/internal/readline/interface.js b/lib/internal/readline/interface.js index b5a5455ed3da85..47e5ca580ab317 100644 --- a/lib/internal/readline/interface.js +++ b/lib/internal/readline/interface.js @@ -38,7 +38,10 @@ const { const { codes } = require('internal/errors'); -const { ERR_INVALID_ARG_VALUE } = codes; +const { + ERR_INVALID_ARG_VALUE, + ERR_USE_AFTER_CLOSE, +} = codes; const { validateAbortSignal, validateArray, @@ -398,6 +401,9 @@ class Interface extends InterfaceConstructor { } question(query, cb) { + if (this.closed) { + throw new ERR_USE_AFTER_CLOSE('readline'); + } if (this[kQuestionCallback]) { this.prompt(); } else { diff --git a/lib/internal/source_map/prepare_stack_trace.js b/lib/internal/source_map/prepare_stack_trace.js index 9492de2283322f..551d3d50fee4b1 100644 --- a/lib/internal/source_map/prepare_stack_trace.js +++ b/lib/internal/source_map/prepare_stack_trace.js @@ -88,8 +88,9 @@ const prepareStackTrace = (globalThis, error, trace) => { } // Construct call site name based on: v8.dev/docs/stack-trace-api: const fnName = t.getFunctionName() ?? t.getMethodName(); - const originalName = `${t.getTypeName() !== 'global' ? - `${t.getTypeName()}.` : ''}${fnName || ''}`; + const typeName = t.getTypeName(); + const namePrefix = typeName !== null && typeName !== 'global' ? `${typeName}.` : ''; + const originalName = `${namePrefix}${fnName || ''}`; // The original call site may have a different symbol name // associated with it, use it: const prefix = (name && name !== originalName) ? @@ -106,7 +107,7 @@ const prepareStackTrace = (globalThis, error, trace) => { } } } catch (err) { - debug(err.stack); + debug(err); } return `${str}${t}`; }), ''); diff --git a/lib/internal/source_map/source_map_cache.js b/lib/internal/source_map/source_map_cache.js index c0de6aeb51868f..e3721d024d9fb1 100644 --- a/lib/internal/source_map/source_map_cache.js +++ b/lib/internal/source_map/source_map_cache.js @@ -79,7 +79,7 @@ function maybeCacheSourceMap(filename, content, cjsModuleInstance) { } catch (err) { // This is most likely an [eval]-wrapper, which is currently not // supported. - debug(err.stack); + debug(err); return; } const match = StringPrototypeMatch( @@ -119,7 +119,7 @@ function dataFromUrl(sourceURL, sourceMappingURL) { return null; } } catch (err) { - debug(err.stack); + debug(err); // If no scheme is present, we assume we are dealing with a file path. const mapURL = new URL(sourceMappingURL, sourceURL).href; return sourceMapFromFile(mapURL); @@ -144,7 +144,7 @@ function sourceMapFromFile(mapURL) { const data = JSONParse(content); return sourcesToAbsolute(mapURL, data); } catch (err) { - debug(err.stack); + debug(err); return null; } } @@ -163,7 +163,7 @@ function sourceMapFromDataUrl(sourceURL, url) { const parsedData = JSONParse(decodedData); return sourcesToAbsolute(sourceURL, parsedData); } catch (err) { - debug(err.stack); + debug(err); return null; } } else { diff --git a/lib/internal/streams/destroy.js b/lib/internal/streams/destroy.js index efa09e05eafef0..10f5471e21d3eb 100644 --- a/lib/internal/streams/destroy.js +++ b/lib/internal/streams/destroy.js @@ -106,20 +106,7 @@ function _destroy(self, err, cb) { } } try { - const result = self._destroy(err || null, onDestroy); - if (result != null) { - const then = result.then; - if (typeof then === 'function') { - then.call( - result, - function() { - process.nextTick(onDestroy, null); - }, - function(err) { - process.nextTick(onDestroy, err); - }); - } - } + self._destroy(err || null, onDestroy); } catch (err) { onDestroy(err); } @@ -285,24 +272,7 @@ function constructNT(stream) { } try { - const result = stream._construct(onConstruct); - if (result != null) { - const then = result.then; - if (typeof then === 'function') { - then.call( - result, - function() { - if (!called) { - process.nextTick(onConstruct, null); - } - }, - function(err) { - if (!called) { - process.nextTick(onConstruct, err); - } - }); - } - } + stream._construct(onConstruct); } catch (err) { onConstruct(err); } diff --git a/lib/internal/streams/operators.js b/lib/internal/streams/operators.js index c48a058303bb9a..3d792e3b495dc1 100644 --- a/lib/internal/streams/operators.js +++ b/lib/internal/streams/operators.js @@ -186,7 +186,7 @@ function asIndexedPairs(options = undefined) { }.call(this); } -async function some(fn, options) { +async function some(fn, options = undefined) { // eslint-disable-next-line no-unused-vars for await (const unused of filter.call(this, fn, options)) { return true; @@ -194,7 +194,7 @@ async function some(fn, options) { return false; } -async function every(fn, options) { +async function every(fn, options = undefined) { if (typeof fn !== 'function') { throw new ERR_INVALID_ARG_TYPE( 'fn', ['Function', 'AsyncFunction'], fn); diff --git a/lib/internal/streams/pipeline.js b/lib/internal/streams/pipeline.js index 36e1aebdd10e10..a3f69c2099ace1 100644 --- a/lib/internal/streams/pipeline.js +++ b/lib/internal/streams/pipeline.js @@ -288,7 +288,9 @@ function pipelineImpl(streams, callback, opts) { then.call(ret, (val) => { value = val; - pt.write(val); + if (val != null) { + pt.write(val); + } if (end) { pt.end(); } diff --git a/lib/internal/streams/readable.js b/lib/internal/streams/readable.js index 3fcd373cb1c708..8081e8fde13e0c 100644 --- a/lib/internal/streams/readable.js +++ b/lib/internal/streams/readable.js @@ -493,18 +493,7 @@ Readable.prototype.read = function(n) { // Call internal read method try { - const result = this._read(state.highWaterMark); - if (result != null) { - const then = result.then; - if (typeof then === 'function') { - then.call( - result, - nop, - function(err) { - errorOrDestroy(this, err); - }); - } - } + this._read(state.highWaterMark); } catch (err) { errorOrDestroy(this, err); } @@ -1122,7 +1111,7 @@ async function* createAsyncIterator(stream, options) { stream.on('readable', next); let error; - eos(stream, { writable: false }, (err) => { + const cleanup = eos(stream, { writable: false }, (err) => { error = err ? aggregateTwoErrors(error, err) : null; callback(); callback = nop; @@ -1150,6 +1139,9 @@ async function* createAsyncIterator(stream, options) { (error === undefined || stream._readableState.autoDestroy) ) { destroyImpl.destroyer(stream, null); + } else { + stream.off('readable', next); + cleanup(); } } } diff --git a/lib/internal/streams/transform.js b/lib/internal/streams/transform.js index cbd23185fad291..fdac76e4062b4b 100644 --- a/lib/internal/streams/transform.js +++ b/lib/internal/streams/transform.js @@ -107,10 +107,8 @@ function Transform(options) { } function final(cb) { - let called = false; if (typeof this._flush === 'function' && !this.destroyed) { - const result = this._flush((er, data) => { - called = true; + this._flush((er, data) => { if (er) { if (cb) { cb(er); @@ -128,33 +126,6 @@ function final(cb) { cb(); } }); - if (result !== undefined && result !== null) { - try { - const then = result.then; - if (typeof then === 'function') { - then.call( - result, - (data) => { - if (called) - return; - if (data != null) - this.push(data); - this.push(null); - if (cb) - process.nextTick(cb); - }, - (err) => { - if (cb) { - process.nextTick(cb, err); - } else { - process.nextTick(() => this.destroy(err)); - } - }); - } - } catch (err) { - process.nextTick(() => this.destroy(err)); - } - } } else { this.push(null); if (cb) { @@ -180,9 +151,7 @@ Transform.prototype._write = function(chunk, encoding, callback) { const wState = this._writableState; const length = rState.length; - let called = false; - const result = this._transform(chunk, encoding, (err, val) => { - called = true; + this._transform(chunk, encoding, (err, val) => { if (err) { callback(err); return; @@ -204,38 +173,6 @@ Transform.prototype._write = function(chunk, encoding, callback) { this[kCallback] = callback; } }); - if (result !== undefined && result != null) { - try { - const then = result.then; - if (typeof then === 'function') { - then.call( - result, - (val) => { - if (called) - return; - - if (val != null) { - this.push(val); - } - - if ( - wState.ended || - length === rState.length || - rState.length < rState.highWaterMark || - rState.length === 0) { - process.nextTick(callback); - } else { - this[kCallback] = callback; - } - }, - (err) => { - process.nextTick(callback, err); - }); - } - } catch (err) { - process.nextTick(callback, err); - } - } }; Transform.prototype._read = function() { diff --git a/lib/internal/streams/writable.js b/lib/internal/streams/writable.js index c8f1cd62861b4d..5ee0cbc969f2ad 100644 --- a/lib/internal/streams/writable.js +++ b/lib/internal/streams/writable.js @@ -693,24 +693,7 @@ function callFinal(stream, state) { state.pendingcb++; try { - const result = stream._final(onFinish); - if (result != null) { - const then = result.then; - if (typeof then === 'function') { - then.call( - result, - function() { - if (!called) { - process.nextTick(onFinish, null); - } - }, - function(err) { - if (!called) { - process.nextTick(onFinish, err); - } - }); - } - } + stream._final(onFinish); } catch (err) { onFinish(err); } diff --git a/lib/internal/test_runner/harness.js b/lib/internal/test_runner/harness.js new file mode 100644 index 00000000000000..66544d91522495 --- /dev/null +++ b/lib/internal/test_runner/harness.js @@ -0,0 +1,131 @@ +'use strict'; +const { FunctionPrototypeBind, SafeMap } = primordials; +const { + createHook, + executionAsyncId, +} = require('async_hooks'); +const { + codes: { + ERR_TEST_FAILURE, + }, +} = require('internal/errors'); +const { Test } = require('internal/test_runner/test'); + +function createProcessEventHandler(eventName, rootTest, testResources) { + return (err) => { + // Check if this error is coming from a test. If it is, fail the test. + const test = testResources.get(executionAsyncId()); + + if (test !== undefined) { + if (test.finished) { + // If the test is already finished, report this as a top level + // diagnostic since this is a malformed test. + const msg = `Warning: Test "${test.name}" generated asynchronous ` + + 'activity after the test ended. This activity created the error ' + + `"${err}" and would have caused the test to fail, but instead ` + + `triggered an ${eventName} event.`; + + rootTest.diagnostic(msg); + return; + } + + test.fail(new ERR_TEST_FAILURE(err, eventName)); + test.postRun(); + } + }; +} + +function setup(root) { + const testResources = new SafeMap(); + const hook = createHook({ + init(asyncId, type, triggerAsyncId, resource) { + if (resource instanceof Test) { + testResources.set(asyncId, resource); + return; + } + + const parent = testResources.get(triggerAsyncId); + + if (parent !== undefined) { + testResources.set(asyncId, parent); + } + }, + destroy(asyncId) { + testResources.delete(asyncId); + } + }); + + hook.enable(); + + const exceptionHandler = + createProcessEventHandler('uncaughtException', root, testResources); + const rejectionHandler = + createProcessEventHandler('unhandledRejection', root, testResources); + + process.on('uncaughtException', exceptionHandler); + process.on('unhandledRejection', rejectionHandler); + process.on('beforeExit', () => { + root.postRun(); + + let passCount = 0; + let failCount = 0; + let skipCount = 0; + let todoCount = 0; + + for (let i = 0; i < root.subtests.length; i++) { + const test = root.subtests[i]; + + // Check SKIP and TODO tests first, as those should not be counted as + // failures. + if (test.skipped) { + skipCount++; + } else if (test.isTodo) { + todoCount++; + } else if (!test.passed) { + failCount++; + } else { + passCount++; + } + } + + root.reporter.plan(root.indent, root.subtests.length); + + for (let i = 0; i < root.diagnostics.length; i++) { + root.reporter.diagnostic(root.indent, root.diagnostics[i]); + } + + root.reporter.diagnostic(root.indent, `tests ${root.subtests.length}`); + root.reporter.diagnostic(root.indent, `pass ${passCount}`); + root.reporter.diagnostic(root.indent, `fail ${failCount}`); + root.reporter.diagnostic(root.indent, `skipped ${skipCount}`); + root.reporter.diagnostic(root.indent, `todo ${todoCount}`); + root.reporter.diagnostic(root.indent, `duration_ms ${process.uptime()}`); + + root.reporter.push(null); + hook.disable(); + process.removeListener('unhandledRejection', rejectionHandler); + process.removeListener('uncaughtException', exceptionHandler); + + if (failCount > 0) { + process.exitCode = 1; + } + }); + + root.reporter.pipe(process.stdout); + root.reporter.version(); +} + +function test(name, options, fn) { + // If this is the first test encountered, bootstrap the test harness. + if (this.subtests.length === 0) { + setup(this); + } + + const subtest = this.createSubtest(name, options, fn); + + return subtest.start(); +} + +const root = new Test({ name: '' }); + +module.exports = FunctionPrototypeBind(test, root); diff --git a/lib/internal/test_runner/tap_stream.js b/lib/internal/test_runner/tap_stream.js new file mode 100644 index 00000000000000..a6bfbb3367cd79 --- /dev/null +++ b/lib/internal/test_runner/tap_stream.js @@ -0,0 +1,220 @@ +'use strict'; +const { + ArrayPrototypeForEach, + ArrayPrototypeJoin, + ArrayPrototypePush, + ArrayPrototypeShift, + ObjectEntries, + StringPrototypeReplaceAll, + StringPrototypeSplit, + RegExpPrototypeSymbolReplace, +} = primordials; +const { inspectWithNoCustomRetry } = require('internal/errors'); +const Readable = require('internal/streams/readable'); +const { isError } = require('internal/util'); +const kFrameStartRegExp = /^ {4}at /; +const kLineBreakRegExp = /\n|\r\n/; +const inspectOptions = { colors: false, breakLength: Infinity }; +let testModule; // Lazy loaded due to circular dependency. + +function lazyLoadTest() { + testModule ??= require('internal/test_runner/test'); + + return testModule; +} + +class TapStream extends Readable { + #buffer; + #canPush; + + constructor() { + super(); + this.#buffer = []; + this.#canPush = true; + } + + _read() { + this.#canPush = true; + + while (this.#buffer.length > 0) { + const line = ArrayPrototypeShift(this.#buffer); + + if (!this.#tryPush(line)) { + return; + } + } + } + + bail(message) { + this.#tryPush(`Bail out!${message ? ` ${tapEscape(message)}` : ''}\n`); + } + + fail(indent, testNumber, description, directive) { + this.#test(indent, testNumber, 'not ok', description, directive); + } + + ok(indent, testNumber, description, directive) { + this.#test(indent, testNumber, 'ok', description, directive); + } + + plan(indent, count, explanation) { + const exp = `${explanation ? ` # ${tapEscape(explanation)}` : ''}`; + + this.#tryPush(`${indent}1..${count}${exp}\n`); + } + + getSkip(reason) { + return `SKIP${reason ? ` ${tapEscape(reason)}` : ''}`; + } + + getTodo(reason) { + return `TODO${reason ? ` ${tapEscape(reason)}` : ''}`; + } + + details(indent, duration, error) { + let details = `${indent} ---\n`; + + details += jsToYaml(indent, 'duration_ms', duration); + details += jsToYaml(indent, null, error); + details += `${indent} ...\n`; + this.#tryPush(details); + } + + diagnostic(indent, message) { + this.#tryPush(`${indent}# ${tapEscape(message)}\n`); + } + + version() { + this.#tryPush('TAP version 13\n'); + } + + #test(indent, testNumber, status, description, directive) { + let line = `${indent}${status} ${testNumber}`; + + if (description) { + line += ` ${tapEscape(description)}`; + } + + if (directive) { + line += ` # ${directive}`; + } + + line += '\n'; + this.#tryPush(line); + } + + #tryPush(message) { + if (this.#canPush) { + this.#canPush = this.push(message); + } else { + ArrayPrototypePush(this.#buffer, message); + } + + return this.#canPush; + } +} + +// In certain places, # and \ need to be escaped as \# and \\. +function tapEscape(input) { + return StringPrototypeReplaceAll( + StringPrototypeReplaceAll(input, '\\', '\\\\'), '#', '\\#' + ); +} + +function jsToYaml(indent, name, value) { + if (value === null || value === undefined) { + return ''; + } + + if (typeof value !== 'object') { + const prefix = `${indent} ${name}: `; + + if (typeof value !== 'string') { + return `${prefix}${inspectWithNoCustomRetry(value, inspectOptions)}\n`; + } + + const lines = StringPrototypeSplit(value, kLineBreakRegExp); + + if (lines.length === 1) { + return `${prefix}${inspectWithNoCustomRetry(value, inspectOptions)}\n`; + } + + let str = `${prefix}|-\n`; + + for (let i = 0; i < lines.length; i++) { + str += `${indent} ${lines[i]}\n`; + } + + return str; + } + + const entries = ObjectEntries(value); + const isErrorObj = isError(value); + let result = ''; + + for (let i = 0; i < entries.length; i++) { + const { 0: key, 1: value } = entries[i]; + + if (isErrorObj && (key === 'cause' || key === 'code')) { + continue; + } + + result += jsToYaml(indent, key, value); + } + + if (isErrorObj) { + const { kTestCodeFailure } = lazyLoadTest(); + const { + cause, + code, + failureType, + message, + stack, + } = value; + let errMsg = message ?? ''; + let errStack = stack; + let errCode = code; + + // If the ERR_TEST_FAILURE came from an error provided by user code, + // then try to unwrap the original error message and stack. + if (code === 'ERR_TEST_FAILURE' && failureType === kTestCodeFailure) { + errMsg = cause?.message ?? errMsg; + errStack = cause?.stack ?? errStack; + errCode = cause?.code ?? errCode; + } + + result += jsToYaml(indent, 'error', errMsg); + + if (errCode) { + result += jsToYaml(indent, 'code', errCode); + } + + if (typeof errStack === 'string') { + const frames = []; + + ArrayPrototypeForEach( + StringPrototypeSplit(errStack, kLineBreakRegExp), + (frame) => { + const processed = RegExpPrototypeSymbolReplace( + kFrameStartRegExp, frame, '' + ); + + if (processed.length > 0 && processed.length !== frame.length) { + ArrayPrototypePush(frames, processed); + } + } + ); + + if (frames.length > 0) { + const frameDelimiter = `\n${indent} `; + + result += `${indent} stack: |-${frameDelimiter}`; + result += `${ArrayPrototypeJoin(frames, `${frameDelimiter}`)}\n`; + } + } + } + + return result; +} + +module.exports = { TapStream }; diff --git a/lib/internal/test_runner/test.js b/lib/internal/test_runner/test.js new file mode 100644 index 00000000000000..d35bf6307bfb15 --- /dev/null +++ b/lib/internal/test_runner/test.js @@ -0,0 +1,450 @@ +'use strict'; +const { + ArrayPrototypePush, + ArrayPrototypeShift, + FunctionPrototype, + Number, + ObjectCreate, + SafeMap, +} = primordials; +const { AsyncResource } = require('async_hooks'); +const { + codes: { + ERR_TEST_FAILURE, + }, + kIsNodeError, +} = require('internal/errors'); +const { getOptionValue } = require('internal/options'); +const { TapStream } = require('internal/test_runner/tap_stream'); +const { createDeferredPromise } = require('internal/util'); +const { isPromise } = require('internal/util/types'); +const { isUint32 } = require('internal/validators'); +const { cpus } = require('os'); +const { bigint: hrtime } = process.hrtime; +const kCallbackAndPromisePresent = 'callbackAndPromisePresent'; +const kCancelledByParent = 'cancelledByParent'; +const kMultipleCallbackInvocations = 'multipleCallbackInvocations'; +const kParentAlreadyFinished = 'parentAlreadyFinished'; +const kSubtestsFailed = 'subtestsFailed'; +const kTestCodeFailure = 'testCodeFailure'; +const kDefaultIndent = ' '; +const noop = FunctionPrototype; +const isTestRunner = getOptionValue('--test'); +const testOnlyFlag = !isTestRunner && getOptionValue('--test-only'); +// TODO(cjihrig): Use uv_available_parallelism() once it lands. +const rootConcurrency = isTestRunner ? cpus().length : 1; + +class TestContext { + #test; + + constructor(test) { + this.#test = test; + } + + diagnostic(message) { + this.#test.diagnostic(message); + } + + runOnly(value) { + this.#test.runOnlySubtests = !!value; + } + + skip(message) { + this.#test.skip(message); + } + + todo(message) { + this.#test.todo(message); + } + + test(name, options, fn) { + const subtest = this.#test.createSubtest(name, options, fn); + + return subtest.start(); + } +} + +class Test extends AsyncResource { + constructor(options) { + super('Test'); + + let { fn, name, parent, skip } = options; + const { concurrency, only, todo } = options; + + if (typeof fn !== 'function') { + fn = noop; + } + + if (typeof name !== 'string' || name === '') { + name = fn.name || ''; + } + + if (!(parent instanceof Test)) { + parent = null; + } + + if (parent === null) { + this.concurrency = rootConcurrency; + this.indent = ''; + this.indentString = kDefaultIndent; + this.only = testOnlyFlag; + this.reporter = new TapStream(); + this.runOnlySubtests = this.only; + this.testNumber = 0; + } else { + const indent = parent.parent === null ? parent.indent : + parent.indent + parent.indentString; + + this.concurrency = parent.concurrency; + this.indent = indent; + this.indentString = parent.indentString; + this.only = only ?? !parent.runOnlySubtests; + this.reporter = parent.reporter; + this.runOnlySubtests = !this.only; + this.testNumber = parent.subtests.length + 1; + } + + if (isUint32(concurrency) && concurrency !== 0) { + this.concurrency = concurrency; + } + + if (testOnlyFlag && !this.only) { + skip = '\'only\' option not set'; + } + + if (skip) { + fn = noop; + } + + this.fn = fn; + this.name = name; + this.parent = parent; + this.cancelled = false; + this.skipped = !!skip; + this.isTodo = !!todo; + this.startTime = null; + this.endTime = null; + this.passed = false; + this.error = null; + this.diagnostics = []; + this.message = typeof skip === 'string' ? skip : + typeof todo === 'string' ? todo : null; + this.activeSubtests = 0; + this.pendingSubtests = []; + this.readySubtests = new SafeMap(); + this.subtests = []; + this.waitingOn = 0; + this.finished = false; + } + + hasConcurrency() { + return this.concurrency > this.activeSubtests; + } + + addPendingSubtest(deferred) { + this.pendingSubtests.push(deferred); + } + + async processPendingSubtests() { + while (this.pendingSubtests.length > 0 && this.hasConcurrency()) { + const deferred = ArrayPrototypeShift(this.pendingSubtests); + await deferred.test.run(); + deferred.resolve(); + } + } + + addReadySubtest(subtest) { + this.readySubtests.set(subtest.testNumber, subtest); + } + + processReadySubtestRange(canSend) { + const start = this.waitingOn; + const end = start + this.readySubtests.size; + + for (let i = start; i < end; i++) { + const subtest = this.readySubtests.get(i); + + // Check if the specified subtest is in the map. If it is not, return + // early to avoid trying to process any more tests since they would be + // out of order. + if (subtest === undefined) { + return; + } + + // Call isClearToSend() in the loop so that it is: + // - Only called if there are results to report in the correct order. + // - Guaranteed to only be called a maximum of once per call to + // processReadySubtestRange(). + canSend = canSend || this.isClearToSend(); + + if (!canSend) { + return; + } + + // Report the subtest's results and remove it from the ready map. + subtest.finalize(); + this.readySubtests.delete(i); + } + } + + createSubtest(name, options, fn) { + if (typeof name === 'function') { + fn = name; + } else if (name !== null && typeof name === 'object') { + fn = options; + options = name; + } else if (typeof options === 'function') { + fn = options; + } + + if (options === null || typeof options !== 'object') { + options = ObjectCreate(null); + } + + let parent = this; + + // If this test has already ended, attach this test to the root test so + // that the error can be properly reported. + if (this.finished) { + while (parent.parent !== null) { + parent = parent.parent; + } + } + + const test = new Test({ fn, name, parent, ...options }); + + if (parent.waitingOn === 0) { + parent.waitingOn = test.testNumber; + } + + if (this.finished) { + test.fail( + new ERR_TEST_FAILURE( + 'test could not be started because its parent finished', + kParentAlreadyFinished + ) + ); + } + + ArrayPrototypePush(parent.subtests, test); + return test; + } + + cancel() { + if (this.endTime !== null) { + return; + } + + this.fail( + new ERR_TEST_FAILURE( + 'test did not finish before its parent and was cancelled', + kCancelledByParent + ) + ); + this.cancelled = true; + } + + fail(err) { + if (this.error !== null) { + return; + } + + this.endTime = hrtime(); + this.passed = false; + this.error = err; + } + + pass() { + if (this.endTime !== null) { + return; + } + + this.endTime = hrtime(); + this.passed = true; + } + + skip(message) { + this.skipped = true; + this.message = message; + } + + todo(message) { + this.isTodo = true; + this.message = message; + } + + diagnostic(message) { + ArrayPrototypePush(this.diagnostics, message); + } + + start() { + // If there is enough available concurrency to run the test now, then do + // it. Otherwise, return a Promise to the caller and mark the test as + // pending for later execution. + if (!this.parent.hasConcurrency()) { + const deferred = createDeferredPromise(); + + deferred.test = this; + this.parent.addPendingSubtest(deferred); + return deferred.promise; + } + + return this.run(); + } + + async run() { + this.parent.activeSubtests++; + this.startTime = hrtime(); + + try { + const ctx = new TestContext(this); + + if (this.fn.length === 2) { + // This test is using legacy Node.js error first callbacks. + const { promise, resolve, reject } = createDeferredPromise(); + let calledCount = 0; + const ret = this.runInAsyncScope(this.fn, ctx, ctx, (err) => { + calledCount++; + + // If the callback is called a second time, let the user know, but + // don't let them know more than once. + if (calledCount > 1) { + if (calledCount === 2) { + throw new ERR_TEST_FAILURE( + 'callback invoked multiple times', + kMultipleCallbackInvocations + ); + } + + return; + } + + if (err) { + return reject(err); + } + + resolve(); + }); + + if (isPromise(ret)) { + this.fail(new ERR_TEST_FAILURE( + 'passed a callback but also returned a Promise', + kCallbackAndPromisePresent + )); + await ret; + } else { + await promise; + } + } else { + // This test is synchronous or using Promises. + await this.runInAsyncScope(this.fn, ctx, ctx); + } + + this.pass(); + } catch (err) { + if (err?.code === 'ERR_TEST_FAILURE' && kIsNodeError in err) { + this.fail(err); + } else { + this.fail(new ERR_TEST_FAILURE(err, kTestCodeFailure)); + } + } + + // Clean up the test. Then, try to report the results and execute any + // tests that were pending due to available concurrency. + this.postRun(); + } + + postRun() { + let failedSubtests = 0; + + // If the test was failed before it even started, then the end time will + // be earlier than the start time. Correct that here. + if (this.endTime < this.startTime) { + this.endTime = hrtime(); + } + + // The test has run, so recursively cancel any outstanding subtests and + // mark this test as failed if any subtests failed. + for (let i = 0; i < this.subtests.length; i++) { + const subtest = this.subtests[i]; + + if (!subtest.finished) { + subtest.cancel(); + subtest.postRun(); + } + + if (!subtest.passed) { + failedSubtests++; + } + } + + if (this.passed && failedSubtests > 0) { + const subtestString = `subtest${failedSubtests > 1 ? 's' : ''}`; + const msg = `${failedSubtests} ${subtestString} failed`; + + this.fail(new ERR_TEST_FAILURE(msg, kSubtestsFailed)); + } + + if (this.parent !== null) { + this.parent.activeSubtests--; + this.parent.addReadySubtest(this); + this.parent.processReadySubtestRange(false); + this.parent.processPendingSubtests(); + } + } + + isClearToSend() { + return this.parent === null || + ( + this.parent.waitingOn === this.testNumber && this.parent.isClearToSend() + ); + } + + finalize() { + // By the time this function is called, the following can be relied on: + // - The current test has completed or been cancelled. + // - All of this test's subtests have completed or been cancelled. + // - It is the current test's turn to report its results. + + // Report any subtests that have not been reported yet. Since all of the + // subtests have finished, it's safe to pass true to + // processReadySubtestRange(), which will finalize all remaining subtests. + this.processReadySubtestRange(true); + + // Output this test's results and update the parent's waiting counter. + if (this.subtests.length > 0) { + this.reporter.plan(this.subtests[0].indent, this.subtests.length); + } + + this.report(); + this.parent.waitingOn++; + this.finished = true; + } + + report() { + // Duration is recorded in BigInt nanoseconds. Convert to seconds. + const duration = Number(this.endTime - this.startTime) / 1_000_000_000; + const message = `- ${this.name}`; + let directive; + + if (this.skipped) { + directive = this.reporter.getSkip(this.message); + } else if (this.isTodo) { + directive = this.reporter.getTodo(this.message); + } + + if (this.passed) { + this.reporter.ok(this.indent, this.testNumber, message, directive); + } else { + this.reporter.fail(this.indent, this.testNumber, message, directive); + } + + this.reporter.details(this.indent, duration, this.error); + + for (let i = 0; i < this.diagnostics.length; i++) { + this.reporter.diagnostic(this.indent, this.diagnostics[i]); + } + } +} + +module.exports = { kDefaultIndent, kSubtestsFailed, kTestCodeFailure, Test }; diff --git a/lib/internal/test_runner/utils.js b/lib/internal/test_runner/utils.js new file mode 100644 index 00000000000000..09803d33aeb508 --- /dev/null +++ b/lib/internal/test_runner/utils.js @@ -0,0 +1,15 @@ +'use strict'; +const { RegExpPrototypeExec } = primordials; +const { basename } = require('path'); +const kSupportedFileExtensions = /\.[cm]?js$/; +const kTestFilePattern = /((^test(-.+)?)|(.+[.\-_]test))\.[cm]?js$/; + +function doesPathMatchFilter(p) { + return RegExpPrototypeExec(kTestFilePattern, basename(p)) !== null; +} + +function isSupportedFileType(p) { + return RegExpPrototypeExec(kSupportedFileExtensions, p) !== null; +} + +module.exports = { isSupportedFileType, doesPathMatchFilter }; diff --git a/lib/internal/timers.js b/lib/internal/timers.js index 7276f8eb32baed..a2a1c1e387bf6a 100644 --- a/lib/internal/timers.js +++ b/lib/internal/timers.js @@ -136,9 +136,6 @@ let timerListId = NumberMIN_SAFE_INTEGER; const kRefed = Symbol('refed'); -// Create a single linked list instance only once at startup -const immediateQueue = new ImmediateList(); - let nextExpiry = Infinity; let refCount = 0; @@ -161,140 +158,148 @@ function initAsyncResource(resource, type) { if (initHooksExist()) emitInit(asyncId, type, triggerAsyncId, resource); } - -// Timer constructor function. -// The entire prototype is defined in lib/timers.js -function Timeout(callback, after, args, isRepeat, isRefed) { - after *= 1; // Coalesce to number or NaN - if (!(after >= 1 && after <= TIMEOUT_MAX)) { - if (after > TIMEOUT_MAX) { - process.emitWarning(`${after} does not fit into` + - ' a 32-bit signed integer.' + - '\nTimeout duration was set to 1.', - 'TimeoutOverflowWarning'); +class Timeout { + // Timer constructor function. + // The entire prototype is defined in lib/timers.js + constructor(callback, after, args, isRepeat, isRefed) { + after *= 1; // Coalesce to number or NaN + if (!(after >= 1 && after <= TIMEOUT_MAX)) { + if (after > TIMEOUT_MAX) { + process.emitWarning(`${after} does not fit into` + + ' a 32-bit signed integer.' + + '\nTimeout duration was set to 1.', + 'TimeoutOverflowWarning'); + } + after = 1; // Schedule on next tick, follows browser behavior } - after = 1; // Schedule on next tick, follows browser behavior - } - this._idleTimeout = after; - this._idlePrev = this; - this._idleNext = this; - this._idleStart = null; - // This must be set to null first to avoid function tracking - // on the hidden class, revisit in V8 versions after 6.2 - this._onTimeout = null; - this._onTimeout = callback; - this._timerArgs = args; - this._repeat = isRepeat ? after : null; - this._destroyed = false; - - if (isRefed) - incRefCount(); - this[kRefed] = isRefed; - this[kHasPrimitive] = false; - - initAsyncResource(this, 'Timeout'); -} + this._idleTimeout = after; + this._idlePrev = this; + this._idleNext = this; + this._idleStart = null; + // This must be set to null first to avoid function tracking + // on the hidden class, revisit in V8 versions after 6.2 + this._onTimeout = null; + this._onTimeout = callback; + this._timerArgs = args; + this._repeat = isRepeat ? after : null; + this._destroyed = false; -// Make sure the linked list only shows the minimal necessary information. -Timeout.prototype[inspect.custom] = function(_, options) { - return inspect(this, { - ...options, - // Only inspect one level. - depth: 0, - // It should not recurse. - customInspect: false - }); -}; + if (isRefed) + incRefCount(); + this[kRefed] = isRefed; + this[kHasPrimitive] = false; -Timeout.prototype.refresh = function() { - if (this[kRefed]) - active(this); - else - unrefActive(this); + initAsyncResource(this, 'Timeout'); + } - return this; -}; + // Make sure the linked list only shows the minimal necessary information. + [inspect.custom](_, options) { + return inspect(this, { + ...options, + // Only inspect one level. + depth: 0, + // It should not recurse. + customInspect: false + }); + } -Timeout.prototype.unref = function() { - if (this[kRefed]) { - this[kRefed] = false; - if (!this._destroyed) - decRefCount(); + refresh() { + if (this[kRefed]) + active(this); + else + unrefActive(this); + + return this; } - return this; -}; -Timeout.prototype.ref = function() { - if (!this[kRefed]) { - this[kRefed] = true; - if (!this._destroyed) - incRefCount(); + unref() { + if (this[kRefed]) { + this[kRefed] = false; + if (!this._destroyed) + decRefCount(); + } + return this; } - return this; -}; -Timeout.prototype.hasRef = function() { - return this[kRefed]; -}; + ref() { + if (!this[kRefed]) { + this[kRefed] = true; + if (!this._destroyed) + incRefCount(); + } + return this; + } -function TimersList(expiry, msecs) { - this._idleNext = this; // Create the list with the linkedlist properties to - this._idlePrev = this; // Prevent any unnecessary hidden class changes. - this.expiry = expiry; - this.id = timerListId++; - this.msecs = msecs; - this.priorityQueuePosition = null; + hasRef() { + return this[kRefed]; + } } -// Make sure the linked list only shows the minimal necessary information. -TimersList.prototype[inspect.custom] = function(_, options) { - return inspect(this, { - ...options, - // Only inspect one level. - depth: 0, - // It should not recurse. - customInspect: false - }); -}; +class TimersList { + constructor(expiry, msecs) { + this._idleNext = this; // Create the list with the linkedlist properties to + this._idlePrev = this; // Prevent any unnecessary hidden class changes. + this.expiry = expiry; + this.id = timerListId++; + this.msecs = msecs; + this.priorityQueuePosition = null; + } -// A linked list for storing `setImmediate()` requests -function ImmediateList() { - this.head = null; - this.tail = null; + // Make sure the linked list only shows the minimal necessary information. + [inspect.custom](_, options) { + return inspect(this, { + ...options, + // Only inspect one level. + depth: 0, + // It should not recurse. + customInspect: false + }); + } } -// Appends an item to the end of the linked list, adjusting the current tail's -// next pointer and the item's previous pointer where applicable -ImmediateList.prototype.append = function(item) { - if (this.tail !== null) { - this.tail._idleNext = item; - item._idlePrev = this.tail; - } else { - this.head = item; +// A linked list for storing `setImmediate()` requests +class ImmediateList { + constructor() { + this.head = null; + this.tail = null; } - this.tail = item; -}; -// Removes an item from the linked list, adjusting the pointers of adjacent -// items and the linked list's head or tail pointers as necessary -ImmediateList.prototype.remove = function(item) { - if (item._idleNext) { - item._idleNext._idlePrev = item._idlePrev; + // Appends an item to the end of the linked list, adjusting the current tail's + // next pointer and the item's previous pointer where applicable + append(item) { + if (this.tail !== null) { + this.tail._idleNext = item; + item._idlePrev = this.tail; + } else { + this.head = item; + } + this.tail = item; } - if (item._idlePrev) { - item._idlePrev._idleNext = item._idleNext; - } + // Removes an item from the linked list, adjusting the pointers of adjacent + // items and the linked list's head or tail pointers as necessary + remove(item) { + if (item._idleNext) { + item._idleNext._idlePrev = item._idlePrev; + } - if (item === this.head) - this.head = item._idleNext; - if (item === this.tail) - this.tail = item._idlePrev; + if (item._idlePrev) { + item._idlePrev._idleNext = item._idleNext; + } - item._idleNext = null; - item._idlePrev = null; -}; + if (item === this.head) + this.head = item._idleNext; + if (item === this.tail) + this.tail = item._idlePrev; + + item._idleNext = null; + item._idlePrev = null; + } +} + +// Create a single linked list instance only once at startup +const immediateQueue = new ImmediateList(); function incRefCount() { if (refCount++ === 0) diff --git a/lib/internal/url.js b/lib/internal/url.js index 41ae1b1a8be429..1b85660e07c73a 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -559,7 +559,7 @@ function onParseComplete(flags, protocol, username, password, initSearchParams(this[searchParams], query); } -function onParseError(flags, input) { +function onParseError(input, flags) { throw new ERR_INVALID_URL(input); } @@ -641,7 +641,8 @@ class URL { } this[context] = new URLContext(); parse(input, -1, base_context, undefined, - FunctionPrototypeBind(onParseComplete, this), onParseError); + FunctionPrototypeBind(onParseComplete, this), + FunctionPrototypeBind(onParseError, this, input)); } get [special]() { @@ -760,7 +761,8 @@ class URL { // toUSVString is not needed. input = `${input}`; parse(input, -1, undefined, undefined, - FunctionPrototypeBind(onParseComplete, this), onParseError); + FunctionPrototypeBind(onParseComplete, this), + FunctionPrototypeBind(onParseError, this, input)); } // readonly diff --git a/lib/internal/validators.js b/lib/internal/validators.js index b07eebfbd8b1a0..e3584aaf2e13f7 100644 --- a/lib/internal/validators.js +++ b/lib/internal/validators.js @@ -82,10 +82,10 @@ const validateInteger = hideStackFrames( const validateInt32 = hideStackFrames( (value, name, min = -2147483648, max = 2147483647) => { // The defaults for min and max correspond to the limits of 32-bit integers. + if (typeof value !== 'number') { + throw new ERR_INVALID_ARG_TYPE(name, 'number', value); + } if (!isInt32(value)) { - if (typeof value !== 'number') { - throw new ERR_INVALID_ARG_TYPE(name, 'number', value); - } if (!NumberIsInteger(value)) { throw new ERR_OUT_OF_RANGE(name, 'an integer', value); } @@ -98,10 +98,10 @@ const validateInt32 = hideStackFrames( ); const validateUint32 = hideStackFrames((value, name, positive) => { + if (typeof value !== 'number') { + throw new ERR_INVALID_ARG_TYPE(name, 'number', value); + } if (!isUint32(value)) { - if (typeof value !== 'number') { - throw new ERR_INVALID_ARG_TYPE(name, 'number', value); - } if (!NumberIsInteger(value)) { throw new ERR_OUT_OF_RANGE(name, 'an integer', value); } diff --git a/lib/internal/vm/module.js b/lib/internal/vm/module.js index 911b8f4426e586..eee629dda4fd05 100644 --- a/lib/internal/vm/module.js +++ b/lib/internal/vm/module.js @@ -34,7 +34,7 @@ const { ERR_VM_MODULE_ALREADY_LINKED, ERR_VM_MODULE_DIFFERENT_CONTEXT, ERR_VM_MODULE_CANNOT_CREATE_CACHED_DATA, - ERR_VM_MODULE_LINKING_ERRORED, + ERR_VM_MODULE_LINK_FAILURE, ERR_VM_MODULE_NOT_MODULE, ERR_VM_MODULE_STATUS, } = require('internal/errors').codes; @@ -317,9 +317,7 @@ class SourceTextModule extends Module { throw new ERR_VM_MODULE_DIFFERENT_CONTEXT(); } if (module.status === 'errored') { - // TODO(devsnek): replace with ERR_VM_MODULE_LINK_FAILURE - // and error cause proposal. - throw new ERR_VM_MODULE_LINKING_ERRORED(); + throw new ERR_VM_MODULE_LINK_FAILURE(`request for '${identifier}' resolved to an errored module`, module.error); } if (module.status === 'unlinked') { await module[kLink](linker); diff --git a/lib/internal/webstreams/adapters.js b/lib/internal/webstreams/adapters.js index a81c173e4714e9..1058c1c0356ead 100644 --- a/lib/internal/webstreams/adapters.js +++ b/lib/internal/webstreams/adapters.js @@ -228,8 +228,9 @@ function newStreamWritableFromWritableStream(writableStream, options = {}) { writev(chunks, callback) { function done(error) { + error = error.filter((e) => e); try { - callback(error); + callback(error.length === 0 ? undefined : error); } catch (error) { // In a next tick because this is happening within // a promise context, and if there are any errors @@ -247,7 +248,7 @@ function newStreamWritableFromWritableStream(writableStream, options = {}) { PromiseAll( ArrayPrototypeMap( chunks, - (chunk) => writer.write(chunk))), + (data) => writer.write(data.chunk))), done, done); }, @@ -633,8 +634,9 @@ function newStreamDuplexFromReadableWritablePair(pair = {}, options = {}) { writev(chunks, callback) { function done(error) { + error = error.filter((e) => e); try { - callback(error); + callback(error.length === 0 ? undefined : error); } catch (error) { // In a next tick because this is happening within // a promise context, and if there are any errors @@ -652,7 +654,7 @@ function newStreamDuplexFromReadableWritablePair(pair = {}, options = {}) { PromiseAll( ArrayPrototypeMap( chunks, - (chunk) => writer.write(chunk))), + (data) => writer.write(data.chunk))), done, done); }, diff --git a/lib/internal/webstreams/readablestream.js b/lib/internal/webstreams/readablestream.js index 0746d44171fed0..bea1084bdd72ad 100644 --- a/lib/internal/webstreams/readablestream.js +++ b/lib/internal/webstreams/readablestream.js @@ -1911,9 +1911,12 @@ function readableStreamDefaultControllerError(controller, error) { function readableStreamDefaultControllerCancelSteps(controller, reason) { resetQueue(controller); - const result = controller[kState].cancelAlgorithm(reason); - readableStreamDefaultControllerClearAlgorithms(controller); - return result; + try { + const result = controller[kState].cancelAlgorithm(reason); + return result; + } finally { + readableStreamDefaultControllerClearAlgorithms(controller); + } } function readableStreamDefaultControllerPullSteps(controller, readRequest) { diff --git a/lib/net.js b/lib/net.js index 53ecbd9a1f5f4d..1c98cf4820178f 100644 --- a/lib/net.js +++ b/lib/net.js @@ -130,6 +130,12 @@ const isWindows = process.platform === 'win32'; const noop = () => {}; +const kPerfHooksNetConnectContext = Symbol('kPerfHooksNetConnectContext'); +const { + startPerf, + stopPerf, +} = require('internal/perf/observe'); + function getFlags(ipv6Only) { return ipv6Only === true ? TCPConstants.UV_TCP_IPV6ONLY : 0; } @@ -754,7 +760,7 @@ protoGetter('remoteAddress', function remoteAddress() { }); protoGetter('remoteFamily', function remoteFamily() { - return this._getpeername().family; + return `IPv${this._getpeername().family}`; }); protoGetter('remotePort', function remotePort() { @@ -952,6 +958,8 @@ function internalConnect( const ex = exceptionWithHostPort(err, 'connect', address, port, details); self.destroy(ex); + } else if (addressType === 6 || addressType === 4) { + startPerf(self, kPerfHooksNetConnectContext, { type: 'net', name: 'connect', detail: { host: address, port } }); } } @@ -1177,7 +1185,7 @@ function afterConnect(status, handle, req, readable, writable) { // this doesn't actually consume any bytes, because len=0. if (readable && !self.isPaused()) self.read(0); - + stopPerf(self, kPerfHooksNetConnectContext); } else { self.connecting = false; let details; diff --git a/lib/os.js b/lib/os.js index f0f0fdb15f61c0..862d6bb4a2bec9 100644 --- a/lib/os.js +++ b/lib/os.js @@ -171,7 +171,7 @@ platform[SymbolToPrimitive] = () => process.platform; * @returns {string} */ function tmpdir() { - var path; + let path; if (isWindows) { path = process.env.TEMP || process.env.TMP || @@ -216,14 +216,14 @@ function getCIDR(address, netmask, family) { let groupLength = 8; let hasZeros = false; - if (family === 'IPv6') { + if (family === 6) { split = ':'; range = 16; groupLength = 16; } const parts = StringPrototypeSplit(netmask, split); - for (var i = 0; i < parts.length; i++) { + for (let i = 0; i < parts.length; i++) { if (parts[i] !== '') { const binary = NumberParseInt(parts[i], range); const tmp = countBinaryOnes(binary); @@ -248,7 +248,7 @@ function getCIDR(address, netmask, family) { * @returns {Record `node:${lib}`); +ArrayPrototypeForEach( + NativeModule.getSchemeOnlyModuleNames(), + (lib) => ArrayPrototypePush(nodeSchemeBuiltinLibs, `node:${lib}`), +); const domain = require('domain'); let debug = require('internal/util/debuglog').debuglog('repl', (fn) => { debug = fn; diff --git a/lib/stream.js b/lib/stream.js index 9794bdb45b4df4..55f903f295fecf 100644 --- a/lib/stream.js +++ b/lib/stream.js @@ -69,12 +69,15 @@ for (const key of ObjectKeys(streamReturningOperators)) { value: fn, enumerable: false, configurable: true, - writable: false, + writable: true, }); } for (const key of ObjectKeys(promiseReturningOperators)) { const op = promiseReturningOperators[key]; function fn(...args) { + if (new.target) { + throw ERR_ILLEGAL_CONSTRUCTOR(); + } return ReflectApply(op, this, args); } ObjectDefineProperty(fn, 'name', { value: op.name }); @@ -83,7 +86,7 @@ for (const key of ObjectKeys(promiseReturningOperators)) { value: fn, enumerable: false, configurable: true, - writable: false, + writable: true, }); } Stream.Writable = require('internal/streams/writable'); diff --git a/lib/string_decoder.js b/lib/string_decoder.js index 7447bb3f4699b1..9eae594aaa27ea 100644 --- a/lib/string_decoder.js +++ b/lib/string_decoder.js @@ -43,6 +43,7 @@ const { const internalUtil = require('internal/util'); const { ERR_INVALID_ARG_TYPE, + ERR_INVALID_THIS, ERR_UNKNOWN_ENCODING } = require('internal/errors').codes; const isEncoding = Buffer[internalUtil.kIsEncodingSymbol]; @@ -101,6 +102,9 @@ StringDecoder.prototype.write = function write(buf) { throw new ERR_INVALID_ARG_TYPE('buf', ['Buffer', 'TypedArray', 'DataView'], buf); + if (!this[kNativeDecoder]) { + throw new ERR_INVALID_THIS('StringDecoder'); + } return decode(this[kNativeDecoder], buf); }; diff --git a/lib/test.js b/lib/test.js new file mode 100644 index 00000000000000..fa319fa17b37bd --- /dev/null +++ b/lib/test.js @@ -0,0 +1,8 @@ +'use strict'; +const test = require('internal/test_runner/harness'); +const { emitExperimentalWarning } = require('internal/util'); + +emitExperimentalWarning('The test runner'); + +module.exports = test; +module.exports.test = test; diff --git a/lib/url.js b/lib/url.js index 745c7c9930deda..328452ba3cb27a 100644 --- a/lib/url.js +++ b/lib/url.js @@ -117,7 +117,6 @@ const { CHAR_TAB, CHAR_CARRIAGE_RETURN, CHAR_LINE_FEED, - CHAR_FORM_FEED, CHAR_NO_BREAK_SPACE, CHAR_ZERO_WIDTH_NOBREAK_SPACE, CHAR_HASH, @@ -178,7 +177,9 @@ function isIpv6Hostname(hostname) { // as IPv6 by isIpv6Hostname above // // [1]: https://url.spec.whatwg.org/#forbidden-host-code-point -const forbiddenHostChars = /[\t\n\r #%/:<>?@[\\\]^|]/; +const forbiddenHostChars = /[\0\t\n\r #%/:<>?@[\\\]^|]/; +// For IPv6, permit '[', ']', and ':'. +const forbiddenHostCharsIpv6 = /[\0\t\n\r #%/<>?@\\^|]/; Url.prototype.parse = function parse(url, parseQueryString, slashesDenoteHost) { validateString(url, 'url'); @@ -196,11 +197,7 @@ Url.prototype.parse = function parse(url, parseQueryString, slashesDenoteHost) { const code = url.charCodeAt(i); // Find first and last non-whitespace characters for trimming - const isWs = code === CHAR_SPACE || - code === CHAR_TAB || - code === CHAR_CARRIAGE_RETURN || - code === CHAR_LINE_FEED || - code === CHAR_FORM_FEED || + const isWs = code < 33 || code === CHAR_NO_BREAK_SPACE || code === CHAR_ZERO_WIDTH_NOBREAK_SPACE; if (start === -1) { @@ -405,27 +402,33 @@ Url.prototype.parse = function parse(url, parseQueryString, slashesDenoteHost) { this.hostname = this.hostname.toLowerCase(); } - if (!ipv6Hostname && this.hostname !== '') { - // IDNA Support: Returns a punycoded representation of "domain". - // It only converts parts of the domain name that - // have non-ASCII characters, i.e. it doesn't matter if - // you call it with a domain that already is ASCII-only. - - // Use lenient mode (`true`) to try to support even non-compliant - // URLs. - this.hostname = toASCII(this.hostname, true); - - // Prevent two potential routes of hostname spoofing. - // 1. If this.hostname is empty, it must have become empty due to toASCII - // since we checked this.hostname above. - // 2. If any of forbiddenHostChars appears in this.hostname, it must have - // also gotten in due to toASCII. This is since getHostname would have - // filtered them out otherwise. - // Rather than trying to correct this by moving the non-host part into - // the pathname as we've done in getHostname, throw an exception to - // convey the severity of this issue. - if (this.hostname === '' || forbiddenHostChars.test(this.hostname)) { - throw new ERR_INVALID_URL(url); + if (this.hostname !== '') { + if (ipv6Hostname) { + if (forbiddenHostCharsIpv6.test(this.hostname)) { + throw new ERR_INVALID_URL(url); + } + } else { + // IDNA Support: Returns a punycoded representation of "domain". + // It only converts parts of the domain name that + // have non-ASCII characters, i.e. it doesn't matter if + // you call it with a domain that already is ASCII-only. + + // Use lenient mode (`true`) to try to support even non-compliant + // URLs. + this.hostname = toASCII(this.hostname, true); + + // Prevent two potential routes of hostname spoofing. + // 1. If this.hostname is empty, it must have become empty due to toASCII + // since we checked this.hostname above. + // 2. If any of forbiddenHostChars appears in this.hostname, it must have + // also gotten in due to toASCII. This is since getHostname would have + // filtered them out otherwise. + // Rather than trying to correct this by moving the non-host part into + // the pathname as we've done in getHostname, throw an exception to + // convey the severity of this issue. + if (this.hostname === '' || forbiddenHostChars.test(this.hostname)) { + throw new ERR_INVALID_URL(url); + } } } diff --git a/lib/v8.js b/lib/v8.js index 75981152851216..e899da46849d9e 100644 --- a/lib/v8.js +++ b/lib/v8.js @@ -107,6 +107,9 @@ const { kPeakMallocedMemoryIndex, kNumberOfNativeContextsIndex, kNumberOfDetachedContextsIndex, + kTotalGlobalHandlesSizeIndex, + kUsedGlobalHandlesSizeIndex, + kExternalMemoryIndex, // Properties for heap spaces statistics buffer extraction. kHeapSpaces, @@ -165,7 +168,10 @@ function getHeapStatistics() { peak_malloced_memory: buffer[kPeakMallocedMemoryIndex], does_zap_garbage: buffer[kDoesZapGarbageIndex], number_of_native_contexts: buffer[kNumberOfNativeContextsIndex], - number_of_detached_contexts: buffer[kNumberOfDetachedContextsIndex] + number_of_detached_contexts: buffer[kNumberOfDetachedContextsIndex], + total_global_handles_size: buffer[kTotalGlobalHandlesSizeIndex], + used_global_handles_size: buffer[kUsedGlobalHandlesSizeIndex], + external_memory: buffer[kExternalMemoryIndex] }; } diff --git a/node.gyp b/node.gyp index 9f45d44e05444d..4e06d00c823290 100644 --- a/node.gyp +++ b/node.gyp @@ -7,6 +7,7 @@ 'node_use_dtrace%': 'false', 'node_use_etw%': 'false', 'node_no_browser_globals%': 'false', + 'node_snapshot_main%': '', 'node_use_node_snapshot%': 'false', 'node_use_v8_platform%': 'true', 'node_use_bundled_v8%': 'true', @@ -315,23 +316,47 @@ 'dependencies': [ 'node_mksnapshot', ], - 'actions': [ - { - 'action_name': 'node_mksnapshot', - 'process_outputs_as_sources': 1, - 'inputs': [ - '<(node_mksnapshot_exec)', - ], - 'outputs': [ - '<(SHARED_INTERMEDIATE_DIR)/node_snapshot.cc', + 'conditions': [ + ['node_snapshot_main!=""', { + 'actions': [ + { + 'action_name': 'node_mksnapshot', + 'process_outputs_as_sources': 1, + 'inputs': [ + '<(node_mksnapshot_exec)', + '<(node_snapshot_main)', + ], + 'outputs': [ + '<(SHARED_INTERMEDIATE_DIR)/node_snapshot.cc', + ], + 'action': [ + '<(node_mksnapshot_exec)', + '--build-snapshot', + '<(node_snapshot_main)', + '<@(_outputs)', + ], + }, ], - 'action': [ - '<@(_inputs)', - '<@(_outputs)', + }, { + 'actions': [ + { + 'action_name': 'node_mksnapshot', + 'process_outputs_as_sources': 1, + 'inputs': [ + '<(node_mksnapshot_exec)', + ], + 'outputs': [ + '<(SHARED_INTERMEDIATE_DIR)/node_snapshot.cc', + ], + 'action': [ + '<@(_inputs)', + '<@(_outputs)', + ], + }, ], - }, + }], ], - }, { + }, { 'sources': [ 'src/node_snapshot_stub.cc' ], @@ -518,6 +543,7 @@ 'src/node_util.cc', 'src/node_v8.cc', 'src/node_wasi.cc', + 'src/node_wasm_web_api.cc', 'src/node_watchdog.cc', 'src/node_worker.cc', 'src/node_zlib.cc', @@ -612,6 +638,7 @@ 'src/node_revert.h', 'src/node_root_certs.h', 'src/node_snapshotable.h', + 'src/node_snapshot_builder.h', 'src/node_sockaddr.h', 'src/node_sockaddr-inl.h', 'src/node_stat_watcher.h', diff --git a/src/api/environment.cc b/src/api/environment.cc index 1ec1e8bc8c1896..f3a8f49812d5ce 100644 --- a/src/api/environment.cc +++ b/src/api/environment.cc @@ -3,8 +3,10 @@ #include "node_errors.h" #include "node_internals.h" #include "node_native_module_env.h" +#include "node_options-inl.h" #include "node_platform.h" #include "node_v8_platform-inl.h" +#include "node_wasm_web_api.h" #include "uv.h" #if HAVE_INSPECTOR @@ -141,8 +143,12 @@ void* DebuggingArrayBufferAllocator::Reallocate(void* data, Mutex::ScopedLock lock(mutex_); void* ret = NodeArrayBufferAllocator::Reallocate(data, old_size, size); if (ret == nullptr) { - if (size == 0) // i.e. equivalent to free(). + if (size == 0) { // i.e. equivalent to free(). + // suppress coverity warning as data is used as key versus as pointer + // in UnregisterPointerInternal + // coverity[pass_freed_arg] UnregisterPointerInternal(data, old_size); + } return nullptr; } @@ -248,6 +254,13 @@ void SetIsolateMiscHandlers(v8::Isolate* isolate, const IsolateSettings& s) { s.allow_wasm_code_generation_callback : AllowWasmCodeGenerationCallback; isolate->SetAllowWasmCodeGenerationCallback(allow_wasm_codegen_cb); + Mutex::ScopedLock lock(node::per_process::cli_options_mutex); + if (per_process::cli_options->get_per_isolate_options() + ->get_per_env_options() + ->experimental_fetch) { + isolate->SetWasmStreamingCallback(wasm_web_api::StartStreamingCompilation); + } + if ((s.flags & SHOULD_NOT_SET_PROMISE_REJECTION_CALLBACK) == 0) { auto* promise_reject_cb = s.promise_reject_callback ? s.promise_reject_callback : PromiseRejectCallback; @@ -344,12 +357,14 @@ Environment* CreateEnvironment( Environment* env = new Environment( isolate_data, context, args, exec_args, nullptr, flags, thread_id); #if HAVE_INSPECTOR - if (inspector_parent_handle) { - env->InitializeInspector( - std::move(static_cast( - inspector_parent_handle.get())->impl)); - } else { - env->InitializeInspector({}); + if (env->should_create_inspector()) { + if (inspector_parent_handle) { + env->InitializeInspector( + std::move(static_cast( + inspector_parent_handle.get())->impl)); + } else { + env->InitializeInspector({}); + } } #endif diff --git a/src/async_wrap.cc b/src/async_wrap.cc index 77922bd04adac7..38f2eb421f487d 100644 --- a/src/async_wrap.cc +++ b/src/async_wrap.cc @@ -214,12 +214,13 @@ void AsyncWrap::WeakCallback(const WeakCallbackInfo& info) { p->env->RemoveCleanupHook(DestroyParamCleanupHook, p.get()); - if (!prop_bag->Get(p->env->context(), p->env->destroyed_string()) + if (!prop_bag.IsEmpty() && + !prop_bag->Get(p->env->context(), p->env->destroyed_string()) .ToLocal(&val)) { return; } - if (val->IsFalse()) { + if (val.IsEmpty() || val->IsFalse()) { AsyncWrap::EmitDestroy(p->env, p->asyncId); } // unique_ptr goes out of scope here and pointer is deleted. @@ -229,14 +230,16 @@ void AsyncWrap::WeakCallback(const WeakCallbackInfo& info) { static void RegisterDestroyHook(const FunctionCallbackInfo& args) { CHECK(args[0]->IsObject()); CHECK(args[1]->IsNumber()); - CHECK(args[2]->IsObject()); + CHECK(args.Length() == 2 || args[2]->IsObject()); Isolate* isolate = args.GetIsolate(); DestroyParam* p = new DestroyParam(); p->asyncId = args[1].As()->Value(); p->env = Environment::GetCurrent(args); p->target.Reset(isolate, args[0].As()); - p->propBag.Reset(isolate, args[2].As()); + if (args.Length() > 2) { + p->propBag.Reset(isolate, args[2].As()); + } p->target.SetWeak(p, AsyncWrap::WeakCallback, WeakCallbackType::kParameter); p->env->AddCleanupHook(DestroyParamCleanupHook, p); } diff --git a/src/crypto/README.md b/src/crypto/README.md index 8ebc3003da3150..0b961979d2aae1 100644 --- a/src/crypto/README.md +++ b/src/crypto/README.md @@ -312,12 +312,12 @@ crypto.randomFill(buf, (err, buf) => { For the legacy Node.js crypto API, asynchronous single-call operations use the traditional Node.js callback pattern, as illustrated in the previous `randomFill()` example. In the -Web Crypto API (accessible via `require('crypto').webcrypto`), +Web Crypto API (accessible via `require('node:crypto').webcrypto`), all asynchronous single-call operations are Promise-based. ```js // Example Web Crypto API asynchronous single-call operation -const { subtle } = require('crypto').webcrypto; +const { subtle } = require('node:crypto').webcrypto; subtle.generateKeys({ name: 'HMAC', length: 256 }, true, ['sign']) .then((key) => { diff --git a/src/crypto/crypto_cipher.cc b/src/crypto/crypto_cipher.cc index 90a0c4d1fd0bf4..f65b7c41bd996d 100644 --- a/src/crypto/crypto_cipher.cc +++ b/src/crypto/crypto_cipher.cc @@ -24,21 +24,20 @@ using v8::Uint32; using v8::Value; namespace crypto { -#ifdef OPENSSL_NO_OCB -# define IS_OCB_MODE(mode) false -#else -# define IS_OCB_MODE(mode) ((mode) == EVP_CIPH_OCB_MODE) -#endif - namespace { bool IsSupportedAuthenticatedMode(const EVP_CIPHER* cipher) { - const int mode = EVP_CIPHER_mode(cipher); - // Check `chacha20-poly1305` separately, it is also an AEAD cipher, - // but its mode is 0 which doesn't indicate - return EVP_CIPHER_nid(cipher) == NID_chacha20_poly1305 || - mode == EVP_CIPH_CCM_MODE || - mode == EVP_CIPH_GCM_MODE || - IS_OCB_MODE(mode); + switch (EVP_CIPHER_mode(cipher)) { + case EVP_CIPH_CCM_MODE: + case EVP_CIPH_GCM_MODE: +#ifndef OPENSSL_NO_OCB + case EVP_CIPH_OCB_MODE: +#endif + return true; + case EVP_CIPH_STREAM_CIPHER: + return EVP_CIPHER_nid(cipher) == NID_chacha20_poly1305; + default: + return false; + } } bool IsSupportedAuthenticatedMode(const EVP_CIPHER_CTX* ctx) { @@ -572,9 +571,17 @@ bool CipherBase::InitAuthenticated( } } else { if (auth_tag_len == kNoAuthTagLength) { - THROW_ERR_CRYPTO_INVALID_AUTH_TAG( - env(), "authTagLength required for %s", cipher_type); - return false; + // We treat ChaCha20-Poly1305 specially. Like GCM, the authentication tag + // length defaults to 16 bytes when encrypting. Unlike GCM, the + // authentication tag length also defaults to 16 bytes when decrypting, + // whereas GCM would accept any valid authentication tag length. + if (EVP_CIPHER_CTX_nid(ctx_.get()) == NID_chacha20_poly1305) { + auth_tag_len = 16; + } else { + THROW_ERR_CRYPTO_INVALID_AUTH_TAG( + env(), "authTagLength required for %s", cipher_type); + return false; + } } // TODO(tniessen) Support CCM decryption in FIPS mode @@ -593,7 +600,8 @@ bool CipherBase::InitAuthenticated( // Tell OpenSSL about the desired length. if (!EVP_CIPHER_CTX_ctrl(ctx_.get(), EVP_CTRL_AEAD_SET_TAG, auth_tag_len, nullptr)) { - THROW_ERR_CRYPTO_INVALID_AUTH_TAG(env()); + THROW_ERR_CRYPTO_INVALID_AUTH_TAG( + env(), "Invalid authentication tag length: %u", auth_tag_len); return false; } diff --git a/src/crypto/crypto_common.cc b/src/crypto/crypto_common.cc index dce0774e8fa632..0d59bac387e839 100644 --- a/src/crypto/crypto_common.cc +++ b/src/crypto/crypto_common.cc @@ -53,7 +53,7 @@ static constexpr int kX509NameFlagsRFC2253WithinUtf8JSON = ~ASN1_STRFLGS_ESC_MSB & ~ASN1_STRFLGS_ESC_CTRL; -int SSL_CTX_get_issuer(SSL_CTX* ctx, X509* cert, X509** issuer) { +bool SSL_CTX_get_issuer(SSL_CTX* ctx, X509* cert, X509** issuer) { X509_STORE* store = SSL_CTX_get_cert_store(ctx); DeleteFnPtr store_ctx( X509_STORE_CTX_new()); @@ -116,27 +116,12 @@ MaybeLocal GetSSLOCSPResponse( return ret; } -bool SetTLSSession( - const SSLPointer& ssl, - const unsigned char* buf, - size_t length) { - SSLSessionPointer s(d2i_SSL_SESSION(nullptr, &buf, length)); - return s == nullptr ? false : SetTLSSession(ssl, s); -} - bool SetTLSSession( const SSLPointer& ssl, const SSLSessionPointer& session) { return session != nullptr && SSL_set_session(ssl.get(), session.get()) == 1; } -SSLSessionPointer GetTLSSession(Local val) { - if (!val->IsArrayBufferView()) - return SSLSessionPointer(); - ArrayBufferViewContents sbuf(val.As()); - return GetTLSSession(sbuf.data(), sbuf.length()); -} - SSLSessionPointer GetTLSSession(const unsigned char* buf, size_t length) { return SSLSessionPointer(d2i_SSL_SESSION(nullptr, &buf, length)); } @@ -163,7 +148,8 @@ long VerifyPeerCertificate( // NOLINT(runtime/int) return err; } -int UseSNIContext(const SSLPointer& ssl, BaseObjectPtr context) { +bool UseSNIContext( + const SSLPointer& ssl, BaseObjectPtr context) { SSL_CTX* ctx = context->ctx_.get(); X509* x509 = SSL_CTX_get0_certificate(ctx); EVP_PKEY* pkey = SSL_CTX_get0_privatekey(ctx); @@ -173,7 +159,7 @@ int UseSNIContext(const SSLPointer& ssl, BaseObjectPtr context) { if (err == 1) err = SSL_use_certificate(ssl.get(), x509); if (err == 1) err = SSL_use_PrivateKey(ssl.get(), pkey); if (err == 1 && chain != nullptr) err = SSL_set1_chain(ssl.get(), chain); - return err; + return err == 1; } const char* GetClientHelloALPN(const SSLPointer& ssl) { @@ -302,7 +288,7 @@ Local ToV8Value(Environment* env, const BIOPointer& bio) { mem->data, NewStringType::kNormal, mem->length); - USE(BIO_reset(bio.get())); + CHECK_EQ(BIO_reset(bio.get()), 1); return ret.FromMaybe(Local()); } @@ -435,34 +421,25 @@ MaybeLocal GetLastIssuedCert( void AddFingerprintDigest( const unsigned char* md, unsigned int md_size, - char (*fingerprint)[3 * EVP_MAX_MD_SIZE + 1]) { + char fingerprint[3 * EVP_MAX_MD_SIZE]) { unsigned int i; const char hex[] = "0123456789ABCDEF"; for (i = 0; i < md_size; i++) { - (*fingerprint)[3*i] = hex[(md[i] & 0xf0) >> 4]; - (*fingerprint)[(3*i)+1] = hex[(md[i] & 0x0f)]; - (*fingerprint)[(3*i)+2] = ':'; + fingerprint[3*i] = hex[(md[i] & 0xf0) >> 4]; + fingerprint[(3*i)+1] = hex[(md[i] & 0x0f)]; + fingerprint[(3*i)+2] = ':'; } - if (md_size > 0) { - (*fingerprint)[(3*(md_size-1))+2] = '\0'; - } else { - (*fingerprint)[0] = '\0'; - } + DCHECK_GT(md_size, 0); + fingerprint[(3 * (md_size - 1)) + 2] = '\0'; } -MaybeLocal GetCurveASN1Name(Environment* env, const int nid) { - const char* nist = OBJ_nid2sn(nid); - return nist != nullptr ? - MaybeLocal(OneByteString(env->isolate(), nist)) : - MaybeLocal(Undefined(env->isolate())); -} - -MaybeLocal GetCurveNistName(Environment* env, const int nid) { - const char* nist = EC_curve_nid2nist(nid); - return nist != nullptr ? - MaybeLocal(OneByteString(env->isolate(), nist)) : +template +MaybeLocal GetCurveName(Environment* env, const int nid) { + const char* name = nid2string(nid); + return name != nullptr ? + MaybeLocal(OneByteString(env->isolate(), name)) : MaybeLocal(Undefined(env->isolate())); } @@ -518,13 +495,7 @@ MaybeLocal GetExponentString( const BIOPointer& bio, const BIGNUM* e) { uint64_t exponent_word = static_cast(BN_get_word(e)); - uint32_t lo = static_cast(exponent_word); - uint32_t hi = static_cast(exponent_word >> 32); - if (hi == 0) - BIO_printf(bio.get(), "0x%x", lo); - else - BIO_printf(bio.get(), "0x%x%08x", hi, lo); - + BIO_printf(bio.get(), "0x%" PRIx64, exponent_word); return ToV8Value(env, bio); } @@ -600,10 +571,10 @@ MaybeLocal GetFingerprintDigest( X509* cert) { unsigned char md[EVP_MAX_MD_SIZE]; unsigned int md_size; - char fingerprint[EVP_MAX_MD_SIZE * 3 + 1]; + char fingerprint[EVP_MAX_MD_SIZE * 3]; if (X509_digest(cert, method, md, &md_size)) { - AddFingerprintDigest(md, md_size, &fingerprint); + AddFingerprintDigest(md, md_size, fingerprint); return OneByteString(env->isolate(), fingerprint); } return Undefined(env->isolate()); @@ -721,9 +692,8 @@ static inline void PrintUtf8AltName(const BIOPointer& out, true, safe_prefix); } -// This function currently emulates the behavior of i2v_GENERAL_NAME in a safer -// and less ambiguous way. -// TODO(tniessen): gradually improve the format in the next major version(s) +// This function emulates the behavior of i2v_GENERAL_NAME in a safer and less +// ambiguous way. "othername:" entries use the GENERAL_NAME_print format. static bool PrintGeneralName(const BIOPointer& out, const GENERAL_NAME* gen) { if (gen->type == GEN_DNS) { ASN1_IA5STRING* name = gen->d.dNSName; @@ -767,9 +737,10 @@ static bool PrintGeneralName(const BIOPointer& out, const GENERAL_NAME* gen) { return false; } char* oline = nullptr; - size_t n_bytes = BIO_get_mem_data(tmp.get(), &oline); + long n_bytes = BIO_get_mem_data(tmp.get(), &oline); // NOLINT(runtime/int) + CHECK_GE(n_bytes, 0); CHECK_IMPLIES(n_bytes != 0, oline != nullptr); - PrintAltName(out, oline, n_bytes, true, nullptr); + PrintAltName(out, oline, static_cast(n_bytes), true, nullptr); } else if (gen->type == GEN_IPADD) { BIO_printf(out.get(), "IP Address:"); const ASN1_OCTET_STRING* ip = gen->d.ip; @@ -795,33 +766,32 @@ static bool PrintGeneralName(const BIOPointer& out, const GENERAL_NAME* gen) { OBJ_obj2txt(oline, sizeof(oline), gen->d.rid, true); BIO_printf(out.get(), "Registered ID:%s", oline); } else if (gen->type == GEN_OTHERNAME) { - // TODO(tniessen): the format that is used here is based on OpenSSL's - // implementation of i2v_GENERAL_NAME (as of OpenSSL 3.0.1), mostly for - // backward compatibility. It is somewhat awkward, especially when passed to - // translatePeerCertificate, and should be changed in the future, probably - // to the format used by GENERAL_NAME_print (in a major release). + // The format that is used here is based on OpenSSL's implementation of + // GENERAL_NAME_print (as of OpenSSL 3.0.1). Earlier versions of Node.js + // instead produced the same format as i2v_GENERAL_NAME, which was somewhat + // awkward, especially when passed to translatePeerCertificate. bool unicode = true; const char* prefix = nullptr; - // OpenSSL 1.1.1 does not support othername in i2v_GENERAL_NAME and may not - // define these NIDs. + // OpenSSL 1.1.1 does not support othername in GENERAL_NAME_print and may + // not define these NIDs. #if OPENSSL_VERSION_MAJOR >= 3 int nid = OBJ_obj2nid(gen->d.otherName->type_id); switch (nid) { case NID_id_on_SmtpUTF8Mailbox: - prefix = " SmtpUTF8Mailbox:"; + prefix = "SmtpUTF8Mailbox"; break; case NID_XmppAddr: - prefix = " XmppAddr:"; + prefix = "XmppAddr"; break; case NID_SRVName: - prefix = " SRVName:"; + prefix = "SRVName"; unicode = false; break; case NID_ms_upn: - prefix = " UPN:"; + prefix = "UPN"; break; case NID_NAIRealm: - prefix = " NAIRealm:"; + prefix = "NAIRealm"; break; } #endif // OPENSSL_VERSION_MAJOR >= 3 @@ -925,7 +895,7 @@ v8::MaybeLocal GetSubjectAltNameString( CHECK_NOT_NULL(ext); if (!SafeX509SubjectAltNamePrint(bio, ext)) { - USE(BIO_reset(bio.get())); + CHECK_EQ(BIO_reset(bio.get()), 1); return v8::Null(env->isolate()); } @@ -944,7 +914,7 @@ v8::MaybeLocal GetInfoAccessString( CHECK_NOT_NULL(ext); if (!SafeX509InfoAccessPrint(bio, ext)) { - USE(BIO_reset(bio.get())); + CHECK_EQ(BIO_reset(bio.get()), 1); return v8::Null(env->isolate()); } @@ -961,7 +931,7 @@ MaybeLocal GetIssuerString( issuer_name, 0, kX509NameFlagsMultiline) <= 0) { - USE(BIO_reset(bio.get())); + CHECK_EQ(BIO_reset(bio.get()), 1); return Undefined(env->isolate()); } @@ -977,7 +947,7 @@ MaybeLocal GetSubject( X509_get_subject_name(cert), 0, kX509NameFlagsMultiline) <= 0) { - USE(BIO_reset(bio.get())); + CHECK_EQ(BIO_reset(bio.get()), 1); return Undefined(env->isolate()); } @@ -1312,8 +1282,7 @@ MaybeLocal GetPeerCert( MaybeLocal X509ToObject( Environment* env, - X509* cert, - bool names_as_string) { + X509* cert) { EscapableHandleScope scope(env->isolate()); Local context = env->context(); Local info = Object::New(env->isolate()); @@ -1321,34 +1290,15 @@ MaybeLocal X509ToObject( BIOPointer bio(BIO_new(BIO_s_mem())); CHECK(bio); - if (names_as_string) { - // TODO(tniessen): this branch should not have to exist. It is only here - // because toLegacyObject() does not actually return a legacy object, and - // instead represents subject and issuer as strings. - if (!Set(context, - info, - env->subject_string(), - GetSubject(env, bio, cert)) || - !Set(context, - info, - env->issuer_string(), - GetIssuerString(env, bio, cert))) { - return MaybeLocal(); - } - } else { - if (!Set(context, - info, - env->subject_string(), - GetX509NameObject(env, cert)) || - !Set(context, - info, - env->issuer_string(), - GetX509NameObject(env, cert))) { - return MaybeLocal(); - } - } - if (!Set(context, + info, + env->subject_string(), + GetX509NameObject(env, cert)) || + !Set(context, + info, + env->issuer_string(), + GetX509NameObject(env, cert)) || + !Set(context, info, env->subjectaltname_string(), GetSubjectAltNameString(env, bio, cert)) || @@ -1413,11 +1363,11 @@ MaybeLocal X509ToObject( if (!Set(context, info, env->asn1curve_string(), - GetCurveASN1Name(env, nid)) || + GetCurveName(env, nid)) || !Set(context, info, env->nistcurve_string(), - GetCurveNistName(env, nid))) { + GetCurveName(env, nid))) { return MaybeLocal(); } } else { diff --git a/src/crypto/crypto_common.h b/src/crypto/crypto_common.h index 2e7ea236b40184..5a6869c6ca8845 100644 --- a/src/crypto/crypto_common.h +++ b/src/crypto/crypto_common.h @@ -25,7 +25,7 @@ struct StackOfXASN1Deleter { }; using StackOfASN1 = std::unique_ptr; -int SSL_CTX_get_issuer(SSL_CTX* ctx, X509* cert, X509** issuer); +bool SSL_CTX_get_issuer(SSL_CTX* ctx, X509* cert, X509** issuer); void LogSecret( const SSLPointer& ssl, @@ -42,24 +42,17 @@ v8::MaybeLocal GetSSLOCSPResponse( SSL* ssl, v8::Local default_value); -bool SetTLSSession( - const SSLPointer& ssl, - const unsigned char* buf, - size_t length); - bool SetTLSSession( const SSLPointer& ssl, const SSLSessionPointer& session); -SSLSessionPointer GetTLSSession(v8::Local val); - SSLSessionPointer GetTLSSession(const unsigned char* buf, size_t length); long VerifyPeerCertificate( // NOLINT(runtime/int) const SSLPointer& ssl, long def = X509_V_ERR_UNSPECIFIED); // NOLINT(runtime/int) -int UseSNIContext(const SSLPointer& ssl, BaseObjectPtr context); +bool UseSNIContext(const SSLPointer& ssl, BaseObjectPtr context); const char* GetClientHelloALPN(const SSLPointer& ssl); @@ -116,8 +109,7 @@ v8::MaybeLocal ECPointToBuffer( v8::MaybeLocal X509ToObject( Environment* env, - X509* cert, - bool names_as_string = false); + X509* cert); v8::MaybeLocal GetValidTo( Environment* env, diff --git a/src/crypto/crypto_dh.cc b/src/crypto/crypto_dh.cc index 8a2c559d783eda..702c5e083f8f80 100644 --- a/src/crypto/crypto_dh.cc +++ b/src/crypto/crypto_dh.cc @@ -13,6 +13,8 @@ namespace node { +using v8::ArrayBuffer; +using v8::BackingStore; using v8::ConstructorBehavior; using v8::DontDelete; using v8::FunctionCallback; @@ -30,14 +32,13 @@ using v8::ReadOnly; using v8::SideEffectType; using v8::Signature; using v8::String; -using v8::Uint8Array; using v8::Value; namespace crypto { namespace { -static void ZeroPadDiffieHellmanSecret(size_t remainder_size, - char* data, - size_t length) { +void ZeroPadDiffieHellmanSecret(size_t remainder_size, + char* data, + size_t length) { // DH_size returns number of bytes in a prime number. // DH_compute_key returns number of bytes in a remainder of exponent, which // may have less bytes than a prime number. Therefore add 0-padding to the @@ -50,10 +51,6 @@ static void ZeroPadDiffieHellmanSecret(size_t remainder_size, memset(data, 0, padding); } } -static void ZeroPadDiffieHellmanSecret(size_t remainder_size, - AllocatedBuffer* ret) { - ZeroPadDiffieHellmanSecret(remainder_size, ret->data(), ret->size()); -} } // namespace DiffieHellman::DiffieHellman(Environment* env, Local wrap) @@ -275,13 +272,24 @@ void DiffieHellman::GenerateKeys(const FunctionCallbackInfo& args) { const BIGNUM* pub_key; DH_get0_key(diffieHellman->dh_.get(), &pub_key, nullptr); - const int size = BN_num_bytes(pub_key); - CHECK_GE(size, 0); - AllocatedBuffer data = AllocatedBuffer::AllocateManaged(env, size); - CHECK_EQ(size, - BN_bn2binpad( - pub_key, reinterpret_cast(data.data()), size)); - args.GetReturnValue().Set(data.ToBuffer().FromMaybe(Local())); + + std::unique_ptr bs; + { + const int size = BN_num_bytes(pub_key); + CHECK_GE(size, 0); + NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); + bs = ArrayBuffer::NewBackingStore(env->isolate(), size); + } + + CHECK_EQ(static_cast(bs->ByteLength()), + BN_bn2binpad(pub_key, + static_cast(bs->Data()), + bs->ByteLength())); + + Local ab = ArrayBuffer::New(env->isolate(), std::move(bs)); + Local buffer; + if (!Buffer::New(env, ab, 0, ab->ByteLength()).ToLocal(&buffer)) return; + args.GetReturnValue().Set(buffer); } @@ -297,13 +305,23 @@ void DiffieHellman::GetField(const FunctionCallbackInfo& args, if (num == nullptr) return THROW_ERR_CRYPTO_INVALID_STATE(env, err_if_null); - const int size = BN_num_bytes(num); - CHECK_GE(size, 0); - AllocatedBuffer data = AllocatedBuffer::AllocateManaged(env, size); - CHECK_EQ( - size, - BN_bn2binpad(num, reinterpret_cast(data.data()), size)); - args.GetReturnValue().Set(data.ToBuffer().FromMaybe(Local())); + std::unique_ptr bs; + { + const int size = BN_num_bytes(num); + CHECK_GE(size, 0); + NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); + bs = ArrayBuffer::NewBackingStore(env->isolate(), size); + } + + CHECK_EQ(static_cast(bs->ByteLength()), + BN_bn2binpad(num, + static_cast(bs->Data()), + bs->ByteLength())); + + Local ab = ArrayBuffer::New(env->isolate(), std::move(bs)); + Local buffer; + if (!Buffer::New(env, ab, 0, ab->ByteLength()).ToLocal(&buffer)) return; + args.GetReturnValue().Set(buffer); } void DiffieHellman::GetPrime(const FunctionCallbackInfo& args) { @@ -352,10 +370,14 @@ void DiffieHellman::ComputeSecret(const FunctionCallbackInfo& args) { return THROW_ERR_OUT_OF_RANGE(env, "secret is too big"); BignumPointer key(BN_bin2bn(key_buf.data(), key_buf.size(), nullptr)); - AllocatedBuffer ret = - AllocatedBuffer::AllocateManaged(env, DH_size(diffieHellman->dh_.get())); + std::unique_ptr bs; + { + NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); + bs = ArrayBuffer::NewBackingStore(env->isolate(), + DH_size(diffieHellman->dh_.get())); + } - int size = DH_compute_key(reinterpret_cast(ret.data()), + int size = DH_compute_key(static_cast(bs->Data()), key.get(), diffieHellman->dh_.get()); @@ -383,9 +405,14 @@ void DiffieHellman::ComputeSecret(const FunctionCallbackInfo& args) { } CHECK_GE(size, 0); - ZeroPadDiffieHellmanSecret(static_cast(size), &ret); + ZeroPadDiffieHellmanSecret(size, + static_cast(bs->Data()), + bs->ByteLength()); - args.GetReturnValue().Set(ret.ToBuffer().FromMaybe(Local())); + Local ab = ArrayBuffer::New(env->isolate(), std::move(bs)); + Local buffer; + if (!Buffer::New(env, ab, 0, ab->ByteLength()).ToLocal(&buffer)) return; + args.GetReturnValue().Set(buffer); } void DiffieHellman::SetKey(const FunctionCallbackInfo& args, @@ -609,8 +636,10 @@ void DiffieHellman::Stateless(const FunctionCallbackInfo& args) { ManagedEVPPKey our_key = our_key_object->Data()->GetAsymmetricKey(); ManagedEVPPKey their_key = their_key_object->Data()->GetAsymmetricKey(); - Local out = StatelessDiffieHellmanThreadsafe(our_key, their_key) - .ToBuffer(env).FromMaybe(Local()); + Local out; + if (!StatelessDiffieHellmanThreadsafe(our_key, their_key) + .ToBuffer(env) + .ToLocal(&out)) return; if (Buffer::Length(out) == 0) return ThrowCryptoError(env, ERR_get_error(), "diffieHellman failed"); diff --git a/src/crypto/crypto_ec.cc b/src/crypto/crypto_ec.cc index 868c3465ad33d1..e05b5388f0450a 100644 --- a/src/crypto/crypto_ec.cc +++ b/src/crypto/crypto_ec.cc @@ -20,10 +20,13 @@ namespace node { using v8::Array; +using v8::ArrayBuffer; +using v8::BackingStore; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; using v8::Int32; using v8::Just; +using v8::JustVoid; using v8::Local; using v8::Maybe; using v8::Nothing; @@ -219,17 +222,23 @@ void ECDH::ComputeSecret(const FunctionCallbackInfo& args) { return; } - // NOTE: field_size is in bits - int field_size = EC_GROUP_get_degree(ecdh->group_); - size_t out_len = (field_size + 7) / 8; - AllocatedBuffer out = AllocatedBuffer::AllocateManaged(env, out_len); + std::unique_ptr bs; + { + NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); + // NOTE: field_size is in bits + int field_size = EC_GROUP_get_degree(ecdh->group_); + size_t out_len = (field_size + 7) / 8; + bs = ArrayBuffer::NewBackingStore(env->isolate(), out_len); + } - int r = ECDH_compute_key( - out.data(), out_len, pub.get(), ecdh->key_.get(), nullptr); - if (!r) + if (!ECDH_compute_key( + bs->Data(), bs->ByteLength(), pub.get(), ecdh->key_.get(), nullptr)) return THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to compute ECDH key"); - args.GetReturnValue().Set(out.ToBuffer().FromMaybe(Local())); + Local ab = ArrayBuffer::New(env->isolate(), std::move(bs)); + Local buffer; + if (!Buffer::New(env, ab, 0, ab->ByteLength()).ToLocal(&buffer)) return; + args.GetReturnValue().Set(buffer); } void ECDH::GetPublicKey(const FunctionCallbackInfo& args) { @@ -269,13 +278,19 @@ void ECDH::GetPrivateKey(const FunctionCallbackInfo& args) { return THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to get ECDH private key"); - const int size = BN_num_bytes(b); - AllocatedBuffer out = AllocatedBuffer::AllocateManaged(env, size); - CHECK_EQ(size, BN_bn2binpad(b, - reinterpret_cast(out.data()), - size)); + std::unique_ptr bs; + { + NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); + bs = ArrayBuffer::NewBackingStore(env->isolate(), BN_num_bytes(b)); + } + CHECK_EQ(static_cast(bs->ByteLength()), + BN_bn2binpad( + b, static_cast(bs->Data()), bs->ByteLength())); - args.GetReturnValue().Set(out.ToBuffer().FromMaybe(Local())); + Local ab = ArrayBuffer::New(env->isolate(), std::move(bs)); + Local buffer; + if (!Buffer::New(env, ab, 0, ab->ByteLength()).ToLocal(&buffer)) return; + args.GetReturnValue().Set(buffer); } void ECDH::SetPrivateKey(const FunctionCallbackInfo& args) { @@ -711,7 +726,7 @@ WebCryptoKeyExportStatus ECKeyExportTraits::DoExport( } } -Maybe ExportJWKEcKey( +Maybe ExportJWKEcKey( Environment* env, std::shared_ptr key, Local target) { @@ -732,13 +747,17 @@ Maybe ExportJWKEcKey( BignumPointer x(BN_new()); BignumPointer y(BN_new()); - EC_POINT_get_affine_coordinates(group, pub, x.get(), y.get(), nullptr); + if (!EC_POINT_get_affine_coordinates(group, pub, x.get(), y.get(), nullptr)) { + ThrowCryptoError(env, ERR_get_error(), + "Failed to get elliptic-curve point coordinates"); + return Nothing(); + } if (target->Set( env->context(), env->jwk_kty_string(), env->jwk_ec_string()).IsNothing()) { - return Nothing(); + return Nothing(); } if (SetEncodedValue( @@ -753,7 +772,7 @@ Maybe ExportJWKEcKey( env->jwk_y_string(), y.get(), degree_bytes).IsNothing()) { - return Nothing(); + return Nothing(); } Local crv_name; @@ -774,14 +793,14 @@ Maybe ExportJWKEcKey( default: { THROW_ERR_CRYPTO_JWK_UNSUPPORTED_CURVE( env, "Unsupported JWK EC curve: %s.", OBJ_nid2sn(nid)); - return Nothing(); + return Nothing(); } } if (target->Set( env->context(), env->jwk_crv_string(), crv_name).IsNothing()) { - return Nothing(); + return Nothing(); } if (key->GetKeyType() == kKeyTypePrivate) { @@ -791,10 +810,10 @@ Maybe ExportJWKEcKey( target, env->jwk_d_string(), pvt, - degree_bytes); + degree_bytes).IsJust() ? JustVoid() : Nothing(); } - return Just(true); + return JustVoid(); } Maybe ExportJWKEdKey( diff --git a/src/crypto/crypto_ec.h b/src/crypto/crypto_ec.h index 34d37c7e44220d..bc4160fc8bee01 100644 --- a/src/crypto/crypto_ec.h +++ b/src/crypto/crypto_ec.h @@ -144,7 +144,7 @@ struct ECKeyExportTraits final { using ECKeyExportJob = KeyExportJob; -v8::Maybe ExportJWKEcKey( +v8::Maybe ExportJWKEcKey( Environment* env, std::shared_ptr key, v8::Local target); diff --git a/src/crypto/crypto_hmac.cc b/src/crypto/crypto_hmac.cc index 38503ac8ac33d4..d6a652ff8f5ee0 100644 --- a/src/crypto/crypto_hmac.cc +++ b/src/crypto/crypto_hmac.cc @@ -124,8 +124,11 @@ void Hmac::HmacDigest(const FunctionCallbackInfo& args) { unsigned int md_len = 0; if (hmac->ctx_) { - HMAC_Final(hmac->ctx_.get(), md_value, &md_len); + bool ok = HMAC_Final(hmac->ctx_.get(), md_value, &md_len); hmac->ctx_.reset(); + if (!ok) { + return ThrowCryptoError(env, ERR_get_error(), "Failed to finalize HMAC"); + } } Local error; diff --git a/src/crypto/crypto_keys.cc b/src/crypto/crypto_keys.cc index c5b3e6b37183fd..5dd04edfbfa1cf 100644 --- a/src/crypto/crypto_keys.cc +++ b/src/crypto/crypto_keys.cc @@ -497,7 +497,8 @@ Maybe ExportJWKAsymmetricKey( break; } case EVP_PKEY_RSA: return ExportJWKRsaKey(env, key, target); - case EVP_PKEY_EC: return ExportJWKEcKey(env, key, target); + case EVP_PKEY_EC: return ExportJWKEcKey(env, key, target).IsJust() ? + Just(true) : Nothing(); case EVP_PKEY_ED25519: // Fall through case EVP_PKEY_ED448: @@ -921,6 +922,7 @@ v8::Local KeyObjectHandle::Initialize(Environment* env) { env->SetProtoMethod(t, "initEDRaw", InitEDRaw); env->SetProtoMethod(t, "initJwk", InitJWK); env->SetProtoMethod(t, "keyDetail", GetKeyDetail); + env->SetProtoMethod(t, "equals", Equals); auto function = t->GetFunction(env->context()).ToLocalChecked(); env->set_crypto_key_object_handle_constructor(function); @@ -939,6 +941,7 @@ void KeyObjectHandle::RegisterExternalReferences( registry->Register(InitEDRaw); registry->Register(InitJWK); registry->Register(GetKeyDetail); + registry->Register(Equals); } MaybeLocal KeyObjectHandle::Create( @@ -1134,6 +1137,54 @@ void KeyObjectHandle::InitEDRaw(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(true); } +void KeyObjectHandle::Equals(const FunctionCallbackInfo& args) { + KeyObjectHandle* self_handle; + KeyObjectHandle* arg_handle; + ASSIGN_OR_RETURN_UNWRAP(&self_handle, args.Holder()); + ASSIGN_OR_RETURN_UNWRAP(&arg_handle, args[0].As()); + std::shared_ptr key = self_handle->Data(); + std::shared_ptr key2 = arg_handle->Data(); + + KeyType key_type = key->GetKeyType(); + CHECK_EQ(key_type, key2->GetKeyType()); + + bool ret; + switch (key_type) { + case kKeyTypeSecret: { + size_t size = key->GetSymmetricKeySize(); + if (size == key2->GetSymmetricKeySize()) { + ret = CRYPTO_memcmp( + key->GetSymmetricKey(), + key2->GetSymmetricKey(), + size) == 0; + } else { + ret = false; + } + break; + } + case kKeyTypePublic: + case kKeyTypePrivate: { + EVP_PKEY* pkey = key->GetAsymmetricKey().get(); + EVP_PKEY* pkey2 = key2->GetAsymmetricKey().get(); +#if OPENSSL_VERSION_MAJOR >= 3 + int ok = EVP_PKEY_eq(pkey, pkey2); +#else + int ok = EVP_PKEY_cmp(pkey, pkey2); +#endif + if (ok == -2) { + Environment* env = Environment::GetCurrent(args); + return THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env); + } + ret = ok == 1; + break; + } + default: + UNREACHABLE("unsupported key type"); + } + + args.GetReturnValue().Set(ret); +} + void KeyObjectHandle::GetKeyDetail(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); KeyObjectHandle* key; @@ -1374,6 +1425,7 @@ WebCryptoKeyExportStatus PKEY_SPKI_Export( ManagedEVPPKey m_pkey = key_data->GetAsymmetricKey(); Mutex::ScopedLock lock(*m_pkey.mutex()); BIOPointer bio(BIO_new(BIO_s_mem())); + CHECK(bio); if (!i2d_PUBKEY_bio(bio.get(), m_pkey.get())) return WebCryptoKeyExportStatus::FAILED; @@ -1389,6 +1441,7 @@ WebCryptoKeyExportStatus PKEY_PKCS8_Export( Mutex::ScopedLock lock(*m_pkey.mutex()); BIOPointer bio(BIO_new(BIO_s_mem())); + CHECK(bio); PKCS8Pointer p8inf(EVP_PKEY2PKCS8(m_pkey.get())); if (!i2d_PKCS8_PRIV_KEY_INFO_bio(bio.get(), p8inf.get())) return WebCryptoKeyExportStatus::FAILED; diff --git a/src/crypto/crypto_keys.h b/src/crypto/crypto_keys.h index 48d38cd20b0899..d06565d8ad74a3 100644 --- a/src/crypto/crypto_keys.h +++ b/src/crypto/crypto_keys.h @@ -189,6 +189,7 @@ class KeyObjectHandle : public BaseObject { static void InitEDRaw(const v8::FunctionCallbackInfo& args); static void InitJWK(const v8::FunctionCallbackInfo& args); static void GetKeyDetail(const v8::FunctionCallbackInfo& args); + static void Equals(const v8::FunctionCallbackInfo& args); static void ExportJWK(const v8::FunctionCallbackInfo& args); diff --git a/src/crypto/crypto_random.cc b/src/crypto/crypto_random.cc index fc88deb460314c..7a05dcc16b7389 100644 --- a/src/crypto/crypto_random.cc +++ b/src/crypto/crypto_random.cc @@ -122,11 +122,9 @@ Maybe RandomPrimeTraits::AdditionalConfig( } } + // The JS interface already ensures that the (positive) size fits into an int. int bits = static_cast(size); - if (bits < 0) { - THROW_ERR_OUT_OF_RANGE(env, "invalid size"); - return Nothing(); - } + CHECK_GT(bits, 0); if (params->add) { if (BN_num_bits(params->add.get()) > bits) { diff --git a/src/crypto/crypto_tls.cc b/src/crypto/crypto_tls.cc index cfe760adb3af1d..2d0f0c4e73555b 100644 --- a/src/crypto/crypto_tls.cc +++ b/src/crypto/crypto_tls.cc @@ -25,7 +25,6 @@ #include "crypto/crypto_util.h" #include "crypto/crypto_bio.h" #include "crypto/crypto_clienthello-inl.h" -#include "allocated_buffer-inl.h" #include "async_wrap-inl.h" #include "debug_utils-inl.h" #include "memory_tracker-inl.h" @@ -1611,9 +1610,19 @@ void TLSWrap::GetFinished(const FunctionCallbackInfo& args) { if (len == 0) return; - AllocatedBuffer buf = AllocatedBuffer::AllocateManaged(env, len); - CHECK_EQ(len, SSL_get_finished(w->ssl_.get(), buf.data(), len)); - args.GetReturnValue().Set(buf.ToBuffer().FromMaybe(Local())); + std::unique_ptr bs; + { + NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); + bs = ArrayBuffer::NewBackingStore(env->isolate(), len); + } + + CHECK_EQ(bs->ByteLength(), + SSL_get_finished(w->ssl_.get(), bs->Data(), bs->ByteLength())); + + Local ab = ArrayBuffer::New(env->isolate(), std::move(bs)); + Local buffer; + if (!Buffer::New(env, ab, 0, ab->ByteLength()).ToLocal(&buffer)) return; + args.GetReturnValue().Set(buffer); } void TLSWrap::GetPeerFinished(const FunctionCallbackInfo& args) { @@ -1632,9 +1641,19 @@ void TLSWrap::GetPeerFinished(const FunctionCallbackInfo& args) { if (len == 0) return; - AllocatedBuffer buf = AllocatedBuffer::AllocateManaged(env, len); - CHECK_EQ(len, SSL_get_peer_finished(w->ssl_.get(), buf.data(), len)); - args.GetReturnValue().Set(buf.ToBuffer().FromMaybe(Local())); + std::unique_ptr bs; + { + NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); + bs = ArrayBuffer::NewBackingStore(env->isolate(), len); + } + + CHECK_EQ(bs->ByteLength(), + SSL_get_peer_finished(w->ssl_.get(), bs->Data(), bs->ByteLength())); + + Local ab = ArrayBuffer::New(env->isolate(), std::move(bs)); + Local buffer; + if (!Buffer::New(env, ab, 0, ab->ByteLength()).ToLocal(&buffer)) return; + args.GetReturnValue().Set(buffer); } void TLSWrap::GetSession(const FunctionCallbackInfo& args) { @@ -1651,10 +1670,19 @@ void TLSWrap::GetSession(const FunctionCallbackInfo& args) { if (slen <= 0) return; // Invalid or malformed session. - AllocatedBuffer sbuf = AllocatedBuffer::AllocateManaged(env, slen); - unsigned char* p = reinterpret_cast(sbuf.data()); + std::unique_ptr bs; + { + NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); + bs = ArrayBuffer::NewBackingStore(env->isolate(), slen); + } + + unsigned char* p = static_cast(bs->Data()); CHECK_LT(0, i2d_SSL_SESSION(sess, &p)); - args.GetReturnValue().Set(sbuf.ToBuffer().FromMaybe(Local())); + + Local ab = ArrayBuffer::New(env->isolate(), std::move(bs)); + Local buffer; + if (!Buffer::New(env, ab, 0, ab->ByteLength()).ToLocal(&buffer)) return; + args.GetReturnValue().Set(buffer); } void TLSWrap::SetSession(const FunctionCallbackInfo& args) { @@ -1667,10 +1695,10 @@ void TLSWrap::SetSession(const FunctionCallbackInfo& args) { return THROW_ERR_MISSING_ARGS(env, "Session argument is mandatory"); THROW_AND_RETURN_IF_NOT_BUFFER(env, args[0], "Session"); - - SSLSessionPointer sess = GetTLSSession(args[0]); + ArrayBufferViewContents sbuf(args[0]); + SSLSessionPointer sess = GetTLSSession(sbuf.data(), sbuf.length()); if (sess == nullptr) - return; + return; // TODO(tniessen): figure out error handling if (!SetTLSSession(w->ssl_, sess)) return env->ThrowError("SSL_set_session error"); @@ -1825,7 +1853,11 @@ void TLSWrap::ExportKeyingMaterial(const FunctionCallbackInfo& args) { uint32_t olen = args[0].As()->Value(); Utf8Value label(env->isolate(), args[1]); - AllocatedBuffer out = AllocatedBuffer::AllocateManaged(env, olen); + std::unique_ptr bs; + { + NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); + bs = ArrayBuffer::NewBackingStore(env->isolate(), olen); + } ByteSource context; bool use_context = !args[2]->IsUndefined(); @@ -1834,11 +1866,11 @@ void TLSWrap::ExportKeyingMaterial(const FunctionCallbackInfo& args) { if (SSL_export_keying_material( w->ssl_.get(), - reinterpret_cast(out.data()), + static_cast(bs->Data()), olen, *label, label.length(), - reinterpret_cast(context.get()), + context.data(), context.size(), use_context) != 1) { return ThrowCryptoError( @@ -1847,7 +1879,10 @@ void TLSWrap::ExportKeyingMaterial(const FunctionCallbackInfo& args) { "SSL_export_keying_material"); } - args.GetReturnValue().Set(out.ToBuffer().FromMaybe(Local())); + Local ab = ArrayBuffer::New(env->isolate(), std::move(bs)); + Local buffer; + if (!Buffer::New(env, ab, 0, ab->ByteLength()).ToLocal(&buffer)) return; + args.GetReturnValue().Set(buffer); } void TLSWrap::EndParser(const FunctionCallbackInfo& args) { diff --git a/src/crypto/crypto_util.cc b/src/crypto/crypto_util.cc index e93edd4b2fc952..bbc86e6d88986f 100644 --- a/src/crypto/crypto_util.cc +++ b/src/crypto/crypto_util.cc @@ -136,7 +136,13 @@ bool InitCryptoOnce(Isolate* isolate) { return true; } +// Protect accesses to FIPS state with a mutex. This should potentially +// be part of a larger mutex for global OpenSSL state. +static Mutex fips_mutex; + void InitCryptoOnce() { + Mutex::ScopedLock lock(per_process::cli_options_mutex); + Mutex::ScopedLock fips_lock(fips_mutex); #ifndef OPENSSL_IS_BORINGSSL OPENSSL_INIT_SETTINGS* settings = OPENSSL_INIT_new(); @@ -196,6 +202,9 @@ void InitCryptoOnce() { } void GetFipsCrypto(const FunctionCallbackInfo& args) { + Mutex::ScopedLock lock(per_process::cli_options_mutex); + Mutex::ScopedLock fips_lock(fips_mutex); + #if OPENSSL_VERSION_MAJOR >= 3 args.GetReturnValue().Set(EVP_default_properties_is_fips_enabled(nullptr) ? 1 : 0); @@ -205,8 +214,13 @@ void GetFipsCrypto(const FunctionCallbackInfo& args) { } void SetFipsCrypto(const FunctionCallbackInfo& args) { + Mutex::ScopedLock lock(per_process::cli_options_mutex); + Mutex::ScopedLock fips_lock(fips_mutex); + CHECK(!per_process::cli_options->force_fips_crypto); Environment* env = Environment::GetCurrent(args); + // TODO(addaleax): This should not be possible to set from worker threads. + // CHECK(env->owns_process_state()); bool enable = args[0]->BooleanValue(env->isolate()); #if OPENSSL_VERSION_MAJOR >= 3 @@ -227,6 +241,9 @@ void SetFipsCrypto(const FunctionCallbackInfo& args) { } void TestFipsCrypto(const v8::FunctionCallbackInfo& args) { + Mutex::ScopedLock lock(per_process::cli_options_mutex); + Mutex::ScopedLock fips_lock(fips_mutex); + #ifdef OPENSSL_FIPS #if OPENSSL_VERSION_MAJOR >= 3 OSSL_PROVIDER* fips_provider = nullptr; diff --git a/src/crypto/crypto_x509.cc b/src/crypto/crypto_x509.cc index 7c299620dfeeb9..a301a1392152ec 100644 --- a/src/crypto/crypto_x509.cc +++ b/src/crypto/crypto_x509.cc @@ -196,6 +196,7 @@ void X509Certificate::Subject(const FunctionCallbackInfo& args) { X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.Holder()); BIOPointer bio(BIO_new(BIO_s_mem())); + CHECK(bio); Local ret; if (GetSubject(env, bio, cert->get()).ToLocal(&ret)) args.GetReturnValue().Set(ret); @@ -206,6 +207,7 @@ void X509Certificate::Issuer(const FunctionCallbackInfo& args) { X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.Holder()); BIOPointer bio(BIO_new(BIO_s_mem())); + CHECK(bio); Local ret; if (GetIssuerString(env, bio, cert->get()).ToLocal(&ret)) args.GetReturnValue().Set(ret); @@ -216,6 +218,7 @@ void X509Certificate::SubjectAltName(const FunctionCallbackInfo& args) { X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.Holder()); BIOPointer bio(BIO_new(BIO_s_mem())); + CHECK(bio); Local ret; if (GetSubjectAltNameString(env, bio, cert->get()).ToLocal(&ret)) args.GetReturnValue().Set(ret); @@ -226,6 +229,7 @@ void X509Certificate::InfoAccess(const FunctionCallbackInfo& args) { X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.Holder()); BIOPointer bio(BIO_new(BIO_s_mem())); + CHECK(bio); Local ret; if (GetInfoAccessString(env, bio, cert->get()).ToLocal(&ret)) args.GetReturnValue().Set(ret); @@ -236,6 +240,7 @@ void X509Certificate::ValidFrom(const FunctionCallbackInfo& args) { X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.Holder()); BIOPointer bio(BIO_new(BIO_s_mem())); + CHECK(bio); Local ret; if (GetValidFrom(env, cert->get(), bio).ToLocal(&ret)) args.GetReturnValue().Set(ret); @@ -246,6 +251,7 @@ void X509Certificate::ValidTo(const FunctionCallbackInfo& args) { X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.Holder()); BIOPointer bio(BIO_new(BIO_s_mem())); + CHECK(bio); Local ret; if (GetValidTo(env, cert->get(), bio).ToLocal(&ret)) args.GetReturnValue().Set(ret); @@ -325,6 +331,7 @@ void X509Certificate::Pem(const FunctionCallbackInfo& args) { X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.Holder()); BIOPointer bio(BIO_new(BIO_s_mem())); + CHECK(bio); if (PEM_write_bio_X509(bio.get(), cert->get())) args.GetReturnValue().Set(ToV8Value(env, bio)); } @@ -470,7 +477,7 @@ void X509Certificate::ToLegacy(const FunctionCallbackInfo& args) { X509Certificate* cert; ASSIGN_OR_RETURN_UNWRAP(&cert, args.Holder()); Local ret; - if (X509ToObject(env, cert->get(), true).ToLocal(&ret)) + if (X509ToObject(env, cert->get()).ToLocal(&ret)) args.GetReturnValue().Set(ret); } diff --git a/src/env-inl.h b/src/env-inl.h index 0a2a70eddd938c..46feb9bfa4ca93 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -107,8 +107,7 @@ inline void AsyncHooks::SetJSPromiseHooks(v8::Local init, js_promise_hooks_[3].Reset(env()->isolate(), resolve); for (auto it = contexts_.begin(); it != contexts_.end(); it++) { if (it->IsEmpty()) { - it = contexts_.erase(it); - it--; + contexts_.erase(it--); continue; } PersistentToLocal::Weak(env()->isolate(), *it) @@ -251,12 +250,11 @@ inline void AsyncHooks::AddContext(v8::Local ctx) { inline void AsyncHooks::RemoveContext(v8::Local ctx) { v8::Isolate* isolate = env()->isolate(); v8::HandleScope handle_scope(isolate); + contexts_.erase(std::remove_if(contexts_.begin(), + contexts_.end(), + [&](auto&& el) { return el.IsEmpty(); }), + contexts_.end()); for (auto it = contexts_.begin(); it != contexts_.end(); it++) { - if (it->IsEmpty()) { - it = contexts_.erase(it); - it--; - continue; - } v8::Local saved_context = PersistentToLocal::Weak(isolate, *it); if (saved_context == ctx) { @@ -869,6 +867,10 @@ inline bool Environment::owns_inspector() const { return flags_ & EnvironmentFlags::kOwnsInspector; } +inline bool Environment::should_create_inspector() const { + return (flags_ & EnvironmentFlags::kNoCreateInspector) == 0; +} + inline bool Environment::tracks_unmanaged_fds() const { return flags_ & EnvironmentFlags::kTrackUnmanagedFds; } diff --git a/src/env.cc b/src/env.cc index f2cb5243944c9d..3c07e9342fd338 100644 --- a/src/env.cc +++ b/src/env.cc @@ -545,21 +545,21 @@ void Environment::InitializeLibuv() { CHECK_EQ(0, uv_timer_init(event_loop(), timer_handle())); uv_unref(reinterpret_cast(timer_handle())); - uv_check_init(event_loop(), immediate_check_handle()); + CHECK_EQ(0, uv_check_init(event_loop(), immediate_check_handle())); uv_unref(reinterpret_cast(immediate_check_handle())); - uv_idle_init(event_loop(), immediate_idle_handle()); + CHECK_EQ(0, uv_idle_init(event_loop(), immediate_idle_handle())); - uv_check_start(immediate_check_handle(), CheckImmediate); + CHECK_EQ(0, uv_check_start(immediate_check_handle(), CheckImmediate)); // Inform V8's CPU profiler when we're idle. The profiler is sampling-based // but not all samples are created equal; mark the wall clock time spent in // epoll_wait() and friends so profiling tools can filter it out. The samples // still end up in v8.log but with state=IDLE rather than state=EXTERNAL. - uv_prepare_init(event_loop(), &idle_prepare_handle_); - uv_check_init(event_loop(), &idle_check_handle_); + CHECK_EQ(0, uv_prepare_init(event_loop(), &idle_prepare_handle_)); + CHECK_EQ(0, uv_check_init(event_loop(), &idle_check_handle_)); - uv_async_init( + CHECK_EQ(0, uv_async_init( event_loop(), &task_queues_async_, [](uv_async_t* async) { @@ -568,7 +568,7 @@ void Environment::InitializeLibuv() { HandleScope handle_scope(env->isolate()); Context::Scope context_scope(env->context()); env->RunAndClearNativeImmediates(); - }); + })); uv_unref(reinterpret_cast(&idle_prepare_handle_)); uv_unref(reinterpret_cast(&idle_check_handle_)); uv_unref(reinterpret_cast(&task_queues_async_)); diff --git a/src/env.h b/src/env.h index 8689129041618b..7e35833e45bd25 100644 --- a/src/env.h +++ b/src/env.h @@ -36,6 +36,7 @@ #include "node_binding.h" #include "node_external_reference.h" #include "node_main_instance.h" +#include "node_native_module.h" #include "node_options.h" #include "node_perf_common.h" #include "node_snapshotable.h" @@ -288,8 +289,6 @@ constexpr size_t kFsStatsBufferLength = V(input_string, "input") \ V(internal_binding_string, "internalBinding") \ V(internal_string, "internal") \ - V(ipv4_string, "IPv4") \ - V(ipv6_string, "IPv6") \ V(isclosing_string, "isClosing") \ V(issuer_string, "issuer") \ V(issuercert_string, "issuerCertificate") \ @@ -551,7 +550,9 @@ constexpr size_t kFsStatsBufferLength = V(tls_wrap_constructor_function, v8::Function) \ V(trace_category_state_function, v8::Function) \ V(udp_constructor_function, v8::Function) \ - V(url_constructor_function, v8::Function) + V(url_constructor_function, v8::Function) \ + V(wasm_streaming_compilation_impl, v8::Function) \ + V(wasm_streaming_object_constructor, v8::Function) class Environment; struct AllocatedBuffer; @@ -972,7 +973,6 @@ struct EnvSerializeInfo { }; struct SnapshotData { - SnapshotData() { blob.data = nullptr; } v8::StartupData blob; std::vector isolate_data_indices; EnvSerializeInfo env_info; @@ -1210,6 +1210,7 @@ class Environment : public MemoryRetainer { inline bool is_main_thread() const; inline bool no_native_addons() const; inline bool should_not_register_esm_loader() const; + inline bool should_create_inspector() const; inline bool owns_process_state() const; inline bool owns_inspector() const; inline bool tracks_unmanaged_fds() const; diff --git a/src/heap_utils.cc b/src/heap_utils.cc index 83334fa52695a4..0a68077d5d9b24 100644 --- a/src/heap_utils.cc +++ b/src/heap_utils.cc @@ -5,6 +5,17 @@ #include "stream_base-inl.h" #include "util-inl.h" +// Copied from https://github.com/nodejs/node/blob/b07dc4d19fdbc15b4f76557dc45b3ce3a43ad0c3/src/util.cc#L36-L41. +#ifdef _WIN32 +#include // _S_IREAD _S_IWRITE +#ifndef S_IRUSR +#define S_IRUSR _S_IREAD +#endif // S_IRUSR +#ifndef S_IWUSR +#define S_IWUSR _S_IWRITE +#endif // S_IWUSR +#endif + using v8::Array; using v8::Boolean; using v8::Context; @@ -16,8 +27,11 @@ using v8::Global; using v8::HandleScope; using v8::HeapSnapshot; using v8::Isolate; +using v8::JustVoid; using v8::Local; +using v8::Maybe; using v8::MaybeLocal; +using v8::Nothing; using v8::Number; using v8::Object; using v8::ObjectTemplate; @@ -206,7 +220,7 @@ void BuildEmbedderGraph(const FunctionCallbackInfo& args) { namespace { class FileOutputStream : public v8::OutputStream { public: - explicit FileOutputStream(FILE* stream) : stream_(stream) {} + FileOutputStream(const int fd, uv_fs_t* req) : fd_(fd), req_(req) {} int GetChunkSize() override { return 65536; // big chunks == faster @@ -214,18 +228,36 @@ class FileOutputStream : public v8::OutputStream { void EndOfStream() override {} - WriteResult WriteAsciiChunk(char* data, int size) override { - const size_t len = static_cast(size); - size_t off = 0; - - while (off < len && !feof(stream_) && !ferror(stream_)) - off += fwrite(data + off, 1, len - off, stream_); - - return off == len ? kContinue : kAbort; + WriteResult WriteAsciiChunk(char* data, const int size) override { + DCHECK_EQ(status_, 0); + int offset = 0; + while (offset < size) { + const uv_buf_t buf = uv_buf_init(data + offset, size - offset); + const int num_bytes_written = uv_fs_write(nullptr, + req_, + fd_, + &buf, + 1, + -1, + nullptr); + uv_fs_req_cleanup(req_); + if (num_bytes_written < 0) { + status_ = num_bytes_written; + return kAbort; + } + DCHECK_LE(num_bytes_written, buf.len); + offset += num_bytes_written; + } + DCHECK_EQ(offset, size); + return kContinue; } + int status() const { return status_; } + private: - FILE* stream_; + const int fd_; + uv_fs_t* req_; + int status_ = 0; }; class HeapSnapshotStream : public AsyncWrap, @@ -316,19 +348,37 @@ inline void TakeSnapshot(Environment* env, v8::OutputStream* out) { } // namespace -bool WriteSnapshot(Environment* env, const char* filename) { - FILE* fp = fopen(filename, "w"); - if (fp == nullptr) { - env->ThrowErrnoException(errno, "open"); - return false; +Maybe WriteSnapshot(Environment* env, const char* filename) { + uv_fs_t req; + int err; + + const int fd = uv_fs_open(nullptr, + &req, + filename, + O_WRONLY | O_CREAT | O_TRUNC, + S_IWUSR | S_IRUSR, + nullptr); + uv_fs_req_cleanup(&req); + if ((err = fd) < 0) { + env->ThrowUVException(err, "open", nullptr, filename); + return Nothing(); } - FileOutputStream stream(fp); + + FileOutputStream stream(fd, &req); TakeSnapshot(env, &stream); - if (fclose(fp) == EOF) { - env->ThrowErrnoException(errno, "close"); - return false; + if ((err = stream.status()) < 0) { + env->ThrowUVException(err, "write", nullptr, filename); + return Nothing(); } - return true; + + err = uv_fs_close(nullptr, &req, fd, nullptr); + uv_fs_req_cleanup(&req); + if (err < 0) { + env->ThrowUVException(err, "close", nullptr, filename); + return Nothing(); + } + + return JustVoid(); } void DeleteHeapSnapshot(const HeapSnapshot* snapshot) { @@ -379,7 +429,7 @@ void TriggerHeapSnapshot(const FunctionCallbackInfo& args) { if (filename_v->IsUndefined()) { DiagnosticFilename name(env, "Heap", "heapsnapshot"); - if (!WriteSnapshot(env, *name)) + if (WriteSnapshot(env, *name).IsNothing()) return; if (String::NewFromUtf8(isolate, *name).ToLocal(&filename_v)) { args.GetReturnValue().Set(filename_v); @@ -389,7 +439,7 @@ void TriggerHeapSnapshot(const FunctionCallbackInfo& args) { BufferValue path(isolate, filename_v); CHECK_NOT_NULL(*path); - if (!WriteSnapshot(env, *path)) + if (WriteSnapshot(env, *path).IsNothing()) return; return args.GetReturnValue().Set(filename_v); } diff --git a/src/inspector_agent.cc b/src/inspector_agent.cc index fd9f514b9b6a7b..34bb11e7d7122c 100644 --- a/src/inspector_agent.cc +++ b/src/inspector_agent.cc @@ -97,11 +97,11 @@ static int StartDebugSignalHandler() { pthread_attr_t attr; CHECK_EQ(0, pthread_attr_init(&attr)); #if defined(PTHREAD_STACK_MIN) && !defined(__FreeBSD__) - // PTHREAD_STACK_MIN is 2 KB with musl libc, which is too small to safely - // receive signals. PTHREAD_STACK_MIN + MINSIGSTKSZ is 8 KB on arm64, which + // PTHREAD_STACK_MIN is 2 KiB with musl libc, which is too small to safely + // receive signals. PTHREAD_STACK_MIN + MINSIGSTKSZ is 8 KiB on arm64, which // is the musl architecture with the biggest MINSIGSTKSZ so let's use that // as a lower bound and let's quadruple it just in case. The goal is to avoid - // creating a big 2 or 4 MB address space gap (problematic on 32 bits + // creating a big 2 or 4 MiB address space gap (problematic on 32 bits // because of fragmentation), not squeeze out every last byte. // Omitted on FreeBSD because it doesn't seem to like small stacks. const size_t stack_size = std::max(static_cast(4 * 8192), @@ -368,6 +368,16 @@ bool IsFilePath(const std::string& path) { } #endif // __POSIX__ +void ThrowUninitializedInspectorError(Environment* env) { + HandleScope scope(env->isolate()); + + const char* msg = "This Environment was initialized without a V8::Inspector"; + Local exception = + v8::String::NewFromUtf8(env->isolate(), msg).ToLocalChecked(); + + env->isolate()->ThrowException(exception); +} + } // namespace class NodeInspectorClient : public V8InspectorClient { @@ -728,6 +738,11 @@ bool Agent::StartIoThread() { if (io_ != nullptr) return true; + if (!parent_env_->should_create_inspector() && !client_) { + ThrowUninitializedInspectorError(parent_env_); + return false; + } + CHECK_NOT_NULL(client_); io_ = InspectorIo::Start(client_->getThreadHandle(), @@ -748,7 +763,13 @@ void Agent::Stop() { std::unique_ptr Agent::Connect( std::unique_ptr delegate, bool prevent_shutdown) { + if (!parent_env_->should_create_inspector() && !client_) { + ThrowUninitializedInspectorError(parent_env_); + return std::unique_ptr{}; + } + CHECK_NOT_NULL(client_); + int session_id = client_->connectFrontend(std::move(delegate), prevent_shutdown); return std::unique_ptr( @@ -758,6 +779,11 @@ std::unique_ptr Agent::Connect( std::unique_ptr Agent::ConnectToMainThread( std::unique_ptr delegate, bool prevent_shutdown) { + if (!parent_env_->should_create_inspector() && !client_) { + ThrowUninitializedInspectorError(parent_env_); + return std::unique_ptr{}; + } + CHECK_NOT_NULL(parent_handle_); CHECK_NOT_NULL(client_); auto thread_safe_delegate = @@ -767,6 +793,11 @@ std::unique_ptr Agent::ConnectToMainThread( } void Agent::WaitForDisconnect() { + if (!parent_env_->should_create_inspector() && !client_) { + ThrowUninitializedInspectorError(parent_env_); + return; + } + CHECK_NOT_NULL(client_); bool is_worker = parent_handle_ != nullptr; parent_handle_.reset(); @@ -912,6 +943,12 @@ void Agent::SetParentHandle( std::unique_ptr Agent::GetParentHandle( uint64_t thread_id, const std::string& url) { + if (!parent_env_->should_create_inspector() && !client_) { + ThrowUninitializedInspectorError(parent_env_); + return std::unique_ptr{}; + } + + CHECK_NOT_NULL(client_); if (!parent_handle_) { return client_->getWorkerManager()->NewParentHandle(thread_id, url); } else { @@ -920,11 +957,21 @@ std::unique_ptr Agent::GetParentHandle( } void Agent::WaitForConnect() { + if (!parent_env_->should_create_inspector() && !client_) { + ThrowUninitializedInspectorError(parent_env_); + return; + } + CHECK_NOT_NULL(client_); client_->waitForFrontend(); } std::shared_ptr Agent::GetWorkerManager() { + if (!parent_env_->should_create_inspector() && !client_) { + ThrowUninitializedInspectorError(parent_env_); + return std::unique_ptr{}; + } + CHECK_NOT_NULL(client_); return client_->getWorkerManager(); } diff --git a/src/inspector_io.cc b/src/inspector_io.cc index 5868289ae67f79..7f52fc605933da 100644 --- a/src/inspector_io.cc +++ b/src/inspector_io.cc @@ -209,7 +209,7 @@ class IoSessionDelegate : public InspectorSessionDelegate { class InspectorIoDelegate: public node::inspector::SocketServerDelegate { public: InspectorIoDelegate(std::shared_ptr queue, - std::shared_ptr main_threade, + std::shared_ptr main_thread, const std::string& target_id, const std::string& script_path, const std::string& script_name); diff --git a/src/inspector_js_api.cc b/src/inspector_js_api.cc index 8de1f8e7b0a88d..4ebb8acd689d58 100644 --- a/src/inspector_js_api.cc +++ b/src/inspector_js_api.cc @@ -75,10 +75,10 @@ class JSBindingsConnection : public AsyncWrap { Isolate* isolate = env_->isolate(); HandleScope handle_scope(isolate); Context::Scope context_scope(env_->context()); - MaybeLocal v8string = - String::NewFromTwoByte(isolate, message.characters16(), - NewStringType::kNormal, message.length()); - Local argument = v8string.ToLocalChecked().As(); + Local argument; + if (!String::NewFromTwoByte(isolate, message.characters16(), + NewStringType::kNormal, + message.length()).ToLocal(&argument)) return; connection_->OnMessage(argument); } @@ -215,10 +215,10 @@ void InspectorConsoleCall(const FunctionCallbackInfo& info) { Local node_method = info[1]; CHECK(node_method->IsFunction()); - node_method.As()->Call(context, + USE(node_method.As()->Call(context, info.Holder(), call_args.length(), - call_args.out()).FromMaybe(Local()); + call_args.out())); } static void* GetAsyncTask(int64_t asyncId) { diff --git a/src/js_native_api.h b/src/js_native_api.h index 50ccf11e240580..364d3672d1c344 100644 --- a/src/js_native_api.h +++ b/src/js_native_api.h @@ -2,8 +2,8 @@ #define SRC_JS_NATIVE_API_H_ // This file needs to be compatible with C compilers. -#include // NOLINT(modernize-deprecated-headers) #include // NOLINT(modernize-deprecated-headers) +#include // NOLINT(modernize-deprecated-headers) // Use INT_MAX, this should only be consumed by the pre-processor anyway. #define NAPI_VERSION_EXPERIMENTAL 2147483647 @@ -26,14 +26,15 @@ // If you need __declspec(dllimport), either include instead, or // define NAPI_EXTERN as __declspec(dllimport) on the compiler's command line. #ifndef NAPI_EXTERN - #ifdef _WIN32 - #define NAPI_EXTERN __declspec(dllexport) - #elif defined(__wasm32__) - #define NAPI_EXTERN __attribute__((visibility("default"))) \ - __attribute__((__import_module__("napi"))) - #else - #define NAPI_EXTERN __attribute__((visibility("default"))) - #endif +#ifdef _WIN32 +#define NAPI_EXTERN __declspec(dllexport) +#elif defined(__wasm32__) +#define NAPI_EXTERN \ + __attribute__((visibility("default"))) \ + __attribute__((__import_module__("napi"))) +#else +#define NAPI_EXTERN __attribute__((visibility("default"))) +#endif #endif #define NAPI_AUTO_LENGTH SIZE_MAX @@ -49,8 +50,7 @@ EXTERN_C_START NAPI_EXTERN napi_status -napi_get_last_error_info(napi_env env, - const napi_extended_error_info** result); +napi_get_last_error_info(napi_env env, const napi_extended_error_info** result); // Getters for defined singletons NAPI_EXTERN napi_status napi_get_undefined(napi_env env, napi_value* result); @@ -145,18 +145,12 @@ NAPI_EXTERN napi_status napi_get_value_bool(napi_env env, bool* result); // Copies LATIN-1 encoded bytes from a string into a buffer. -NAPI_EXTERN napi_status napi_get_value_string_latin1(napi_env env, - napi_value value, - char* buf, - size_t bufsize, - size_t* result); +NAPI_EXTERN napi_status napi_get_value_string_latin1( + napi_env env, napi_value value, char* buf, size_t bufsize, size_t* result); // Copies UTF-8 encoded bytes from a string into a buffer. -NAPI_EXTERN napi_status napi_get_value_string_utf8(napi_env env, - napi_value value, - char* buf, - size_t bufsize, - size_t* result); +NAPI_EXTERN napi_status napi_get_value_string_utf8( + napi_env env, napi_value value, char* buf, size_t bufsize, size_t* result); // Copies UTF-16 encoded bytes from a string into a buffer. NAPI_EXTERN napi_status napi_get_value_string_utf16(napi_env env, @@ -208,17 +202,17 @@ NAPI_EXTERN napi_status napi_has_own_property(napi_env env, napi_value key, bool* result); NAPI_EXTERN napi_status napi_set_named_property(napi_env env, - napi_value object, - const char* utf8name, - napi_value value); + napi_value object, + const char* utf8name, + napi_value value); NAPI_EXTERN napi_status napi_has_named_property(napi_env env, - napi_value object, - const char* utf8name, - bool* result); + napi_value object, + const char* utf8name, + bool* result); NAPI_EXTERN napi_status napi_get_named_property(napi_env env, - napi_value object, - const char* utf8name, - napi_value* result); + napi_value object, + const char* utf8name, + napi_value* result); NAPI_EXTERN napi_status napi_set_element(napi_env env, napi_value object, uint32_t index, @@ -359,12 +353,10 @@ NAPI_EXTERN napi_status napi_open_handle_scope(napi_env env, napi_handle_scope* result); NAPI_EXTERN napi_status napi_close_handle_scope(napi_env env, napi_handle_scope scope); -NAPI_EXTERN napi_status -napi_open_escapable_handle_scope(napi_env env, - napi_escapable_handle_scope* result); -NAPI_EXTERN napi_status -napi_close_escapable_handle_scope(napi_env env, - napi_escapable_handle_scope scope); +NAPI_EXTERN napi_status napi_open_escapable_handle_scope( + napi_env env, napi_escapable_handle_scope* result); +NAPI_EXTERN napi_status napi_close_escapable_handle_scope( + napi_env env, napi_escapable_handle_scope scope); NAPI_EXTERN napi_status napi_escape_handle(napi_env env, napi_escapable_handle_scope scope, @@ -377,11 +369,11 @@ NAPI_EXTERN napi_status napi_throw_error(napi_env env, const char* code, const char* msg); NAPI_EXTERN napi_status napi_throw_type_error(napi_env env, - const char* code, - const char* msg); + const char* code, + const char* msg); NAPI_EXTERN napi_status napi_throw_range_error(napi_env env, - const char* code, - const char* msg); + const char* code, + const char* msg); #ifdef NAPI_EXPERIMENTAL NAPI_EXTERN napi_status node_api_throw_syntax_error(napi_env env, const char* code, @@ -542,8 +534,7 @@ NAPI_EXTERN napi_status napi_set_instance_data(napi_env env, napi_finalize finalize_cb, void* finalize_hint); -NAPI_EXTERN napi_status napi_get_instance_data(napi_env env, - void** data); +NAPI_EXTERN napi_status napi_get_instance_data(napi_env env, void** data); #endif // NAPI_VERSION >= 6 #if NAPI_VERSION >= 7 @@ -567,10 +558,8 @@ napi_check_object_type_tag(napi_env env, napi_value value, const napi_type_tag* type_tag, bool* result); -NAPI_EXTERN napi_status napi_object_freeze(napi_env env, - napi_value object); -NAPI_EXTERN napi_status napi_object_seal(napi_env env, - napi_value object); +NAPI_EXTERN napi_status napi_object_freeze(napi_env env, napi_value object); +NAPI_EXTERN napi_status napi_object_seal(napi_env env, napi_value object); #endif // NAPI_VERSION >= 8 EXTERN_C_END diff --git a/src/js_native_api_types.h b/src/js_native_api_types.h index 6aba06629b3154..da4bff19d38044 100644 --- a/src/js_native_api_types.h +++ b/src/js_native_api_types.h @@ -8,7 +8,7 @@ #include // NOLINT(modernize-deprecated-headers) #if !defined __cplusplus || (defined(_MSC_VER) && _MSC_VER < 1900) - typedef uint16_t char16_t; +typedef uint16_t char16_t; #endif // JSVM API types are all opaque pointers for ABI stability @@ -36,9 +36,7 @@ typedef enum { napi_default_method = napi_writable | napi_configurable, // Default for object properties, like in JS obj[prop]. - napi_default_jsproperty = napi_writable | - napi_enumerable | - napi_configurable, + napi_default_jsproperty = napi_writable | napi_enumerable | napi_configurable, #endif // NAPI_VERSION >= 8 } napi_property_attributes; @@ -102,8 +100,7 @@ typedef enum { // * the definition of `napi_status` in doc/api/n-api.md to reflect the newly // added value(s). -typedef napi_value (*napi_callback)(napi_env env, - napi_callback_info info); +typedef napi_value (*napi_callback)(napi_env env, napi_callback_info info); typedef void (*napi_finalize)(napi_env env, void* finalize_data, void* finalize_hint); diff --git a/src/js_native_api_v8.cc b/src/js_native_api_v8.cc index 1c29c43836a0c3..611a0521fceab8 100644 --- a/src/js_native_api_v8.cc +++ b/src/js_native_api_v8.cc @@ -1,43 +1,41 @@ +#include #include // INT_MAX #include -#include #define NAPI_EXPERIMENTAL #include "env-inl.h" -#include "js_native_api_v8.h" #include "js_native_api.h" +#include "js_native_api_v8.h" #include "util-inl.h" -#define CHECK_MAYBE_NOTHING(env, maybe, status) \ +#define CHECK_MAYBE_NOTHING(env, maybe, status) \ RETURN_STATUS_IF_FALSE((env), !((maybe).IsNothing()), (status)) -#define CHECK_MAYBE_NOTHING_WITH_PREAMBLE(env, maybe, status) \ +#define CHECK_MAYBE_NOTHING_WITH_PREAMBLE(env, maybe, status) \ RETURN_STATUS_IF_FALSE_WITH_PREAMBLE((env), !((maybe).IsNothing()), (status)) -#define CHECK_TO_NUMBER(env, context, result, src) \ +#define CHECK_TO_NUMBER(env, context, result, src) \ CHECK_TO_TYPE((env), Number, (context), (result), (src), napi_number_expected) // n-api defines NAPI_AUTO_LENGTH as the indicator that a string // is null terminated. For V8 the equivalent is -1. The assert // validates that our cast of NAPI_AUTO_LENGTH results in -1 as // needed by V8. -#define CHECK_NEW_FROM_UTF8_LEN(env, result, str, len) \ - do { \ - static_assert(static_cast(NAPI_AUTO_LENGTH) == -1, \ - "Casting NAPI_AUTO_LENGTH to int must result in -1"); \ - RETURN_STATUS_IF_FALSE((env), \ - (len == NAPI_AUTO_LENGTH) || len <= INT_MAX, \ - napi_invalid_arg); \ - RETURN_STATUS_IF_FALSE((env), \ - (str) != nullptr, \ - napi_invalid_arg); \ - auto str_maybe = v8::String::NewFromUtf8( \ - (env)->isolate, (str), v8::NewStringType::kInternalized, \ - static_cast(len)); \ - CHECK_MAYBE_EMPTY((env), str_maybe, napi_generic_failure); \ - (result) = str_maybe.ToLocalChecked(); \ +#define CHECK_NEW_FROM_UTF8_LEN(env, result, str, len) \ + do { \ + static_assert(static_cast(NAPI_AUTO_LENGTH) == -1, \ + "Casting NAPI_AUTO_LENGTH to int must result in -1"); \ + RETURN_STATUS_IF_FALSE( \ + (env), (len == NAPI_AUTO_LENGTH) || len <= INT_MAX, napi_invalid_arg); \ + RETURN_STATUS_IF_FALSE((env), (str) != nullptr, napi_invalid_arg); \ + auto str_maybe = v8::String::NewFromUtf8((env)->isolate, \ + (str), \ + v8::NewStringType::kInternalized, \ + static_cast(len)); \ + CHECK_MAYBE_EMPTY((env), str_maybe, napi_generic_failure); \ + (result) = str_maybe.ToLocalChecked(); \ } while (0) -#define CHECK_NEW_FROM_UTF8(env, result, str) \ +#define CHECK_NEW_FROM_UTF8(env, result, str) \ CHECK_NEW_FROM_UTF8_LEN((env), (result), (str), NAPI_AUTO_LENGTH) #define CREATE_TYPED_ARRAY( \ @@ -45,12 +43,15 @@ do { \ if ((size_of_element) > 1) { \ THROW_RANGE_ERROR_IF_FALSE( \ - (env), (byte_offset) % (size_of_element) == 0, \ + (env), \ + (byte_offset) % (size_of_element) == 0, \ "ERR_NAPI_INVALID_TYPEDARRAY_ALIGNMENT", \ - "start offset of "#type" should be a multiple of "#size_of_element); \ + "start offset of " #type \ + " should be a multiple of " #size_of_element); \ } \ - THROW_RANGE_ERROR_IF_FALSE((env), (length) * (size_of_element) + \ - (byte_offset) <= buffer->ByteLength(), \ + THROW_RANGE_ERROR_IF_FALSE( \ + (env), \ + (length) * (size_of_element) + (byte_offset) <= buffer->ByteLength(), \ "ERR_NAPI_INVALID_TYPEDARRAY_LENGTH", \ "Invalid typed array length"); \ (out) = v8::type::New((buffer), (byte_offset), (length)); \ @@ -60,15 +61,15 @@ namespace v8impl { namespace { -inline static napi_status -V8NameFromPropertyDescriptor(napi_env env, - const napi_property_descriptor* p, - v8::Local* result) { +inline static napi_status V8NameFromPropertyDescriptor( + napi_env env, + const napi_property_descriptor* p, + v8::Local* result) { if (p->utf8name != nullptr) { CHECK_NEW_FROM_UTF8(env, *result, p->utf8name); } else { v8::Local property_value = - v8impl::V8LocalValueFromJsValue(p->name); + v8impl::V8LocalValueFromJsValue(p->name); RETURN_STATUS_IF_FALSE(env, property_value->IsName(), napi_name_expected); *result = property_value.As(); @@ -85,7 +86,7 @@ inline static v8::PropertyAttribute V8PropertyAttributesFromDescriptor( // The napi_writable attribute is ignored for accessor descriptors, but // V8 would throw `TypeError`s on assignment with nonexistence of a setter. if ((descriptor->getter == nullptr && descriptor->setter == nullptr) && - (descriptor->attributes & napi_writable) == 0) { + (descriptor->attributes & napi_writable) == 0) { attribute_flags |= v8::PropertyAttribute::ReadOnly; } @@ -99,13 +100,13 @@ inline static v8::PropertyAttribute V8PropertyAttributesFromDescriptor( return static_cast(attribute_flags); } -inline static napi_deferred -JsDeferredFromNodePersistent(v8impl::Persistent* local) { +inline static napi_deferred JsDeferredFromNodePersistent( + v8impl::Persistent* local) { return reinterpret_cast(local); } -inline static v8impl::Persistent* -NodePersistentFromJsDeferred(napi_deferred local) { +inline static v8impl::Persistent* NodePersistentFromJsDeferred( + napi_deferred local) { return reinterpret_cast*>(local); } @@ -126,9 +127,7 @@ class EscapableHandleScopeWrapper { public: explicit EscapableHandleScopeWrapper(v8::Isolate* isolate) : scope(isolate), escape_called_(false) {} - bool escape_called() const { - return escape_called_; - } + bool escape_called() const { return escape_called_; } template v8::Local Escape(v8::Local handle) { escape_called_ = true; @@ -140,13 +139,13 @@ class EscapableHandleScopeWrapper { bool escape_called_; }; -inline static napi_handle_scope -JsHandleScopeFromV8HandleScope(HandleScopeWrapper* s) { +inline static napi_handle_scope JsHandleScopeFromV8HandleScope( + HandleScopeWrapper* s) { return reinterpret_cast(s); } -inline static HandleScopeWrapper* -V8HandleScopeFromJsHandleScope(napi_handle_scope s) { +inline static HandleScopeWrapper* V8HandleScopeFromJsHandleScope( + napi_handle_scope s) { return reinterpret_cast(s); } @@ -177,9 +176,11 @@ inline static napi_status ConcludeDeferred(napi_env env, auto v8_resolver = v8_deferred.As(); - v8::Maybe success = is_resolved ? - v8_resolver->Resolve(context, v8impl::V8LocalValueFromJsValue(result)) : - v8_resolver->Reject(context, v8impl::V8LocalValueFromJsValue(result)); + v8::Maybe success = + is_resolved ? v8_resolver->Resolve( + context, v8impl::V8LocalValueFromJsValue(result)) + : v8_resolver->Reject( + context, v8impl::V8LocalValueFromJsValue(result)); delete deferred_ref; @@ -188,10 +189,7 @@ inline static napi_status ConcludeDeferred(napi_env env, return GET_RETURN_STATUS(env); } -enum UnwrapAction { - KeepWrap, - RemoveWrap -}; +enum UnwrapAction { KeepWrap, RemoveWrap }; inline static napi_status Unwrap(napi_env env, napi_value js_object, @@ -210,7 +208,7 @@ inline static napi_status Unwrap(napi_env env, v8::Local obj = value.As(); auto val = obj->GetPrivate(context, NAPI_PRIVATE_KEY(context, wrapper)) - .ToLocalChecked(); + .ToLocalChecked(); RETURN_STATUS_IF_FALSE(env, val->IsExternal(), napi_invalid_arg); Reference* reference = static_cast(val.As()->Value()); @@ -221,7 +219,7 @@ inline static napi_status Unwrap(napi_env env, if (action == RemoveWrap) { CHECK(obj->DeletePrivate(context, NAPI_PRIVATE_KEY(context, wrapper)) - .FromJust()); + .FromJust()); Reference::Delete(reference); } @@ -240,8 +238,9 @@ class CallbackBundle { public: // Creates an object to be made available to the static function callback // wrapper, used to retrieve the native callback function and data pointer. - static inline v8::Local - New(napi_env env, napi_callback cb, void* data) { + static inline v8::Local New(napi_env env, + napi_callback cb, + void* data) { CallbackBundle* bundle = new CallbackBundle(); bundle->cb = cb; bundle->cb_data = data; @@ -251,9 +250,10 @@ class CallbackBundle { Reference::New(env, cbdata, 0, true, Delete, bundle, nullptr); return cbdata; } - napi_env env; // Necessary to invoke C++ NAPI callback - void* cb_data; // The user provided callback data - napi_callback cb; + napi_env env; // Necessary to invoke C++ NAPI callback + void* cb_data; // The user provided callback data + napi_callback cb; + private: static void Delete(napi_env env, void* data, void* hint) { CallbackBundle* bundle = static_cast(data); @@ -288,9 +288,8 @@ class CallbackWrapperBase : public CallbackWrapper { public: inline CallbackWrapperBase(const v8::FunctionCallbackInfo& cbinfo, const size_t args_length) - : CallbackWrapper(JsValueFromV8LocalValue(cbinfo.This()), - args_length, - nullptr), + : CallbackWrapper( + JsValueFromV8LocalValue(cbinfo.This()), args_length, nullptr), _cbinfo(cbinfo) { _bundle = reinterpret_cast( cbinfo.Data().As()->Value()); @@ -308,12 +307,11 @@ class CallbackWrapperBase : public CallbackWrapper { napi_value result = nullptr; bool exceptionOccurred = false; - env->CallIntoModule([&](napi_env env) { - result = cb(env, cbinfo_wrapper); - }, [&](napi_env env, v8::Local value) { - exceptionOccurred = true; - env->isolate->ThrowException(value); - }); + env->CallIntoModule([&](napi_env env) { result = cb(env, cbinfo_wrapper); }, + [&](napi_env env, v8::Local value) { + exceptionOccurred = true; + env->isolate->ThrowException(value); + }); if (!exceptionOccurred && (result != nullptr)) { this->SetReturnValue(result); @@ -324,8 +322,7 @@ class CallbackWrapperBase : public CallbackWrapper { CallbackBundle* _bundle; }; -class FunctionCallbackWrapper - : public CallbackWrapperBase { +class FunctionCallbackWrapper : public CallbackWrapperBase { public: static void Invoke(const v8::FunctionCallbackInfo& info) { FunctionCallbackWrapper cbwrapper(info); @@ -347,11 +344,12 @@ class FunctionCallbackWrapper return napi_clear_last_error(env); } - static inline napi_status NewTemplate(napi_env env, - napi_callback cb, - void* cb_data, - v8::Local* result, - v8::Local sig = v8::Local()) { + static inline napi_status NewTemplate( + napi_env env, + napi_callback cb, + void* cb_data, + v8::Local* result, + v8::Local sig = v8::Local()) { v8::Local cbdata = v8impl::CallbackBundle::New(env, cb, cb_data); RETURN_STATUS_IF_FALSE(env, !cbdata.IsEmpty(), napi_generic_failure); @@ -396,10 +394,7 @@ class FunctionCallbackWrapper } }; -enum WrapType { - retrievable, - anonymous -}; +enum WrapType { retrievable, anonymous }; template inline napi_status Wrap(napi_env env, @@ -419,9 +414,10 @@ inline napi_status Wrap(napi_env env, if (wrap_type == retrievable) { // If we've already wrapped this object, we error out. - RETURN_STATUS_IF_FALSE(env, + RETURN_STATUS_IF_FALSE( + env, !obj->HasPrivate(context, NAPI_PRIVATE_KEY(context, wrapper)) - .FromJust(), + .FromJust(), napi_invalid_arg); } else if (wrap_type == anonymous) { // If no finalize callback is provided, we error out. @@ -440,13 +436,21 @@ inline napi_status Wrap(napi_env env, *result = reinterpret_cast(reference); } else { // Create a self-deleting reference. - reference = v8impl::Reference::New(env, obj, 0, true, finalize_cb, - native_object, finalize_cb == nullptr ? nullptr : finalize_hint); + reference = v8impl::Reference::New( + env, + obj, + 0, + true, + finalize_cb, + native_object, + finalize_cb == nullptr ? nullptr : finalize_hint); } if (wrap_type == retrievable) { - CHECK(obj->SetPrivate(context, NAPI_PRIVATE_KEY(context, wrapper), - v8::External::New(env->isolate, reference)).FromJust()); + CHECK(obj->SetPrivate(context, + NAPI_PRIVATE_KEY(context, wrapper), + v8::External::New(env->isolate, reference)) + .FromJust()); } return GET_RETURN_STATUS(env); @@ -721,29 +725,29 @@ void Reference::SecondPassCallback( } // end of namespace v8impl // Warning: Keep in-sync with napi_status enum -static -const char* error_messages[] = {nullptr, - "Invalid argument", - "An object was expected", - "A string was expected", - "A string or symbol was expected", - "A function was expected", - "A number was expected", - "A boolean was expected", - "An array was expected", - "Unknown failure", - "An exception is pending", - "The async work item was cancelled", - "napi_escape_handle already called on scope", - "Invalid handle scope usage", - "Invalid callback scope usage", - "Thread-safe function queue is full", - "Thread-safe function handle is closing", - "A bigint was expected", - "A date was expected", - "An arraybuffer was expected", - "A detachable arraybuffer was expected", - "Main thread would deadlock", +static const char* error_messages[] = { + nullptr, + "Invalid argument", + "An object was expected", + "A string was expected", + "A string or symbol was expected", + "A function was expected", + "A number was expected", + "A boolean was expected", + "An array was expected", + "Unknown failure", + "An exception is pending", + "The async work item was cancelled", + "napi_escape_handle already called on scope", + "Invalid handle scope usage", + "Invalid callback scope usage", + "Thread-safe function queue is full", + "Thread-safe function handle is closing", + "A bigint was expected", + "A date was expected", + "An arraybuffer was expected", + "A detachable arraybuffer was expected", + "Main thread would deadlock", }; napi_status napi_get_last_error_info(napi_env env, @@ -757,17 +761,15 @@ napi_status napi_get_last_error_info(napi_env env, // change each time a message was added. const int last_status = napi_would_deadlock; - static_assert( - NAPI_ARRAYSIZE(error_messages) == last_status + 1, - "Count of error messages must match count of error values"); + static_assert(NAPI_ARRAYSIZE(error_messages) == last_status + 1, + "Count of error messages must match count of error values"); CHECK_LE(env->last_error.error_code, last_status); // Wait until someone requests the last error information to fetch the error // message string - env->last_error.error_message = - error_messages[env->last_error.error_code]; + env->last_error.error_message = error_messages[env->last_error.error_code]; if (env->last_error.error_code == napi_ok) { - napi_clear_last_error(env); + napi_clear_last_error(env); } *result = &(env->last_error); return napi_ok; @@ -860,12 +862,11 @@ napi_status napi_define_class(napi_env env, env, p->setter, p->data, &setter_tpl)); } - tpl->PrototypeTemplate()->SetAccessorProperty( - property_name, - getter_tpl, - setter_tpl, - attributes, - v8::AccessControl::DEFAULT); + tpl->PrototypeTemplate()->SetAccessorProperty(property_name, + getter_tpl, + setter_tpl, + attributes, + v8::AccessControl::DEFAULT); } else if (p->method != nullptr) { v8::Local t; STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate( @@ -893,10 +894,8 @@ napi_status napi_define_class(napi_env env, } } - STATUS_CALL(napi_define_properties(env, - *result, - static_descriptors.size(), - static_descriptors.data())); + STATUS_CALL(napi_define_properties( + env, *result, static_descriptors.size(), static_descriptors.data())); } return GET_RETURN_STATUS(env); @@ -909,8 +908,7 @@ napi_status napi_get_property_names(napi_env env, env, object, napi_key_include_prototypes, - static_cast(napi_key_enumerable | - napi_key_skip_symbols), + static_cast(napi_key_enumerable | napi_key_skip_symbols), napi_key_numbers_to_strings, result); } @@ -930,29 +928,24 @@ napi_status napi_get_all_property_names(napi_env env, v8::PropertyFilter filter = v8::PropertyFilter::ALL_PROPERTIES; if (key_filter & napi_key_writable) { - filter = - static_cast(filter | - v8::PropertyFilter::ONLY_WRITABLE); + filter = static_cast(filter | + v8::PropertyFilter::ONLY_WRITABLE); } if (key_filter & napi_key_enumerable) { - filter = - static_cast(filter | - v8::PropertyFilter::ONLY_ENUMERABLE); + filter = static_cast( + filter | v8::PropertyFilter::ONLY_ENUMERABLE); } if (key_filter & napi_key_configurable) { - filter = - static_cast(filter | - v8::PropertyFilter::ONLY_WRITABLE); + filter = static_cast(filter | + v8::PropertyFilter::ONLY_WRITABLE); } if (key_filter & napi_key_skip_strings) { - filter = - static_cast(filter | - v8::PropertyFilter::SKIP_STRINGS); + filter = static_cast(filter | + v8::PropertyFilter::SKIP_STRINGS); } if (key_filter & napi_key_skip_symbols) { - filter = - static_cast(filter | - v8::PropertyFilter::SKIP_SYMBOLS); + filter = static_cast(filter | + v8::PropertyFilter::SKIP_SYMBOLS); } v8::KeyCollectionMode collection_mode; v8::KeyConversionMode conversion_mode; @@ -1076,8 +1069,7 @@ napi_status napi_delete_property(napi_env env, v8::Maybe delete_maybe = obj->Delete(context, k); CHECK_MAYBE_NOTHING(env, delete_maybe, napi_generic_failure); - if (result != nullptr) - *result = delete_maybe.FromMaybe(false); + if (result != nullptr) *result = delete_maybe.FromMaybe(false); return GET_RETURN_STATUS(env); } @@ -1247,8 +1239,7 @@ napi_status napi_delete_element(napi_env env, v8::Maybe delete_maybe = obj->Delete(context, index); CHECK_MAYBE_NOTHING(env, delete_maybe, napi_generic_failure); - if (result != nullptr) - *result = delete_maybe.FromMaybe(false); + if (result != nullptr) *result = delete_maybe.FromMaybe(false); return GET_RETURN_STATUS(env); } @@ -1290,9 +1281,8 @@ napi_status napi_define_properties(napi_env env, descriptor.set_enumerable((p->attributes & napi_enumerable) != 0); descriptor.set_configurable((p->attributes & napi_configurable) != 0); - auto define_maybe = obj->DefineProperty(context, - property_name, - descriptor); + auto define_maybe = + obj->DefineProperty(context, property_name, descriptor); if (!define_maybe.FromMaybe(false)) { return napi_set_last_error(env, napi_invalid_arg); @@ -1306,9 +1296,8 @@ napi_status napi_define_properties(napi_env env, descriptor.set_enumerable((p->attributes & napi_enumerable) != 0); descriptor.set_configurable((p->attributes & napi_configurable) != 0); - auto define_maybe = obj->DefineProperty(context, - property_name, - descriptor); + auto define_maybe = + obj->DefineProperty(context, property_name, descriptor); if (!define_maybe.FromMaybe(false)) { return napi_set_last_error(env, napi_generic_failure); @@ -1333,8 +1322,7 @@ napi_status napi_define_properties(napi_env env, return GET_RETURN_STATUS(env); } -napi_status napi_object_freeze(napi_env env, - napi_value object) { +napi_status napi_object_freeze(napi_env env, napi_value object) { NAPI_PREAMBLE(env); v8::Local context = env->context(); @@ -1343,16 +1331,15 @@ napi_status napi_object_freeze(napi_env env, CHECK_TO_OBJECT(env, context, obj, object); v8::Maybe set_frozen = - obj->SetIntegrityLevel(context, v8::IntegrityLevel::kFrozen); + obj->SetIntegrityLevel(context, v8::IntegrityLevel::kFrozen); - RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(env, - set_frozen.FromMaybe(false), napi_generic_failure); + RETURN_STATUS_IF_FALSE_WITH_PREAMBLE( + env, set_frozen.FromMaybe(false), napi_generic_failure); return GET_RETURN_STATUS(env); } -napi_status napi_object_seal(napi_env env, - napi_value object) { +napi_status napi_object_seal(napi_env env, napi_value object) { NAPI_PREAMBLE(env); v8::Local context = env->context(); @@ -1361,10 +1348,10 @@ napi_status napi_object_seal(napi_env env, CHECK_TO_OBJECT(env, context, obj, object); v8::Maybe set_sealed = - obj->SetIntegrityLevel(context, v8::IntegrityLevel::kSealed); + obj->SetIntegrityLevel(context, v8::IntegrityLevel::kSealed); - RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(env, - set_sealed.FromMaybe(false), napi_generic_failure); + RETURN_STATUS_IF_FALSE_WITH_PREAMBLE( + env, set_sealed.FromMaybe(false), napi_generic_failure); return GET_RETURN_STATUS(env); } @@ -1432,8 +1419,7 @@ napi_status napi_create_object(napi_env env, napi_value* result) { CHECK_ENV(env); CHECK_ARG(env, result); - *result = v8impl::JsValueFromV8LocalValue( - v8::Object::New(env->isolate)); + *result = v8impl::JsValueFromV8LocalValue(v8::Object::New(env->isolate)); return napi_clear_last_error(env); } @@ -1442,8 +1428,7 @@ napi_status napi_create_array(napi_env env, napi_value* result) { CHECK_ENV(env); CHECK_ARG(env, result); - *result = v8impl::JsValueFromV8LocalValue( - v8::Array::New(env->isolate)); + *result = v8impl::JsValueFromV8LocalValue(v8::Array::New(env->isolate)); return napi_clear_last_error(env); } @@ -1454,8 +1439,8 @@ napi_status napi_create_array_with_length(napi_env env, CHECK_ENV(env); CHECK_ARG(env, result); - *result = v8impl::JsValueFromV8LocalValue( - v8::Array::New(env->isolate, length)); + *result = + v8impl::JsValueFromV8LocalValue(v8::Array::New(env->isolate, length)); return napi_clear_last_error(env); } @@ -1465,12 +1450,10 @@ napi_status napi_create_string_latin1(napi_env env, size_t length, napi_value* result) { CHECK_ENV(env); - if (length > 0) - CHECK_ARG(env, str); + if (length > 0) CHECK_ARG(env, str); CHECK_ARG(env, result); - RETURN_STATUS_IF_FALSE(env, - (length == NAPI_AUTO_LENGTH) || length <= INT_MAX, - napi_invalid_arg); + RETURN_STATUS_IF_FALSE( + env, (length == NAPI_AUTO_LENGTH) || length <= INT_MAX, napi_invalid_arg); auto isolate = env->isolate; auto str_maybe = @@ -1489,19 +1472,14 @@ napi_status napi_create_string_utf8(napi_env env, size_t length, napi_value* result) { CHECK_ENV(env); - if (length > 0) - CHECK_ARG(env, str); + if (length > 0) CHECK_ARG(env, str); CHECK_ARG(env, result); - RETURN_STATUS_IF_FALSE(env, - (length == NAPI_AUTO_LENGTH) || length <= INT_MAX, - napi_invalid_arg); + RETURN_STATUS_IF_FALSE( + env, (length == NAPI_AUTO_LENGTH) || length <= INT_MAX, napi_invalid_arg); auto isolate = env->isolate; - auto str_maybe = - v8::String::NewFromUtf8(isolate, - str, - v8::NewStringType::kNormal, - static_cast(length)); + auto str_maybe = v8::String::NewFromUtf8( + isolate, str, v8::NewStringType::kNormal, static_cast(length)); CHECK_MAYBE_EMPTY(env, str_maybe, napi_generic_failure); *result = v8impl::JsValueFromV8LocalValue(str_maybe.ToLocalChecked()); return napi_clear_last_error(env); @@ -1512,12 +1490,10 @@ napi_status napi_create_string_utf16(napi_env env, size_t length, napi_value* result) { CHECK_ENV(env); - if (length > 0) - CHECK_ARG(env, str); + if (length > 0) CHECK_ARG(env, str); CHECK_ARG(env, result); - RETURN_STATUS_IF_FALSE(env, - (length == NAPI_AUTO_LENGTH) || length <= INT_MAX, - napi_invalid_arg); + RETURN_STATUS_IF_FALSE( + env, (length == NAPI_AUTO_LENGTH) || length <= INT_MAX, napi_invalid_arg); auto isolate = env->isolate; auto str_maybe = @@ -1531,26 +1507,22 @@ napi_status napi_create_string_utf16(napi_env env, return napi_clear_last_error(env); } -napi_status napi_create_double(napi_env env, - double value, - napi_value* result) { +napi_status napi_create_double(napi_env env, double value, napi_value* result) { CHECK_ENV(env); CHECK_ARG(env, result); - *result = v8impl::JsValueFromV8LocalValue( - v8::Number::New(env->isolate, value)); + *result = + v8impl::JsValueFromV8LocalValue(v8::Number::New(env->isolate, value)); return napi_clear_last_error(env); } -napi_status napi_create_int32(napi_env env, - int32_t value, - napi_value* result) { +napi_status napi_create_int32(napi_env env, int32_t value, napi_value* result) { CHECK_ENV(env); CHECK_ARG(env, result); - *result = v8impl::JsValueFromV8LocalValue( - v8::Integer::New(env->isolate, value)); + *result = + v8impl::JsValueFromV8LocalValue(v8::Integer::New(env->isolate, value)); return napi_clear_last_error(env); } @@ -1567,9 +1539,7 @@ napi_status napi_create_uint32(napi_env env, return napi_clear_last_error(env); } -napi_status napi_create_int64(napi_env env, - int64_t value, - napi_value* result) { +napi_status napi_create_int64(napi_env env, int64_t value, napi_value* result) { CHECK_ENV(env); CHECK_ARG(env, result); @@ -1585,8 +1555,8 @@ napi_status napi_create_bigint_int64(napi_env env, CHECK_ENV(env); CHECK_ARG(env, result); - *result = v8impl::JsValueFromV8LocalValue( - v8::BigInt::New(env->isolate, value)); + *result = + v8impl::JsValueFromV8LocalValue(v8::BigInt::New(env->isolate, value)); return napi_clear_last_error(env); } @@ -1614,11 +1584,10 @@ napi_status napi_create_bigint_words(napi_env env, v8::Local context = env->context(); - RETURN_STATUS_IF_FALSE( - env, word_count <= INT_MAX, napi_invalid_arg); + RETURN_STATUS_IF_FALSE(env, word_count <= INT_MAX, napi_invalid_arg); - v8::MaybeLocal b = v8::BigInt::NewFromWords( - context, sign_bit, word_count, words); + v8::MaybeLocal b = + v8::BigInt::NewFromWords(context, sign_bit, word_count, words); CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, b, napi_generic_failure); @@ -1656,7 +1625,7 @@ napi_status napi_create_symbol(napi_env env, RETURN_STATUS_IF_FALSE(env, desc->IsString(), napi_string_expected); *result = v8impl::JsValueFromV8LocalValue( - v8::Symbol::New(isolate, desc.As())); + v8::Symbol::New(isolate, desc.As())); } return napi_clear_last_error(env); @@ -1670,15 +1639,13 @@ napi_status node_api_symbol_for(napi_env env, CHECK_ARG(env, result); napi_value js_description_string; - STATUS_CALL(napi_create_string_utf8(env, - utf8description, - length, - &js_description_string)); + STATUS_CALL(napi_create_string_utf8( + env, utf8description, length, &js_description_string)); v8::Local description_string = - v8impl::V8LocalValueFromJsValue(js_description_string).As(); + v8impl::V8LocalValueFromJsValue(js_description_string).As(); *result = v8impl::JsValueFromV8LocalValue( - v8::Symbol::For(env->isolate, description_string)); + v8::Symbol::For(env->isolate, description_string)); return napi_clear_last_error(env); } @@ -1703,9 +1670,8 @@ static inline napi_status set_error_code(napi_env env, CHECK_NEW_FROM_UTF8(env, code_key, "code"); v8::Maybe set_maybe = err_object->Set(context, code_key, code_value); - RETURN_STATUS_IF_FALSE(env, - set_maybe.FromMaybe(false), - napi_generic_failure); + RETURN_STATUS_IF_FALSE( + env, set_maybe.FromMaybe(false), napi_generic_failure); } return napi_ok; } @@ -1837,8 +1803,7 @@ napi_status napi_get_undefined(napi_env env, napi_value* result) { CHECK_ENV(env); CHECK_ARG(env, result); - *result = v8impl::JsValueFromV8LocalValue( - v8::Undefined(env->isolate)); + *result = v8impl::JsValueFromV8LocalValue(v8::Undefined(env->isolate)); return napi_clear_last_error(env); } @@ -1847,8 +1812,7 @@ napi_status napi_get_null(napi_env env, napi_value* result) { CHECK_ENV(env); CHECK_ARG(env, result); - *result = v8impl::JsValueFromV8LocalValue( - v8::Null(env->isolate)); + *result = v8impl::JsValueFromV8LocalValue(v8::Null(env->isolate)); return napi_clear_last_error(env); } @@ -1918,8 +1882,11 @@ napi_status napi_call_function(napi_env env, v8::Local v8func; CHECK_TO_FUNCTION(env, v8func, func); - auto maybe = v8func->Call(context, v8recv, argc, - reinterpret_cast*>(const_cast(argv))); + auto maybe = v8func->Call( + context, + v8recv, + argc, + reinterpret_cast*>(const_cast(argv))); if (try_catch.HasCaught()) { return napi_set_last_error(env, napi_pending_exception); @@ -1953,9 +1920,7 @@ napi_status napi_throw(napi_env env, napi_value error) { return napi_clear_last_error(env); } -napi_status napi_throw_error(napi_env env, - const char* code, - const char* msg) { +napi_status napi_throw_error(napi_env env, const char* code, const char* msg) { NAPI_PREAMBLE(env); v8::Isolate* isolate = env->isolate; @@ -2226,11 +2191,8 @@ napi_status napi_get_value_bool(napi_env env, napi_value value, bool* result) { // If buf is NULL, this method returns the length of the string (in bytes) // via the result parameter. // The result argument is optional unless buf is NULL. -napi_status napi_get_value_string_latin1(napi_env env, - napi_value value, - char* buf, - size_t bufsize, - size_t* result) { +napi_status napi_get_value_string_latin1( + napi_env env, napi_value value, char* buf, size_t bufsize, size_t* result) { CHECK_ENV(env); CHECK_ARG(env, value); @@ -2267,11 +2229,8 @@ napi_status napi_get_value_string_latin1(napi_env env, // If buf is NULL, this method returns the length of the string (in bytes) // via the result parameter. // The result argument is optional unless buf is NULL. -napi_status napi_get_value_string_utf8(napi_env env, - napi_value value, - char* buf, - size_t bufsize, - size_t* result) { +napi_status napi_get_value_string_utf8( + napi_env env, napi_value value, char* buf, size_t bufsize, size_t* result) { CHECK_ENV(env); CHECK_ARG(env, value); @@ -2350,26 +2309,25 @@ napi_status napi_coerce_to_bool(napi_env env, v8::Isolate* isolate = env->isolate; v8::Local b = - v8impl::V8LocalValueFromJsValue(value)->ToBoolean(isolate); + v8impl::V8LocalValueFromJsValue(value)->ToBoolean(isolate); *result = v8impl::JsValueFromV8LocalValue(b); return GET_RETURN_STATUS(env); } -#define GEN_COERCE_FUNCTION(UpperCaseName, MixedCaseName, LowerCaseName) \ - napi_status napi_coerce_to_##LowerCaseName(napi_env env, \ - napi_value value, \ - napi_value* result) { \ - NAPI_PREAMBLE(env); \ - CHECK_ARG(env, value); \ - CHECK_ARG(env, result); \ - \ - v8::Local context = env->context(); \ - v8::Local str; \ - \ - CHECK_TO_##UpperCaseName(env, context, str, value); \ - \ - *result = v8impl::JsValueFromV8LocalValue(str); \ - return GET_RETURN_STATUS(env); \ +#define GEN_COERCE_FUNCTION(UpperCaseName, MixedCaseName, LowerCaseName) \ + napi_status napi_coerce_to_##LowerCaseName( \ + napi_env env, napi_value value, napi_value* result) { \ + NAPI_PREAMBLE(env); \ + CHECK_ARG(env, value); \ + CHECK_ARG(env, result); \ + \ + v8::Local context = env->context(); \ + v8::Local str; \ + \ + CHECK_TO_##UpperCaseName(env, context, str, value); \ + \ + *result = v8impl::JsValueFromV8LocalValue(str); \ + return GET_RETURN_STATUS(env); \ } GEN_COERCE_FUNCTION(NUMBER, Number, number) @@ -2384,12 +2342,8 @@ napi_status napi_wrap(napi_env env, napi_finalize finalize_cb, void* finalize_hint, napi_ref* result) { - return v8impl::Wrap(env, - js_object, - native_object, - finalize_cb, - finalize_hint, - result); + return v8impl::Wrap( + env, js_object, native_object, finalize_cb, finalize_hint, result); } napi_status napi_unwrap(napi_env env, napi_value obj, void** result) { @@ -2414,13 +2368,8 @@ napi_status napi_create_external(napi_env env, // The Reference object will delete itself after invoking the finalizer // callback. - v8impl::Reference::New(env, - external_value, - 0, - true, - finalize_cb, - data, - finalize_hint); + v8impl::Reference::New( + env, external_value, 0, true, finalize_cb, data, finalize_hint); *result = v8impl::JsValueFromV8LocalValue(external_value); @@ -2439,21 +2388,17 @@ NAPI_EXTERN napi_status napi_type_tag_object(napi_env env, auto key = NAPI_PRIVATE_KEY(context, type_tag); auto maybe_has = obj->HasPrivate(context, key); CHECK_MAYBE_NOTHING_WITH_PREAMBLE(env, maybe_has, napi_generic_failure); - RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(env, - !maybe_has.FromJust(), - napi_invalid_arg); - - auto tag = v8::BigInt::NewFromWords(context, - 0, - 2, - reinterpret_cast(type_tag)); + RETURN_STATUS_IF_FALSE_WITH_PREAMBLE( + env, !maybe_has.FromJust(), napi_invalid_arg); + + auto tag = v8::BigInt::NewFromWords( + context, 0, 2, reinterpret_cast(type_tag)); CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, tag, napi_generic_failure); auto maybe_set = obj->SetPrivate(context, key, tag.ToLocalChecked()); CHECK_MAYBE_NOTHING_WITH_PREAMBLE(env, maybe_set, napi_generic_failure); - RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(env, - maybe_set.FromJust(), - napi_generic_failure); + RETURN_STATUS_IF_FALSE_WITH_PREAMBLE( + env, maybe_set.FromJust(), napi_generic_failure); return GET_RETURN_STATUS(env); } @@ -2470,8 +2415,8 @@ napi_check_object_type_tag(napi_env env, CHECK_ARG_WITH_PREAMBLE(env, type_tag); CHECK_ARG_WITH_PREAMBLE(env, result); - auto maybe_value = obj->GetPrivate(context, - NAPI_PRIVATE_KEY(context, type_tag)); + auto maybe_value = + obj->GetPrivate(context, NAPI_PRIVATE_KEY(context, type_tag)); CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, maybe_value, napi_generic_failure); v8::Local val = maybe_value.ToLocalChecked(); @@ -2483,9 +2428,8 @@ napi_check_object_type_tag(napi_env env, int sign; int size = 2; napi_type_tag tag; - val.As()->ToWordsArray(&sign, - &size, - reinterpret_cast(&tag)); + val.As()->ToWordsArray( + &sign, &size, reinterpret_cast(&tag)); if (size == 2 && sign == 0) *result = (tag.lower == type_tag->lower && tag.upper == type_tag->upper); } @@ -2637,8 +2581,7 @@ napi_status napi_close_handle_scope(napi_env env, napi_handle_scope scope) { } napi_status napi_open_escapable_handle_scope( - napi_env env, - napi_escapable_handle_scope* result) { + napi_env env, napi_escapable_handle_scope* result) { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. CHECK_ENV(env); @@ -2651,8 +2594,7 @@ napi_status napi_open_escapable_handle_scope( } napi_status napi_close_escapable_handle_scope( - napi_env env, - napi_escapable_handle_scope scope) { + napi_env env, napi_escapable_handle_scope scope) { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. CHECK_ENV(env); @@ -2704,8 +2646,10 @@ napi_status napi_new_instance(napi_env env, v8::Local ctor; CHECK_TO_FUNCTION(env, ctor, constructor); - auto maybe = ctor->NewInstance(context, argc, - reinterpret_cast*>(const_cast(argv))); + auto maybe = ctor->NewInstance( + context, + argc, + reinterpret_cast*>(const_cast(argv))); CHECK_MAYBE_EMPTY(env, maybe, napi_pending_exception); @@ -2729,9 +2673,8 @@ napi_status napi_instanceof(napi_env env, CHECK_TO_OBJECT(env, context, ctor, constructor); if (!ctor->IsFunction()) { - napi_throw_type_error(env, - "ERR_NAPI_CONS_FUNCTION", - "Constructor must be a function"); + napi_throw_type_error( + env, "ERR_NAPI_CONS_FUNCTION", "Constructor must be a function"); return napi_set_last_error(env, napi_function_expected); } @@ -2767,7 +2710,7 @@ napi_status napi_get_and_clear_last_exception(napi_env env, return napi_get_undefined(env, result); } else { *result = v8impl::JsValueFromV8LocalValue( - v8::Local::New(env->isolate, env->last_exception)); + v8::Local::New(env->isolate, env->last_exception)); env->last_exception.Reset(); } @@ -2817,20 +2760,9 @@ napi_status napi_create_external_arraybuffer(napi_env env, // `Buffer` variant for easier implementation. napi_value buffer; STATUS_CALL(napi_create_external_buffer( - env, - byte_length, - external_data, - finalize_cb, - finalize_hint, - &buffer)); + env, byte_length, external_data, finalize_cb, finalize_hint, &buffer)); return napi_get_typedarray_info( - env, - buffer, - nullptr, - nullptr, - nullptr, - result, - nullptr); + env, buffer, nullptr, nullptr, nullptr, result, nullptr); } napi_status napi_get_arraybuffer_info(napi_env env, @@ -3019,15 +2951,14 @@ napi_status napi_create_dataview(napi_env env, v8::Local buffer = value.As(); if (byte_length + byte_offset > buffer->ByteLength()) { - napi_throw_range_error( - env, - "ERR_NAPI_INVALID_DATAVIEW_ARGS", - "byte_offset + byte_length should be less than or " - "equal to the size in bytes of the array passed in"); + napi_throw_range_error(env, + "ERR_NAPI_INVALID_DATAVIEW_ARGS", + "byte_offset + byte_length should be less than or " + "equal to the size in bytes of the array passed in"); return napi_set_last_error(env, napi_pending_exception); } - v8::Local DataView = v8::DataView::New(buffer, byte_offset, - byte_length); + v8::Local DataView = + v8::DataView::New(buffer, byte_offset, byte_length); *result = v8impl::JsValueFromV8LocalValue(DataView); return GET_RETURN_STATUS(env); @@ -3123,9 +3054,7 @@ napi_status napi_reject_deferred(napi_env env, return v8impl::ConcludeDeferred(env, deferred, resolution, false); } -napi_status napi_is_promise(napi_env env, - napi_value value, - bool* is_promise) { +napi_status napi_is_promise(napi_env env, napi_value value, bool* is_promise) { CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, is_promise); @@ -3135,9 +3064,7 @@ napi_status napi_is_promise(napi_env env, return napi_clear_last_error(env); } -napi_status napi_create_date(napi_env env, - double time, - napi_value* result) { +napi_status napi_create_date(napi_env env, double time, napi_value* result) { NAPI_PREAMBLE(env); CHECK_ARG(env, result); @@ -3149,9 +3076,7 @@ napi_status napi_create_date(napi_env env, return GET_RETURN_STATUS(env); } -napi_status napi_is_date(napi_env env, - napi_value value, - bool* is_date) { +napi_status napi_is_date(napi_env env, napi_value value, bool* is_date) { CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, is_date); @@ -3195,8 +3120,7 @@ napi_status napi_run_script(napi_env env, auto maybe_script = v8::Script::Compile(context, v8_script.As()); CHECK_MAYBE_EMPTY(env, maybe_script, napi_generic_failure); - auto script_result = - maybe_script.ToLocalChecked()->Run(context); + auto script_result = maybe_script.ToLocalChecked()->Run(context); CHECK_MAYBE_EMPTY(env, script_result, napi_generic_failure); *result = v8impl::JsValueFromV8LocalValue(script_result.ToLocalChecked()); @@ -3209,12 +3133,8 @@ napi_status napi_add_finalizer(napi_env env, napi_finalize finalize_cb, void* finalize_hint, napi_ref* result) { - return v8impl::Wrap(env, - js_object, - native_object, - finalize_cb, - finalize_hint, - result); + return v8impl::Wrap( + env, js_object, native_object, finalize_cb, finalize_hint, result); } napi_status napi_adjust_external_memory(napi_env env, @@ -3223,8 +3143,8 @@ napi_status napi_adjust_external_memory(napi_env env, CHECK_ENV(env); CHECK_ARG(env, adjusted_value); - *adjusted_value = env->isolate->AdjustAmountOfExternalAllocatedMemory( - change_in_bytes); + *adjusted_value = + env->isolate->AdjustAmountOfExternalAllocatedMemory(change_in_bytes); return napi_clear_last_error(env); } @@ -3242,18 +3162,13 @@ napi_status napi_set_instance_data(napi_env env, v8impl::RefBase::Delete(old_data); } - env->instance_data = v8impl::RefBase::New(env, - 0, - true, - finalize_cb, - data, - finalize_hint); + env->instance_data = + v8impl::RefBase::New(env, 0, true, finalize_cb, data, finalize_hint); return napi_clear_last_error(env); } -napi_status napi_get_instance_data(napi_env env, - void** data) { +napi_status napi_get_instance_data(napi_env env, void** data) { CHECK_ENV(env); CHECK_ARG(env, data); diff --git a/src/js_native_api_v8.h b/src/js_native_api_v8.h index bdf68505a7d7a3..ffe351f4f6c65e 100644 --- a/src/js_native_api_v8.h +++ b/src/js_native_api_v8.h @@ -53,8 +53,7 @@ class RefTracker { struct napi_env__ { explicit napi_env__(v8::Local context) - : isolate(context->GetIsolate()), - context_persistent(isolate, context) { + : isolate(context->GetIsolate()), context_persistent(isolate, context) { CHECK_EQ(isolate, context->GetIsolate()); napi_clear_last_error(this); } @@ -75,7 +74,9 @@ struct napi_env__ { } inline void Ref() { refs++; } - inline void Unref() { if ( --refs == 0) delete this; } + inline void Unref() { + if (--refs == 0) delete this; + } virtual bool can_call_into_js() const { return true; } virtual v8::Maybe mark_arraybuffer_as_untransferable( @@ -83,8 +84,7 @@ struct napi_env__ { return v8::Just(true); } - static inline void - HandleThrow(napi_env env, v8::Local value) { + static inline void HandleThrow(napi_env env, v8::Local value) { env->isolate->ThrowException(value); } @@ -104,9 +104,7 @@ struct napi_env__ { virtual void CallFinalizer(napi_finalize cb, void* data, void* hint) { v8::HandleScope handle_scope(isolate); - CallIntoModule([&](napi_env env) { - cb(env, data, hint); - }); + CallIntoModule([&](napi_env env) { cb(env, data, hint); }); } v8impl::Persistent last_exception; @@ -127,11 +125,9 @@ struct napi_env__ { // is exception safe versus calling Ref/Unref directly class EnvRefHolder { public: - explicit EnvRefHolder(napi_env env) : _env(env) { - _env->Ref(); - } + explicit EnvRefHolder(napi_env env) : _env(env) { _env->Ref(); } - explicit EnvRefHolder(const EnvRefHolder& other): _env(other.env()) { + explicit EnvRefHolder(const EnvRefHolder& other) : _env(other.env()) { _env->Ref(); } @@ -146,9 +142,7 @@ class EnvRefHolder { } } - napi_env env(void) const { - return _env; - } + napi_env env(void) const { return _env; } private: napi_env _env; @@ -164,21 +158,21 @@ static inline napi_status napi_clear_last_error(napi_env env) { return napi_ok; } -static inline -napi_status napi_set_last_error(napi_env env, napi_status error_code, - uint32_t engine_error_code = 0, - void* engine_reserved = nullptr) { +static inline napi_status napi_set_last_error(napi_env env, + napi_status error_code, + uint32_t engine_error_code = 0, + void* engine_reserved = nullptr) { env->last_error.error_code = error_code; env->last_error.engine_error_code = engine_error_code; env->last_error.engine_reserved = engine_reserved; return error_code; } -#define RETURN_STATUS_IF_FALSE(env, condition, status) \ - do { \ - if (!(condition)) { \ - return napi_set_last_error((env), (status)); \ - } \ +#define RETURN_STATUS_IF_FALSE(env, condition, status) \ + do { \ + if (!(condition)) { \ + return napi_set_last_error((env), (status)); \ + } \ } while (0) #define RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(env, condition, status) \ @@ -189,84 +183,81 @@ napi_status napi_set_last_error(napi_env env, napi_status error_code, } \ } while (0) -#define CHECK_ENV(env) \ - do { \ - if ((env) == nullptr) { \ - return napi_invalid_arg; \ - } \ +#define CHECK_ENV(env) \ + do { \ + if ((env) == nullptr) { \ + return napi_invalid_arg; \ + } \ } while (0) -#define CHECK_ARG(env, arg) \ +#define CHECK_ARG(env, arg) \ RETURN_STATUS_IF_FALSE((env), ((arg) != nullptr), napi_invalid_arg) -#define CHECK_ARG_WITH_PREAMBLE(env, arg) \ - RETURN_STATUS_IF_FALSE_WITH_PREAMBLE((env), \ - ((arg) != nullptr), \ - napi_invalid_arg) +#define CHECK_ARG_WITH_PREAMBLE(env, arg) \ + RETURN_STATUS_IF_FALSE_WITH_PREAMBLE( \ + (env), ((arg) != nullptr), napi_invalid_arg) -#define CHECK_MAYBE_EMPTY(env, maybe, status) \ +#define CHECK_MAYBE_EMPTY(env, maybe, status) \ RETURN_STATUS_IF_FALSE((env), !((maybe).IsEmpty()), (status)) #define CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, maybe, status) \ RETURN_STATUS_IF_FALSE_WITH_PREAMBLE((env), !((maybe).IsEmpty()), (status)) // NAPI_PREAMBLE is not wrapped in do..while: try_catch must have function scope -#define NAPI_PREAMBLE(env) \ - CHECK_ENV((env)); \ - RETURN_STATUS_IF_FALSE((env), \ - (env)->last_exception.IsEmpty() && (env)->can_call_into_js(), \ - napi_pending_exception); \ - napi_clear_last_error((env)); \ +#define NAPI_PREAMBLE(env) \ + CHECK_ENV((env)); \ + RETURN_STATUS_IF_FALSE( \ + (env), \ + (env)->last_exception.IsEmpty() && (env)->can_call_into_js(), \ + napi_pending_exception); \ + napi_clear_last_error((env)); \ v8impl::TryCatch try_catch((env)) -#define CHECK_TO_TYPE(env, type, context, result, src, status) \ - do { \ - CHECK_ARG((env), (src)); \ - auto maybe = v8impl::V8LocalValueFromJsValue((src))->To##type((context)); \ - CHECK_MAYBE_EMPTY((env), maybe, (status)); \ - (result) = maybe.ToLocalChecked(); \ +#define CHECK_TO_TYPE(env, type, context, result, src, status) \ + do { \ + CHECK_ARG((env), (src)); \ + auto maybe = v8impl::V8LocalValueFromJsValue((src))->To##type((context)); \ + CHECK_MAYBE_EMPTY((env), maybe, (status)); \ + (result) = maybe.ToLocalChecked(); \ } while (0) -#define CHECK_TO_TYPE_WITH_PREAMBLE(env, type, context, result, src, status) \ - do { \ - CHECK_ARG_WITH_PREAMBLE((env), (src)); \ - auto maybe = v8impl::V8LocalValueFromJsValue((src))->To##type((context)); \ - CHECK_MAYBE_EMPTY_WITH_PREAMBLE((env), maybe, (status)); \ - (result) = maybe.ToLocalChecked(); \ +#define CHECK_TO_TYPE_WITH_PREAMBLE(env, type, context, result, src, status) \ + do { \ + CHECK_ARG_WITH_PREAMBLE((env), (src)); \ + auto maybe = v8impl::V8LocalValueFromJsValue((src))->To##type((context)); \ + CHECK_MAYBE_EMPTY_WITH_PREAMBLE((env), maybe, (status)); \ + (result) = maybe.ToLocalChecked(); \ } while (0) -#define CHECK_TO_FUNCTION(env, result, src) \ - do { \ - CHECK_ARG((env), (src)); \ - v8::Local v8value = v8impl::V8LocalValueFromJsValue((src)); \ - RETURN_STATUS_IF_FALSE((env), v8value->IsFunction(), napi_invalid_arg); \ - (result) = v8value.As(); \ +#define CHECK_TO_FUNCTION(env, result, src) \ + do { \ + CHECK_ARG((env), (src)); \ + v8::Local v8value = v8impl::V8LocalValueFromJsValue((src)); \ + RETURN_STATUS_IF_FALSE((env), v8value->IsFunction(), napi_invalid_arg); \ + (result) = v8value.As(); \ } while (0) -#define CHECK_TO_OBJECT(env, context, result, src) \ +#define CHECK_TO_OBJECT(env, context, result, src) \ CHECK_TO_TYPE((env), Object, (context), (result), (src), napi_object_expected) -#define CHECK_TO_OBJECT_WITH_PREAMBLE(env, context, result, src) \ - CHECK_TO_TYPE_WITH_PREAMBLE((env), \ - Object, \ - (context), \ - (result), \ - (src), \ - napi_object_expected) +#define CHECK_TO_OBJECT_WITH_PREAMBLE(env, context, result, src) \ + CHECK_TO_TYPE_WITH_PREAMBLE( \ + (env), Object, (context), (result), (src), napi_object_expected) -#define CHECK_TO_STRING(env, context, result, src) \ +#define CHECK_TO_STRING(env, context, result, src) \ CHECK_TO_TYPE((env), String, (context), (result), (src), napi_string_expected) -#define GET_RETURN_STATUS(env) \ - (!try_catch.HasCaught() ? napi_ok \ - : napi_set_last_error((env), napi_pending_exception)) +#define GET_RETURN_STATUS(env) \ + (!try_catch.HasCaught() \ + ? napi_ok \ + : napi_set_last_error((env), napi_pending_exception)) -#define THROW_RANGE_ERROR_IF_FALSE(env, condition, error, message) \ - do { \ - if (!(condition)) { \ - napi_throw_range_error((env), (error), (message)); \ - return napi_set_last_error((env), napi_generic_failure); \ - } \ +#define THROW_RANGE_ERROR_IF_FALSE(env, condition, error, message) \ + do { \ + if (!(condition)) { \ + napi_throw_range_error((env), (error), (message)); \ + return napi_set_last_error((env), napi_generic_failure); \ + } \ } while (0) #define RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(env, condition, status) \ @@ -287,7 +278,7 @@ namespace v8impl { // This asserts v8::Local<> will always be implemented with a single // pointer field so that we can pass it around as a void*. static_assert(sizeof(v8::Local) == sizeof(napi_value), - "Cannot convert between v8::Local and napi_value"); + "Cannot convert between v8::Local and napi_value"); inline napi_value JsValueFromV8LocalValue(v8::Local local) { return reinterpret_cast(*local); @@ -305,10 +296,7 @@ class Finalizer { // Some Finalizers are run during shutdown when the napi_env is destroyed, // and some need to keep an explicit reference to the napi_env because they // are run independently. - enum EnvReferenceMode { - kNoEnvReference, - kKeepEnvReference - }; + enum EnvReferenceMode { kNoEnvReference, kKeepEnvReference }; protected: Finalizer(napi_env env, @@ -316,18 +304,16 @@ class Finalizer { void* finalize_data, void* finalize_hint, EnvReferenceMode refmode = kNoEnvReference) - : _env(env), - _finalize_callback(finalize_callback), - _finalize_data(finalize_data), - _finalize_hint(finalize_hint), - _has_env_reference(refmode == kKeepEnvReference) { - if (_has_env_reference) - _env->Ref(); + : _env(env), + _finalize_callback(finalize_callback), + _finalize_data(finalize_data), + _finalize_hint(finalize_hint), + _has_env_reference(refmode == kKeepEnvReference) { + if (_has_env_reference) _env->Ref(); } ~Finalizer() { - if (_has_env_reference) - _env->Unref(); + if (_has_env_reference) _env->Unref(); } public: @@ -340,9 +326,7 @@ class Finalizer { env, finalize_callback, finalize_data, finalize_hint, refmode); } - static void Delete(Finalizer* finalizer) { - delete finalizer; - } + static void Delete(Finalizer* finalizer) { delete finalizer; } protected: napi_env _env; @@ -355,8 +339,7 @@ class Finalizer { class TryCatch : public v8::TryCatch { public: - explicit TryCatch(napi_env env) - : v8::TryCatch(env->isolate), _env(env) {} + explicit TryCatch(napi_env env) : v8::TryCatch(env->isolate), _env(env) {} ~TryCatch() { if (HasCaught()) { @@ -444,10 +427,10 @@ class Reference : public RefBase { } // end of namespace v8impl -#define STATUS_CALL(call) \ - do { \ - napi_status status = (call); \ - if (status != napi_ok) return status; \ +#define STATUS_CALL(call) \ + do { \ + napi_status status = (call); \ + if (status != napi_ok) return status; \ } while (0) #endif // SRC_JS_NATIVE_API_V8_H_ diff --git a/src/js_native_api_v8_internals.h b/src/js_native_api_v8_internals.h index 8428390ef1eaf3..4f1b94d3d0c9d7 100644 --- a/src/js_native_api_v8_internals.h +++ b/src/js_native_api_v8_internals.h @@ -14,18 +14,18 @@ // included below, defines `NAPI_VERSION`. #include "node_version.h" + #include "env.h" -#include "node_internals.h" #include "gtest/gtest_prod.h" +#include "node_internals.h" -#define NAPI_ARRAYSIZE(array) \ - node::arraysize((array)) +#define NAPI_ARRAYSIZE(array) node::arraysize((array)) -#define NAPI_FIXED_ONE_BYTE_STRING(isolate, string) \ +#define NAPI_FIXED_ONE_BYTE_STRING(isolate, string) \ node::FIXED_ONE_BYTE_STRING((isolate), (string)) -#define NAPI_PRIVATE_KEY(context, suffix) \ - (node::Environment::GetCurrent((context))->napi_ ## suffix()) +#define NAPI_PRIVATE_KEY(context, suffix) \ + (node::Environment::GetCurrent((context))->napi_##suffix()) namespace v8impl { diff --git a/src/js_udp_wrap.cc b/src/js_udp_wrap.cc index c01289033e6764..3f02771ee1a907 100644 --- a/src/js_udp_wrap.cc +++ b/src/js_udp_wrap.cc @@ -5,6 +5,9 @@ #include +// TODO(RaisinTen): Replace all uses with empty `v8::Maybe`s. +#define JS_EXCEPTION_PENDING UV_EPROTO + namespace node { using errors::TryCatchScope; @@ -60,7 +63,7 @@ int JSUDPWrap::RecvStart() { Context::Scope context_scope(env()->context()); TryCatchScope try_catch(env()); Local value; - int32_t value_int = UV_EPROTO; + int32_t value_int = JS_EXCEPTION_PENDING; if (!MakeCallback(env()->onreadstart_string(), 0, nullptr).ToLocal(&value) || !value->Int32Value(env()->context()).To(&value_int)) { if (try_catch.HasCaught() && !try_catch.HasTerminated()) @@ -74,7 +77,7 @@ int JSUDPWrap::RecvStop() { Context::Scope context_scope(env()->context()); TryCatchScope try_catch(env()); Local value; - int32_t value_int = UV_EPROTO; + int32_t value_int = JS_EXCEPTION_PENDING; if (!MakeCallback(env()->onreadstop_string(), 0, nullptr).ToLocal(&value) || !value->Int32Value(env()->context()).To(&value_int)) { if (try_catch.HasCaught() && !try_catch.HasTerminated()) @@ -90,7 +93,7 @@ ssize_t JSUDPWrap::Send(uv_buf_t* bufs, Context::Scope context_scope(env()->context()); TryCatchScope try_catch(env()); Local value; - int64_t value_int = UV_EPROTO; + int64_t value_int = JS_EXCEPTION_PENDING; size_t total_len = 0; MaybeStackBuffer, 16> buffers(nbufs); @@ -100,10 +103,13 @@ ssize_t JSUDPWrap::Send(uv_buf_t* bufs, total_len += bufs[i].len; } + Local address; + if (!AddressToJS(env(), addr).ToLocal(&address)) return value_int; + Local args[] = { listener()->CreateSendWrap(total_len)->object(), Array::New(env()->isolate(), buffers.out(), nbufs), - AddressToJS(env(), addr) + address, }; if (!MakeCallback(env()->onwrite_string(), arraysize(args), args) diff --git a/src/large_pages/node_large_page.cc b/src/large_pages/node_large_page.cc index eb546c581a1652..1dddcb9f6c3560 100644 --- a/src/large_pages/node_large_page.cc +++ b/src/large_pages/node_large_page.cc @@ -68,6 +68,11 @@ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif // ifndef _GNU_SOURCE +#include +#if !defined(PR_SET_VMA) +#define PR_SET_VMA 0x53564d41 +#define PR_SET_VMA_ANON_NAME 0 +#endif #elif defined(__FreeBSD__) #include "uv.h" // uv_exepath #endif // defined(__linux__) @@ -312,6 +317,21 @@ class MemoryMapPointer { mem_ = nullptr; size_ = 0; } + static void SetName(void* mem, size_t size, const char* name) { +#if defined(__linux__) + // Available since the 5.17 kernel release and if the + // CONFIG_ANON_VMA_NAME option, we can set an identifier + // to an anonymous mapped region. However if the kernel + // option is not present or it s an older kernel, it is a no-op. + if (mem != MAP_FAILED && mem != nullptr) + prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, + reinterpret_cast(mem), + size, + reinterpret_cast(name)); +#else + (void)name; +#endif + } FORCE_INLINE ~MemoryMapPointer() { if (mem_ == nullptr) return; if (mem_ == MAP_FAILED) return; @@ -382,6 +402,7 @@ MoveTextRegionToLargePages(const text_region& r) { #endif if (mprotect(start, size, PROT_READ | PROT_EXEC) == -1) goto fail; + MemoryMapPointer::SetName(start, size, "nodejs Large Page"); // We need not `munmap(tmem, size)` on success. tmem.Reset(); diff --git a/src/node.cc b/src/node.cc index 4840b18feed691..892615f1d8eb30 100644 --- a/src/node.cc +++ b/src/node.cc @@ -25,8 +25,8 @@ #include "debug_utils-inl.h" #include "env-inl.h" -#include "memory_tracker-inl.h" #include "histogram-inl.h" +#include "memory_tracker-inl.h" #include "node_binding.h" #include "node_errors.h" #include "node_internals.h" @@ -38,6 +38,7 @@ #include "node_process-inl.h" #include "node_report.h" #include "node_revert.h" +#include "node_snapshot_builder.h" #include "node_v8_platform-inl.h" #include "node_version.h" @@ -495,6 +496,10 @@ MaybeLocal StartExecution(Environment* env, StartExecutionCallback cb) { return StartExecution(env, "internal/main/inspect"); } + if (per_process::cli_options->build_snapshot) { + return StartExecution(env, "internal/main/mksnapshot"); + } + if (per_process::cli_options->print_help) { return StartExecution(env, "internal/main/print_help"); } @@ -513,6 +518,10 @@ MaybeLocal StartExecution(Environment* env, StartExecutionCallback cb) { return StartExecution(env, "internal/main/check_syntax"); } + if (env->options()->test_runner) { + return StartExecution(env, "internal/main/test_runner"); + } + if (!first_argv.empty() && first_argv != "-") { return StartExecution(env, "internal/main/run_main_module"); } @@ -816,7 +825,7 @@ int ProcessGlobalArgs(std::vector* args, // is removed in V8. if (std::find(v8_args.begin(), v8_args.end(), "--no-harmony-import-assertions") == v8_args.end()) { - v8_args.push_back("--harmony-import-assertions"); + v8_args.emplace_back("--harmony-import-assertions"); } auto env_opts = per_process::cli_options->per_isolate->per_env; @@ -1153,29 +1162,26 @@ int Start(int argc, char** argv) { return result.exit_code; } + if (per_process::cli_options->build_snapshot) { + fprintf(stderr, + "--build-snapshot is not yet supported in the node binary\n"); + return 1; + } + { - Isolate::CreateParams params; - const std::vector* indices = nullptr; - const EnvSerializeInfo* env_info = nullptr; bool use_node_snapshot = per_process::cli_options->per_isolate->node_snapshot; - if (use_node_snapshot) { - v8::StartupData* blob = NodeMainInstance::GetEmbeddedSnapshotBlob(); - if (blob != nullptr) { - params.snapshot_blob = blob; - indices = NodeMainInstance::GetIsolateDataIndices(); - env_info = NodeMainInstance::GetEnvSerializeInfo(); - } - } + const SnapshotData* snapshot_data = + use_node_snapshot ? SnapshotBuilder::GetEmbeddedSnapshotData() + : nullptr; uv_loop_configure(uv_default_loop(), UV_METRICS_IDLE_TIME); - NodeMainInstance main_instance(¶ms, + NodeMainInstance main_instance(snapshot_data, uv_default_loop(), per_process::v8_platform.Platform(), result.args, - result.exec_args, - indices); - result.exit_code = main_instance.Run(env_info); + result.exec_args); + result.exit_code = main_instance.Run(); } TearDownOncePerProcess(); diff --git a/src/node.h b/src/node.h index 1631fff20cf30c..dec8dd6805fac9 100644 --- a/src/node.h +++ b/src/node.h @@ -442,6 +442,11 @@ enum Flags : uint64_t { kNoGlobalSearchPaths = 1 << 7, // Do not export browser globals like setTimeout, console, etc. kNoBrowserGlobals = 1 << 8, + // Controls whether or not the Environment should call V8Inspector::create(). + // This control is needed by embedders who may not want to initialize the V8 + // inspector in situations where one has already been created, + // e.g. Blink's in Chromium. + kNoCreateInspector = 1 << 9 }; } // namespace EnvironmentFlags @@ -822,11 +827,13 @@ extern "C" NODE_EXTERN void node_module_register(void* mod); #endif #if defined(_MSC_VER) -#pragma section(".CRT$XCU", read) #define NODE_C_CTOR(fn) \ NODE_CTOR_PREFIX void __cdecl fn(void); \ - __declspec(dllexport, allocate(".CRT$XCU")) \ - void (__cdecl*fn ## _)(void) = fn; \ + namespace { \ + struct fn##_ { \ + fn##_() { fn(); }; \ + } fn##_v_; \ + } \ NODE_CTOR_PREFIX void __cdecl fn(void) #else #define NODE_C_CTOR(fn) \ diff --git a/src/node_api.cc b/src/node_api.cc index b1b85073508672..aa4f8a6a2401e2 100644 --- a/src/node_api.cc +++ b/src/node_api.cc @@ -9,6 +9,7 @@ #include "node_buffer.h" #include "node_errors.h" #include "node_internals.h" +#include "node_url.h" #include "threadpoolwork-inl.h" #include "tracing/traced_value.h" #include "util-inl.h" @@ -36,7 +37,7 @@ v8::Maybe node_napi_env__::mark_arraybuffer_as_untransferable( void node_napi_env__::CallFinalizer(napi_finalize cb, void* data, void* hint) { // we need to keep the env live until the finalizer has been run // EnvRefHolder provides an exception safe wrapper to Ref and then - // Unref once the lamba is freed + // Unref once the lambda is freed EnvRefHolder liveEnv(static_cast(this)); node_env()->SetImmediate( [=, liveEnv = std::move(liveEnv)](node::Environment* node_env) { @@ -63,18 +64,16 @@ class BufferFinalizer : private Finalizer { static_cast(finalizer->_env)->node_env(); node_env->SetImmediate( [finalizer = std::move(finalizer)](node::Environment* env) { - if (finalizer->_finalize_callback == nullptr) return; + if (finalizer->_finalize_callback == nullptr) return; - v8::HandleScope handle_scope(finalizer->_env->isolate); - v8::Context::Scope context_scope(finalizer->_env->context()); + v8::HandleScope handle_scope(finalizer->_env->isolate); + v8::Context::Scope context_scope(finalizer->_env->context()); - finalizer->_env->CallIntoModule([&](napi_env env) { - finalizer->_finalize_callback( - env, - finalizer->_finalize_data, - finalizer->_finalize_hint); - }); - }); + finalizer->_env->CallIntoModule([&](napi_env env) { + finalizer->_finalize_callback( + env, finalizer->_finalize_data, finalizer->_finalize_hint); + }); + }); } struct Deleter { @@ -84,8 +83,8 @@ class BufferFinalizer : private Finalizer { }; }; -static inline napi_env -NewEnv(v8::Local context, const std::string& module_filename) { +static inline napi_env NewEnv(v8::Local context, + const std::string& module_filename) { node_napi_env result; result = new node_napi_env__(context, module_filename); @@ -97,18 +96,16 @@ NewEnv(v8::Local context, const std::string& module_filename) { // once all N-API addons using this napi_env are unloaded. // For now, a per-Environment cleanup hook is the best we can do. result->node_env()->AddCleanupHook( - [](void* arg) { - static_cast(arg)->Unref(); - }, + [](void* arg) { static_cast(arg)->Unref(); }, static_cast(result)); return result; } -static inline void trigger_fatal_exception( - napi_env env, v8::Local local_err) { +static inline void trigger_fatal_exception(napi_env env, + v8::Local local_err) { v8::Local local_msg = - v8::Exception::CreateMessage(env->isolate, local_err); + v8::Exception::CreateMessage(env->isolate, local_err); node::errors::TriggerUncaughtException(env->isolate, local_err, local_msg); } @@ -123,20 +120,20 @@ class ThreadSafeFunction : public node::AsyncResource { node_napi_env env_, void* finalize_data_, napi_finalize finalize_cb_, - napi_threadsafe_function_call_js call_js_cb_): - AsyncResource(env_->isolate, - resource, - *v8::String::Utf8Value(env_->isolate, name)), - thread_count(thread_count_), - is_closing(false), - dispatch_state(kDispatchIdle), - context(context_), - max_queue_size(max_queue_size_), - env(env_), - finalize_data(finalize_data_), - finalize_cb(finalize_cb_), - call_js_cb(call_js_cb_ == nullptr ? CallJs : call_js_cb_), - handles_closing(false) { + napi_threadsafe_function_call_js call_js_cb_) + : AsyncResource(env_->isolate, + resource, + *v8::String::Utf8Value(env_->isolate, name)), + thread_count(thread_count_), + is_closing(false), + dispatch_state(kDispatchIdle), + context(context_), + max_queue_size(max_queue_size_), + env(env_), + finalize_data(finalize_data_), + finalize_cb(finalize_cb_), + call_js_cb(call_js_cb_ == nullptr ? CallJs : call_js_cb_), + handles_closing(false) { ref.Reset(env->isolate, func); node::AddEnvironmentCleanupHook(env->isolate, Cleanup, this); env->Ref(); @@ -152,9 +149,8 @@ class ThreadSafeFunction : public node::AsyncResource { napi_status Push(void* data, napi_threadsafe_function_call_mode mode) { node::Mutex::ScopedLock lock(this->mutex); - while (queue.size() >= max_queue_size && - max_queue_size > 0 && - !is_closing) { + while (queue.size() >= max_queue_size && max_queue_size > 0 && + !is_closing) { if (mode == napi_tsfn_nonblocking) { return napi_queue_full; } @@ -210,7 +206,7 @@ class ThreadSafeFunction : public node::AsyncResource { } void EmptyQueueAndDelete() { - for (; !queue.empty() ; queue.pop()) { + for (; !queue.empty(); queue.pop()) { call_js_cb(nullptr, nullptr, context, queue.front()); } delete this; @@ -261,9 +257,7 @@ class ThreadSafeFunction : public node::AsyncResource { return napi_ok; } - inline void* Context() { - return context; - } + inline void* Context() { return context; } protected: void Dispatch() { @@ -328,12 +322,11 @@ class ThreadSafeFunction : public node::AsyncResource { napi_value js_callback = nullptr; if (!ref.IsEmpty()) { v8::Local js_cb = - v8::Local::New(env->isolate, ref); + v8::Local::New(env->isolate, ref); js_callback = v8impl::JsValueFromV8LocalValue(js_cb); } - env->CallIntoModule([&](napi_env env) { - call_js_cb(env, js_callback, context, data); - }); + env->CallIntoModule( + [&](napi_env env) { call_js_cb(env, js_callback, context, data); }); } return has_more; @@ -343,9 +336,8 @@ class ThreadSafeFunction : public node::AsyncResource { v8::HandleScope scope(env->isolate); if (finalize_cb) { CallbackScope cb_scope(this); - env->CallIntoModule([&](napi_env env) { - finalize_cb(env, finalize_data, context); - }); + env->CallIntoModule( + [&](napi_env env) { finalize_cb(env, finalize_data, context); }); } EmptyQueueAndDelete(); } @@ -392,15 +384,16 @@ class ThreadSafeFunction : public node::AsyncResource { status = napi_get_undefined(env, &recv); if (status != napi_ok) { - napi_throw_error(env, "ERR_NAPI_TSFN_GET_UNDEFINED", - "Failed to retrieve undefined value"); + napi_throw_error(env, + "ERR_NAPI_TSFN_GET_UNDEFINED", + "Failed to retrieve undefined value"); return; } status = napi_call_function(env, recv, cb, 0, nullptr, nullptr); if (status != napi_ok && status != napi_pending_exception) { - napi_throw_error(env, "ERR_NAPI_TSFN_CALL_JS", - "Failed to call JS callback"); + napi_throw_error( + env, "ERR_NAPI_TSFN_CALL_JS", "Failed to call JS callback"); return; } } @@ -413,8 +406,8 @@ class ThreadSafeFunction : public node::AsyncResource { } static void Cleanup(void* data) { - reinterpret_cast(data) - ->CloseHandlesAndMaybeDelete(true); + reinterpret_cast(data)->CloseHandlesAndMaybeDelete( + true); } private: @@ -563,7 +556,10 @@ static void napi_module_register_cb(v8::Local exports, v8::Local module, v8::Local context, void* priv) { - napi_module_register_by_symbol(exports, module, context, + napi_module_register_by_symbol( + exports, + module, + context, static_cast(priv)->nm_register_func); } @@ -575,8 +571,7 @@ void napi_module_register_by_symbol(v8::Local exports, std::string module_filename = ""; if (init == nullptr) { CHECK_NOT_NULL(node_env); - node_env->ThrowError( - "Module has no declared entry point."); + node_env->ThrowError("Module has no declared entry point."); return; } @@ -589,13 +584,13 @@ void napi_module_register_by_symbol(v8::Local exports, if (module->ToObject(context).ToLocal(&modobj) && modobj->Get(context, node_env->filename_string()).ToLocal(&filename_js) && filename_js->IsString()) { - node::Utf8Value filename(node_env->isolate(), filename_js); // Cast + node::Utf8Value filename(node_env->isolate(), filename_js); // Turn the absolute path into a URL. Currently the absolute path is always // a file system path. // TODO(gabrielschulhof): Pass the `filename` through unchanged if/when we // receive it as a URL already. - module_filename = std::string("file://") + (*filename); + module_filename = node::url::URL::FromFilePath(filename.ToString()).href(); } // Create a new napi_env for this specific module. @@ -619,23 +614,23 @@ void napi_module_register_by_symbol(v8::Local exports, namespace node { node_module napi_module_to_node_module(const napi_module* mod) { return { - -1, - mod->nm_flags | NM_F_DELETEME, - nullptr, - mod->nm_filename, - nullptr, - napi_module_register_cb, - mod->nm_modname, - const_cast(mod), // priv - nullptr, + -1, + mod->nm_flags | NM_F_DELETEME, + nullptr, + mod->nm_filename, + nullptr, + napi_module_register_cb, + mod->nm_modname, + const_cast(mod), // priv + nullptr, }; } } // namespace node // Registers a NAPI module. void napi_module_register(napi_module* mod) { - node::node_module* nm = new node::node_module( - node::napi_module_to_node_module(mod)); + node::node_module* nm = + new node::node_module(node::napi_module_to_node_module(mod)); node::node_module_register(nm); } @@ -664,23 +659,20 @@ napi_status napi_remove_env_cleanup_hook(napi_env env, struct napi_async_cleanup_hook_handle__ { napi_async_cleanup_hook_handle__(napi_env env, napi_async_cleanup_hook user_hook, - void* user_data): - env_(env), - user_hook_(user_hook), - user_data_(user_data) { + void* user_data) + : env_(env), user_hook_(user_hook), user_data_(user_data) { handle_ = node::AddEnvironmentCleanupHook(env->isolate, Hook, this); env->Ref(); } ~napi_async_cleanup_hook_handle__() { node::RemoveEnvironmentCleanupHook(std::move(handle_)); - if (done_cb_ != nullptr) - done_cb_(done_data_); + if (done_cb_ != nullptr) done_cb_(done_data_); // Release the `env` handle asynchronously since it would be surprising if // a call to a N-API function would destroy `env` synchronously. - static_cast(env_)->node_env() - ->SetImmediate([env = env_](node::Environment*) { env->Unref(); }); + static_cast(env_)->node_env()->SetImmediate( + [env = env_](node::Environment*) { env->Unref(); }); } static void Hook(void* data, void (*done_cb)(void*), void* done_data) { @@ -708,19 +700,16 @@ napi_status napi_add_async_cleanup_hook( CHECK_ARG(env, hook); napi_async_cleanup_hook_handle__* handle = - new napi_async_cleanup_hook_handle__(env, hook, arg); + new napi_async_cleanup_hook_handle__(env, hook, arg); - if (remove_handle != nullptr) - *remove_handle = handle; + if (remove_handle != nullptr) *remove_handle = handle; return napi_clear_last_error(env); } napi_status napi_remove_async_cleanup_hook( napi_async_cleanup_hook_handle remove_handle) { - - if (remove_handle == nullptr) - return napi_invalid_arg; + if (remove_handle == nullptr) return napi_invalid_arg; delete remove_handle; @@ -745,19 +734,15 @@ NAPI_NO_RETURN void napi_fatal_error(const char* location, std::string message_string; if (location_len != NAPI_AUTO_LENGTH) { - location_string.assign( - const_cast(location), location_len); + location_string.assign(const_cast(location), location_len); } else { - location_string.assign( - const_cast(location), strlen(location)); + location_string.assign(const_cast(location), strlen(location)); } if (message_len != NAPI_AUTO_LENGTH) { - message_string.assign( - const_cast(message), message_len); + message_string.assign(const_cast(message), message_len); } else { - message_string.assign( - const_cast(message), strlen(message)); + message_string.assign(const_cast(message), strlen(message)); } node::FatalError(location_string.c_str(), message_string.c_str()); @@ -830,8 +815,7 @@ napi_status napi_async_init(napi_env env, return napi_clear_last_error(env); } -napi_status napi_async_destroy(napi_env env, - napi_async_context async_context) { +napi_status napi_async_destroy(napi_env env, napi_async_context async_context) { CHECK_ENV(env); CHECK_ARG(env, async_context); @@ -889,8 +873,8 @@ napi_status napi_make_callback(napi_env env, } else { CHECK_MAYBE_EMPTY(env, callback_result, napi_generic_failure); if (result != nullptr) { - *result = v8impl::JsValueFromV8LocalValue( - callback_result.ToLocalChecked()); + *result = + v8impl::JsValueFromV8LocalValue(callback_result.ToLocalChecked()); } } @@ -931,16 +915,19 @@ napi_status napi_create_external_buffer(napi_env env, v8::Isolate* isolate = env->isolate; // The finalizer object will delete itself after invoking the callback. - v8impl::Finalizer* finalizer = v8impl::Finalizer::New( - env, finalize_cb, nullptr, finalize_hint, - v8impl::Finalizer::kKeepEnvReference); - - v8::MaybeLocal maybe = node::Buffer::New( - isolate, - static_cast(data), - length, - v8impl::BufferFinalizer::FinalizeBufferCallback, - finalizer); + v8impl::Finalizer* finalizer = + v8impl::Finalizer::New(env, + finalize_cb, + nullptr, + finalize_hint, + v8impl::Finalizer::kKeepEnvReference); + + v8::MaybeLocal maybe = + node::Buffer::New(isolate, + static_cast(data), + length, + v8impl::BufferFinalizer::FinalizeBufferCallback, + finalizer); CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure); @@ -960,9 +947,8 @@ napi_status napi_create_buffer_copy(napi_env env, NAPI_PREAMBLE(env); CHECK_ARG(env, result); - v8::MaybeLocal maybe = node::Buffer::Copy( - env->isolate, - static_cast(data), length); + v8::MaybeLocal maybe = + node::Buffer::Copy(env->isolate, static_cast(data), length); CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure); @@ -1009,11 +995,7 @@ napi_status napi_get_node_version(napi_env env, CHECK_ENV(env); CHECK_ARG(env, result); static const napi_node_version version = { - NODE_MAJOR_VERSION, - NODE_MINOR_VERSION, - NODE_PATCH_VERSION, - NODE_RELEASE - }; + NODE_MAJOR_VERSION, NODE_MINOR_VERSION, NODE_PATCH_VERSION, NODE_RELEASE}; *result = &version; return napi_clear_last_error(env); } @@ -1043,15 +1025,15 @@ class Work : public node::AsyncResource, public node::ThreadPoolWork { napi_async_execute_callback execute, napi_async_complete_callback complete = nullptr, void* data = nullptr) - : AsyncResource(env->isolate, - async_resource, - *v8::String::Utf8Value(env->isolate, async_resource_name)), - ThreadPoolWork(env->node_env()), - _env(env), - _data(data), - _execute(execute), - _complete(complete) { - } + : AsyncResource( + env->isolate, + async_resource, + *v8::String::Utf8Value(env->isolate, async_resource_name)), + ThreadPoolWork(env->node_env()), + _env(env), + _data(data), + _execute(execute), + _complete(complete) {} ~Work() override = default; @@ -1062,21 +1044,16 @@ class Work : public node::AsyncResource, public node::ThreadPoolWork { napi_async_execute_callback execute, napi_async_complete_callback complete, void* data) { - return new Work(env, async_resource, async_resource_name, - execute, complete, data); + return new Work( + env, async_resource, async_resource_name, execute, complete, data); } - static void Delete(Work* work) { - delete work; - } + static void Delete(Work* work) { delete work; } - void DoThreadPoolWork() override { - _execute(_env, _data); - } + void DoThreadPoolWork() override { _execute(_env, _data); } void AfterThreadPoolWork(int status) override { - if (_complete == nullptr) - return; + if (_complete == nullptr) return; // Establish a handle scope here so that every callback doesn't have to. // Also it is needed for the exception-handling below. @@ -1084,14 +1061,16 @@ class Work : public node::AsyncResource, public node::ThreadPoolWork { CallbackScope callback_scope(this); - _env->CallIntoModule([&](napi_env env) { - _complete(env, ConvertUVErrorCode(status), _data); - }, [](napi_env env, v8::Local local_err) { - // If there was an unhandled exception in the complete callback, - // report it as a fatal exception. (There is no JavaScript on the - // callstack that can possibly handle it.) - v8impl::trigger_fatal_exception(env, local_err); - }); + _env->CallIntoModule( + [&](napi_env env) { + _complete(env, ConvertUVErrorCode(status), _data); + }, + [](napi_env env, v8::Local local_err) { + // If there was an unhandled exception in the complete callback, + // report it as a fatal exception. (There is no JavaScript on the + // callstack that can possibly handle it.) + v8impl::trigger_fatal_exception(env, local_err); + }); // Note: Don't access `work` after this point because it was // likely deleted by the complete callback. @@ -1107,13 +1086,13 @@ class Work : public node::AsyncResource, public node::ThreadPoolWork { } // end of namespace uvimpl } // end of anonymous namespace -#define CALL_UV(env, condition) \ - do { \ - int result = (condition); \ - napi_status status = uvimpl::ConvertUVErrorCode(result); \ - if (status != napi_ok) { \ - return napi_set_last_error(env, status, result); \ - } \ +#define CALL_UV(env, condition) \ + do { \ + int result = (condition); \ + napi_status status = uvimpl::ConvertUVErrorCode(result); \ + if (status != napi_ok) { \ + return napi_set_last_error(env, status, result); \ + } \ } while (0) napi_status napi_create_async_work(napi_env env, @@ -1192,18 +1171,18 @@ napi_status napi_cancel_async_work(napi_env env, napi_async_work work) { return napi_clear_last_error(env); } -napi_status -napi_create_threadsafe_function(napi_env env, - napi_value func, - napi_value async_resource, - napi_value async_resource_name, - size_t max_queue_size, - size_t initial_thread_count, - void* thread_finalize_data, - napi_finalize thread_finalize_cb, - void* context, - napi_threadsafe_function_call_js call_js_cb, - napi_threadsafe_function* result) { +napi_status napi_create_threadsafe_function( + napi_env env, + napi_value func, + napi_value async_resource, + napi_value async_resource_name, + size_t max_queue_size, + size_t initial_thread_count, + void* thread_finalize_data, + napi_finalize thread_finalize_cb, + void* context, + napi_threadsafe_function_call_js call_js_cb, + napi_threadsafe_function* result) { CHECK_ENV(env); CHECK_ARG(env, async_resource_name); RETURN_STATUS_IF_FALSE(env, initial_thread_count > 0, napi_invalid_arg); @@ -1255,9 +1234,8 @@ napi_create_threadsafe_function(napi_env env, return napi_set_last_error(env, status); } -napi_status -napi_get_threadsafe_function_context(napi_threadsafe_function func, - void** result) { +napi_status napi_get_threadsafe_function_context(napi_threadsafe_function func, + void** result) { CHECK_NOT_NULL(func); CHECK_NOT_NULL(result); @@ -1265,36 +1243,34 @@ napi_get_threadsafe_function_context(napi_threadsafe_function func, return napi_ok; } -napi_status -napi_call_threadsafe_function(napi_threadsafe_function func, - void* data, - napi_threadsafe_function_call_mode is_blocking) { +napi_status napi_call_threadsafe_function( + napi_threadsafe_function func, + void* data, + napi_threadsafe_function_call_mode is_blocking) { CHECK_NOT_NULL(func); return reinterpret_cast(func)->Push(data, is_blocking); } -napi_status -napi_acquire_threadsafe_function(napi_threadsafe_function func) { +napi_status napi_acquire_threadsafe_function(napi_threadsafe_function func) { CHECK_NOT_NULL(func); return reinterpret_cast(func)->Acquire(); } -napi_status -napi_release_threadsafe_function(napi_threadsafe_function func, - napi_threadsafe_function_release_mode mode) { +napi_status napi_release_threadsafe_function( + napi_threadsafe_function func, napi_threadsafe_function_release_mode mode) { CHECK_NOT_NULL(func); return reinterpret_cast(func)->Release(mode); } -napi_status -napi_unref_threadsafe_function(napi_env env, napi_threadsafe_function func) { +napi_status napi_unref_threadsafe_function(napi_env env, + napi_threadsafe_function func) { CHECK_NOT_NULL(func); return reinterpret_cast(func)->Unref(); } -napi_status -napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func) { +napi_status napi_ref_threadsafe_function(napi_env env, + napi_threadsafe_function func) { CHECK_NOT_NULL(func); return reinterpret_cast(func)->Ref(); } diff --git a/src/node_api.h b/src/node_api.h index 1772c67c15afb2..d95046676a699d 100644 --- a/src/node_api.h +++ b/src/node_api.h @@ -2,12 +2,12 @@ #define SRC_NODE_API_H_ #ifdef BUILDING_NODE_EXTENSION - #ifdef _WIN32 - // Building native module against node - #define NAPI_EXTERN __declspec(dllimport) - #elif defined(__wasm32__) - #define NAPI_EXTERN __attribute__((__import_module__("napi"))) - #endif +#ifdef _WIN32 +// Building native module against node +#define NAPI_EXTERN __declspec(dllimport) +#elif defined(__wasm32__) +#define NAPI_EXTERN __attribute__((__import_module__("napi"))) +#endif #endif #include "js_native_api.h" #include "node_api_types.h" @@ -15,17 +15,17 @@ struct uv_loop_s; // Forward declaration. #ifdef _WIN32 -# define NAPI_MODULE_EXPORT __declspec(dllexport) +#define NAPI_MODULE_EXPORT __declspec(dllexport) #else -# define NAPI_MODULE_EXPORT __attribute__((visibility("default"))) +#define NAPI_MODULE_EXPORT __attribute__((visibility("default"))) #endif #if defined(__GNUC__) -# define NAPI_NO_RETURN __attribute__((noreturn)) +#define NAPI_NO_RETURN __attribute__((noreturn)) #elif defined(_WIN32) -# define NAPI_NO_RETURN __declspec(noreturn) +#define NAPI_NO_RETURN __declspec(noreturn) #else -# define NAPI_NO_RETURN +#define NAPI_NO_RETURN #endif typedef napi_value (*napi_addon_register_func)(napi_env env, @@ -41,36 +41,50 @@ typedef struct napi_module { void* reserved[4]; } napi_module; -#define NAPI_MODULE_VERSION 1 +#define NAPI_MODULE_VERSION 1 #if defined(_MSC_VER) +#if defined(__cplusplus) +#define NAPI_C_CTOR(fn) \ + static void __cdecl fn(void); \ + namespace { \ + struct fn##_ { \ + fn##_() { fn(); } \ + } fn##_v_; \ + } \ + static void __cdecl fn(void) +#else // !defined(__cplusplus) #pragma section(".CRT$XCU", read) -#define NAPI_C_CTOR(fn) \ - static void __cdecl fn(void); \ - __declspec(dllexport, allocate(".CRT$XCU")) void(__cdecl * fn##_)(void) = \ - fn; \ +// The NAPI_C_CTOR macro defines a function fn that is called during CRT +// initialization. +// C does not support dynamic initialization of static variables and this code +// simulates C++ behavior. Exporting the function pointer prevents it from being +// optimized. See for details: +// https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-initialization?view=msvc-170 +#define NAPI_C_CTOR(fn) \ + static void __cdecl fn(void); \ + __declspec(dllexport, allocate(".CRT$XCU")) void(__cdecl * fn##_)(void) = \ + fn; \ static void __cdecl fn(void) +#endif // defined(__cplusplus) #else -#define NAPI_C_CTOR(fn) \ - static void fn(void) __attribute__((constructor)); \ +#define NAPI_C_CTOR(fn) \ + static void fn(void) __attribute__((constructor)); \ static void fn(void) #endif -#define NAPI_MODULE_X(modname, regfunc, priv, flags) \ - EXTERN_C_START \ - static napi_module _module = \ - { \ - NAPI_MODULE_VERSION, \ - flags, \ - __FILE__, \ - regfunc, \ - #modname, \ - priv, \ - {0}, \ - }; \ - NAPI_C_CTOR(_register_ ## modname) { \ - napi_module_register(&_module); \ - } \ +#define NAPI_MODULE_X(modname, regfunc, priv, flags) \ + EXTERN_C_START \ + static napi_module _module = { \ + NAPI_MODULE_VERSION, \ + flags, \ + __FILE__, \ + regfunc, \ + #modname, \ + priv, \ + {0}, \ + }; \ + NAPI_C_CTOR(_register_##modname) { napi_module_register(&_module); } \ EXTERN_C_END #define NAPI_MODULE_INITIALIZER_X(base, version) \ @@ -88,24 +102,22 @@ typedef struct napi_module { } \ EXTERN_C_END #else -#define NAPI_MODULE(modname, regfunc) \ +#define NAPI_MODULE(modname, regfunc) \ NAPI_MODULE_X(modname, regfunc, NULL, 0) // NOLINT (readability/null_usage) #endif #define NAPI_MODULE_INITIALIZER_BASE napi_register_module_v -#define NAPI_MODULE_INITIALIZER \ - NAPI_MODULE_INITIALIZER_X(NAPI_MODULE_INITIALIZER_BASE, \ - NAPI_MODULE_VERSION) +#define NAPI_MODULE_INITIALIZER \ + NAPI_MODULE_INITIALIZER_X(NAPI_MODULE_INITIALIZER_BASE, NAPI_MODULE_VERSION) -#define NAPI_MODULE_INIT() \ - EXTERN_C_START \ - NAPI_MODULE_EXPORT napi_value \ - NAPI_MODULE_INITIALIZER(napi_env env, napi_value exports); \ - EXTERN_C_END \ - NAPI_MODULE(NODE_GYP_MODULE_NAME, NAPI_MODULE_INITIALIZER) \ - napi_value NAPI_MODULE_INITIALIZER(napi_env env, \ - napi_value exports) +#define NAPI_MODULE_INIT() \ + EXTERN_C_START \ + NAPI_MODULE_EXPORT napi_value NAPI_MODULE_INITIALIZER(napi_env env, \ + napi_value exports); \ + EXTERN_C_END \ + NAPI_MODULE(NODE_GYP_MODULE_NAME, NAPI_MODULE_INITIALIZER) \ + napi_value NAPI_MODULE_INITIALIZER(napi_env env, napi_value exports) EXTERN_C_START @@ -225,9 +237,8 @@ napi_create_threadsafe_function(napi_env env, napi_threadsafe_function_call_js call_js_cb, napi_threadsafe_function* result); -NAPI_EXTERN napi_status -napi_get_threadsafe_function_context(napi_threadsafe_function func, - void** result); +NAPI_EXTERN napi_status napi_get_threadsafe_function_context( + napi_threadsafe_function func, void** result); NAPI_EXTERN napi_status napi_call_threadsafe_function(napi_threadsafe_function func, @@ -237,9 +248,8 @@ napi_call_threadsafe_function(napi_threadsafe_function func, NAPI_EXTERN napi_status napi_acquire_threadsafe_function(napi_threadsafe_function func); -NAPI_EXTERN napi_status -napi_release_threadsafe_function(napi_threadsafe_function func, - napi_threadsafe_function_release_mode mode); +NAPI_EXTERN napi_status napi_release_threadsafe_function( + napi_threadsafe_function func, napi_threadsafe_function_release_mode mode); NAPI_EXTERN napi_status napi_unref_threadsafe_function(napi_env env, napi_threadsafe_function func); @@ -252,21 +262,21 @@ napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func); #if NAPI_VERSION >= 8 -NAPI_EXTERN napi_status napi_add_async_cleanup_hook( - napi_env env, - napi_async_cleanup_hook hook, - void* arg, - napi_async_cleanup_hook_handle* remove_handle); +NAPI_EXTERN napi_status +napi_add_async_cleanup_hook(napi_env env, + napi_async_cleanup_hook hook, + void* arg, + napi_async_cleanup_hook_handle* remove_handle); -NAPI_EXTERN napi_status napi_remove_async_cleanup_hook( - napi_async_cleanup_hook_handle remove_handle); +NAPI_EXTERN napi_status +napi_remove_async_cleanup_hook(napi_async_cleanup_hook_handle remove_handle); #endif // NAPI_VERSION >= 8 #ifdef NAPI_EXPERIMENTAL -NAPI_EXTERN napi_status -node_api_get_module_file_name(napi_env env, const char** result); +NAPI_EXTERN napi_status node_api_get_module_file_name(napi_env env, + const char** result); #endif // NAPI_EXPERIMENTAL diff --git a/src/node_api_types.h b/src/node_api_types.h index 58ffc61b3a5f51..4ad26a8fb6476e 100644 --- a/src/node_api_types.h +++ b/src/node_api_types.h @@ -22,8 +22,7 @@ typedef enum { } napi_threadsafe_function_call_mode; #endif // NAPI_VERSION >= 4 -typedef void (*napi_async_execute_callback)(napi_env env, - void* data); +typedef void (*napi_async_execute_callback)(napi_env env, void* data); typedef void (*napi_async_complete_callback)(napi_env env, napi_status status, void* data); diff --git a/src/node_binding.cc b/src/node_binding.cc index 050c5dff0ad5fc..2991ee34746e0f 100644 --- a/src/node_binding.cc +++ b/src/node_binding.cc @@ -59,6 +59,7 @@ V(js_udp_wrap) \ V(messaging) \ V(module_wrap) \ + V(mksnapshot) \ V(native_module) \ V(options) \ V(os) \ @@ -86,8 +87,9 @@ V(uv) \ V(v8) \ V(wasi) \ - V(worker) \ + V(wasm_web_api) \ V(watchdog) \ + V(worker) \ V(zlib) #define NODE_BUILTIN_MODULES(V) \ diff --git a/src/node_blob.cc b/src/node_blob.cc index 0d23158256cd9b..e1bf3be2b07b9e 100644 --- a/src/node_blob.cc +++ b/src/node_blob.cc @@ -444,7 +444,9 @@ void BlobBindingData::store_data_object( } void BlobBindingData::revoke_data_object(const std::string& uuid) { - CHECK_NE(data_objects_.find(uuid), data_objects_.end()); + if (data_objects_.find(uuid) == data_objects_.end()) { + return; + } data_objects_.erase(uuid); CHECK_EQ(data_objects_.find(uuid), data_objects_.end()); } diff --git a/src/node_buffer.cc b/src/node_buffer.cc index be9324a8bd82ab..215bd8003aabe1 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -220,9 +220,8 @@ inline MUST_USE_RESULT Maybe ParseArrayIndex(Environment* env, return Just(false); // Check that the result fits in a size_t. - const uint64_t kSizeMax = static_cast(static_cast(-1)); // coverity[pointless_expression] - if (static_cast(tmp_i) > kSizeMax) + if (static_cast(tmp_i) > std::numeric_limits::max()) return Just(false); *ret = static_cast(tmp_i); diff --git a/src/node_constants.cc b/src/node_constants.cc index 38c8f2738b4bad..3269e3003acd4d 100644 --- a/src/node_constants.cc +++ b/src/node_constants.cc @@ -47,6 +47,16 @@ #include #endif +#if defined(_WIN32) +#include // _S_IREAD _S_IWRITE +#ifndef S_IRUSR +#define S_IRUSR _S_IREAD +#endif // S_IRUSR +#ifndef S_IWUSR +#define S_IWUSR _S_IWRITE +#endif // S_IWUSR +#endif + #include #include #include diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 267705b7cbd4c3..c3f400f1ae5cd4 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -75,8 +75,6 @@ void Initialize(Local target, void* priv) { Environment* env = Environment::GetCurrent(context); - // TODO(joyeecheung): this needs to be called again if the instance is - // deserialized from a snapshot with the crypto bindings. if (!InitCryptoOnce(env->isolate())) { return; } diff --git a/src/node_env_var.cc b/src/node_env_var.cc index 27c833d498ec77..98f1ed07998e13 100644 --- a/src/node_env_var.cc +++ b/src/node_env_var.cc @@ -28,6 +28,7 @@ using v8::Nothing; using v8::Object; using v8::ObjectTemplate; using v8::PropertyCallbackInfo; +using v8::PropertyDescriptor; using v8::PropertyHandlerFlags; using v8::ReadOnly; using v8::String; @@ -396,11 +397,57 @@ static void EnvEnumerator(const PropertyCallbackInfo& info) { env->env_vars()->Enumerate(env->isolate())); } +static void EnvDefiner(Local property, + const PropertyDescriptor& desc, + const PropertyCallbackInfo& info) { + Environment* env = Environment::GetCurrent(info); + if (desc.has_value()) { + if (!desc.has_writable() || + !desc.has_enumerable() || + !desc.has_configurable()) { + THROW_ERR_INVALID_OBJECT_DEFINE_PROPERTY(env, + "'process.env' only accepts a " + "configurable, writable," + " and enumerable " + "data descriptor"); + } else if (!desc.configurable() || + !desc.enumerable() || + !desc.writable()) { + THROW_ERR_INVALID_OBJECT_DEFINE_PROPERTY(env, + "'process.env' only accepts a " + "configurable, writable," + " and enumerable " + "data descriptor"); + } else { + return EnvSetter(property, desc.value(), info); + } + } else if (desc.has_get() || desc.has_set()) { + // we don't accept a getter/setter in 'process.env' + THROW_ERR_INVALID_OBJECT_DEFINE_PROPERTY(env, + "'process.env' does not accept an" + "accessor(getter/setter)" + " descriptor"); + } else { + THROW_ERR_INVALID_OBJECT_DEFINE_PROPERTY(env, + "'process.env' only accepts a " + "configurable, writable," + " and enumerable " + "data descriptor"); + } +} + MaybeLocal CreateEnvVarProxy(Local context, Isolate* isolate) { EscapableHandleScope scope(isolate); Local env_proxy_template = ObjectTemplate::New(isolate); env_proxy_template->SetHandler(NamedPropertyHandlerConfiguration( - EnvGetter, EnvSetter, EnvQuery, EnvDeleter, EnvEnumerator, Local(), + EnvGetter, + EnvSetter, + EnvQuery, + EnvDeleter, + EnvEnumerator, + EnvDefiner, + nullptr, + Local(), PropertyHandlerFlags::kHasNoSideEffect)); return scope.EscapeMaybe(env_proxy_template->NewInstance(context)); } @@ -411,6 +458,7 @@ void RegisterEnvVarExternalReferences(ExternalReferenceRegistry* registry) { registry->Register(EnvQuery); registry->Register(EnvDeleter); registry->Register(EnvEnumerator); + registry->Register(EnvDefiner); } } // namespace node diff --git a/src/node_errors.h b/src/node_errors.h index f540b3e2a37de4..d5c669b3df12b9 100644 --- a/src/node_errors.h +++ b/src/node_errors.h @@ -64,6 +64,7 @@ void OnFatalError(const char* location, const char* message); V(ERR_INVALID_ARG_VALUE, TypeError) \ V(ERR_OSSL_EVP_INVALID_DIGEST, Error) \ V(ERR_INVALID_ARG_TYPE, TypeError) \ + V(ERR_INVALID_OBJECT_DEFINE_PROPERTY, TypeError) \ V(ERR_INVALID_MODULE, Error) \ V(ERR_INVALID_THIS, TypeError) \ V(ERR_INVALID_TRANSFER_OBJECT, TypeError) \ diff --git a/src/node_external_reference.cc b/src/node_external_reference.cc index 94198719b6a002..9a89977094bb55 100644 --- a/src/node_external_reference.cc +++ b/src/node_external_reference.cc @@ -7,9 +7,11 @@ namespace node { const std::vector& ExternalReferenceRegistry::external_references() { - CHECK(!is_finalized_); - external_references_.push_back(reinterpret_cast(nullptr)); - is_finalized_ = true; + if (!is_finalized_) { + external_references_.push_back(reinterpret_cast(nullptr)); + is_finalized_ = true; + } + return external_references_; } diff --git a/src/node_external_reference.h b/src/node_external_reference.h index 347945a7496d5f..306c726631a214 100644 --- a/src/node_external_reference.h +++ b/src/node_external_reference.h @@ -66,7 +66,9 @@ class ExternalReferenceRegistry { V(handle_wrap) \ V(heap_utils) \ V(messaging) \ + V(mksnapshot) \ V(native_module) \ + V(options) \ V(os) \ V(performance) \ V(process_methods) \ diff --git a/src/node_file.cc b/src/node_file.cc index 115a9fc9bfc287..105ed4c4150608 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -277,13 +277,13 @@ inline void FileHandle::Close() { detail.fd); if (env->filehandle_close_warning()) { env->set_filehandle_close_warning(false); - ProcessEmitDeprecationWarning( + USE(ProcessEmitDeprecationWarning( env, "Closing a FileHandle object on garbage collection is deprecated. " "Please close FileHandle objects explicitly using " "FileHandle.prototype.close(). In the future, an error will be " "thrown if a file descriptor is closed during garbage collection.", - "DEP0137").IsNothing(); + "DEP0137")); } }, CallbackFlags::kUnrefed); } diff --git a/src/node_http2.cc b/src/node_http2.cc index 9c609f802b8695..e0786677264b2e 100644 --- a/src/node_http2.cc +++ b/src/node_http2.cc @@ -197,7 +197,8 @@ Http2Options::Http2Options(Http2State* http2_state, SessionType type) { // Important: The maxSessionMemory option in javascript is expressed in // terms of MB increments (i.e. the value 1 == 1 MB) if (flags & (1 << IDX_OPTIONS_MAX_SESSION_MEMORY)) - set_max_session_memory(buffer[IDX_OPTIONS_MAX_SESSION_MEMORY] * 1000000); + set_max_session_memory(buffer[IDX_OPTIONS_MAX_SESSION_MEMORY] * + static_cast(1000000)); if (flags & (1 << IDX_OPTIONS_MAX_SETTINGS)) { nghttp2_option_set_max_settings( @@ -1024,6 +1025,27 @@ void Http2Session::DecrefHeaders(const nghttp2_frame* frame) { } } +uint32_t TranslateNghttp2ErrorCode(const int libErrorCode) { + switch (libErrorCode) { + case NGHTTP2_ERR_STREAM_CLOSED: + return NGHTTP2_STREAM_CLOSED; + case NGHTTP2_ERR_HEADER_COMP: + return NGHTTP2_COMPRESSION_ERROR; + case NGHTTP2_ERR_FRAME_SIZE_ERROR: + return NGHTTP2_FRAME_SIZE_ERROR; + case NGHTTP2_ERR_FLOW_CONTROL: + return NGHTTP2_FLOW_CONTROL_ERROR; + case NGHTTP2_ERR_REFUSED_STREAM: + return NGHTTP2_REFUSED_STREAM; + case NGHTTP2_ERR_PROTO: + case NGHTTP2_ERR_HTTP_HEADER: + case NGHTTP2_ERR_HTTP_MESSAGING: + return NGHTTP2_PROTOCOL_ERROR; + default: + return NGHTTP2_INTERNAL_ERROR; + } +} + // If nghttp2 is unable to send a queued up frame, it will call this callback // to let us know. If the failure occurred because we are in the process of // closing down the session or stream, we go ahead and ignore it. We don't @@ -1060,7 +1082,7 @@ int Http2Session::OnFrameNotSent(nghttp2_session* handle, Local argv[3] = { Integer::New(isolate, frame->hd.stream_id), Integer::New(isolate, frame->hd.type), - Integer::New(isolate, error_code) + Integer::New(isolate, TranslateNghttp2ErrorCode(error_code)) }; session->MakeCallback( env->http2session_on_frame_error_function(), @@ -2624,7 +2646,6 @@ void Http2Session::New(const FunctionCallbackInfo& args) { static_cast( args[0]->Int32Value(env->context()).ToChecked()); Http2Session* session = new Http2Session(state, args.This(), type); - session->get_async_id(); // avoid compiler warning Debug(session, "session created"); } diff --git a/src/node_http_common.h b/src/node_http_common.h index ad9f2a864e0af9..4440b5c58d93d7 100644 --- a/src/node_http_common.h +++ b/src/node_http_common.h @@ -196,7 +196,7 @@ enum http_status_codes { // Unlike the HTTP/1 implementation, the HTTP/2 implementation is not limited // to a fixed number of known supported HTTP methods. These constants, therefore // are provided strictly as a convenience to users and are exposed via the -// require('http2').constants object. +// require('node:http2').constants object. #define HTTP_KNOWN_METHODS(V) \ V(ACL, "ACL") \ V(BASELINE_CONTROL, "BASELINE-CONTROL") \ diff --git a/src/node_http_parser.cc b/src/node_http_parser.cc index d70e15b8c03f01..c1255b8cbd3b13 100644 --- a/src/node_http_parser.cc +++ b/src/node_http_parser.cc @@ -60,6 +60,7 @@ using v8::FunctionTemplate; using v8::HandleScope; using v8::Int32; using v8::Integer; +using v8::Isolate; using v8::Local; using v8::MaybeLocal; using v8::Number; @@ -187,7 +188,58 @@ struct StringPtr { size_t size_; }; +class Parser; + +struct ParserComparator { + bool operator()(const Parser* lhs, const Parser* rhs) const; +}; + +class ConnectionsList : public BaseObject { + public: + static void New(const FunctionCallbackInfo& args); + + static void All(const FunctionCallbackInfo& args); + + static void Idle(const FunctionCallbackInfo& args); + + static void Active(const FunctionCallbackInfo& args); + + static void Expired(const FunctionCallbackInfo& args); + + void Push(Parser* parser) { + all_connections_.insert(parser); + } + + void Pop(Parser* parser) { + all_connections_.erase(parser); + } + + void PushActive(Parser* parser) { + active_connections_.insert(parser); + } + + void PopActive(Parser* parser) { + active_connections_.erase(parser); + } + + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(ConnectionsList) + SET_SELF_SIZE(ConnectionsList) + + private: + ConnectionsList(Environment* env, Local object) + : BaseObject(env, object) { + MakeWeak(); + } + + std::set all_connections_; + std::set active_connections_; +}; + class Parser : public AsyncWrap, public StreamListener { + friend class ConnectionsList; + friend struct ParserComparator; + public: Parser(BindingData* binding_data, Local wrap) : AsyncWrap(binding_data->env(), wrap), @@ -205,10 +257,21 @@ class Parser : public AsyncWrap, public StreamListener { SET_SELF_SIZE(Parser) int on_message_begin() { + // Important: Pop from the list BEFORE resetting the last_message_start_ + // otherwise std::set.erase will fail. + if (connectionsList_ != nullptr) { + connectionsList_->PopActive(this); + } + num_fields_ = num_values_ = 0; + headers_completed_ = false; + last_message_start_ = uv_hrtime(); url_.Reset(); status_message_.Reset(); - header_parsing_start_time_ = uv_hrtime(); + + if (connectionsList_ != nullptr) { + connectionsList_->PushActive(this); + } Local cb = object()->Get(env()->context(), kOnMessageBegin) .ToLocalChecked(); @@ -297,8 +360,8 @@ class Parser : public AsyncWrap, public StreamListener { int on_headers_complete() { + headers_completed_ = true; header_nread_ = 0; - header_parsing_start_time_ = 0; // Arguments for the on-headers-complete javascript callback. This // list needs to be kept in sync with the actual argument list for @@ -429,6 +492,14 @@ class Parser : public AsyncWrap, public StreamListener { int on_message_complete() { HandleScope scope(env()->isolate()); + // Important: Pop from the list BEFORE resetting the last_message_start_ + // otherwise std::set.erase will fail. + if (connectionsList_ != nullptr) { + connectionsList_->PopActive(this); + } + + last_message_start_ = 0; + if (num_fields_) Flush(); // Flush trailing HTTP headers. @@ -486,6 +557,11 @@ class Parser : public AsyncWrap, public StreamListener { Parser* parser; ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); + if (parser->connectionsList_ != nullptr) { + parser->connectionsList_->Pop(parser); + parser->connectionsList_->PopActive(parser); + } + // Since the Parser destructor isn't going to run the destroy() callbacks // it needs to be triggered manually. parser->EmitTraceEventDestroy(); @@ -506,7 +582,6 @@ class Parser : public AsyncWrap, public StreamListener { } } - // var bytesParsed = parser->execute(buffer); static void Execute(const FunctionCallbackInfo& args) { Parser* parser; @@ -545,8 +620,8 @@ class Parser : public AsyncWrap, public StreamListener { Environment* env = Environment::GetCurrent(args); uint64_t max_http_header_size = 0; - uint64_t headers_timeout = 0; uint32_t lenient_flags = kLenientNone; + ConnectionsList* connectionsList = nullptr; CHECK(args[0]->IsInt32()); CHECK(args[1]->IsObject()); @@ -565,9 +640,9 @@ class Parser : public AsyncWrap, public StreamListener { lenient_flags = args[3].As()->Value(); } - if (args.Length() > 4) { - CHECK(args[4]->IsInt32()); - headers_timeout = args[4].As()->Value(); + if (args.Length() > 4 && !args[4]->IsNullOrUndefined()) { + CHECK(args[4]->IsObject()); + ASSIGN_OR_RETURN_UNWRAP(&connectionsList, args[4]); } llhttp_type_t type = @@ -586,7 +661,21 @@ class Parser : public AsyncWrap, public StreamListener { parser->set_provider_type(provider); parser->AsyncReset(args[1].As()); - parser->Init(type, max_http_header_size, lenient_flags, headers_timeout); + parser->Init(type, max_http_header_size, lenient_flags); + + if (connectionsList != nullptr) { + parser->connectionsList_ = connectionsList; + + parser->connectionsList_->Push(parser); + + // This protects from a DoS attack where an attacker establishes + // the connection without sending any data on applications where + // server.timeout is left to the default value of zero. + parser->last_message_start_ = uv_hrtime(); + parser->connectionsList_->PushActive(parser); + } else { + parser->connectionsList_ = nullptr; + } } template @@ -644,6 +733,26 @@ class Parser : public AsyncWrap, public StreamListener { args.GetReturnValue().Set(ret); } + static void Duration(const FunctionCallbackInfo& args) { + Parser* parser; + ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); + + if (parser->last_message_start_ == 0) { + args.GetReturnValue().Set(0); + return; + } + + double duration = (uv_hrtime() - parser->last_message_start_) / 1e6; + args.GetReturnValue().Set(duration); + } + + static void HeadersCompleted(const FunctionCallbackInfo& args) { + Parser* parser; + ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); + + args.GetReturnValue().Set(parser->headers_completed_); + } + protected: static const size_t kAllocBufferSize = 64 * 1024; @@ -690,24 +799,6 @@ class Parser : public AsyncWrap, public StreamListener { if (ret.IsEmpty()) return; - // check header parsing time - if (header_parsing_start_time_ != 0 && headers_timeout_ != 0) { - uint64_t now = uv_hrtime(); - uint64_t parsing_time = (now - header_parsing_start_time_) / 1000000; - - if (parsing_time > headers_timeout_) { - Local cb = - object()->Get(env()->context(), kOnTimeout).ToLocalChecked(); - - if (!cb->IsFunction()) - return; - - MakeCallback(cb.As(), 0, nullptr); - - return; - } - } - Local cb = object()->Get(env()->context(), kOnExecute).ToLocalChecked(); @@ -853,7 +944,7 @@ class Parser : public AsyncWrap, public StreamListener { void Init(llhttp_type_t type, uint64_t max_http_header_size, - uint32_t lenient_flags, uint64_t headers_timeout) { + uint32_t lenient_flags) { llhttp_init(&parser_, type, &settings); if (lenient_flags & kLenientHeaders) { @@ -873,9 +964,8 @@ class Parser : public AsyncWrap, public StreamListener { num_values_ = 0; have_flushed_ = false; got_exception_ = false; + headers_completed_ = false; max_http_header_size_ = max_http_header_size; - header_parsing_start_time_ = 0; - headers_timeout_ = headers_timeout; } @@ -923,11 +1013,12 @@ class Parser : public AsyncWrap, public StreamListener { size_t current_buffer_len_; const char* current_buffer_data_; unsigned int execute_depth_ = 0; + bool headers_completed_ = false; bool pending_pause_ = false; uint64_t header_nread_ = 0; uint64_t max_http_header_size_; - uint64_t headers_timeout_; - uint64_t header_parsing_start_time_ = 0; + uint64_t last_message_start_; + ConnectionsList* connectionsList_; BaseObjectPtr binding_data_; @@ -952,6 +1043,135 @@ class Parser : public AsyncWrap, public StreamListener { static const llhttp_settings_t settings; }; +bool ParserComparator::operator()(const Parser* lhs, const Parser* rhs) const { + if (lhs->last_message_start_ == 0) { + return false; + } else if (rhs->last_message_start_ == 0) { + return true; + } + + return lhs->last_message_start_ < rhs->last_message_start_; +} + +void ConnectionsList::New(const FunctionCallbackInfo& args) { + Local context = args.GetIsolate()->GetCurrentContext(); + Environment* env = Environment::GetCurrent(context); + + new ConnectionsList(env, args.This()); +} + +void ConnectionsList::All(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + Local context = isolate->GetCurrentContext(); + + Local all = Array::New(isolate); + ConnectionsList* list; + + ASSIGN_OR_RETURN_UNWRAP(&list, args.Holder()); + + uint32_t i = 0; + for (auto parser : list->all_connections_) { + if (all->Set(context, i++, parser->object()).IsNothing()) { + return; + } + } + + return args.GetReturnValue().Set(all); +} + +void ConnectionsList::Idle(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + Local context = isolate->GetCurrentContext(); + + Local idle = Array::New(isolate); + ConnectionsList* list; + + ASSIGN_OR_RETURN_UNWRAP(&list, args.Holder()); + + uint32_t i = 0; + for (auto parser : list->all_connections_) { + if (parser->last_message_start_ == 0) { + if (idle->Set(context, i++, parser->object()).IsNothing()) { + return; + } + } + } + + return args.GetReturnValue().Set(idle); +} + +void ConnectionsList::Active(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + Local context = isolate->GetCurrentContext(); + + Local active = Array::New(isolate); + ConnectionsList* list; + + ASSIGN_OR_RETURN_UNWRAP(&list, args.Holder()); + + uint32_t i = 0; + for (auto parser : list->active_connections_) { + if (active->Set(context, i++, parser->object()).IsNothing()) { + return; + } + } + + return args.GetReturnValue().Set(active); +} + +void ConnectionsList::Expired(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + Local context = isolate->GetCurrentContext(); + + Local expired = Array::New(isolate); + ConnectionsList* list; + + ASSIGN_OR_RETURN_UNWRAP(&list, args.Holder()); + CHECK(args[0]->IsNumber()); + CHECK(args[1]->IsNumber()); + uint64_t headers_timeout = + static_cast(args[0].As()->Value()) * 1000000; + uint64_t request_timeout = + static_cast(args[1].As()->Value()) * 1000000; + + if (headers_timeout == 0 && request_timeout == 0) { + return args.GetReturnValue().Set(expired); + } else if (request_timeout > 0 && headers_timeout > request_timeout) { + std::swap(headers_timeout, request_timeout); + } + + const uint64_t now = uv_hrtime(); + const uint64_t headers_deadline = + headers_timeout > 0 ? now - headers_timeout : 0; + const uint64_t request_deadline = + request_timeout > 0 ? now - request_timeout : 0; + + uint32_t i = 0; + auto iter = list->active_connections_.begin(); + auto end = list->active_connections_.end(); + while (iter != end) { + Parser* parser = *iter; + iter++; + + // Check for expiration. + if ( + (!parser->headers_completed_ && headers_deadline > 0 && + parser->last_message_start_ < headers_deadline) || + ( + request_deadline > 0 && + parser->last_message_start_ < request_deadline) + ) { + if (expired->Set(context, i++, parser->object()).IsNothing()) { + return; + } + + list->active_connections_.erase(parser); + } + } + + return args.GetReturnValue().Set(expired); +} + const llhttp_settings_t Parser::settings = { Proxy::Raw, Proxy::Raw, @@ -1038,8 +1258,19 @@ void InitializeHttpParser(Local target, env->SetProtoMethod(t, "consume", Parser::Consume); env->SetProtoMethod(t, "unconsume", Parser::Unconsume); env->SetProtoMethod(t, "getCurrentBuffer", Parser::GetCurrentBuffer); + env->SetProtoMethod(t, "duration", Parser::Duration); + env->SetProtoMethod(t, "headersCompleted", Parser::HeadersCompleted); env->SetConstructorFunction(target, "HTTPParser", t); + + Local c = env->NewFunctionTemplate(ConnectionsList::New); + c->InstanceTemplate() + ->SetInternalFieldCount(ConnectionsList::kInternalFieldCount); + env->SetProtoMethod(c, "all", ConnectionsList::All); + env->SetProtoMethod(c, "idle", ConnectionsList::Idle); + env->SetProtoMethod(c, "active", ConnectionsList::Active); + env->SetProtoMethod(c, "expired", ConnectionsList::Expired); + env->SetConstructorFunction(target, "ConnectionsList", c); } } // anonymous namespace diff --git a/src/node_internals.h b/src/node_internals.h index 5a33dad196dd95..bf760f4d99534f 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -58,7 +58,7 @@ class Environment; // Convert a struct sockaddr to a { address: '1.2.3.4', port: 1234 } JS object. // Sets address and port properties on the info object and returns it. // If |info| is omitted, a new object is returned. -v8::Local AddressToJS( +v8::MaybeLocal AddressToJS( Environment* env, const sockaddr* addr, v8::Local info = v8::Local()); @@ -379,7 +379,7 @@ class DiagnosticFilename { }; namespace heap { -bool WriteSnapshot(Environment* env, const char* filename); +v8::Maybe WriteSnapshot(Environment* env, const char* filename); } class TraceEventScope { diff --git a/src/node_main_instance.cc b/src/node_main_instance.cc index ce319cca3edca3..e7218148ea044f 100644 --- a/src/node_main_instance.cc +++ b/src/node_main_instance.cc @@ -1,9 +1,13 @@ #include "node_main_instance.h" #include +#if HAVE_OPENSSL +#include "crypto/crypto_util.h" +#endif // HAVE_OPENSSL #include "debug_utils-inl.h" #include "node_external_reference.h" #include "node_internals.h" #include "node_options-inl.h" +#include "node_snapshot_builder.h" #include "node_snapshotable.h" #include "node_v8_platform-inl.h" #include "util-inl.h" @@ -23,8 +27,6 @@ using v8::Isolate; using v8::Local; using v8::Locker; -std::unique_ptr NodeMainInstance::registry_ = - nullptr; NodeMainInstance::NodeMainInstance(Isolate* isolate, uv_loop_t* event_loop, MultiIsolatePlatform* platform, @@ -36,21 +38,13 @@ NodeMainInstance::NodeMainInstance(Isolate* isolate, isolate_(isolate), platform_(platform), isolate_data_(nullptr), - owns_isolate_(false), - deserialize_mode_(false) { + snapshot_data_(nullptr) { isolate_data_ = std::make_unique(isolate_, event_loop, platform, nullptr); SetIsolateMiscHandlers(isolate_, {}); } -const std::vector& NodeMainInstance::CollectExternalReferences() { - // Cannot be called more than once. - CHECK_NULL(registry_); - registry_.reset(new ExternalReferenceRegistry()); - return registry_->external_references(); -} - std::unique_ptr NodeMainInstance::Create( Isolate* isolate, uv_loop_t* event_loop, @@ -61,28 +55,23 @@ std::unique_ptr NodeMainInstance::Create( new NodeMainInstance(isolate, event_loop, platform, args, exec_args)); } -NodeMainInstance::NodeMainInstance( - Isolate::CreateParams* params, - uv_loop_t* event_loop, - MultiIsolatePlatform* platform, - const std::vector& args, - const std::vector& exec_args, - const std::vector* per_isolate_data_indexes) +NodeMainInstance::NodeMainInstance(const SnapshotData* snapshot_data, + uv_loop_t* event_loop, + MultiIsolatePlatform* platform, + const std::vector& args, + const std::vector& exec_args) : args_(args), exec_args_(exec_args), array_buffer_allocator_(ArrayBufferAllocator::Create()), isolate_(nullptr), platform_(platform), - isolate_data_(nullptr), - owns_isolate_(true) { - params->array_buffer_allocator = array_buffer_allocator_.get(); - deserialize_mode_ = per_isolate_data_indexes != nullptr; - if (deserialize_mode_) { - // TODO(joyeecheung): collect external references and set it in - // params.external_references. - const std::vector& external_references = - CollectExternalReferences(); - params->external_references = external_references.data(); + isolate_data_(), + isolate_params_(std::make_unique()), + snapshot_data_(snapshot_data) { + isolate_params_->array_buffer_allocator = array_buffer_allocator_.get(); + if (snapshot_data != nullptr) { + SnapshotBuilder::InitializeIsolateParams(snapshot_data, + isolate_params_.get()); } isolate_ = Isolate::Allocate(); @@ -90,48 +79,52 @@ NodeMainInstance::NodeMainInstance( // Register the isolate on the platform before the isolate gets initialized, // so that the isolate can access the platform during initialization. platform->RegisterIsolate(isolate_, event_loop); - SetIsolateCreateParamsForNode(params); - Isolate::Initialize(isolate_, *params); + SetIsolateCreateParamsForNode(isolate_params_.get()); + Isolate::Initialize(isolate_, *isolate_params_); // If the indexes are not nullptr, we are not deserializing - CHECK_IMPLIES(deserialize_mode_, params->external_references != nullptr); - isolate_data_ = std::make_unique(isolate_, - event_loop, - platform, - array_buffer_allocator_.get(), - per_isolate_data_indexes); + isolate_data_ = std::make_unique( + isolate_, + event_loop, + platform, + array_buffer_allocator_.get(), + snapshot_data == nullptr ? nullptr + : &(snapshot_data->isolate_data_indices)); IsolateSettings s; SetIsolateMiscHandlers(isolate_, s); - if (!deserialize_mode_) { + if (snapshot_data == nullptr) { // If in deserialize mode, delay until after the deserialization is // complete. SetIsolateErrorHandlers(isolate_, s); } isolate_data_->max_young_gen_size = - params->constraints.max_young_generation_size_in_bytes(); + isolate_params_->constraints.max_young_generation_size_in_bytes(); } void NodeMainInstance::Dispose() { - CHECK(!owns_isolate_); + // This should only be called on a main instance that does not own its + // isolate. + CHECK_NULL(isolate_params_); platform_->DrainTasks(isolate_); } NodeMainInstance::~NodeMainInstance() { - if (!owns_isolate_) { + if (isolate_params_ == nullptr) { return; } + // This should only be done on a main instance that owns its isolate. platform_->UnregisterIsolate(isolate_); isolate_->Dispose(); } -int NodeMainInstance::Run(const EnvSerializeInfo* env_info) { +int NodeMainInstance::Run() { Locker locker(isolate_); Isolate::Scope isolate_scope(isolate_); HandleScope handle_scope(isolate_); int exit_code = 0; DeleteFnPtr env = - CreateMainEnvironment(&exit_code, env_info); + CreateMainEnvironment(&exit_code); CHECK_NOT_NULL(env); Context::Scope context_scope(env->context()); @@ -167,8 +160,7 @@ void NodeMainInstance::Run(int* exit_code, Environment* env) { } DeleteFnPtr -NodeMainInstance::CreateMainEnvironment(int* exit_code, - const EnvSerializeInfo* env_info) { +NodeMainInstance::CreateMainEnvironment(int* exit_code) { *exit_code = 0; // Reset the exit code to 0 HandleScope handle_scope(isolate_); @@ -179,16 +171,15 @@ NodeMainInstance::CreateMainEnvironment(int* exit_code, isolate_->GetHeapProfiler()->StartTrackingHeapObjects(true); } - CHECK_IMPLIES(deserialize_mode_, env_info != nullptr); Local context; DeleteFnPtr env; - if (deserialize_mode_) { + if (snapshot_data_ != nullptr) { env.reset(new Environment(isolate_data_.get(), isolate_, args_, exec_args_, - env_info, + &(snapshot_data_->env_info), EnvironmentFlags::kDefaultFlags, {})); context = Context::FromSnapshot(isolate_, @@ -200,11 +191,15 @@ NodeMainInstance::CreateMainEnvironment(int* exit_code, Context::Scope context_scope(context); CHECK(InitializeContextRuntime(context).IsJust()); SetIsolateErrorHandlers(isolate_, {}); - env->InitializeMainContext(context, env_info); + env->InitializeMainContext(context, &(snapshot_data_->env_info)); #if HAVE_INSPECTOR env->InitializeInspector({}); #endif env->DoneBootstrapping(); + +#if HAVE_OPENSSL + crypto::InitCryptoOnce(isolate_); +#endif // HAVE_OPENSSL } else { context = NewContext(isolate_); CHECK(!context.IsEmpty()); diff --git a/src/node_main_instance.h b/src/node_main_instance.h index 047bdca873ebfd..fc44bc9df6f030 100644 --- a/src/node_main_instance.h +++ b/src/node_main_instance.h @@ -15,6 +15,7 @@ namespace node { class ExternalReferenceRegistry; struct EnvSerializeInfo; +struct SnapshotData; // TODO(joyeecheung): align this with the Worker/WorkerThreadData class. // We may be able to create an abstract class to reuse some of the routines. @@ -48,30 +49,21 @@ class NodeMainInstance { void Dispose(); // Create a main instance that owns the isolate - NodeMainInstance( - v8::Isolate::CreateParams* params, - uv_loop_t* event_loop, - MultiIsolatePlatform* platform, - const std::vector& args, - const std::vector& exec_args, - const std::vector* per_isolate_data_indexes = nullptr); + NodeMainInstance(const SnapshotData* snapshot_data, + uv_loop_t* event_loop, + MultiIsolatePlatform* platform, + const std::vector& args, + const std::vector& exec_args); ~NodeMainInstance(); // Start running the Node.js instances, return the exit code when finished. - int Run(const EnvSerializeInfo* env_info); + int Run(); void Run(int* exit_code, Environment* env); IsolateData* isolate_data() { return isolate_data_.get(); } DeleteFnPtr CreateMainEnvironment( - int* exit_code, const EnvSerializeInfo* env_info); - - // If nullptr is returned, the binary is not built with embedded - // snapshot. - static const std::vector* GetIsolateDataIndices(); - static v8::StartupData* GetEmbeddedSnapshotBlob(); - static const EnvSerializeInfo* GetEnvSerializeInfo(); - static const std::vector& CollectExternalReferences(); + int* exit_code); static const size_t kNodeContextIndex = 0; NodeMainInstance(const NodeMainInstance&) = delete; @@ -93,8 +85,8 @@ class NodeMainInstance { v8::Isolate* isolate_; MultiIsolatePlatform* platform_; std::unique_ptr isolate_data_; - bool owns_isolate_ = false; - bool deserialize_mode_ = false; + std::unique_ptr isolate_params_; + const SnapshotData* snapshot_data_ = nullptr; }; } // namespace node diff --git a/src/node_messaging.cc b/src/node_messaging.cc index aac1245f269a87..6403950e9c8a96 100644 --- a/src/node_messaging.cc +++ b/src/node_messaging.cc @@ -980,7 +980,7 @@ void MessagePort::PostMessage(const FunctionCallbackInfo& args) { // Even if the backing MessagePort object has already been deleted, we still // want to serialize the message to ensure spec-compliant behavior w.r.t. // transfers. - if (port == nullptr) { + if (port == nullptr || port->IsHandleClosing()) { Message msg; USE(msg.Serialize(env, context, args[0], transfer_list, obj)); return; diff --git a/src/node_options.cc b/src/node_options.cc index b8b6d85f39e4ed..b2b4c2c18c7f9f 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -3,6 +3,7 @@ #include "env-inl.h" #include "node_binding.h" +#include "node_external_reference.h" #include "node_internals.h" #if HAVE_OPENSSL #include "openssl/opensslv.h" @@ -142,6 +143,24 @@ void EnvironmentOptions::CheckOptions(std::vector* errors) { errors->push_back("--heap-snapshot-near-heap-limit must not be negative"); } + if (test_runner) { + if (syntax_check_only) { + errors->push_back("either --test or --check can be used, not both"); + } + + if (has_eval_string) { + errors->push_back("either --test or --eval can be used, not both"); + } + + if (force_repl) { + errors->push_back("either --test or --interactive can be used, not both"); + } + + if (debug_options_.inspector_enabled) { + errors->push_back("the inspector cannot be used with --test"); + } + } + #if HAVE_INSPECTOR if (!cpu_prof) { if (!cpu_prof_name.empty()) { @@ -497,6 +516,13 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() { "write warnings to file instead of stderr", &EnvironmentOptions::redirect_warnings, kAllowedInEnvironment); + AddOption("--test", + "launch test runner on startup", + &EnvironmentOptions::test_runner); + AddOption("--test-only", + "run tests with 'only' option set", + &EnvironmentOptions::test_only, + kAllowedInEnvironment); AddOption("--test-udp-no-try-send", "", // For testing only. &EnvironmentOptions::test_udp_no_try_send); AddOption("--throw-deprecation", @@ -724,6 +750,11 @@ PerProcessOptionsParser::PerProcessOptionsParser( "disable Object.prototype.__proto__", &PerProcessOptions::disable_proto, kAllowedInEnvironment); + AddOption("--build-snapshot", + "Generate a snapshot blob when the process exits." + "Currently only supported in the node_mksnapshot binary.", + &PerProcessOptions::build_snapshot, + kDisallowedInEnvironment); // 12.x renamed this inadvertently, so alias it for consistency within the // release line, while using the original name for consistency with older @@ -1133,6 +1164,10 @@ void Initialize(Local target, .Check(); } +void RegisterExternalReferences(ExternalReferenceRegistry* registry) { + registry->Register(GetCLIOptions); + registry->Register(GetEmbedderOptions); +} } // namespace options_parser void HandleEnvOptions(std::shared_ptr env_options) { @@ -1199,3 +1234,5 @@ std::vector ParseNodeOptionsEnvVar( } // namespace node NODE_MODULE_CONTEXT_AWARE_INTERNAL(options, node::options_parser::Initialize) +NODE_MODULE_EXTERNAL_REFERENCE(options, + node::options_parser::RegisterExternalReferences) diff --git a/src/node_options.h b/src/node_options.h index 3335c12b8cdcf7..0757a767a16208 100644 --- a/src/node_options.h +++ b/src/node_options.h @@ -148,6 +148,8 @@ class EnvironmentOptions : public Options { #endif // HAVE_INSPECTOR std::string redirect_warnings; std::string diagnostic_dir; + bool test_runner = false; + bool test_only = false; bool test_udp_no_try_send = false; bool throw_deprecation = false; bool trace_atomics_wait = false; @@ -229,6 +231,7 @@ class PerProcessOptions : public Options { bool zero_fill_all_buffers = false; bool debug_arraybuffer_allocations = false; std::string disable_proto; + bool build_snapshot; std::vector security_reverts; bool print_bash_completion = false; diff --git a/src/node_os.cc b/src/node_os.cc index 046a6106ccd0e5..6e62af00f5f3cc 100644 --- a/src/node_os.cc +++ b/src/node_os.cc @@ -174,7 +174,8 @@ static void GetInterfaceAddresses(const FunctionCallbackInfo& args) { char ip[INET6_ADDRSTRLEN]; char netmask[INET6_ADDRSTRLEN]; std::array mac; - Local name, family; + Local name; + Local family; int err = uv_interface_addresses(&interfaces, &count); @@ -214,14 +215,14 @@ static void GetInterfaceAddresses(const FunctionCallbackInfo& args) { if (interfaces[i].address.address4.sin_family == AF_INET) { uv_ip4_name(&interfaces[i].address.address4, ip, sizeof(ip)); uv_ip4_name(&interfaces[i].netmask.netmask4, netmask, sizeof(netmask)); - family = env->ipv4_string(); + family = Integer::New(env->isolate(), 4); } else if (interfaces[i].address.address4.sin_family == AF_INET6) { uv_ip6_name(&interfaces[i].address.address6, ip, sizeof(ip)); uv_ip6_name(&interfaces[i].netmask.netmask6, netmask, sizeof(netmask)); - family = env->ipv6_string(); + family = Integer::New(env->isolate(), 6); } else { strncpy(ip, "", INET6_ADDRSTRLEN); - family = env->unknown_string(); + family = Integer::New(env->isolate(), 0); } result.emplace_back(name); diff --git a/src/node_perf_common.h b/src/node_perf_common.h index 1fea2e4fc95b47..ed520e69153b46 100644 --- a/src/node_perf_common.h +++ b/src/node_perf_common.h @@ -34,7 +34,9 @@ extern uint64_t performance_v8_start; #define NODE_PERFORMANCE_ENTRY_TYPES(V) \ V(GC, "gc") \ V(HTTP, "http") \ - V(HTTP2, "http2") + V(HTTP2, "http2") \ + V(NET, "net") \ + V(DNS, "dns") enum PerformanceMilestone { #define V(name, _) NODE_PERFORMANCE_MILESTONE_##name, diff --git a/src/node_report_utils.cc b/src/node_report_utils.cc index 82ed385ad176bd..6d8b211b6d1c51 100644 --- a/src/node_report_utils.cc +++ b/src/node_report_utils.cc @@ -95,6 +95,8 @@ static void ReportPipeEndpoints(uv_handle_t* h, JSONWriter* writer) { buffer = MallocedBuffer(buffer_size); if (buffer.data != nullptr) { rc = uv_pipe_getsockname(&handle->pipe, buffer.data, &buffer_size); + } else { + buffer_size = 0; } } if (rc == 0 && buffer_size != 0 && buffer.data != nullptr) { diff --git a/src/node_snapshot_builder.h b/src/node_snapshot_builder.h new file mode 100644 index 00000000000000..183c39bdd76219 --- /dev/null +++ b/src/node_snapshot_builder.h @@ -0,0 +1,43 @@ + +#ifndef SRC_NODE_SNAPSHOT_BUILDER_H_ +#define SRC_NODE_SNAPSHOT_BUILDER_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include +#include "node_mutex.h" +#include "v8.h" + +namespace node { + +class ExternalReferenceRegistry; +struct SnapshotData; + +class SnapshotBuilder { + public: + static std::string Generate(const std::vector args, + const std::vector exec_args); + + // Generate the snapshot into out. + static void Generate(SnapshotData* out, + const std::vector args, + const std::vector exec_args); + + // If nullptr is returned, the binary is not built with embedded + // snapshot. + static const SnapshotData* GetEmbeddedSnapshotData(); + static void InitializeIsolateParams(const SnapshotData* data, + v8::Isolate::CreateParams* params); + + private: + // Used to synchronize access to the snapshot data + static Mutex snapshot_data_mutex_; + static const std::vector& CollectExternalReferences(); + + static std::unique_ptr registry_; +}; +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_NODE_SNAPSHOT_BUILDER_H_ diff --git a/src/node_snapshot_stub.cc b/src/node_snapshot_stub.cc index 7c13d4e8c602c8..664f878c0671db 100644 --- a/src/node_snapshot_stub.cc +++ b/src/node_snapshot_stub.cc @@ -2,19 +2,11 @@ // NODE_WANT_INTERNALS, so we define it here manually. #define NODE_WANT_INTERNALS 1 -#include "node_main_instance.h" +#include "node_snapshot_builder.h" namespace node { -v8::StartupData* NodeMainInstance::GetEmbeddedSnapshotBlob() { - return nullptr; -} - -const std::vector* NodeMainInstance::GetIsolateDataIndices() { - return nullptr; -} - -const EnvSerializeInfo* NodeMainInstance::GetEnvSerializeInfo() { +const SnapshotData* SnapshotBuilder::GetEmbeddedSnapshotData() { return nullptr; } diff --git a/src/node_snapshotable.cc b/src/node_snapshotable.cc index 6f45ce537907cd..1938e1b143ae2e 100644 --- a/src/node_snapshotable.cc +++ b/src/node_snapshotable.cc @@ -12,18 +12,29 @@ #include "node_internals.h" #include "node_main_instance.h" #include "node_process.h" +#include "node_snapshot_builder.h" #include "node_v8.h" #include "node_v8_platform-inl.h" +#if HAVE_INSPECTOR +#include "inspector/worker_inspector.h" // ParentInspectorHandle +#endif + namespace node { using v8::Context; +using v8::Function; +using v8::FunctionCallbackInfo; using v8::HandleScope; using v8::Isolate; using v8::Local; +using v8::MaybeLocal; using v8::Object; +using v8::ScriptCompiler; +using v8::ScriptOrigin; using v8::SnapshotCreator; using v8::StartupData; +using v8::String; using v8::TryCatch; using v8::Value; @@ -39,7 +50,7 @@ std::string FormatBlob(SnapshotData* data) { ss << R"(#include #include "env.h" -#include "node_main_instance.h" +#include "node_snapshot_builder.h" #include "v8.h" // This file is generated by tools/snapshot. Do not edit. @@ -53,35 +64,46 @@ static const char blob_data[] = { static const int blob_size = )" << data->blob.raw_size << R"(; -static v8::StartupData blob = { blob_data, blob_size }; -)"; - - ss << R"(v8::StartupData* NodeMainInstance::GetEmbeddedSnapshotBlob() { - return &blob; -} -static const std::vector isolate_data_indices { +SnapshotData snapshot_data { + // -- blob begins -- + { blob_data, blob_size }, + // -- blob ends -- + // -- isolate_data_indices begins -- + { )"; WriteVector(&ss, data->isolate_data_indices.data(), data->isolate_data_indices.size()); - ss << R"(}; + ss << R"(}, + // -- isolate_data_indices ends -- + // -- env_info begins -- +)" << data->env_info + << R"( + // -- env_info ends -- +}; + +const SnapshotData* SnapshotBuilder::GetEmbeddedSnapshotData() { + Mutex::ScopedLock lock(snapshot_data_mutex_); + return &snapshot_data; +} +} // namespace node +)"; -const std::vector* NodeMainInstance::GetIsolateDataIndices() { - return &isolate_data_indices; + return ss.str(); } -static const EnvSerializeInfo env_info )" - << data->env_info << R"(; +Mutex SnapshotBuilder::snapshot_data_mutex_; -const EnvSerializeInfo* NodeMainInstance::GetEnvSerializeInfo() { - return &env_info; +const std::vector& SnapshotBuilder::CollectExternalReferences() { + static auto registry = std::make_unique(); + return registry->external_references(); } -} // namespace node -)"; - - return ss.str(); +void SnapshotBuilder::InitializeIsolateParams(const SnapshotData* data, + Isolate::CreateParams* params) { + params->external_references = CollectExternalReferences().data(); + params->snapshot_blob = const_cast(&(data->blob)); } void SnapshotBuilder::Generate(SnapshotData* out, @@ -97,7 +119,7 @@ void SnapshotBuilder::Generate(SnapshotData* out, { const std::vector& external_references = - NodeMainInstance::CollectExternalReferences(); + CollectExternalReferences(); SnapshotCreator creator(isolate, external_references.data()); Environment* env; { @@ -133,14 +155,45 @@ void SnapshotBuilder::Generate(SnapshotData* out, nullptr, node::EnvironmentFlags::kDefaultFlags, {}); + // Run scripts in lib/internal/bootstrap/ { TryCatch bootstrapCatch(isolate); - v8::MaybeLocal result = env->RunBootstrapping(); + MaybeLocal result = env->RunBootstrapping(); + if (bootstrapCatch.HasCaught()) { + PrintCaughtException(isolate, context, bootstrapCatch); + } + result.ToLocalChecked(); + } + + // If --build-snapshot is true, lib/internal/main/mksnapshot.js would be + // loaded via LoadEnvironment() to execute process.argv[1] as the entry + // point (we currently only support this kind of entry point, but we + // could also explore snapshotting other kinds of execution modes + // in the future). + if (per_process::cli_options->build_snapshot) { +#if HAVE_INSPECTOR + env->InitializeInspector({}); +#endif + TryCatch bootstrapCatch(isolate); + // TODO(joyeecheung): we could use the result for something special, + // like setting up initializers that should be invoked at snapshot + // dehydration. + MaybeLocal result = + LoadEnvironment(env, StartExecutionCallback{}); if (bootstrapCatch.HasCaught()) { PrintCaughtException(isolate, context, bootstrapCatch); } result.ToLocalChecked(); + // FIXME(joyeecheung): right now running the loop in the snapshot + // builder seems to introduces inconsistencies in JS land that need to + // be synchronized again after snapshot restoration. + int exit_code = SpinEventLoop(env).FromMaybe(1); + CHECK_EQ(exit_code, 0); + if (bootstrapCatch.HasCaught()) { + PrintCaughtException(isolate, context, bootstrapCatch); + abort(); + } } if (per_process::enabled_debug_list.enabled(DebugCategory::MKSNAPSHOT)) { @@ -312,4 +365,57 @@ void SerializeBindingData(Environment* env, }); } +namespace mksnapshot { + +static void CompileSnapshotMain(const FunctionCallbackInfo& args) { + CHECK(args[0]->IsString()); + Local filename = args[0].As(); + Local source = args[1].As(); + Isolate* isolate = args.GetIsolate(); + Local context = isolate->GetCurrentContext(); + ScriptOrigin origin(isolate, filename, 0, 0, true); + // TODO(joyeecheung): do we need all of these? Maybe we would want a less + // internal version of them. + std::vector> parameters = { + FIXED_ONE_BYTE_STRING(isolate, "require"), + FIXED_ONE_BYTE_STRING(isolate, "__filename"), + FIXED_ONE_BYTE_STRING(isolate, "__dirname"), + }; + ScriptCompiler::Source script_source(source, origin); + Local fn; + if (ScriptCompiler::CompileFunctionInContext(context, + &script_source, + parameters.size(), + parameters.data(), + 0, + nullptr, + ScriptCompiler::kEagerCompile) + .ToLocal(&fn)) { + args.GetReturnValue().Set(fn); + } +} + +static void Initialize(Local target, + Local unused, + Local context, + void* priv) { + Environment* env = Environment::GetCurrent(context); + Isolate* isolate = context->GetIsolate(); + env->SetMethod(target, "compileSnapshotMain", CompileSnapshotMain); + target + ->Set(context, + FIXED_ONE_BYTE_STRING(isolate, "cleanups"), + v8::Array::New(isolate)) + .Check(); +} + +static void RegisterExternalReferences(ExternalReferenceRegistry* registry) { + registry->Register(CompileSnapshotMain); + registry->Register(MarkBootstrapComplete); +} +} // namespace mksnapshot } // namespace node + +NODE_MODULE_CONTEXT_AWARE_INTERNAL(mksnapshot, node::mksnapshot::Initialize) +NODE_MODULE_EXTERNAL_REFERENCE(mksnapshot, + node::mksnapshot::RegisterExternalReferences) diff --git a/src/node_snapshotable.h b/src/node_snapshotable.h index 1ccd9a93226241..f0a8bce215e027 100644 --- a/src/node_snapshotable.h +++ b/src/node_snapshotable.h @@ -12,6 +12,7 @@ namespace node { class Environment; struct EnvSerializeInfo; struct SnapshotData; +class ExternalReferenceRegistry; #define SERIALIZABLE_OBJECT_TYPES(V) \ V(fs_binding_data, fs::BindingData) \ @@ -122,15 +123,6 @@ void SerializeBindingData(Environment* env, EnvSerializeInfo* info); bool IsSnapshotableType(FastStringKey key); - -class SnapshotBuilder { - public: - static std::string Generate(const std::vector args, - const std::vector exec_args); - static void Generate(SnapshotData* out, - const std::vector args, - const std::vector exec_args); -}; } // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS diff --git a/src/node_sockaddr-inl.h b/src/node_sockaddr-inl.h index 0b2361595f3db7..e16a09b04c7d6f 100644 --- a/src/node_sockaddr-inl.h +++ b/src/node_sockaddr-inl.h @@ -157,7 +157,7 @@ void SocketAddress::Update(const sockaddr* data, size_t len) { memcpy(&address_, data, len); } -v8::Local SocketAddress::ToJS( +v8::MaybeLocal SocketAddress::ToJS( Environment* env, v8::Local info) const { return AddressToJS(env, data(), info); diff --git a/src/node_sockaddr.cc b/src/node_sockaddr.cc index 09a74f302923f7..f6afaaac4f3d66 100644 --- a/src/node_sockaddr.cc +++ b/src/node_sockaddr.cc @@ -847,7 +847,9 @@ void SocketAddressBase::LegacyDetail(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); SocketAddressBase* base; ASSIGN_OR_RETURN_UNWRAP(&base, args.Holder()); - args.GetReturnValue().Set(base->address_->ToJS(env)); + Local address; + if (!base->address_->ToJS(env).ToLocal(&address)) return; + args.GetReturnValue().Set(address); } SocketAddressBase::SocketAddressBase( diff --git a/src/node_sockaddr.h b/src/node_sockaddr.h index 4cc5291ceefead..0a4633b9a33d7e 100644 --- a/src/node_sockaddr.h +++ b/src/node_sockaddr.h @@ -131,7 +131,7 @@ class SocketAddress : public MemoryRetainer { static SocketAddress FromPeerName(const uv_udp_t& handle); static SocketAddress FromPeerName(const uv_tcp_t& handle); - inline v8::Local ToJS( + inline v8::MaybeLocal ToJS( Environment* env, v8::Local obj = v8::Local()) const; diff --git a/src/node_url.cc b/src/node_url.cc index 6e47acf7ae3c3f..b13c94f030fa59 100644 --- a/src/node_url.cc +++ b/src/node_url.cc @@ -142,22 +142,12 @@ URLHost::~URLHost() { XX(ARG_FRAGMENT) \ XX(ARG_COUNT) // This one has to be last. -#define ERR_ARGS(XX) \ - XX(ERR_ARG_FLAGS) \ - XX(ERR_ARG_INPUT) \ - enum url_cb_args { #define XX(name) name, ARGS(XX) #undef XX }; -enum url_error_cb_args { -#define XX(name) name, - ERR_ARGS(XX) -#undef XX -}; - #define TWO_CHAR_STRING_TEST(bits, name, expr) \ template \ bool name(const T ch1, const T ch2) { \ @@ -232,15 +222,14 @@ void AppendOrEscape(std::string* str, *str += ch; } -template -unsigned hex2bin(const T ch) { +unsigned hex2bin(const char ch) { if (ch >= '0' && ch <= '9') return ch - '0'; if (ch >= 'A' && ch <= 'F') return 10 + (ch - 'A'); if (ch >= 'a' && ch <= 'f') return 10 + (ch - 'a'); - return static_cast(-1); + UNREACHABLE(); } std::string PercentDecode(const char* input, size_t len) { @@ -932,7 +921,10 @@ void URL::Parse(const char* input, url->flags &= ~URL_FLAGS_SPECIAL; special = false; } - special_back_slash = (special && ch == '\\'); + // `special_back_slash` equals to `(special && ch == '\\')` and `ch` + // here always not equals to `\\`. So `special_back_slash` here always + // equals to `false`. + special_back_slash = false; buffer.clear(); if (has_state_override) return; @@ -1674,14 +1666,10 @@ void Parse(Environment* env, null, // fragment defaults to null }; SetArgs(env, argv, url); - cb->Call(context, recv, arraysize(argv), argv).FromMaybe(Local()); + USE(cb->Call(context, recv, arraysize(argv), argv)); } else if (error_cb->IsFunction()) { - Local argv[2] = { undef, undef }; - argv[ERR_ARG_FLAGS] = Integer::NewFromUnsigned(isolate, url.flags); - argv[ERR_ARG_INPUT] = - String::NewFromUtf8(env->isolate(), input).ToLocalChecked(); - error_cb.As()->Call(context, recv, arraysize(argv), argv) - .FromMaybe(Local()); + Local flags = Integer::NewFromUnsigned(isolate, url.flags); + USE(error_cb.As()->Call(context, recv, 1, &flags)); } } diff --git a/src/node_v8.cc b/src/node_v8.cc index de3d3daad501f6..e984621a4c0a99 100644 --- a/src/node_v8.cc +++ b/src/node_v8.cc @@ -47,19 +47,21 @@ using v8::Uint32; using v8::V8; using v8::Value; - -#define HEAP_STATISTICS_PROPERTIES(V) \ - V(0, total_heap_size, kTotalHeapSizeIndex) \ - V(1, total_heap_size_executable, kTotalHeapSizeExecutableIndex) \ - V(2, total_physical_size, kTotalPhysicalSizeIndex) \ - V(3, total_available_size, kTotalAvailableSize) \ - V(4, used_heap_size, kUsedHeapSizeIndex) \ - V(5, heap_size_limit, kHeapSizeLimitIndex) \ - V(6, malloced_memory, kMallocedMemoryIndex) \ - V(7, peak_malloced_memory, kPeakMallocedMemoryIndex) \ - V(8, does_zap_garbage, kDoesZapGarbageIndex) \ - V(9, number_of_native_contexts, kNumberOfNativeContextsIndex) \ - V(10, number_of_detached_contexts, kNumberOfDetachedContextsIndex) +#define HEAP_STATISTICS_PROPERTIES(V) \ + V(0, total_heap_size, kTotalHeapSizeIndex) \ + V(1, total_heap_size_executable, kTotalHeapSizeExecutableIndex) \ + V(2, total_physical_size, kTotalPhysicalSizeIndex) \ + V(3, total_available_size, kTotalAvailableSize) \ + V(4, used_heap_size, kUsedHeapSizeIndex) \ + V(5, heap_size_limit, kHeapSizeLimitIndex) \ + V(6, malloced_memory, kMallocedMemoryIndex) \ + V(7, peak_malloced_memory, kPeakMallocedMemoryIndex) \ + V(8, does_zap_garbage, kDoesZapGarbageIndex) \ + V(9, number_of_native_contexts, kNumberOfNativeContextsIndex) \ + V(10, number_of_detached_contexts, kNumberOfDetachedContextsIndex) \ + V(11, total_global_handles_size, kTotalGlobalHandlesSizeIndex) \ + V(12, used_global_handles_size, kUsedGlobalHandlesSizeIndex) \ + V(13, external_memory, kExternalMemoryIndex) #define V(a, b, c) +1 static constexpr size_t kHeapStatisticsPropertiesCount = diff --git a/src/node_version.h b/src/node_version.h index 05128631b72368..fa83c7cce33740 100644 --- a/src/node_version.h +++ b/src/node_version.h @@ -22,7 +22,7 @@ #ifndef SRC_NODE_VERSION_H_ #define SRC_NODE_VERSION_H_ -#define NODE_MAJOR_VERSION 18 +#define NODE_MAJOR_VERSION 19 #define NODE_MINOR_VERSION 0 #define NODE_PATCH_VERSION 0 @@ -89,7 +89,7 @@ * version matching should open a pull request to reserve a number in this * registry. */ -#define NODE_MODULE_VERSION 105 +#define NODE_MODULE_VERSION 108 // The NAPI_VERSION provided by this version of the runtime. This is the version // which the Node binary being built supports. diff --git a/src/node_wasi.cc b/src/node_wasi.cc index ffcc37c5274c98..803edf0c91dda1 100644 --- a/src/node_wasi.cc +++ b/src/node_wasi.cc @@ -236,11 +236,9 @@ void WASI::New(const FunctionCallbackInfo& args) { delete[] options.argv; } - if (options.envp != nullptr) { - for (uint32_t i = 0; options.envp[i]; i++) - free(const_cast(options.envp[i])); - delete[] options.envp; - } + for (uint32_t i = 0; options.envp[i]; i++) + free(const_cast(options.envp[i])); + delete[] options.envp; if (options.preopens != nullptr) { for (uint32_t i = 0; i < options.preopenc; i++) { diff --git a/src/node_wasm_web_api.cc b/src/node_wasm_web_api.cc new file mode 100644 index 00000000000000..b23096120b1121 --- /dev/null +++ b/src/node_wasm_web_api.cc @@ -0,0 +1,196 @@ +#include "node_wasm_web_api.h" + +#include "memory_tracker-inl.h" +#include "node_errors.h" + +namespace node { +namespace wasm_web_api { + +using v8::ArrayBuffer; +using v8::ArrayBufferView; +using v8::Context; +using v8::Function; +using v8::FunctionCallbackInfo; +using v8::FunctionTemplate; +using v8::Local; +using v8::MaybeLocal; +using v8::Object; +using v8::Value; +using v8::WasmStreaming; + +Local WasmStreamingObject::Initialize(Environment* env) { + Local templ = env->wasm_streaming_object_constructor(); + if (!templ.IsEmpty()) { + return templ; + } + + Local t = env->NewFunctionTemplate(New); + t->Inherit(BaseObject::GetConstructorTemplate(env)); + t->InstanceTemplate()->SetInternalFieldCount( + WasmStreamingObject::kInternalFieldCount); + + env->SetProtoMethod(t, "push", Push); + env->SetProtoMethod(t, "finish", Finish); + env->SetProtoMethod(t, "abort", Abort); + + auto function = t->GetFunction(env->context()).ToLocalChecked(); + env->set_wasm_streaming_object_constructor(function); + return function; +} + +void WasmStreamingObject::RegisterExternalReferences( + ExternalReferenceRegistry* registry) { + registry->Register(Push); + registry->Register(Finish); + registry->Register(Abort); +} + +void WasmStreamingObject::MemoryInfo(MemoryTracker* tracker) const { + // v8::WasmStreaming is opaque. We assume that the size of the WebAssembly + // module that is being compiled is roughly what V8 allocates (as in, off by + // only a small factor). + tracker->TrackFieldWithSize("streaming", wasm_size_); +} + +MaybeLocal WasmStreamingObject::Create( + Environment* env, std::shared_ptr streaming) { + Local ctor = Initialize(env); + Local obj; + if (!ctor->NewInstance(env->context(), 0, nullptr).ToLocal(&obj)) { + return MaybeLocal(); + } + + CHECK(streaming); + + WasmStreamingObject* ptr = Unwrap(obj); + CHECK_NOT_NULL(ptr); + ptr->streaming_ = streaming; + ptr->wasm_size_ = 0; + return obj; +} + +void WasmStreamingObject::New(const FunctionCallbackInfo& args) { + CHECK(args.IsConstructCall()); + Environment* env = Environment::GetCurrent(args); + new WasmStreamingObject(env, args.This()); +} + +void WasmStreamingObject::Push(const FunctionCallbackInfo& args) { + WasmStreamingObject* obj; + ASSIGN_OR_RETURN_UNWRAP(&obj, args.Holder()); + CHECK(obj->streaming_); + + CHECK_EQ(args.Length(), 1); + Local chunk = args[0]; + + // The start of the memory section backing the ArrayBuffer(View), the offset + // of the ArrayBuffer(View) within the memory section, and its size in bytes. + const void* bytes; + size_t offset; + size_t size; + + if (LIKELY(chunk->IsArrayBufferView())) { + Local view = chunk.As(); + bytes = view->Buffer()->GetBackingStore()->Data(); + offset = view->ByteOffset(); + size = view->ByteLength(); + } else if (LIKELY(chunk->IsArrayBuffer())) { + Local buffer = chunk.As(); + bytes = buffer->GetBackingStore()->Data(); + offset = 0; + size = buffer->ByteLength(); + } else { + return node::THROW_ERR_INVALID_ARG_TYPE( + Environment::GetCurrent(args), + "chunk must be an ArrayBufferView or an ArrayBuffer"); + } + + // Forward the data to V8. Internally, V8 will make a copy. + obj->streaming_->OnBytesReceived(static_cast(bytes) + offset, + size); + obj->wasm_size_ += size; +} + +void WasmStreamingObject::Finish(const FunctionCallbackInfo& args) { + WasmStreamingObject* obj; + ASSIGN_OR_RETURN_UNWRAP(&obj, args.Holder()); + CHECK(obj->streaming_); + + CHECK_EQ(args.Length(), 0); + obj->streaming_->Finish(); +} + +void WasmStreamingObject::Abort(const FunctionCallbackInfo& args) { + WasmStreamingObject* obj; + ASSIGN_OR_RETURN_UNWRAP(&obj, args.Holder()); + CHECK(obj->streaming_); + + CHECK_EQ(args.Length(), 1); + obj->streaming_->Abort(args[0]); +} + +void StartStreamingCompilation(const FunctionCallbackInfo& info) { + // V8 passes an instance of v8::WasmStreaming to this callback, which we can + // use to pass the WebAssembly module bytes to V8 as we receive them. + // Unfortunately, our fetch() implementation is a JavaScript dependency, so it + // is difficult to implement the required logic here. Instead, we create a + // a WasmStreamingObject that encapsulates v8::WasmStreaming and that we can + // pass to the JavaScript implementation. The JavaScript implementation can + // then push() bytes from the Response and eventually either finish() or + // abort() the operation. + + // Create the wrapper object. + std::shared_ptr streaming = + WasmStreaming::Unpack(info.GetIsolate(), info.Data()); + Environment* env = Environment::GetCurrent(info); + Local obj; + if (!WasmStreamingObject::Create(env, streaming).ToLocal(&obj)) { + // A JavaScript exception is pending. Let V8 deal with it. + return; + } + + // V8 always passes one argument to this callback. + CHECK_EQ(info.Length(), 1); + + // Prepare the JavaScript implementation for invocation. We will pass the + // WasmStreamingObject as the first argument, followed by the argument that we + // received from V8, i.e., the first argument passed to compileStreaming (or + // instantiateStreaming). + Local impl = env->wasm_streaming_compilation_impl(); + CHECK(!impl.IsEmpty()); + Local args[] = {obj, info[0]}; + + // Hand control to the JavaScript implementation. It should never throw an + // error, but if it does, we leave it to the calling V8 code to handle that + // gracefully. Otherwise, we assert that the JavaScript function does not + // return anything. + MaybeLocal maybe_ret = + impl->Call(env->context(), info.This(), 2, args); + Local ret; + CHECK_IMPLIES(maybe_ret.ToLocal(&ret), ret->IsUndefined()); +} + +// Called once by JavaScript during initialization. +void SetImplementation(const FunctionCallbackInfo& info) { + Environment* env = Environment::GetCurrent(info); + env->set_wasm_streaming_compilation_impl(info[0].As()); +} + +void Initialize(Local target, + Local, + Local context, + void*) { + Environment* env = Environment::GetCurrent(context); + env->SetMethod(target, "setImplementation", SetImplementation); +} + +void RegisterExternalReferences(ExternalReferenceRegistry* registry) { + registry->Register(SetImplementation); +} + +} // namespace wasm_web_api +} // namespace node + +NODE_MODULE_CONTEXT_AWARE_INTERNAL(wasm_web_api, node::wasm_web_api::Initialize) +NODE_MODULE_EXTERNAL_REFERENCE(wasm_web_api, + node::wasm_web_api::RegisterExternalReferences) diff --git a/src/node_wasm_web_api.h b/src/node_wasm_web_api.h new file mode 100644 index 00000000000000..9f5fe868167635 --- /dev/null +++ b/src/node_wasm_web_api.h @@ -0,0 +1,54 @@ +#ifndef SRC_NODE_WASM_WEB_API_H_ +#define SRC_NODE_WASM_WEB_API_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "base_object-inl.h" +#include "v8.h" + +namespace node { +namespace wasm_web_api { + +// Wrapper for interacting with a v8::WasmStreaming instance from JavaScript. +class WasmStreamingObject final : public BaseObject { + public: + static v8::Local Initialize(Environment* env); + + static void RegisterExternalReferences(ExternalReferenceRegistry* registry); + + void MemoryInfo(MemoryTracker* tracker) const override; + SET_MEMORY_INFO_NAME(WasmStreamingObject) + SET_SELF_SIZE(WasmStreamingObject) + + static v8::MaybeLocal Create( + Environment* env, std::shared_ptr streaming); + + private: + WasmStreamingObject(Environment* env, v8::Local object) + : BaseObject(env, object) { + MakeWeak(); + } + + ~WasmStreamingObject() override {} + + private: + static void New(const v8::FunctionCallbackInfo& args); + static void Push(const v8::FunctionCallbackInfo& args); + static void Finish(const v8::FunctionCallbackInfo& args); + static void Abort(const v8::FunctionCallbackInfo& args); + + std::shared_ptr streaming_; + size_t wasm_size_; +}; + +// This is a v8::WasmStreamingCallback implementation that must be passed to +// v8::Isolate::SetWasmStreamingCallback when setting up the isolate in order to +// enable the WebAssembly.(compile|instantiate)Streaming APIs. +void StartStreamingCompilation(const v8::FunctionCallbackInfo& args); + +} // namespace wasm_web_api +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_NODE_WASM_WEB_API_H_ diff --git a/src/node_worker.cc b/src/node_worker.cc index c9743fcf583a08..2a509165c250c0 100644 --- a/src/node_worker.cc +++ b/src/node_worker.cc @@ -7,6 +7,7 @@ #include "node_buffer.h" #include "node_options-inl.h" #include "node_perf.h" +#include "node_snapshot_builder.h" #include "util-inl.h" #include "async_wrap-inl.h" @@ -146,6 +147,20 @@ class WorkerThreadData { SetIsolateCreateParamsForNode(¶ms); params.array_buffer_allocator_shared = allocator; + bool use_node_snapshot = true; + if (w_->per_isolate_opts_) { + use_node_snapshot = w_->per_isolate_opts_->node_snapshot; + } else { + // IsolateData is created after the Isolate is created so we'll + // inherit the option from the parent here. + use_node_snapshot = per_process::cli_options->per_isolate->node_snapshot; + } + const SnapshotData* snapshot_data = + use_node_snapshot ? SnapshotBuilder::GetEmbeddedSnapshotData() + : nullptr; + if (snapshot_data != nullptr) { + SnapshotBuilder::InitializeIsolateParams(snapshot_data, ¶ms); + } w->UpdateResourceConstraints(¶ms.constraints); Isolate* isolate = Isolate::Allocate(); @@ -665,6 +680,12 @@ void Worker::Ref(const FunctionCallbackInfo& args) { } } +void Worker::HasRef(const FunctionCallbackInfo& args) { + Worker* w; + ASSIGN_OR_RETURN_UNWRAP(&w, args.This()); + args.GetReturnValue().Set(w->has_ref_); +} + void Worker::Unref(const FunctionCallbackInfo& args) { Worker* w; ASSIGN_OR_RETURN_UNWRAP(&w, args.This()); @@ -827,6 +848,7 @@ void InitWorker(Local target, env->SetProtoMethod(w, "startThread", Worker::StartThread); env->SetProtoMethod(w, "stopThread", Worker::StopThread); + env->SetProtoMethod(w, "hasRef", Worker::HasRef); env->SetProtoMethod(w, "ref", Worker::Ref); env->SetProtoMethod(w, "unref", Worker::Unref); env->SetProtoMethod(w, "getResourceLimits", Worker::GetResourceLimits); @@ -890,6 +912,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) { registry->Register(Worker::New); registry->Register(Worker::StartThread); registry->Register(Worker::StopThread); + registry->Register(Worker::HasRef); registry->Register(Worker::Ref); registry->Register(Worker::Unref); registry->Register(Worker::GetResourceLimits); diff --git a/src/node_worker.h b/src/node_worker.h index d400c4c991dcbc..f2a9386aeabff3 100644 --- a/src/node_worker.h +++ b/src/node_worker.h @@ -61,6 +61,7 @@ class Worker : public AsyncWrap { static void SetEnvVars(const v8::FunctionCallbackInfo& args); static void StartThread(const v8::FunctionCallbackInfo& args); static void StopThread(const v8::FunctionCallbackInfo& args); + static void HasRef(const v8::FunctionCallbackInfo& args); static void Ref(const v8::FunctionCallbackInfo& args); static void Unref(const v8::FunctionCallbackInfo& args); static void GetResourceLimits( diff --git a/src/node_zlib.cc b/src/node_zlib.cc index ec0a8e90f1cbcf..5930ffd7a8ae8e 100644 --- a/src/node_zlib.cc +++ b/src/node_zlib.cc @@ -49,7 +49,6 @@ using v8::Context; using v8::Function; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; -using v8::Global; using v8::HandleScope; using v8::Int32; using v8::Integer; @@ -107,8 +106,8 @@ enum node_zlib_mode { BROTLI_ENCODE }; -#define GZIP_HEADER_ID1 0x1f -#define GZIP_HEADER_ID2 0x8b +constexpr uint8_t GZIP_HEADER_ID1 = 0x1f; +constexpr uint8_t GZIP_HEADER_ID2 = 0x8b; struct CompressionError { CompressionError(const char* message, const char* code, int err) @@ -127,14 +126,14 @@ struct CompressionError { inline bool IsError() const { return code != nullptr; } }; -class ZlibContext : public MemoryRetainer { +class ZlibContext final : public MemoryRetainer { public: ZlibContext() = default; // Streaming-related, should be available for all compression libraries: void Close(); void DoThreadPoolWork(); - void SetBuffers(char* in, uint32_t in_len, char* out, uint32_t out_len); + void SetBuffers(const char* in, uint32_t in_len, char* out, uint32_t out_len); void SetFlush(int flush); void GetAfterWriteOffsets(uint32_t* avail_in, uint32_t* avail_out) const; CompressionError GetErrorInfo() const; @@ -183,7 +182,7 @@ class BrotliContext : public MemoryRetainer { public: BrotliContext() = default; - void SetBuffers(char* in, uint32_t in_len, char* out, uint32_t out_len); + void SetBuffers(const char* in, uint32_t in_len, char* out, uint32_t out_len); void SetFlush(int flush); void GetAfterWriteOffsets(uint32_t* avail_in, uint32_t* avail_out) const; inline void SetMode(node_zlib_mode mode) { mode_ = mode; } @@ -193,7 +192,7 @@ class BrotliContext : public MemoryRetainer { protected: node_zlib_mode mode_ = NONE; - uint8_t* next_in_ = nullptr; + const uint8_t* next_in_ = nullptr; uint8_t* next_out_ = nullptr; size_t avail_in_ = 0; size_t avail_out_ = 0; @@ -251,6 +250,12 @@ class BrotliDecoderContext final : public BrotliContext { template class CompressionStream : public AsyncWrap, public ThreadPoolWork { public: + enum InternalFields { + kCompressionStreamBaseField = AsyncWrap::kInternalFieldCount, + kWriteJSCallback, + kInternalFieldCount + }; + CompressionStream(Environment* env, Local wrap) : AsyncWrap(env, wrap, AsyncWrap::PROVIDER_ZLIB), ThreadPoolWork(env), @@ -259,7 +264,7 @@ class CompressionStream : public AsyncWrap, public ThreadPoolWork { } ~CompressionStream() override { - CHECK_EQ(false, write_in_progress_ && "write in progress"); + CHECK(!write_in_progress_); Close(); CHECK_EQ(zlib_memory_, 0); CHECK_EQ(unreported_allocations_, 0); @@ -295,7 +300,7 @@ class CompressionStream : public AsyncWrap, public ThreadPoolWork { CHECK_EQ(args.Length(), 7); uint32_t in_off, in_len, out_off, out_len, flush; - char* in; + const char* in; char* out; CHECK_EQ(false, args[0]->IsUndefined() && "must provide flush value"); @@ -340,7 +345,7 @@ class CompressionStream : public AsyncWrap, public ThreadPoolWork { template void Write(uint32_t flush, - char* in, uint32_t in_len, + const char* in, uint32_t in_len, char* out, uint32_t out_len) { AllocScope alloc_scope(this); @@ -394,6 +399,7 @@ class CompressionStream : public AsyncWrap, public ThreadPoolWork { // v8 land! void AfterThreadPoolWork(int status) override { + DCHECK(init_done_); AllocScope alloc_scope(this); auto on_scope_leave = OnScopeLeave([&]() { Unref(); }); @@ -416,9 +422,8 @@ class CompressionStream : public AsyncWrap, public ThreadPoolWork { UpdateWriteResult(); // call the write() cb - Local cb = PersistentToLocal::Default(env->isolate(), - write_js_callback_); - MakeCallback(cb, 0, nullptr); + Local cb = object()->GetInternalField(kWriteJSCallback); + MakeCallback(cb.As(), 0, nullptr); if (pending_close_) Close(); @@ -431,7 +436,7 @@ class CompressionStream : public AsyncWrap, public ThreadPoolWork { CHECK_EQ(env->context(), env->isolate()->GetCurrentContext()); HandleScope scope(env->isolate()); - Local args[3] = { + Local args[] = { OneByteString(env->isolate(), err.message), Integer::New(env->isolate(), err.err), OneByteString(env->isolate(), err.code) @@ -465,7 +470,7 @@ class CompressionStream : public AsyncWrap, public ThreadPoolWork { void InitStream(uint32_t* write_result, Local write_js_callback) { write_result_ = write_result; - write_js_callback_.Reset(AsyncWrap::env()->isolate(), write_js_callback); + object()->SetInternalField(kWriteJSCallback, write_js_callback); init_done_ = true; } @@ -540,14 +545,13 @@ class CompressionStream : public AsyncWrap, public ThreadPoolWork { bool closed_ = false; unsigned int refs_ = 0; uint32_t* write_result_ = nullptr; - Global write_js_callback_; std::atomic unreported_allocations_{0}; size_t zlib_memory_ = 0; CompressionContext ctx_; }; -class ZlibStream : public CompressionStream { +class ZlibStream final : public CompressionStream { public: ZlibStream(Environment* env, Local wrap, node_zlib_mode mode) : CompressionStream(env, wrap) { @@ -646,7 +650,8 @@ class ZlibStream : public CompressionStream { }; template -class BrotliCompressionStream : public CompressionStream { +class BrotliCompressionStream final : + public CompressionStream { public: BrotliCompressionStream(Environment* env, Local wrap, @@ -857,10 +862,10 @@ void ZlibContext::DoThreadPoolWork() { } -void ZlibContext::SetBuffers(char* in, uint32_t in_len, +void ZlibContext::SetBuffers(const char* in, uint32_t in_len, char* out, uint32_t out_len) { strm_.avail_in = in_len; - strm_.next_in = reinterpret_cast(in); + strm_.next_in = const_cast(reinterpret_cast(in)); strm_.avail_out = out_len; strm_.next_out = reinterpret_cast(out); } @@ -1093,9 +1098,9 @@ CompressionError ZlibContext::SetParams(int level, int strategy) { } -void BrotliContext::SetBuffers(char* in, uint32_t in_len, +void BrotliContext::SetBuffers(const char* in, uint32_t in_len, char* out, uint32_t out_len) { - next_in_ = reinterpret_cast(in); + next_in_ = reinterpret_cast(in); next_out_ = reinterpret_cast(out); avail_in_ = in_len; avail_out_ = out_len; diff --git a/src/string_bytes.cc b/src/string_bytes.cc index daff1424d22e5a..5b530c85477310 100644 --- a/src/string_bytes.cc +++ b/src/string_bytes.cc @@ -642,10 +642,6 @@ MaybeLocal StringBytes::Encode(Isolate* isolate, switch (encoding) { case BUFFER: { - if (buflen > node::Buffer::kMaxLength) { - *error = node::ERR_BUFFER_TOO_LARGE(isolate); - return MaybeLocal(); - } auto maybe_buf = Buffer::Copy(isolate, buf, buflen); Local buf; if (!maybe_buf.ToLocal(&buf)) { diff --git a/src/tcp_wrap.cc b/src/tcp_wrap.cc index 669206fc6bf94b..747d3e028c23cc 100644 --- a/src/tcp_wrap.cc +++ b/src/tcp_wrap.cc @@ -342,9 +342,9 @@ void TCPWrap::Connect(const FunctionCallbackInfo& args, // also used by udp_wrap.cc -Local AddressToJS(Environment* env, - const sockaddr* addr, - Local info) { +MaybeLocal AddressToJS(Environment* env, + const sockaddr* addr, + Local info) { EscapableHandleScope scope(env->isolate()); char ip[INET6_ADDRSTRLEN + UV_IF_NAMESIZE]; const sockaddr_in* a4; @@ -371,8 +371,7 @@ Local AddressToJS(Environment* env, &scopeidlen); if (r) { env->ThrowUVException(r, "uv_if_indextoiid"); - // TODO(addaleax): Do proper MaybeLocal handling here - return scope.Escape(info); + return {}; } } port = ntohs(a6->sin6_port); @@ -381,7 +380,7 @@ Local AddressToJS(Environment* env, OneByteString(env->isolate(), ip)).Check(); info->Set(env->context(), env->family_string(), - env->ipv6_string()).Check(); + Integer::New(env->isolate(), 6)).Check(); info->Set(env->context(), env->port_string(), Integer::New(env->isolate(), port)).Check(); @@ -396,7 +395,7 @@ Local AddressToJS(Environment* env, OneByteString(env->isolate(), ip)).Check(); info->Set(env->context(), env->family_string(), - env->ipv4_string()).Check(); + Integer::New(env->isolate(), 4)).Check(); info->Set(env->context(), env->port_string(), Integer::New(env->isolate(), port)).Check(); diff --git a/src/udp_wrap.cc b/src/udp_wrap.cc index 4a0c6aeaa940d2..127a1a6e5d8fe7 100644 --- a/src/udp_wrap.cc +++ b/src/udp_wrap.cc @@ -22,6 +22,7 @@ #include "udp_wrap.h" #include "env-inl.h" #include "node_buffer.h" +#include "node_errors.h" #include "node_sockaddr-inl.h" #include "handle_wrap.h" #include "req_wrap-inl.h" @@ -29,6 +30,7 @@ namespace node { +using errors::TryCatchScope; using v8::Array; using v8::ArrayBuffer; using v8::BackingStore; @@ -728,9 +730,45 @@ void UDPWrap::OnRecv(ssize_t nread, bs = BackingStore::Reallocate(isolate, std::move(bs), nread); } + Local address; + { + bool has_caught = false; + { + TryCatchScope try_catch(env); + if (!AddressToJS(env, addr).ToLocal(&address)) { + DCHECK(try_catch.HasCaught() && !try_catch.HasTerminated()); + argv[2] = try_catch.Exception(); + DCHECK(!argv[2].IsEmpty()); + has_caught = true; + } + } + if (has_caught) { + DCHECK(!argv[2].IsEmpty()); + MakeCallback(env->onerror_string(), arraysize(argv), argv); + return; + } + } + Local ab = ArrayBuffer::New(isolate, std::move(bs)); - argv[2] = Buffer::New(env, ab, 0, ab->ByteLength()).ToLocalChecked(); - argv[3] = AddressToJS(env, addr); + { + bool has_caught = false; + { + TryCatchScope try_catch(env); + if (!Buffer::New(env, ab, 0, ab->ByteLength()).ToLocal(&argv[2])) { + DCHECK(try_catch.HasCaught() && !try_catch.HasTerminated()); + argv[2] = try_catch.Exception(); + DCHECK(!argv[2].IsEmpty()); + has_caught = true; + } + } + if (has_caught) { + DCHECK(!argv[2].IsEmpty()); + MakeCallback(env->onerror_string(), arraysize(argv), argv); + return; + } + } + + argv[3] = address; MakeCallback(env->onmessage_string(), arraysize(argv), argv); } diff --git a/test/.eslintrc.yaml b/test/.eslintrc.yaml index 878ea7b975df11..e8295cd3e23621 100644 --- a/test/.eslintrc.yaml +++ b/test/.eslintrc.yaml @@ -6,7 +6,6 @@ env: rules: multiline-comment-style: [error, separate-lines] - no-var: error prefer-const: error symbol-description: off diff --git a/test/async-hooks/test-graph.http.js b/test/async-hooks/test-graph.http.js index 3e36646e54e2ee..0a003427f9e133 100644 --- a/test/async-hooks/test-graph.http.js +++ b/test/async-hooks/test-graph.http.js @@ -44,7 +44,7 @@ process.on('exit', () => { triggerAsyncId: 'tcp:2' }, { type: 'Timeout', id: 'timeout:1', - triggerAsyncId: 'httpincomingmessage:1' }, + triggerAsyncId: null }, { type: 'SHUTDOWNWRAP', id: 'shutdown:1', triggerAsyncId: 'tcp:2' } ] diff --git a/test/cctest/node_test_fixture.cc b/test/cctest/node_test_fixture.cc index b6f4af6b9e70e0..8179c7864436b1 100644 --- a/test/cctest/node_test_fixture.cc +++ b/test/cctest/node_test_fixture.cc @@ -1,7 +1,39 @@ #include "node_test_fixture.h" +#include "cppgc/platform.h" ArrayBufferUniquePtr NodeZeroIsolateTestFixture::allocator{nullptr, nullptr}; uv_loop_t NodeZeroIsolateTestFixture::current_loop; NodePlatformUniquePtr NodeZeroIsolateTestFixture::platform; TracingAgentUniquePtr NodeZeroIsolateTestFixture::tracing_agent; bool NodeZeroIsolateTestFixture::node_initialized = false; + + +void NodeTestEnvironment::SetUp() { + NodeZeroIsolateTestFixture::tracing_agent = + std::make_unique(); + node::tracing::TraceEventHelper::SetAgent( + NodeZeroIsolateTestFixture::tracing_agent.get()); + node::tracing::TracingController* tracing_controller = + NodeZeroIsolateTestFixture::tracing_agent->GetTracingController(); + static constexpr int kV8ThreadPoolSize = 4; + NodeZeroIsolateTestFixture::platform.reset( + new node::NodePlatform(kV8ThreadPoolSize, tracing_controller)); + v8::V8::InitializePlatform(NodeZeroIsolateTestFixture::platform.get()); +#ifdef V8_SANDBOX + ASSERT_TRUE(v8::V8::InitializeSandbox()); +#endif + cppgc::InitializeProcess( + NodeZeroIsolateTestFixture::platform->GetPageAllocator()); + v8::V8::Initialize(); +} + +void NodeTestEnvironment::TearDown() { + v8::V8::Dispose(); + v8::V8::DisposePlatform(); + NodeZeroIsolateTestFixture::platform->Shutdown(); + NodeZeroIsolateTestFixture::platform.reset(nullptr); + NodeZeroIsolateTestFixture::tracing_agent.reset(nullptr); +} + +::testing::Environment* const node_env = +::testing::AddGlobalTestEnvironment(new NodeTestEnvironment()); diff --git a/test/cctest/node_test_fixture.h b/test/cctest/node_test_fixture.h index 936085d4ae57bd..4c687118a37054 100644 --- a/test/cctest/node_test_fixture.h +++ b/test/cctest/node_test_fixture.h @@ -60,18 +60,26 @@ using ArrayBufferUniquePtr = std::unique_ptr; using NodePlatformUniquePtr = std::unique_ptr; +class NodeTestEnvironment final : public ::testing::Environment { + public: + NodeTestEnvironment() = default; + void SetUp() override; + void TearDown() override; +}; + + class NodeZeroIsolateTestFixture : public ::testing::Test { protected: - static ArrayBufferUniquePtr allocator; - static TracingAgentUniquePtr tracing_agent; - static NodePlatformUniquePtr platform; static uv_loop_t current_loop; static bool node_initialized; + static ArrayBufferUniquePtr allocator; + static NodePlatformUniquePtr platform; + static TracingAgentUniquePtr tracing_agent; static void SetUpTestCase() { if (!node_initialized) { - uv_os_unsetenv("NODE_OPTIONS"); node_initialized = true; + uv_os_unsetenv("NODE_OPTIONS"); std::vector argv { "cctest" }; std::vector exec_argv; std::vector errors; @@ -80,25 +88,13 @@ class NodeZeroIsolateTestFixture : public ::testing::Test { CHECK_EQ(exitcode, 0); CHECK(errors.empty()); } - - tracing_agent = std::make_unique(); - node::tracing::TraceEventHelper::SetAgent(tracing_agent.get()); - node::tracing::TracingController* tracing_controller = - tracing_agent->GetTracingController(); CHECK_EQ(0, uv_loop_init(¤t_loop)); - static constexpr int kV8ThreadPoolSize = 4; - platform.reset( - new node::NodePlatform(kV8ThreadPoolSize, tracing_controller)); - v8::V8::InitializePlatform(platform.get()); - v8::V8::Initialize(); } static void TearDownTestCase() { - platform->Shutdown(); while (uv_loop_alive(¤t_loop)) { uv_run(¤t_loop, UV_RUN_ONCE); } - v8::V8::DisposePlatform(); CHECK_EQ(0, uv_loop_close(¤t_loop)); } @@ -106,6 +102,8 @@ class NodeZeroIsolateTestFixture : public ::testing::Test { allocator = ArrayBufferUniquePtr(node::CreateArrayBufferAllocator(), &node::FreeArrayBufferAllocator); } + + friend NodeTestEnvironment; }; diff --git a/test/common/README.md b/test/common/README.md index f1789c1577d8ca..136f4b68d518ae 100644 --- a/test/common/README.md +++ b/test/common/README.md @@ -59,7 +59,7 @@ On non-Windows platforms, this always returns `true`. ### `createZeroFilledFile(filename)` -Creates a 10 MB file of all null characters. +Creates a 10 MiB file of all null characters. ### `enoughTestMem` @@ -359,7 +359,7 @@ Platform normalized `pwd` command options. Usage example: ```js const common = require('../common'); -const { spawn } = require('child_process'); +const { spawn } = require('node:child_process'); spawn(...common.pwdCommand, { stdio: ['pipe'] }); ``` @@ -998,7 +998,7 @@ an `emitReceived()` API for actin as if data has been received on it. `makeUDPPair` returns an object `{ clientSide, serverSide }` where each side is an `FakeUDPWrap` connected to the other side. -There is no difference between cient or server side beyond their names. +There is no difference between client or server side beyond their names. ## WPT Module diff --git a/test/common/index.js b/test/common/index.js index b69d726e8ef323..d2b23a6570f562 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -120,6 +120,17 @@ const isFreeBSD = process.platform === 'freebsd'; const isOpenBSD = process.platform === 'openbsd'; const isLinux = process.platform === 'linux'; const isOSX = process.platform === 'darwin'; +const isPi = (() => { + try { + // Normal Raspberry Pi detection is to find the `Raspberry Pi` string in + // the contents of `/sys/firmware/devicetree/base/model` but that doesn't + // work inside a container. Match the chipset model number instead. + const cpuinfo = fs.readFileSync('/proc/cpuinfo', { encoding: 'utf8' }); + return /^Hardware\s*:\s*(.*)$/im.exec(cpuinfo)?.[1] === 'BCM2835'; + } catch { + return false; + } +})(); const isDumbTerminal = process.env.TERM === 'dumb'; @@ -246,15 +257,10 @@ function platformTimeout(ms) { if (isAIX) return multipliers.two * ms; // Default localhost speed is slower on AIX - if (process.arch !== 'arm') - return ms; - - const armv = process.config.variables.arm_version; + if (isPi) + return multipliers.two * ms; // Raspberry Pi devices - if (armv === '7') - return multipliers.two * ms; // ARMv7 - - return ms; // ARMv8+ + return ms; } let knownGlobals = [ @@ -309,6 +315,27 @@ if (hasCrypto && global.crypto) { knownGlobals.push(global.CryptoKey); knownGlobals.push(global.SubtleCrypto); } +if (global.ReadableStream) { + knownGlobals.push( + global.ReadableStream, + global.ReadableStreamDefaultReader, + global.ReadableStreamBYOBReader, + global.ReadableStreamBYOBRequest, + global.ReadableByteStreamController, + global.ReadableStreamDefaultController, + global.TransformStream, + global.TransformStreamDefaultController, + global.WritableStream, + global.WritableStreamDefaultWriter, + global.WritableStreamDefaultController, + global.ByteLengthQueuingStrategy, + global.CountQueuingStrategy, + global.TextEncoderStream, + global.TextDecoderStream, + global.CompressionStream, + global.DecompressionStream, + ); +} function allowGlobals(...allowlist) { knownGlobals = knownGlobals.concat(allowlist); @@ -790,6 +817,7 @@ const common = { isMainThread, isOpenBSD, isOSX, + isPi, isSunOS, isWindows, localIPv6Hosts, @@ -824,7 +852,7 @@ const common = { const re = isWindows ? /Loopback Pseudo-Interface/ : /lo/; return Object.keys(iFaces).some((name) => { return re.test(name) && - iFaces[name].some(({ family }) => family === 'IPv6'); + iFaces[name].some(({ family }) => family === 6); }); }, diff --git a/test/common/inspector-helper.js b/test/common/inspector-helper.js index 28b5ab5209a298..cebc048362ef40 100644 --- a/test/common/inspector-helper.js +++ b/test/common/inspector-helper.js @@ -5,8 +5,7 @@ const fs = require('fs'); const http = require('http'); const fixtures = require('../common/fixtures'); const { spawn } = require('child_process'); -const { parse: parseURL } = require('url'); -const { pathToFileURL } = require('url'); +const { URL, pathToFileURL } = require('url'); const { EventEmitter } = require('events'); const _MAINSCRIPT = fixtures.path('loop.js'); @@ -418,7 +417,7 @@ class NodeInstance extends EventEmitter { return http.get({ port, family: 4, - path: parseURL(devtoolsUrl).path, + path: new URL(devtoolsUrl).pathname, headers: { 'Connection': 'Upgrade', 'Upgrade': 'websocket', diff --git a/test/common/internet.js b/test/common/internet.js index 227e979d3b8c75..0943ac96c45ec3 100644 --- a/test/common/internet.js +++ b/test/common/internet.js @@ -32,7 +32,7 @@ const addresses = { NAPTR_HOST: 'sip2sip.info', // A host with SOA records registered SOA_HOST: 'nodejs.org', - // A host with CAA record registred + // A host with CAA record registered CAA_HOST: 'google.com', // A host with CNAME records registered CNAME_HOST: 'blog.nodejs.org', diff --git a/test/common/udppair.js b/test/common/udppair.js index 6213f4becfd6aa..fccbca042b3a5e 100644 --- a/test/common/udppair.js +++ b/test/common/udppair.js @@ -16,7 +16,7 @@ class FakeUDPWrap extends EventEmitter { this._handle.onwrite = (wrap, buffers, addr) => this._write(wrap, buffers, addr); this._handle.getsockname = (obj) => { - Object.assign(obj, { address: '127.0.0.1', family: 'IPv4', port: 1337 }); + Object.assign(obj, { address: '127.0.0.1', family: 4, port: 1337 }); return 0; }; @@ -72,8 +72,8 @@ class FakeUDPWrap extends EventEmitter { let familyInt; switch (family) { - case 'IPv4': familyInt = 4; break; - case 'IPv6': familyInt = 6; break; + case 4: familyInt = 4; break; + case 6: familyInt = 6; break; default: throw new Error('bad family'); } diff --git a/test/common/wpt.js b/test/common/wpt.js index 2a772f6f0fa8b1..ff969f4358c990 100644 --- a/test/common/wpt.js +++ b/test/common/wpt.js @@ -56,7 +56,7 @@ class ResourceLoader { /** * Load a resource in test/fixtures/wpt specified with a URL * @param {string} from the path of the file loading this resource, - * relative to thw WPT folder. + * relative to the WPT folder. * @param {string} url the url of the resource being loaded. * @param {boolean} asFetch if true, return the resource in a * pseudo-Response object. @@ -78,7 +78,7 @@ class ResourceLoader { } class StatusRule { - constructor(key, value, pattern = undefined) { + constructor(key, value, pattern) { this.key = key; this.requires = value.requires || []; this.fail = value.fail; diff --git a/test/doctool/test-make-doc.mjs b/test/doctool/test-make-doc.mjs index 06ec6e028bf4e8..54483c7d68932d 100644 --- a/test/doctool/test-make-doc.mjs +++ b/test/doctool/test-make-doc.mjs @@ -40,7 +40,8 @@ const links = toc.match(globalRe); assert.notStrictEqual(links, null); // Filter out duplicate links, leave just filenames, add expected JSON files. -const linkedHtmls = [...new Set(links)].map((link) => link.match(re)[1]); +const linkedHtmls = [...new Set(links)].map((link) => link.match(re)[1]) + .concat(['index.html']); const expectedJsons = linkedHtmls .map((name) => name.replace('.html', '.json')); const expectedDocs = linkedHtmls.concat(expectedJsons); diff --git a/test/es-module/test-esm-basic-imports.mjs b/test/es-module/test-esm-basic-imports.mjs index 5009fbadb39657..173f80ac955ff1 100644 --- a/test/es-module/test-esm-basic-imports.mjs +++ b/test/es-module/test-esm-basic-imports.mjs @@ -1,7 +1,10 @@ import '../common/index.mjs'; import assert from 'assert'; import ok from '../fixtures/es-modules/test-esm-ok.mjs'; -import okShebang from './test-esm-shebang.mjs'; +import * as okShebangNs from './test-esm-shebang.mjs'; +// encode the '.' +import * as okShebangPercentNs from './test-esm-shebang%2emjs'; assert(ok); -assert(okShebang); +assert(okShebangNs.default); +assert.strict.equal(okShebangNs, okShebangPercentNs); diff --git a/test/es-module/test-esm-experimental-warnings.mjs b/test/es-module/test-esm-experimental-warnings.mjs new file mode 100644 index 00000000000000..bf92b158e485eb --- /dev/null +++ b/test/es-module/test-esm-experimental-warnings.mjs @@ -0,0 +1,32 @@ +import { mustCall } from '../common/index.mjs'; +import { fileURL } from '../common/fixtures.mjs'; +import { match, strictEqual } from 'assert'; +import { spawn } from 'child_process'; +import { execPath } from 'process'; + +// Verify experimental warnings are printed +for ( + const [experiment, arg] of [ + [/Custom ESM Loaders/, `--experimental-loader=${fileURL('es-module-loaders', 'hooks-custom.mjs')}`], + [/Network Imports/, '--experimental-network-imports'], + [/specifier resolution/, '--experimental-specifier-resolution=node'], + ] +) { + const input = `import ${JSON.stringify(fileURL('es-module-loaders', 'module-named-exports.mjs'))}`; + const child = spawn(execPath, [ + arg, + '--input-type=module', + '--eval', + input, + ]); + + let stderr = ''; + child.stderr.setEncoding('utf8'); + child.stderr.on('data', (data) => { stderr += data; }); + child.on('close', mustCall((code, signal) => { + strictEqual(code, 0); + strictEqual(signal, null); + match(stderr, /ExperimentalWarning/); + match(stderr, experiment); + })); +} diff --git a/test/es-module/test-esm-loader-resolve-type.mjs b/test/es-module/test-esm-loader-resolve-type.mjs index 913a7f40d2c551..722cf5404d25de 100644 --- a/test/es-module/test-esm-loader-resolve-type.mjs +++ b/test/es-module/test-esm-loader-resolve-type.mjs @@ -37,6 +37,6 @@ await import(`${moduleName}`).finally(() => { const { importedESM: importedESMAfter, importedCJS: importedCJSAfter } = global.getModuleTypeStats(); -// Dynamic import above should incriment ESM counter but not CJS counter +// Dynamic import above should increment ESM counter but not CJS counter assert.strictEqual(importedESMBefore + 1, importedESMAfter); assert.strictEqual(importedCJSBefore, importedCJSAfter); diff --git a/test/es-module/test-esm-process.mjs b/test/es-module/test-esm-process.mjs index 8fa006a304ed2f..f88eba849df715 100644 --- a/test/es-module/test-esm-process.mjs +++ b/test/es-module/test-esm-process.mjs @@ -1,6 +1,7 @@ import '../common/index.mjs'; import assert from 'assert'; -import process from 'process'; +import process, { report } from 'process'; assert.strictEqual(Object.prototype.toString.call(process), '[object process]'); assert(Object.getOwnPropertyDescriptor(process, Symbol.toStringTag).writable); +assert(report); diff --git a/test/es-module/test-esm-specifiers-symlink.mjs b/test/es-module/test-esm-specifiers-symlink.mjs new file mode 100644 index 00000000000000..9c60c1da89706f --- /dev/null +++ b/test/es-module/test-esm-specifiers-symlink.mjs @@ -0,0 +1,40 @@ +import * as common from '../common/index.mjs'; +import path from 'path'; +import fs from 'fs/promises'; +import tmpdir from '../common/tmpdir.js'; +import { spawn } from 'child_process'; +import assert from 'assert'; + +tmpdir.refresh(); +const tmpDir = tmpdir.path; + +// Create the following file structure: +// ├── index.mjs +// ├── subfolder +// │ ├── index.mjs +// │ └── node_modules +// │ └── package-a +// │ └── index.mjs +// └── symlink.mjs -> ./subfolder/index.mjs +const entry = path.join(tmpDir, 'index.mjs'); +const symlink = path.join(tmpDir, 'symlink.mjs'); +const real = path.join(tmpDir, 'subfolder', 'index.mjs'); +const packageDir = path.join(tmpDir, 'subfolder', 'node_modules', 'package-a'); +const packageEntry = path.join(packageDir, 'index.mjs'); +try { + await fs.symlink(real, symlink); +} catch (err) { + if (err.code !== 'EPERM') throw err; + common.skip('insufficient privileges for symlinks'); +} +await fs.mkdir(packageDir, { recursive: true }); +await Promise.all([ + fs.writeFile(entry, 'import "./symlink.mjs";'), + fs.writeFile(real, 'export { a } from "package-a/index.mjs"'), + fs.writeFile(packageEntry, 'export const a = 1;'), +]); + +spawn(process.execPath, ['--experimental-specifier-resolution=node', entry], + { stdio: 'inherit' }).on('exit', common.mustCall((code) => { + assert.strictEqual(code, 0); +})); diff --git a/test/es-module/test-esm-unknown-or-no-extension.js b/test/es-module/test-esm-unknown-or-no-extension.js index 3b1802a4dcedbd..40f840ad670cf3 100644 --- a/test/es-module/test-esm-unknown-or-no-extension.js +++ b/test/es-module/test-esm-unknown-or-no-extension.js @@ -31,6 +31,10 @@ const assert = require('assert'); assert.strictEqual(code, 1); assert.strictEqual(signal, null); assert.strictEqual(stdout, ''); - assert.ok(stderr.indexOf('ERR_UNKNOWN_FILE_EXTENSION') !== -1); + assert.ok(stderr.includes('ERR_UNKNOWN_FILE_EXTENSION')); + if (fixturePath.includes('noext')) { + // Check for explanation to users + assert.ok(stderr.includes('extensionless')); + } })); }); diff --git a/test/es-module/test-http-imports.mjs b/test/es-module/test-http-imports.mjs index a9a5204ffb7947..5b1f2dbc578e35 100644 --- a/test/es-module/test-http-imports.mjs +++ b/test/es-module/test-http-imports.mjs @@ -39,10 +39,10 @@ const internalInterfaces = Object.values(os.networkInterfaces()).flat().filter( ); for (const iface of internalInterfaces) { testListeningOptions.push({ - hostname: iface?.family === 'IPv6' ? `[${iface?.address}]` : iface?.address, + hostname: iface?.family === 6 ? `[${iface.address}]` : iface?.address, listenOptions: { host: iface?.address, - ipv6Only: iface?.family === 'IPv6' + ipv6Only: iface?.family === 6 } }); } @@ -61,9 +61,18 @@ for (const { protocol, createServer } of [ const host = new URL(base); host.protocol = protocol; host.hostname = hostname; + // /not-found is a 404 + // ?redirect causes a redirect, no body. JSON.parse({status:number,location:string}) + // ?mime sets the content-type, string + // ?body sets the body, string const server = createServer(function(_req, res) { const url = new URL(_req.url, host); const redirect = url.searchParams.get('redirect'); + if (url.pathname === '/not-found') { + res.writeHead(404); + res.end(); + return; + } if (redirect) { const { status, location } = JSON.parse(redirect); res.writeHead(status, { @@ -107,6 +116,21 @@ for (const { protocol, createServer } of [ assert.strict.notEqual(redirectedNS.default, ns.default); assert.strict.equal(redirectedNS.url, url.href); + // Redirects have the same import.meta.url but different cache + // entry on Web + const relativeAfterRedirect = new URL(url.href + 'foo/index.js'); + const redirected = new URL(url.href + 'bar/index.js'); + redirected.searchParams.set('body', 'export let relativeDepURL = (await import("./baz.js")).url'); + relativeAfterRedirect.searchParams.set('redirect', JSON.stringify({ + status: 302, + location: redirected.href + })); + const relativeAfterRedirectedNS = await import(relativeAfterRedirect.href); + assert.strict.equal( + relativeAfterRedirectedNS.relativeDepURL, + url.href + 'bar/baz.js' + ); + const crossProtocolRedirect = new URL(url.href); crossProtocolRedirect.searchParams.set('redirect', JSON.stringify({ status: 302, @@ -128,6 +152,14 @@ for (const { protocol, createServer } of [ assert.strict.equal(depsNS.data, 1); assert.strict.equal(depsNS.http, ns); + const relativeDeps = new URL(url.href); + relativeDeps.searchParams.set('body', ` + import * as http from "./"; + export {http}; + `); + const relativeDepsNS = await import(relativeDeps.href); + assert.strict.deepStrictEqual(Object.keys(relativeDepsNS), ['http']); + assert.strict.equal(relativeDepsNS.http, ns); const fileDep = new URL(url.href); const { href } = pathToFileURL(path('/es-modules/message.mjs')); fileDep.searchParams.set('body', ` @@ -165,6 +197,12 @@ for (const { protocol, createServer } of [ import(unsupportedMIME.href), { code: 'ERR_UNKNOWN_MODULE_FORMAT' } ); + const notFound = new URL(url.href); + notFound.pathname = '/not-found'; + await assert.rejects( + import(notFound.href), + { code: 'ERR_MODULE_NOT_FOUND' }, + ); server.close(); } diff --git a/test/fixtures/gc.js b/test/fixtures/gc.js new file mode 100644 index 00000000000000..1e965f336e4dea --- /dev/null +++ b/test/fixtures/gc.js @@ -0,0 +1,9 @@ +let arr = new Array(300_000).fill('a'); + +for (let index = 0; index < arr.length; index++) { + arr[index] = Math.random(); +} + +arr = []; +// .gc() is called to generate a Mark-sweep event +global.gc(); diff --git a/test/fixtures/keys/selfsigned-no-keycertsign/cert.pem b/test/fixtures/keys/selfsigned-no-keycertsign/cert.pem index c0829b82caf8d3..c2b824ef45e907 100644 --- a/test/fixtures/keys/selfsigned-no-keycertsign/cert.pem +++ b/test/fixtures/keys/selfsigned-no-keycertsign/cert.pem @@ -1,18 +1,19 @@ -----BEGIN CERTIFICATE----- -MIIC9jCCAd6gAwIBAgIJANHflGRpZM1IMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV -BAMMCWxvY2FsaG9zdDAeFw0yMTAzMTUwOTEzMjdaFw0yMjAzMTUwOTEzMjdaMBQx -EjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC -ggEBANMt6TLw9gIxucRgZBn8owavEIMAddxMTjkHiR7jGfaBrvvVTB8ymsIizw/Q -KTANmu2r3EOqeR9Ht25KZFKxOKCDMd3aKHht38HInXIF6CQe8c5P0xsVKZAWkell -8ohL05EsFpcrJODIdHfaovODrtX8w1WexqDsUoPQdEk7pISJ2HhmXzpf7QmV00Ux -8J+64v2pTg8/C9VgpSgxE4oXlfJEqdSIAzGDT+VX96GWXTh7QqLjiQ9T96QHUJEn -Bx0Sr4rO9mY2lOQG408QuCLR/ng2J+lYx+03SC8Lq7lrtt4M06Ffr8TQRgpDAjkU -0YitbuysD5XgtCeFq0Fi3v1z700CAwEAAaNLMEkwCwYDVR0PBAQDAgWgMBMGA1Ud -JQQMMAoGCCsGAQUFBwMBMCUGA1UdEQQeMByCCTEyNy4wLjAuMYIJbG9jYWxob3N0 -hwR/AAABMA0GCSqGSIb3DQEBCwUAA4IBAQDAUCt/8Le2EO0ONOkQYUcPmSut6Siz -UIQrJ8Lwfs0fb+Zk9ElNGLwYTzooKDgzK8cLQ8g8F2WkolBEPXDsy1Ab+e66WkJH -NH/zAgEyG6cXXRNc+ObM5KbjY0YuDGiajKcndknuuCB+onlC1Pv5oFUSNa3/06+S -sziFloGbg5S0AHT6lYnwZSM6G7Pre8mcRNRxL6Yw1FOOUpQZKPd7juy4GBRlCucn -wmp/Fl0wIBDs91Vprig2TO+U6GvtqJ3n/RKXUz1ykUKETtRneSkqa6hFYjwRzawd -ANpjy/orrVkqXriAbI/1xvBMInWdcMpXNeiOkxQeQdy8TLBk0ZViSJnf +MIIDATCCAemgAwIBAgIUb32MablwTzVJh3UQXwdun9pBoF0wDQYJKoZIhvcNAQEL +BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTIyMDMxNTE2MzQwOFoXDTMyMDMx +MjE2MzQwOFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEA11wz7hAhdN072sp2aTySdsla65aZT10L1o74JNBZmMye +DSq7Lbbvqq8XuLlwogS0ACAQVEjL7GPVMgJwzRytdaSWdzr26dftPCL3m1h8azU/ +Bs1nafHWhi3rx8RQnLfyusq0fThR9Xd3xfjDuaqhoa7Nc36XbG98XWkgxpm2R7wR +pLyNT9K2Ixvry9orZicTxv0Zyq+tD3CzSEch5OCApVZNaqEcIDgiN1zE/zqIe83v +9+NYpLo61yPiqqedCqZaKp037uwJCp/RL9g6bh7IFDGZcNFv3k0xjZB4BVm4BZMp +SlbI49hncVIJZNB7qWt5ilCzEbwOhgLt6Y3Yh8rgMwIDAQABo0swSTALBgNVHQ8E +BAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwJQYDVR0RBB4wHIIJMTI3LjAuMC4x +gglsb2NhbGhvc3SHBH8AAAEwDQYJKoZIhvcNAQELBQADggEBAEEXVnMzax6vmGPY +TNNK6HJuN8xUUbEKVHKPDTwKbn2ZPQRGNOs4CJ5vVc1h2tU1mfgg20FZzPqZ+y9N +hIFIaESTpX881NXM87aE21Gqo4rS66lubcoEUHWeY1+/LcdD6Gh/Ur0V4CBORZjA +W/H1nASZQKQLMHIkriopbclMQPOsUYM11JxLsTH6hOPCJRM5cl1K1KsHWY2yJ656 +Gw9IX0NBTak0flEAL/JQkFlm2PRFJhWShPBHsC8/AWBcvgJH/0noNnDS5vAtCWa6 +rJO6SoHFJwWK3xCVU2YsC6No2DrAukyqEdN8arifJ/2hEjx1XAfockNsWzrT1mB5 +EpLq2L0= -----END CERTIFICATE----- diff --git a/test/fixtures/keys/selfsigned-no-keycertsign/https_renew_cert.sh b/test/fixtures/keys/selfsigned-no-keycertsign/https_renew_cert.sh old mode 100644 new mode 100755 index 092f27a8867cbb..c40046d83b4727 --- a/test/fixtures/keys/selfsigned-no-keycertsign/https_renew_cert.sh +++ b/test/fixtures/keys/selfsigned-no-keycertsign/https_renew_cert.sh @@ -1,6 +1,13 @@ -#!/bin/bash -openssl genrsa -out rsa.pem 2048 -openssl rsa -in rsa.pem -out key.pem -openssl req -sha256 -new -key key.pem -out csr.pem -subj "/CN=localhost" -openssl x509 -req -extfile cert.conf -extensions v3_req -days 365 -in csr.pem -signkey key.pem -out cert.pem +#!/usr/bin/env bash +set -euo pipefail +shopt -s inherit_errexit +cd -- "$(dirname -- "${BASH_SOURCE[0]}")" + +if [ ! -f key.pem ]; then + openssl genrsa -out key.pem 2048 +fi + +openssl req -sha256 -new -key key.pem -subj "/CN=localhost" | \ + openssl x509 -req -extfile cert.conf -extensions v3_req -days 3650 -signkey key.pem -out cert.pem +openssl x509 -in cert.pem -noout -text diff --git a/test/fixtures/keys/selfsigned-no-keycertsign/key.pem b/test/fixtures/keys/selfsigned-no-keycertsign/key.pem index 5f0549276a4cae..15eec5376a6f90 100644 --- a/test/fixtures/keys/selfsigned-no-keycertsign/key.pem +++ b/test/fixtures/keys/selfsigned-no-keycertsign/key.pem @@ -1,27 +1,27 @@ -----BEGIN RSA PRIVATE KEY----- -MIIEogIBAAKCAQEA0y3pMvD2AjG5xGBkGfyjBq8QgwB13ExOOQeJHuMZ9oGu+9VM -HzKawiLPD9ApMA2a7avcQ6p5H0e3bkpkUrE4oIMx3dooeG3fwcidcgXoJB7xzk/T -GxUpkBaR6WXyiEvTkSwWlysk4Mh0d9qi84Ou1fzDVZ7GoOxSg9B0STukhInYeGZf -Ol/tCZXTRTHwn7ri/alODz8L1WClKDETiheV8kSp1IgDMYNP5Vf3oZZdOHtCouOJ -D1P3pAdQkScHHRKvis72ZjaU5AbjTxC4ItH+eDYn6VjH7TdILwuruWu23gzToV+v -xNBGCkMCORTRiK1u7KwPleC0J4WrQWLe/XPvTQIDAQABAoIBAFIlWMIVE0z1NNLb -v/SP3oaaEK00v6QLFp5+fOtD4fSOq5eQeATmtWZxDeSTz4G+uRZctNipdmYhiovf -ajj0cReXEQ3Ab9+wtcp2lDAndg6e7uaXDIJLcBh5fxawLnCwNkMRSFRTVwwNTajV -pm9dOORKZ11l3tP4OXzG2IUoKy3Wj/1SKLL4zrdHi7802+L/GstK6/BGma+NFrFz -U6yNqpvuzv7BH7w9G3nSz7u+8SjcY22Vs6q69GAQG3yf356cYCJhV7QIJXU0/VAF -GFx5UDwlsOT2NhoOd/b9Q9RexKDl+qDupXQo0YFOObHIjHs8UGLOZkBtv4apCarA -6u+BOwECgYEA9GbrP/5SfmN8xvF2XVjqjk9IUcvWAuTM4Bxav72e6aR9IOdye9vi -+GhwM6qON+LOnMVNhUKJ0+R/jjLy6Jq+00uKU65Q79x7lCBVSDDXWacV0IFIoAOp -P4LkykjRZyzpIvjK5HGL1JYqZi89im93uuOiyMjoFS2syU+19b83UUECgYEA3TNk -JVGWYLMcD3uVTe2e/yZSsX+0+QL8hm3bUSOIJ/mIe2dqCXb6MK0ndMS0aCLGtDSt -wGTWwuc4rFattHYEI8Iro+tshgQs9bLM037hmiCrZvmcQsgt+3FNuYv4oCGp5U85 -mWYF5SVUYRyv8M9aZoKTjc8meR0Wv3ZGGC9iDw0CgYA0XKyAPGO+MmB0Wx1J6Jfw -P2o2JB7I5e5DAbArrluSoSwx1YSApt6c6/tGBn+L16r+iYMPTu8ql6UAeUfzr9u8 -d02+mfU7Ppi3Zqn+2n/49ERHNLuzlLU5JzkPYcSDf2q/lGAby3vy4u1YkTx1IWac -gtLIg8q9ZtjDFLHeYcZfQQKBgCCOpdjQT1/gPOsSd4FGzjYjv9wcPdjA1cY7eSJS -JoIruijfqb3G40Ay3DHVmfAR3kk7z68XqHx7Z94Fy/9Zt3ZD6ARybEC1cKChNoCS -lkYHNPMtHhC+QfZWUOhUb72x9r2nkYTAfXGisu6wOD0rZ9TatzkSGkmNPIHluJ9q -qfYpAoGAPJiBBdSt7DC9ZZraQGMEHfRkE5CxEIRbIHJ9+U3Z7LTQT6MJ1y3VfcGs -PetHcWtbU0Cl8blShaSwpxyCI01x3tUPw/b7tXMan/ImzjUgRe7kQXh2sf39V3b/ -fvzKXWBvOvc1lgG0pFgI/2xtGQQGTe74MzX5xFgw6eadRUnJeKI= +MIIEpgIBAAKCAQEA11wz7hAhdN072sp2aTySdsla65aZT10L1o74JNBZmMyeDSq7 +Lbbvqq8XuLlwogS0ACAQVEjL7GPVMgJwzRytdaSWdzr26dftPCL3m1h8azU/Bs1n +afHWhi3rx8RQnLfyusq0fThR9Xd3xfjDuaqhoa7Nc36XbG98XWkgxpm2R7wRpLyN +T9K2Ixvry9orZicTxv0Zyq+tD3CzSEch5OCApVZNaqEcIDgiN1zE/zqIe83v9+NY +pLo61yPiqqedCqZaKp037uwJCp/RL9g6bh7IFDGZcNFv3k0xjZB4BVm4BZMpSlbI +49hncVIJZNB7qWt5ilCzEbwOhgLt6Y3Yh8rgMwIDAQABAoIBAQCWK7oeX+skdXxe +RV2qZk1vPVsD+kCvYZ92nr0T1qETdmMjpU9eQjj/GRb+fXi30XW+vJ0GWLix/q9U +LvV/YWbnKLyvKVOxnhrUG0HzdhFUJI3tbV+WNce0SuMlqpPXpEFC1URkKNilxQek +6aF5ny0T9DNZPMXUHC1paXwsYFUF0SwtRalY/UheRvTLZgJJAS13SR4mDZ2MJNiy +NkLOaokGqI+U9euNsbZoJi1Oh4jOoA623NlVntPjfDc1Wzwsp/rMrJFiyfrBHlln +sjAcArcNrAR6GosX4kfQIKdHXZFMhRLm6vJSSbROIIoqOTPJF8kFYKqSQMQCT0sd +v8fVw35pAoGBAO7/LTrKcFth/VuqkyHFCvfIuXrHtEJon0MjsCN+IZu+WFsjjZ/7 +mMOHqWPBb1oXBdarWFshdq5zVCcoA/hH1H6BAi4zOlU2vaQdeejbhlWWt5GVRl70 +fa2RpyGmzTIbj6QDpv1GROQxhk4lsFp/4Jtd4wnI71P2fF+gTk+VxG1VAoGBAOau +iPMVeNgSHAT0f0ms6+tdyiRf3jo8afd9TSN4g0324Cd/hIzd5ysjf9WfN3xni0tQ +Ho8v1CKvt1xFCG9kgApHE+umLOEF5X1LwZzpPwp9PEN7tj0U6jis/spPqmToSrlB +hcqLVK3DRF268RADZtYCvbCeICUQIydeRzeqL1dnAoGBAORvkRq9flFKQvFIA+Pm +Q8p82BRsJ3bGK1vwE56JI7SKZkeOnwLIZ8aUt6p6rGUu1kzOb+CDBr5Ny6S1kb3R +YmEcDCp+moXykab46ZpRoX/TVFhZlu6RwEKTkm0O0tjPESeSuh0h+h0m3Rl62qpV +yiiWv7iNj3vwgAai4dcd7qcVAoGBAJtNwhG10QfXGVoUqWWWr7ZkDGV0zY5zgfwH +Ndm9ltj0bnDpHvofozSdz8CoUpjEtTjdFdXco3s+xbmvGEC2bqV8uOKbm/dbGufC +CvVy6uhK7DmanlR9Mjs57MeT3unUogobHNIB/96EahCYfRcE00Udz0uLCaUqOiCV +DmlhgEHdAoGBAIBgtzi2mZ7smMEvJqOSeRQT11PBLRasM0Ba/79oebSMFpbZ8mok +8PqfJvWYxISKYMWIHRygGbOmswhRDDrVN1E61NAisIwBc42tSTRafxfVfZ0LYa4I +c967Kr2Krio1rwUQptIhWqQQnLWhycvMKImLLE55J9mmDEf9Rat0TAQJ -----END RSA PRIVATE KEY----- diff --git a/test/fixtures/source-map/throw-async.mjs b/test/fixtures/source-map/throw-async.mjs new file mode 100644 index 00000000000000..a44412ef3ecc72 --- /dev/null +++ b/test/fixtures/source-map/throw-async.mjs @@ -0,0 +1,11 @@ +const message = 'Message ' + Math.random(); +export async function Throw() { + throw new Error(message); +} +await Throw(); +// To recreate: +// +// npx tsc --module esnext --target es2017 --outDir test/fixtures/source-map --sourceMap test/fixtures/source-map/throw-async.ts +// + rename js to mjs +// + rename js to mjs in source map comment in mjs file +//# sourceMappingURL=throw-async.mjs.map \ No newline at end of file diff --git a/test/fixtures/source-map/throw-async.mjs.map b/test/fixtures/source-map/throw-async.mjs.map new file mode 100644 index 00000000000000..33d1eaed6dd239 --- /dev/null +++ b/test/fixtures/source-map/throw-async.mjs.map @@ -0,0 +1 @@ +{"version":3,"file":"throw-async.mjs","sourceRoot":"","sources":["throw-async.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,GAAG,UAAU,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;AAE3C,MAAM,CAAC,KAAK,UAAU,KAAK;IACzB,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAA;AAC1B,CAAC;AAED,MAAM,KAAK,EAAE,CAAC;AAEd,eAAe;AACf,EAAE;AACF,gIAAgI;AAChI,qBAAqB;AACrB,uDAAuD"} \ No newline at end of file diff --git a/test/fixtures/source-map/throw-async.ts b/test/fixtures/source-map/throw-async.ts new file mode 100644 index 00000000000000..080b07afa58184 --- /dev/null +++ b/test/fixtures/source-map/throw-async.ts @@ -0,0 +1,13 @@ +const message = 'Message ' + Math.random(); + +export async function Throw() { + throw new Error(message) +} + +await Throw(); + +// To recreate: +// +// npx tsc --module esnext --target es2017 --outDir test/fixtures/source-map --sourceMap test/fixtures/source-map/throw-async.ts +// + rename js to mjs +// + rename js to mjs in source map comment in mjs file \ No newline at end of file diff --git a/test/fixtures/test-runner/index.js b/test/fixtures/test-runner/index.js new file mode 100644 index 00000000000000..fcf4b4d8eaa0ad --- /dev/null +++ b/test/fixtures/test-runner/index.js @@ -0,0 +1,2 @@ +'use strict'; +throw new Error('thrown from index.js'); diff --git a/test/fixtures/test-runner/index.test.js b/test/fixtures/test-runner/index.test.js new file mode 100644 index 00000000000000..2a722c504b9fa5 --- /dev/null +++ b/test/fixtures/test-runner/index.test.js @@ -0,0 +1,4 @@ +'use strict'; +const test = require('node:test'); + +test('this should pass'); diff --git a/test/fixtures/test-runner/node_modules/test-nm.js b/test/fixtures/test-runner/node_modules/test-nm.js new file mode 100644 index 00000000000000..30024eab1f17e4 --- /dev/null +++ b/test/fixtures/test-runner/node_modules/test-nm.js @@ -0,0 +1,2 @@ +'use strict'; +throw new Error('thrown from node_modules'); diff --git a/test/fixtures/test-runner/random.test.mjs b/test/fixtures/test-runner/random.test.mjs new file mode 100644 index 00000000000000..a87a671d006ab6 --- /dev/null +++ b/test/fixtures/test-runner/random.test.mjs @@ -0,0 +1,5 @@ +import test from 'node:test'; + +test('this should fail', () => { + throw new Error('this is a failing test'); +}); diff --git a/test/fixtures/test-runner/subdir/subdir_test.js b/test/fixtures/test-runner/subdir/subdir_test.js new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/test/fixtures/test-runner/test/random.cjs b/test/fixtures/test-runner/test/random.cjs new file mode 100644 index 00000000000000..2a722c504b9fa5 --- /dev/null +++ b/test/fixtures/test-runner/test/random.cjs @@ -0,0 +1,4 @@ +'use strict'; +const test = require('node:test'); + +test('this should pass'); diff --git a/test/fixtures/wpt/README.md b/test/fixtures/wpt/README.md index f5d946eff1123b..927a8a6f80fb58 100644 --- a/test/fixtures/wpt/README.md +++ b/test/fixtures/wpt/README.md @@ -26,6 +26,8 @@ Last update: - streams: https://github.com/web-platform-tests/wpt/tree/8f60d94439/streams - url: https://github.com/web-platform-tests/wpt/tree/77d54aa9e0/url - user-timing: https://github.com/web-platform-tests/wpt/tree/df24fb604e/user-timing +- wasm/jsapi: https://github.com/web-platform-tests/wpt/tree/1dd414c796/wasm/jsapi +- wasm/webapi: https://github.com/web-platform-tests/wpt/tree/fd1b23eeaa/wasm/webapi - WebCryptoAPI: https://github.com/web-platform-tests/wpt/tree/cdd0f03df4/WebCryptoAPI - webidl/ecmascript-binding/es-exceptions: https://github.com/web-platform-tests/wpt/tree/a370aad338/webidl/ecmascript-binding/es-exceptions diff --git a/test/fixtures/wpt/versions.json b/test/fixtures/wpt/versions.json index 9f81c28c198c48..bde6cf862f6358 100644 --- a/test/fixtures/wpt/versions.json +++ b/test/fixtures/wpt/versions.json @@ -63,6 +63,14 @@ "commit": "df24fb604e2d40528ac1d1b5dd970e32fc5c2978", "path": "user-timing" }, + "wasm/jsapi": { + "commit": "1dd414c79616489ea021c800eb0375a709e8114e", + "path": "wasm/jsapi" + }, + "wasm/webapi": { + "commit": "fd1b23eeaaf9a01555d4fa29cf79ed11a4c44a50", + "path": "wasm/webapi" + }, "WebCryptoAPI": { "commit": "cdd0f03df41b222aed098fbbb11c6a3cc500a86b", "path": "WebCryptoAPI" diff --git a/test/fixtures/wpt/wasm/jsapi/META.yml b/test/fixtures/wpt/wasm/jsapi/META.yml new file mode 100644 index 00000000000000..cf5525ae1157f7 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/META.yml @@ -0,0 +1 @@ +spec: https://webassembly.github.io/spec/js-api/ diff --git a/test/fixtures/wpt/wasm/jsapi/assertions.js b/test/fixtures/wpt/wasm/jsapi/assertions.js new file mode 100644 index 00000000000000..162f5a9a6b8dcc --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/assertions.js @@ -0,0 +1,100 @@ +function assert_function_name(fn, name, description) { + const propdesc = Object.getOwnPropertyDescriptor(fn, "name"); + assert_equals(typeof propdesc, "object", `${description} should have name property`); + assert_false(propdesc.writable, "writable", `${description} name should not be writable`); + assert_false(propdesc.enumerable, "enumerable", `${description} name should not be enumerable`); + assert_true(propdesc.configurable, "configurable", `${description} name should be configurable`); + assert_equals(propdesc.value, name, `${description} name should be ${name}`); +} + +function assert_function_length(fn, length, description) { + const propdesc = Object.getOwnPropertyDescriptor(fn, "length"); + assert_equals(typeof propdesc, "object", `${description} should have length property`); + assert_false(propdesc.writable, "writable", `${description} length should not be writable`); + assert_false(propdesc.enumerable, "enumerable", `${description} length should not be enumerable`); + assert_true(propdesc.configurable, "configurable", `${description} length should be configurable`); + assert_equals(propdesc.value, length, `${description} length should be ${length}`); +} + +function assert_exported_function(fn, { name, length }, description) { + if (WebAssembly.Function === undefined) { + assert_equals(Object.getPrototypeOf(fn), Function.prototype, + `${description}: prototype`); + } else { + assert_equals(Object.getPrototypeOf(fn), WebAssembly.Function.prototype, + `${description}: prototype`); + } + + assert_function_name(fn, name, description); + assert_function_length(fn, length, description); +} + +function assert_Instance(instance, expected_exports) { + assert_equals(Object.getPrototypeOf(instance), WebAssembly.Instance.prototype, + "prototype"); + assert_true(Object.isExtensible(instance), "extensible"); + + assert_equals(instance.exports, instance.exports, "exports should be idempotent"); + const exports = instance.exports; + + assert_equals(Object.getPrototypeOf(exports), null, "exports prototype"); + assert_false(Object.isExtensible(exports), "extensible exports"); + assert_array_equals(Object.keys(exports), Object.keys(expected_exports), "matching export keys"); + for (const [key, expected] of Object.entries(expected_exports)) { + const property = Object.getOwnPropertyDescriptor(exports, key); + assert_equals(typeof property, "object", `${key} should be present`); + assert_false(property.writable, `${key}: writable`); + assert_true(property.enumerable, `${key}: enumerable`); + assert_false(property.configurable, `${key}: configurable`); + const actual = property.value; + assert_true(Object.isExtensible(actual), `${key}: extensible`); + + switch (expected.kind) { + case "function": + assert_exported_function(actual, expected, `value of ${key}`); + break; + case "global": + assert_equals(Object.getPrototypeOf(actual), WebAssembly.Global.prototype, + `value of ${key}: prototype`); + assert_equals(actual.value, expected.value, `value of ${key}: value`); + assert_equals(actual.valueOf(), expected.value, `value of ${key}: valueOf()`); + break; + case "memory": + assert_equals(Object.getPrototypeOf(actual), WebAssembly.Memory.prototype, + `value of ${key}: prototype`); + assert_equals(Object.getPrototypeOf(actual.buffer), ArrayBuffer.prototype, + `value of ${key}: prototype of buffer`); + assert_equals(actual.buffer.byteLength, 0x10000 * expected.size, `value of ${key}: size of buffer`); + const array = new Uint8Array(actual.buffer); + assert_equals(array[0], 0, `value of ${key}: first element of buffer`); + assert_equals(array[array.byteLength - 1], 0, `value of ${key}: last element of buffer`); + break; + case "table": + assert_equals(Object.getPrototypeOf(actual), WebAssembly.Table.prototype, + `value of ${key}: prototype`); + assert_equals(actual.length, expected.length, `value of ${key}: length of table`); + break; + } + } +} + +function assert_WebAssemblyInstantiatedSource(actual, expected_exports={}) { + assert_equals(Object.getPrototypeOf(actual), Object.prototype, + "Prototype"); + assert_true(Object.isExtensible(actual), "Extensibility"); + + const module = Object.getOwnPropertyDescriptor(actual, "module"); + assert_equals(typeof module, "object", "module: type of descriptor"); + assert_true(module.writable, "module: writable"); + assert_true(module.enumerable, "module: enumerable"); + assert_true(module.configurable, "module: configurable"); + assert_equals(Object.getPrototypeOf(module.value), WebAssembly.Module.prototype, + "module: prototype"); + + const instance = Object.getOwnPropertyDescriptor(actual, "instance"); + assert_equals(typeof instance, "object", "instance: type of descriptor"); + assert_true(instance.writable, "instance: writable"); + assert_true(instance.enumerable, "instance: enumerable"); + assert_true(instance.configurable, "instance: configurable"); + assert_Instance(instance.value, expected_exports); +} diff --git a/test/fixtures/wpt/wasm/jsapi/bad-imports.js b/test/fixtures/wpt/wasm/jsapi/bad-imports.js new file mode 100644 index 00000000000000..786fc650e326b6 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/bad-imports.js @@ -0,0 +1,185 @@ +/** + * `t` should be a function that takes at least three arguments: + * + * - the name of the test; + * - the expected error (to be passed to `assert_throws_js`); + * - a function that takes a `WasmModuleBuilder` and initializes it; + * - (optionally) an options object. + * + * The function is expected to create a test that checks if instantiating a + * module with the result of the `WasmModuleBuilder` and the options object + * (if any) yields the correct error. + */ +function test_bad_imports(t) { + function value_type(type) { + switch (type) { + case "i32": return kWasmI32; + case "i64": return kWasmI64; + case "f32": return kWasmF32; + case "f64": return kWasmF64; + default: throw new TypeError(`Unexpected type ${type}`); + } + } + + for (const value of [null, true, "", Symbol(), 1, 0.1, NaN]) { + t(`Non-object imports argument: ${format_value(value)}`, + TypeError, + builder => {}, + value); + } + + for (const value of [undefined, null, true, "", Symbol(), 1, 0.1, NaN]) { + const imports = { + "module": value, + }; + t(`Non-object module: ${format_value(value)}`, + TypeError, + builder => { + builder.addImport("module", "fn", kSig_v_v); + }, + imports); + } + + t(`Missing imports argument`, + TypeError, + builder => { + builder.addImport("module", "fn", kSig_v_v); + }); + + for (const [value, name] of [[undefined, "undefined"], [{}, "empty object"], [{ "module\0": null }, "wrong property"]]) { + t(`Imports argument with missing property: ${name}`, + TypeError, + builder => { + builder.addImport("module", "fn", kSig_v_v); + }, + value); + } + + for (const value of [undefined, null, true, "", Symbol(), 1, 0.1, NaN, {}]) { + t(`Importing a function with an incorrectly-typed value: ${format_value(value)}`, + WebAssembly.LinkError, + builder => { + builder.addImport("module", "fn", kSig_v_v); + }, + { + "module": { + "fn": value, + }, + }); + } + + const nonGlobals = [ + [undefined], + [null], + [true], + [""], + [Symbol()], + [{}, "plain object"], + [WebAssembly.Global, "WebAssembly.Global"], + [WebAssembly.Global.prototype, "WebAssembly.Global.prototype"], + [Object.create(WebAssembly.Global.prototype), "Object.create(WebAssembly.Global.prototype)"], + ]; + + for (const type of ["i32", "i64", "f32", "f64"]) { + const extendedNonGlobals = nonGlobals.concat([ + type === "i64" ? [0, "Number"] : [0n, "BigInt"], + [new WebAssembly.Global({value: type === "f32" ? "f64" : "f32"}), "WebAssembly.Global object (wrong value type)"], + ]); + for (const [value, name = format_value(value)] of extendedNonGlobals) { + t(`Importing an ${type} global with an incorrectly-typed value: ${name}`, + WebAssembly.LinkError, + builder => { + builder.addImportedGlobal("module", "global", value_type(type)); + }, + { + "module": { + "global": value, + }, + }); + } + } + + for (const type of ["i32", "i64", "f32", "f64"]) { + const value = type === "i64" ? 0n : 0; + t(`Importing an ${type} mutable global with a primitive value`, + WebAssembly.LinkError, + builder => { + builder.addImportedGlobal("module", "global", value_type(type), true); + }, + { + "module": { + "global": value, + }, + }); + + const global = new WebAssembly.Global({ "value": type }, value); + t(`Importing an ${type} mutable global with an immutable Global object`, + WebAssembly.LinkError, + builder => { + builder.addImportedGlobal("module", "global", value_type(type), true); + }, + { + "module": { + "global": global, + }, + }); + } + + const nonMemories = [ + [undefined], + [null], + [true], + [""], + [Symbol()], + [1], + [0.1], + [NaN], + [{}, "plain object"], + [WebAssembly.Memory, "WebAssembly.Memory"], + [WebAssembly.Memory.prototype, "WebAssembly.Memory.prototype"], + [Object.create(WebAssembly.Memory.prototype), "Object.create(WebAssembly.Memory.prototype)"], + [new WebAssembly.Memory({"initial": 256}), "WebAssembly.Memory object (too large)"], + ]; + + for (const [value, name = format_value(value)] of nonMemories) { + t(`Importing memory with an incorrectly-typed value: ${name}`, + WebAssembly.LinkError, + builder => { + builder.addImportedMemory("module", "memory", 0, 128); + }, + { + "module": { + "memory": value, + }, + }); + } + + const nonTables = [ + [undefined], + [null], + [true], + [""], + [Symbol()], + [1], + [0.1], + [NaN], + [{}, "plain object"], + [WebAssembly.Table, "WebAssembly.Table"], + [WebAssembly.Table.prototype, "WebAssembly.Table.prototype"], + [Object.create(WebAssembly.Table.prototype), "Object.create(WebAssembly.Table.prototype)"], + [new WebAssembly.Table({"element": "anyfunc", "initial": 256}), "WebAssembly.Table object (too large)"], + ]; + + for (const [value, name = format_value(value)] of nonTables) { + t(`Importing table with an incorrectly-typed value: ${name}`, + WebAssembly.LinkError, + builder => { + builder.addImportedTable("module", "table", 0, 128); + }, + { + "module": { + "table": value, + }, + }); + } +} diff --git a/test/fixtures/wpt/wasm/jsapi/constructor/compile.any.js b/test/fixtures/wpt/wasm/jsapi/constructor/compile.any.js new file mode 100644 index 00000000000000..e94ce11717369f --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/constructor/compile.any.js @@ -0,0 +1,85 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/wasm-module-builder.js + +function assert_Module(module) { + assert_equals(Object.getPrototypeOf(module), WebAssembly.Module.prototype, + "Prototype"); + assert_true(Object.isExtensible(module), "Extensibility"); +} + +let emptyModuleBinary; +setup(() => { + emptyModuleBinary = new WasmModuleBuilder().toBuffer(); +}); + +promise_test(t => { + return promise_rejects_js(t, TypeError, WebAssembly.compile()); +}, "Missing argument"); + +promise_test(t => { + const invalidArguments = [ + undefined, + null, + true, + "", + Symbol(), + 1, + {}, + ArrayBuffer, + ArrayBuffer.prototype, + Array.from(emptyModuleBinary), + ]; + return Promise.all(invalidArguments.map(argument => { + return promise_rejects_js(t, TypeError, WebAssembly.compile(argument), + `compile(${format_value(argument)})`); + })); +}, "Invalid arguments"); + +promise_test(() => { + const fn = WebAssembly.compile; + const thisValues = [ + undefined, + null, + true, + "", + Symbol(), + 1, + {}, + WebAssembly, + ]; + return Promise.all(thisValues.map(thisValue => { + return fn.call(thisValue, emptyModuleBinary).then(assert_Module); + })); +}, "Branding"); + +test(() => { + const promise = WebAssembly.compile(emptyModuleBinary); + assert_equals(Object.getPrototypeOf(promise), Promise.prototype, "prototype"); + assert_true(Object.isExtensible(promise), "extensibility"); +}, "Promise type"); + +promise_test(t => { + const buffer = new Uint8Array(); + return promise_rejects_js(t, WebAssembly.CompileError, WebAssembly.compile(buffer)); +}, "Empty buffer"); + +promise_test(t => { + const buffer = new Uint8Array(Array.from(emptyModuleBinary).concat([0, 0])); + return promise_rejects_js(t, WebAssembly.CompileError, WebAssembly.compile(buffer)); +}, "Invalid code"); + +promise_test(() => { + return WebAssembly.compile(emptyModuleBinary).then(assert_Module); +}, "Result type"); + +promise_test(() => { + return WebAssembly.compile(emptyModuleBinary, {}).then(assert_Module); +}, "Stray argument"); + +promise_test(() => { + const buffer = new WasmModuleBuilder().toBuffer(); + assert_equals(buffer[0], 0); + const promise = WebAssembly.compile(buffer); + buffer[0] = 1; + return promise.then(assert_Module); +}, "Changing the buffer"); diff --git a/test/fixtures/wpt/wasm/jsapi/constructor/instantiate-bad-imports.any.js b/test/fixtures/wpt/wasm/jsapi/constructor/instantiate-bad-imports.any.js new file mode 100644 index 00000000000000..30252bd6eeb3ab --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/constructor/instantiate-bad-imports.any.js @@ -0,0 +1,22 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/wasm-module-builder.js +// META: script=/wasm/jsapi/bad-imports.js + +test_bad_imports((name, error, build, ...arguments) => { + promise_test(t => { + const builder = new WasmModuleBuilder(); + build(builder); + const buffer = builder.toBuffer(); + const module = new WebAssembly.Module(buffer); + return promise_rejects_js(t, error, WebAssembly.instantiate(module, ...arguments)); + }, `WebAssembly.instantiate(module): ${name}`); +}); + +test_bad_imports((name, error, build, ...arguments) => { + promise_test(t => { + const builder = new WasmModuleBuilder(); + build(builder); + const buffer = builder.toBuffer(); + return promise_rejects_js(t, error, WebAssembly.instantiate(buffer, ...arguments)); + }, `WebAssembly.instantiate(buffer): ${name}`); +}); diff --git a/test/fixtures/wpt/wasm/jsapi/constructor/instantiate.any.js b/test/fixtures/wpt/wasm/jsapi/constructor/instantiate.any.js new file mode 100644 index 00000000000000..8152f3a56f3f43 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/constructor/instantiate.any.js @@ -0,0 +1,152 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/wasm-module-builder.js +// META: script=/wasm/jsapi/assertions.js +// META: script=/wasm/jsapi/instanceTestFactory.js + +let emptyModuleBinary; +setup(() => { + emptyModuleBinary = new WasmModuleBuilder().toBuffer(); +}); + +promise_test(t => { + return promise_rejects_js(t, TypeError, WebAssembly.instantiate()); +}, "Missing arguments"); + +promise_test(() => { + const fn = WebAssembly.instantiate; + const thisValues = [ + undefined, + null, + true, + "", + Symbol(), + 1, + {}, + WebAssembly, + ]; + return Promise.all(thisValues.map(thisValue => { + return fn.call(thisValue, emptyModuleBinary).then(assert_WebAssemblyInstantiatedSource); + })); +}, "Branding"); + +promise_test(t => { + const invalidArguments = [ + undefined, + null, + true, + "", + Symbol(), + 1, + {}, + WebAssembly.Module, + WebAssembly.Module.prototype, + ArrayBuffer, + ArrayBuffer.prototype, + Array.from(emptyModuleBinary), + ]; + return Promise.all(invalidArguments.map(argument => { + return promise_rejects_js(t, TypeError, WebAssembly.instantiate(argument), + `instantiate(${format_value(argument)})`); + })); +}, "Invalid arguments"); + +test(() => { + const promise = WebAssembly.instantiate(emptyModuleBinary); + assert_equals(Object.getPrototypeOf(promise), Promise.prototype, "prototype"); + assert_true(Object.isExtensible(promise), "extensibility"); +}, "Promise type"); + +for (const [name, fn] of instanceTestFactory) { + promise_test(() => { + const { buffer, args, exports, verify } = fn(); + return WebAssembly.instantiate(buffer, ...args).then(result => { + assert_WebAssemblyInstantiatedSource(result, exports); + verify(result.instance); + }); + }, `${name}: BufferSource argument`); + + promise_test(() => { + const { buffer, args, exports, verify } = fn(); + const module = new WebAssembly.Module(buffer); + return WebAssembly.instantiate(module, ...args).then(instance => { + assert_Instance(instance, exports); + verify(instance); + }); + }, `${name}: Module argument`); +} + +promise_test(() => { + const builder = new WasmModuleBuilder(); + builder.addImportedGlobal("module", "global", kWasmI32); + const buffer = builder.toBuffer(); + const order = []; + + const imports = { + get module() { + order.push("module getter"); + return { + get global() { + order.push("global getter"); + return 0; + }, + } + }, + }; + + const expected = [ + "module getter", + "global getter", + ]; + const p = WebAssembly.instantiate(buffer, imports); + assert_array_equals(order, []); + return p.then(result => { + assert_WebAssemblyInstantiatedSource(result); + assert_array_equals(order, expected); + }); +}, "Synchronous options handling: Buffer argument"); + +promise_test(() => { + const builder = new WasmModuleBuilder(); + builder.addImportedGlobal("module", "global", kWasmI32); + const buffer = builder.toBuffer(); + const module = new WebAssembly.Module(buffer); + const order = []; + + const imports = { + get module() { + order.push("module getter"); + return { + get global() { + order.push("global getter"); + return 0; + }, + } + }, + }; + + const expected = [ + "module getter", + "global getter", + ]; + const p = WebAssembly.instantiate(module, imports); + assert_array_equals(order, expected); + return p.then(instance => assert_Instance(instance, {})); +}, "Synchronous options handling: Module argument"); + +promise_test(t => { + const buffer = new Uint8Array(); + return promise_rejects_js(t, WebAssembly.CompileError, WebAssembly.instantiate(buffer)); +}, "Empty buffer"); + +promise_test(t => { + const buffer = new Uint8Array(Array.from(emptyModuleBinary).concat([0, 0])); + return promise_rejects_js(t, WebAssembly.CompileError, WebAssembly.instantiate(buffer)); +}, "Invalid code"); + +promise_test(() => { + const buffer = new WasmModuleBuilder().toBuffer(); + assert_equals(buffer[0], 0); + const promise = WebAssembly.instantiate(buffer); + buffer[0] = 1; + return promise.then(assert_WebAssemblyInstantiatedSource); +}, "Changing the buffer"); diff --git a/test/fixtures/wpt/wasm/jsapi/constructor/multi-value.any.js b/test/fixtures/wpt/wasm/jsapi/constructor/multi-value.any.js new file mode 100644 index 00000000000000..4b06d1da3c49b9 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/constructor/multi-value.any.js @@ -0,0 +1,149 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/wasm-module-builder.js +// META: script=/wasm/jsapi/assertions.js + +const type_if_fi = makeSig([kWasmF64, kWasmI32], [kWasmI32, kWasmF64]); + +promise_test(async () => { + const builder = new WasmModuleBuilder(); + + builder + .addFunction("swap", type_if_fi) + .addBody([ + kExprLocalGet, 1, + kExprLocalGet, 0, + kExprReturn, + ]) + .exportFunc(); + + const buffer = builder.toBuffer(); + + const result = await WebAssembly.instantiate(buffer); + const swapped = result.instance.exports.swap(4.2, 7); + assert_true(Array.isArray(swapped)); + assert_equals(Object.getPrototypeOf(swapped), Array.prototype); + assert_array_equals(swapped, [7, 4.2]); +}, "multiple return values from wasm to js"); + +promise_test(async () => { + const builder = new WasmModuleBuilder(); + + const swap = builder + .addFunction("swap", type_if_fi) + .addBody([ + kExprLocalGet, 1, + kExprLocalGet, 0, + kExprReturn, + ]); + builder + .addFunction("callswap", kSig_i_v) + .addBody([ + ...wasmF64Const(4.2), + ...wasmI32Const(7), + kExprCallFunction, swap.index, + kExprDrop, + kExprReturn, + ]) + .exportFunc(); + + const buffer = builder.toBuffer(); + + const result = await WebAssembly.instantiate(buffer); + const swapped = result.instance.exports.callswap(); + assert_equals(swapped, 7); +}, "multiple return values inside wasm"); + +promise_test(async () => { + const builder = new WasmModuleBuilder(); + + const fnIndex = builder.addImport("module", "fn", type_if_fi); + builder + .addFunction("callfn", kSig_i_v) + .addBody([ + ...wasmF64Const(4.2), + ...wasmI32Const(7), + kExprCallFunction, fnIndex, + kExprDrop, + kExprReturn, + ]) + .exportFunc(); + + const buffer = builder.toBuffer(); + + const actual = []; + const imports = { + "module": { + fn(f32, i32) { + assert_equals(f32, 4.2); + assert_equals(i32, 7); + const result = [2, 7.3]; + let i = 0; + return { + get [Symbol.iterator]() { + actual.push("@@iterator getter"); + return function iterator() { + actual.push("@@iterator call"); + return { + get next() { + actual.push("next getter"); + return function next(...args) { + assert_array_equals(args, []); + let j = ++i; + actual.push(`next call ${j}`); + if (j > result.length) { + return { + get done() { + actual.push(`done call ${j}`); + return true; + } + }; + } + return { + get done() { + actual.push(`done call ${j}`); + return false; + }, + get value() { + actual.push(`value call ${j}`); + return { + get valueOf() { + actual.push(`valueOf get ${j}`); + return function() { + actual.push(`valueOf call ${j}`); + return result[j - 1]; + }; + } + }; + } + }; + }; + } + }; + } + }, + }; + }, + } + }; + + const { instance } = await WebAssembly.instantiate(buffer, imports); + const result = instance.exports.callfn(); + assert_equals(result, 2); + assert_array_equals(actual, [ + "@@iterator getter", + "@@iterator call", + "next getter", + "next call 1", + "done call 1", + "value call 1", + "next call 2", + "done call 2", + "value call 2", + "next call 3", + "done call 3", + "valueOf get 1", + "valueOf call 1", + "valueOf get 2", + "valueOf call 2", + ]); +}, "multiple return values from js to wasm"); diff --git a/test/fixtures/wpt/wasm/jsapi/constructor/toStringTag.any.js b/test/fixtures/wpt/wasm/jsapi/constructor/toStringTag.any.js new file mode 100644 index 00000000000000..c6d2cdaf662e8b --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/constructor/toStringTag.any.js @@ -0,0 +1,42 @@ +// META: global=window,dedicatedworker,jsshell + +"use strict"; +// https://webidl.spec.whatwg.org/#es-namespaces +// https://webassembly.github.io/spec/js-api/#namespacedef-webassembly + +test(() => { + assert_own_property(WebAssembly, Symbol.toStringTag); + + const propDesc = Object.getOwnPropertyDescriptor(WebAssembly, Symbol.toStringTag); + assert_equals(propDesc.value, "WebAssembly", "value"); + assert_equals(propDesc.writable, false, "writable"); + assert_equals(propDesc.enumerable, false, "enumerable"); + assert_equals(propDesc.configurable, true, "configurable"); +}, "@@toStringTag exists on the namespace object with the appropriate descriptor"); + +test(() => { + assert_equals(WebAssembly.toString(), "[object WebAssembly]"); + assert_equals(Object.prototype.toString.call(WebAssembly), "[object WebAssembly]"); +}, "Object.prototype.toString applied to the namespace object"); + +test(t => { + assert_own_property(WebAssembly, Symbol.toStringTag, "Precondition: @@toStringTag on the namespace object"); + t.add_cleanup(() => { + Object.defineProperty(WebAssembly, Symbol.toStringTag, { value: "WebAssembly" }); + }); + + Object.defineProperty(WebAssembly, Symbol.toStringTag, { value: "Test" }); + assert_equals(WebAssembly.toString(), "[object Test]"); + assert_equals(Object.prototype.toString.call(WebAssembly), "[object Test]"); +}, "Object.prototype.toString applied after modifying the namespace object's @@toStringTag"); + +test(t => { + assert_own_property(WebAssembly, Symbol.toStringTag, "Precondition: @@toStringTag on the namespace object"); + t.add_cleanup(() => { + Object.defineProperty(WebAssembly, Symbol.toStringTag, { value: "WebAssembly" }); + }); + + assert_true(delete WebAssembly[Symbol.toStringTag]); + assert_equals(WebAssembly.toString(), "[object Object]"); + assert_equals(Object.prototype.toString.call(WebAssembly), "[object Object]"); +}, "Object.prototype.toString applied after deleting @@toStringTag"); diff --git a/test/fixtures/wpt/wasm/jsapi/constructor/validate.any.js b/test/fixtures/wpt/wasm/jsapi/constructor/validate.any.js new file mode 100644 index 00000000000000..8b4f4582ab2987 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/constructor/validate.any.js @@ -0,0 +1,99 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/wasm-module-builder.js + +let emptyModuleBinary; +setup(() => { + emptyModuleBinary = new WasmModuleBuilder().toBuffer(); +}); + +test(() => { + assert_throws_js(TypeError, () => WebAssembly.validate()); +}, "Missing argument"); + +test(() => { + const invalidArguments = [ + undefined, + null, + true, + "", + Symbol(), + 1, + {}, + ArrayBuffer, + ArrayBuffer.prototype, + Array.from(emptyModuleBinary), + ]; + for (const argument of invalidArguments) { + assert_throws_js(TypeError, () => WebAssembly.validate(argument), + `validate(${format_value(argument)})`); + } +}, "Invalid arguments"); + +test(() => { + const fn = WebAssembly.validate; + const thisValues = [ + undefined, + null, + true, + "", + Symbol(), + 1, + {}, + WebAssembly, + ]; + for (const thisValue of thisValues) { + assert_true(fn.call(thisValue, emptyModuleBinary), `this=${format_value(thisValue)}`); + } +}, "Branding"); + +const modules = [ + // Incomplete header. + [[], false], + [[0x00], false], + [[0x00, 0x61], false], + [[0x00, 0x61, 0x73], false], + [[0x00, 0x61, 0x73, 0x6d], false], + [[0x00, 0x61, 0x73, 0x6d, 0x01], false], + [[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00], false], + [[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00], false], + + // Complete header. + [[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00], true], + + // Invalid version. + [[0x00, 0x61, 0x73, 0x6d, 0x00, 0x00, 0x00, 0x00], false], + [[0x00, 0x61, 0x73, 0x6d, 0x02, 0x00, 0x00, 0x00], false], + + // Nameless custom section. + [[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00], false], + + // Custom section with empty name. + [[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00], true], + + // Custom section with name "a". + [[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x61], true], +]; +const bufferTypes = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, +]; +for (const [module, expected] of modules) { + const name = module.map(n => n.toString(16)).join(" "); + for (const bufferType of bufferTypes) { + if (module.length % bufferType.BYTES_PER_ELEMENT === 0) { + test(() => { + const bytes = new Uint8Array(module); + const moduleBuffer = new bufferType(bytes.buffer); + assert_equals(WebAssembly.validate(moduleBuffer), expected); + }, `Validating module [${name}] in ${bufferType.name}`); + } + } +} + +test(() => { + assert_true(WebAssembly.validate(emptyModuleBinary, {})); +}, "Stray argument"); diff --git a/test/fixtures/wpt/wasm/jsapi/error-interfaces-no-symbol-tostringtag.js b/test/fixtures/wpt/wasm/jsapi/error-interfaces-no-symbol-tostringtag.js new file mode 100644 index 00000000000000..572db0c01b620d --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/error-interfaces-no-symbol-tostringtag.js @@ -0,0 +1,13 @@ +// META: global=jsshell + +test(() => { + assert_not_own_property(WebAssembly.CompileError.prototype, Symbol.toStringTag); +}, "WebAssembly.CompileError"); + +test(() => { + assert_not_own_property(WebAssembly.LinkError.prototype, Symbol.toStringTag); +}, "WebAssembly.LinkError"); + +test(() => { + assert_not_own_property(WebAssembly.RuntimeError.prototype, Symbol.toStringTag); +}, "WebAssembly.RuntimeError"); diff --git a/test/fixtures/wpt/wasm/jsapi/exception/basic.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/exception/basic.tentative.any.js new file mode 100644 index 00000000000000..9ddebae0e968a2 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/exception/basic.tentative.any.js @@ -0,0 +1,121 @@ +// META: global=window,worker,jsshell +// META: script=/wasm/jsapi/wasm-module-builder.js + +function assert_throws_wasm(fn, message) { + try { + fn(); + assert_not_reached(`expected to throw with ${message}`); + } catch (e) { + assert_true(e instanceof WebAssembly.Exception, `Error should be a WebAssembly.Exception with ${message}`); + } +} + +promise_test(async () => { + const kWasmAnyRef = 0x6f; + const kSig_v_r = makeSig([kWasmAnyRef], []); + const builder = new WasmModuleBuilder(); + const except = builder.addException(kSig_v_r); + builder.addFunction("throw_param", kSig_v_r) + .addBody([ + kExprLocalGet, 0, + kExprThrow, except, + ]) + .exportFunc(); + const buffer = builder.toBuffer(); + const {instance} = await WebAssembly.instantiate(buffer, {}); + const values = [ + undefined, + null, + true, + false, + "test", + Symbol(), + 0, + 1, + 4.2, + NaN, + Infinity, + {}, + () => {}, + ]; + for (const v of values) { + assert_throws_wasm(() => instance.exports.throw_param(v), String(v)); + } +}, "Wasm function throws argument"); + +promise_test(async () => { + const builder = new WasmModuleBuilder(); + const except = builder.addException(kSig_v_a); + builder.addFunction("throw_null", kSig_v_v) + .addBody([ + kExprRefNull, kWasmAnyFunc, + kExprThrow, except, + ]) + .exportFunc(); + const buffer = builder.toBuffer(); + const {instance} = await WebAssembly.instantiate(buffer, {}); + assert_throws_wasm(() => instance.exports.throw_null()); +}, "Wasm function throws null"); + +promise_test(async () => { + const builder = new WasmModuleBuilder(); + const except = builder.addException(kSig_v_i); + builder.addFunction("throw_int", kSig_v_v) + .addBody([ + ...wasmI32Const(7), + kExprThrow, except, + ]) + .exportFunc(); + const buffer = builder.toBuffer(); + const {instance} = await WebAssembly.instantiate(buffer, {}); + assert_throws_wasm(() => instance.exports.throw_int()); +}, "Wasm function throws integer"); + +promise_test(async () => { + const builder = new WasmModuleBuilder(); + const fnIndex = builder.addImport("module", "fn", kSig_v_v); + const except = builder.addException(kSig_v_r); + builder.addFunction("catch_exception", kSig_r_v) + .addBody([ + kExprTry, kWasmStmt, + kExprCallFunction, fnIndex, + kExprCatch, except, + kExprReturn, + kExprEnd, + kExprRefNull, kWasmAnyRef, + ]) + .exportFunc(); + + const buffer = builder.toBuffer(); + + const error = new Error(); + const fn = () => { throw error }; + const {instance} = await WebAssembly.instantiate(buffer, { + module: { fn } + }); + assert_throws_exactly(error, () => instance.exports.catch_exception()); +}, "Imported JS function throws"); + +promise_test(async () => { + const builder = new WasmModuleBuilder(); + const fnIndex = builder.addImport("module", "fn", kSig_v_v); + builder.addFunction("catch_and_rethrow", kSig_r_v) + .addBody([ + kExprTry, kWasmStmt, + kExprCallFunction, fnIndex, + kExprCatchAll, + kExprRethrow, 0x00, + kExprEnd, + kExprRefNull, kWasmAnyRef, + ]) + .exportFunc(); + + const buffer = builder.toBuffer(); + + const error = new Error(); + const fn = () => { throw error }; + const {instance} = await WebAssembly.instantiate(buffer, { + module: { fn } + }); + assert_throws_exactly(error, () => instance.exports.catch_and_rethrow()); +}, "Imported JS function throws, Wasm catches and rethrows"); diff --git a/test/fixtures/wpt/wasm/jsapi/exception/constructor.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/exception/constructor.tentative.any.js new file mode 100644 index 00000000000000..0fd47b455e023c --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/exception/constructor.tentative.any.js @@ -0,0 +1,62 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/assertions.js + +test(() => { + assert_function_name( + WebAssembly.Exception, + "Exception", + "WebAssembly.Exception" + ); +}, "name"); + +test(() => { + assert_function_length(WebAssembly.Exception, 1, "WebAssembly.Exception"); +}, "length"); + +test(() => { + assert_throws_js(TypeError, () => new WebAssembly.Exception()); +}, "No arguments"); + +test(() => { + const argument = new WebAssembly.Tag({ parameters: [] }); + assert_throws_js(TypeError, () => WebAssembly.Exception(argument)); +}, "Calling"); + +test(() => { + const invalidArguments = [ + undefined, + null, + false, + true, + "", + "test", + Symbol(), + 1, + NaN, + {}, + ]; + for (const invalidArgument of invalidArguments) { + assert_throws_js( + TypeError, + () => new WebAssembly.Exception(invalidArgument), + `new Exception(${format_value(invalidArgument)})` + ); + } +}, "Invalid descriptor argument"); + +test(() => { + const typesAndArgs = [ + ["i32", 123n], + ["i32", Symbol()], + ["f32", 123n], + ["f64", 123n], + ["i64", undefined], + ]; + for (const typeAndArg of typesAndArgs) { + const exn = new WebAssembly.Tag({ parameters: [typeAndArg[0]] }); + assert_throws_js( + TypeError, + () => new WebAssembly.Exception(exn, typeAndArg[1]) + ); + } +}, "Invalid exception argument"); diff --git a/test/fixtures/wpt/wasm/jsapi/exception/getArg.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/exception/getArg.tentative.any.js new file mode 100644 index 00000000000000..ecd2fbd42fd18a --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/exception/getArg.tentative.any.js @@ -0,0 +1,54 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/memory/assertions.js + +test(() => { + const tag = new WebAssembly.Tag({ parameters: [] }); + const exn = new WebAssembly.Exception(tag, []); + assert_throws_js(TypeError, () => exn.getArg()); + assert_throws_js(TypeError, () => exn.getArg(tag)); +}, "Missing arguments"); + +test(() => { + const invalidValues = [undefined, null, true, "", Symbol(), 1, {}]; + const tag = new WebAssembly.Tag({ parameters: [] }); + const exn = new WebAssembly.Exception(tag, []); + for (argument of invalidValues) { + assert_throws_js(TypeError, () => exn.getArg(argument, 0)); + } +}, "Invalid exception argument"); + +test(() => { + const tag = new WebAssembly.Tag({ parameters: [] }); + const exn = new WebAssembly.Exception(tag, []); + assert_throws_js(TypeError, () => exn.getArg(tag, 1)); +}, "Index out of bounds"); + +test(() => { + const outOfRangeValues = [ + undefined, + NaN, + Infinity, + -Infinity, + -1, + 0x100000000, + 0x1000000000, + "0x100000000", + { + valueOf() { + return 0x100000000; + }, + }, + ]; + + const tag = new WebAssembly.Tag({ parameters: [] }); + const exn = new WebAssembly.Exception(tag, []); + for (const value of outOfRangeValues) { + assert_throws_js(TypeError, () => exn.getArg(tag, value)); + } +}, "Getting out-of-range argument"); + +test(() => { + const tag = new WebAssembly.Tag({ parameters: ["i32"] }); + const exn = new WebAssembly.Exception(tag, [42]); + assert_equals(exn.getArg(tag, 0), 42); +}, "getArg"); diff --git a/test/fixtures/wpt/wasm/jsapi/exception/is.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/exception/is.tentative.any.js new file mode 100644 index 00000000000000..e28a88a3c5fdcf --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/exception/is.tentative.any.js @@ -0,0 +1,25 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/memory/assertions.js + +test(() => { + const tag = new WebAssembly.Tag({ parameters: [] }); + const exn = new WebAssembly.Exception(tag, []); + assert_throws_js(TypeError, () => exn.is()); +}, "Missing arguments"); + +test(() => { + const invalidValues = [undefined, null, true, "", Symbol(), 1, {}]; + const tag = new WebAssembly.Tag({ parameters: [] }); + const exn = new WebAssembly.Exception(tag, []); + for (argument of invalidValues) { + assert_throws_js(TypeError, () => exn.is(argument)); + } +}, "Invalid exception argument"); + +test(() => { + const tag1 = new WebAssembly.Tag({ parameters: ["i32"] }); + const tag2 = new WebAssembly.Tag({ parameters: ["i32"] }); + const exn = new WebAssembly.Exception(tag1, [42]); + assert_true(exn.is(tag1)); + assert_false(exn.is(tag2)); +}, "is"); diff --git a/test/fixtures/wpt/wasm/jsapi/exception/toString.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/exception/toString.tentative.any.js new file mode 100644 index 00000000000000..52635186c762fc --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/exception/toString.tentative.any.js @@ -0,0 +1,21 @@ +// META: global=window,dedicatedworker,jsshell + +test(() => { + const argument = { parameters: [] }; + const tag = new WebAssembly.Tag(argument); + const exception = new WebAssembly.Exception(tag, []); + assert_class_string(exception, "WebAssembly.Exception"); +}, "Object.prototype.toString on an Exception"); + +test(() => { + assert_own_property(WebAssembly.Exception.prototype, Symbol.toStringTag); + + const propDesc = Object.getOwnPropertyDescriptor( + WebAssembly.Exception.prototype, + Symbol.toStringTag + ); + assert_equals(propDesc.value, "WebAssembly.Exception", "value"); + assert_equals(propDesc.configurable, true, "configurable"); + assert_equals(propDesc.enumerable, false, "enumerable"); + assert_equals(propDesc.writable, false, "writable"); +}, "@@toStringTag exists on the prototype with the appropriate descriptor"); diff --git a/test/fixtures/wpt/wasm/jsapi/function/call.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/function/call.tentative.any.js new file mode 100644 index 00000000000000..626cd13c9f0095 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/function/call.tentative.any.js @@ -0,0 +1,16 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/assertions.js + +function addxy(x, y) { + return x + y +} + +test(() => { + var fun = new WebAssembly.Function({parameters: ["i32", "i32"], results: ["i32"]}, addxy); + assert_equals(fun(1, 2), 3) +}, "test calling function") + +test(() => { + var fun = new WebAssembly.Function({parameters: ["i32", "i32"], results: ["i32"]}, addxy); + assert_throws_js(TypeError, () => new fun(1, 2)); +}, "test constructing function"); diff --git a/test/fixtures/wpt/wasm/jsapi/function/constructor.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/function/constructor.tentative.any.js new file mode 100644 index 00000000000000..636aeca4dc1fa0 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/function/constructor.tentative.any.js @@ -0,0 +1,65 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/assertions.js + +function addxy(x, y) { + return x + y +} + +test(() => { + assert_implements(WebAssembly.Function, "WebAssembly.Function is not implemented"); + assert_function_name(WebAssembly.Function, "Function", "WebAssembly.Function"); +}, "name"); + +test(() => { + assert_implements(WebAssembly.Function, "WebAssembly.Function is not implemented"); + assert_function_length(WebAssembly.Function, 2, "WebAssembly.Function"); +}, "length"); + +test(() => { + assert_implements(WebAssembly.Function, "WebAssembly.Function is not implemented"); + assert_throws_js(TypeError, () => new WebAssembly.Function()); + const argument = {parameters: [], results: []}; + assert_throws_js(TypeError, () => new WebAssembly.Function(argument)); +}, "Too few arguments"); + +test(() => { + assert_implements(WebAssembly.Function, "WebAssembly.Function is not implemented"); + const arguments = [{parameters: ["i32", "i32"], results: ["i32"]}, addxy]; + assert_throws_js(TypeError, () => WebAssembly.Function(...arguments)); +}, "Calling"); + +test(() => { + assert_implements(WebAssembly.Function, "WebAssembly.Function is not implemented"); + var fun = new WebAssembly.Function({parameters: ["i32", "i32"], results: ["i32"]}, addxy); + assert_true(fun instanceof WebAssembly.Function) +}, "construct with JS function") + +test(() => { + assert_implements(WebAssembly.Function, "WebAssembly.Function is not implemented"); + assert_throws_js(TypeError, () => new WebAssembly.Function({parameters: []}, addxy)) +}, "fail with missing results") + +test(() => { + assert_implements(WebAssembly.Function, "WebAssembly.Function is not implemented"); + assert_throws_js(TypeError, () => new WebAssembly.Function({results: []}, addxy)) +}, "fail with missing parameters") + +test(() => { + assert_implements(WebAssembly.Function, "WebAssembly.Function is not implemented"); + assert_throws_js(TypeError, () => new WebAssembly.Function({parameters: [1], results: [true]}, addxy)) +}, "fail with non-string parameters & results") + +test(() => { + assert_implements(WebAssembly.Function, "WebAssembly.Function is not implemented"); + assert_throws_js(TypeError, () => new WebAssembly.Function({parameters: ["invalid"], results: ["invalid"]}, addxy)) +}, "fail with non-existent parameter and result type") + +test(() => { + assert_implements(WebAssembly.Function, "WebAssembly.Function is not implemented"); + assert_throws_js(TypeError, () => new WebAssembly.Function({parameters: [], results: []}, 72)) +}, "fail with non-function object") + +test(() => { + assert_implements(WebAssembly.Function, "WebAssembly.Function is not implemented"); + assert_throws_js(TypeError, () => new WebAssembly.Function({parameters: [], results: []}, {})) +}, "fail to construct with non-callable object") diff --git a/test/fixtures/wpt/wasm/jsapi/function/table.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/function/table.tentative.any.js new file mode 100644 index 00000000000000..d7d0d86e3b6a88 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/function/table.tentative.any.js @@ -0,0 +1,30 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/assertions.js + +function testfunc(n) {} + +test(() => { + var table = new WebAssembly.Table({element: "anyfunc", initial: 3}) + var func1 = new WebAssembly.Function({parameters: ["i32"], results: []}, testfunc) + table.set(0, func1) + var func2 = new WebAssembly.Function({parameters: ["f32"], results: []}, testfunc) + table.set(1, func2) + var func3 = new WebAssembly.Function({parameters: ["i64"], results: []}, testfunc) + table.set(2, func3) + + var first = table.get(0) + assert_true(first instanceof WebAssembly.Function) + assert_equals(first, func1) + assert_equals(first.type().parameters[0], func1.type().parameters[0]) + + var second = table.get(1) + assert_true(second instanceof WebAssembly.Function) + assert_equals(second, func2) + assert_equals(second.type().parameters[0], func2.type().parameters[0]) + + var third = table.get(2) + assert_true(third instanceof WebAssembly.Function) + assert_equals(third, func3) + assert_equals(third.type().parameters[0], func3.type().parameters[0]) + +}, "Test insertion into table") diff --git a/test/fixtures/wpt/wasm/jsapi/function/type.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/function/type.tentative.any.js new file mode 100644 index 00000000000000..e01a23a9e4339e --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/function/type.tentative.any.js @@ -0,0 +1,28 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/assertions.js + +function addNumbers(x, y, z) { + return x+y+z; +} + +function doNothing() {} + +function assert_function(functype, func) { + var wasmFunc = new WebAssembly.Function(functype, func); + assert_equals(functype.parameters.length, wasmFunc.type().parameters.length); + for(let i = 0; i < functype.parameters.length; i++) { + assert_equals(functype.parameters[i], wasmFunc.type().parameters[i]); + } + assert_equals(functype.results.length, wasmFunc.type().results.length); + for(let i = 0; i < functype.results.length; i++) { + assert_equals(functype.results[i], wasmFunc.type().results[i]); + } +} + +test(() => { + assert_function({results: [], parameters: []}, doNothing); +}, "Check empty results and parameters") + +test(() => { + assert_function({results: ["f64"], parameters: ["i32", "i64", "f32"]}, addNumbers) +}, "Check all types") diff --git a/test/fixtures/wpt/wasm/jsapi/functions/entry-different-function-realm.html b/test/fixtures/wpt/wasm/jsapi/functions/entry-different-function-realm.html new file mode 100644 index 00000000000000..3af3dd924fb435 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/functions/entry-different-function-realm.html @@ -0,0 +1,45 @@ + + +Entry settings object for host functions when the function realm is different from the test realm + + + + + + + + + + + diff --git a/test/fixtures/wpt/wasm/jsapi/functions/entry.html b/test/fixtures/wpt/wasm/jsapi/functions/entry.html new file mode 100644 index 00000000000000..15018074491054 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/functions/entry.html @@ -0,0 +1,43 @@ + + +Entry settings object for host functions + + + + + + + + + + diff --git a/test/fixtures/wpt/wasm/jsapi/functions/helper.js b/test/fixtures/wpt/wasm/jsapi/functions/helper.js new file mode 100644 index 00000000000000..487791c69ad430 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/functions/helper.js @@ -0,0 +1,12 @@ +function call_later(f) { + const builder = new WasmModuleBuilder(); + const functionIndex = builder.addImport("module", "imported", kSig_v_v); + builder.addStart(functionIndex); + const buffer = builder.toBuffer(); + + WebAssembly.instantiate(buffer, { + "module": { + "imported": f, + } + }); +} diff --git a/test/fixtures/wpt/wasm/jsapi/functions/incumbent.html b/test/fixtures/wpt/wasm/jsapi/functions/incumbent.html new file mode 100644 index 00000000000000..cb2763297709a8 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/functions/incumbent.html @@ -0,0 +1,54 @@ + + +Incumbent settings object for host functions + + + + + + + + diff --git a/test/fixtures/wpt/wasm/jsapi/functions/resources/README.md b/test/fixtures/wpt/wasm/jsapi/functions/resources/README.md new file mode 100644 index 00000000000000..a89258a4e01267 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/functions/resources/README.md @@ -0,0 +1,5 @@ +A couple notes about the files scattered in this `resources/` directory: + +* The nested directory structure is necessary here so that relative URL resolution can be tested; we need different sub-paths for each document. + +* The semi-duplicate `window-to-open.html`s scattered throughout are present because Firefox, at least, does not fire `Window` `load` events for 404s, so we want to ensure that no matter which global is used, `window`'s `load` event is hit and our tests can proceed. diff --git a/test/fixtures/wpt/wasm/jsapi/functions/resources/current/current.html b/test/fixtures/wpt/wasm/jsapi/functions/resources/current/current.html new file mode 100644 index 00000000000000..63d9c437fc5683 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/functions/resources/current/current.html @@ -0,0 +1,4 @@ + + +Current page used as a test helper + diff --git a/test/fixtures/wpt/wasm/jsapi/functions/resources/current/resources/window-to-open.html b/test/fixtures/wpt/wasm/jsapi/functions/resources/current/resources/window-to-open.html new file mode 100644 index 00000000000000..1bc4cca9a3920f --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/functions/resources/current/resources/window-to-open.html @@ -0,0 +1,3 @@ + + +If the current settings object is used this page will be opened diff --git a/test/fixtures/wpt/wasm/jsapi/functions/resources/entry-incumbent.html b/test/fixtures/wpt/wasm/jsapi/functions/resources/entry-incumbent.html new file mode 100644 index 00000000000000..6b210563e99bc2 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/functions/resources/entry-incumbent.html @@ -0,0 +1,15 @@ + + +Incumbent page used as a test helper + + + + + diff --git a/test/fixtures/wpt/wasm/jsapi/functions/resources/function/function.html b/test/fixtures/wpt/wasm/jsapi/functions/resources/function/function.html new file mode 100644 index 00000000000000..979b902eaa0e17 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/functions/resources/function/function.html @@ -0,0 +1,3 @@ + + +Realm for a host function used as a test helper diff --git a/test/fixtures/wpt/wasm/jsapi/functions/resources/function/resources/window-to-open.html b/test/fixtures/wpt/wasm/jsapi/functions/resources/function/resources/window-to-open.html new file mode 100644 index 00000000000000..3928c1f8aa9e96 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/functions/resources/function/resources/window-to-open.html @@ -0,0 +1,3 @@ + + +If the function's settings object is used this page will be opened diff --git a/test/fixtures/wpt/wasm/jsapi/functions/resources/incumbent-incumbent.html b/test/fixtures/wpt/wasm/jsapi/functions/resources/incumbent-incumbent.html new file mode 100644 index 00000000000000..5e84f65a084e68 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/functions/resources/incumbent-incumbent.html @@ -0,0 +1,24 @@ + + +Incumbent page used as a test helper + + + + + + + + diff --git a/test/fixtures/wpt/wasm/jsapi/functions/resources/relevant/relevant.html b/test/fixtures/wpt/wasm/jsapi/functions/resources/relevant/relevant.html new file mode 100644 index 00000000000000..06df91c23741f5 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/functions/resources/relevant/relevant.html @@ -0,0 +1,14 @@ + + +Relevant page used as a test helper + + diff --git a/test/fixtures/wpt/wasm/jsapi/functions/resources/relevant/resources/window-to-open.html b/test/fixtures/wpt/wasm/jsapi/functions/resources/relevant/resources/window-to-open.html new file mode 100644 index 00000000000000..4138b5a084409d --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/functions/resources/relevant/resources/window-to-open.html @@ -0,0 +1,3 @@ + + +If the relevant settings object is used this page will be opened diff --git a/test/fixtures/wpt/wasm/jsapi/functions/resources/resources/window-to-open.html b/test/fixtures/wpt/wasm/jsapi/functions/resources/resources/window-to-open.html new file mode 100644 index 00000000000000..7743b9b578201e --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/functions/resources/resources/window-to-open.html @@ -0,0 +1,3 @@ + + +If the incumbent settings object is used this page will be opened diff --git a/test/fixtures/wpt/wasm/jsapi/functions/resources/window-to-open.html b/test/fixtures/wpt/wasm/jsapi/functions/resources/window-to-open.html new file mode 100644 index 00000000000000..ce357937f5e4f9 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/functions/resources/window-to-open.html @@ -0,0 +1,3 @@ + + +If the entry settings object is used this page will be opened diff --git a/test/fixtures/wpt/wasm/jsapi/global/constructor.any.js b/test/fixtures/wpt/wasm/jsapi/global/constructor.any.js new file mode 100644 index 00000000000000..f536f5d7b5df6c --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/global/constructor.any.js @@ -0,0 +1,166 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/assertions.js + +function assert_Global(actual, expected) { + assert_equals(Object.getPrototypeOf(actual), WebAssembly.Global.prototype, + "prototype"); + assert_true(Object.isExtensible(actual), "extensible"); + + assert_equals(actual.value, expected, "value"); + assert_equals(actual.valueOf(), expected, "valueOf"); +} + +test(() => { + assert_function_name(WebAssembly.Global, "Global", "WebAssembly.Global"); +}, "name"); + +test(() => { + assert_function_length(WebAssembly.Global, 1, "WebAssembly.Global"); +}, "length"); + +test(() => { + assert_throws_js(TypeError, () => new WebAssembly.Global()); +}, "No arguments"); + +test(() => { + const argument = { "value": "i32" }; + assert_throws_js(TypeError, () => WebAssembly.Global(argument)); +}, "Calling"); + +test(() => { + const order = []; + + new WebAssembly.Global({ + get value() { + order.push("descriptor value"); + return { + toString() { + order.push("descriptor value toString"); + return "f64"; + }, + }; + }, + + get mutable() { + order.push("descriptor mutable"); + return false; + }, + }, { + valueOf() { + order.push("value valueOf()"); + } + }); + + assert_array_equals(order, [ + "descriptor mutable", + "descriptor value", + "descriptor value toString", + "value valueOf()", + ]); +}, "Order of evaluation"); + +test(() => { + const invalidArguments = [ + undefined, + null, + false, + true, + "", + "test", + Symbol(), + 1, + NaN, + {}, + ]; + for (const invalidArgument of invalidArguments) { + assert_throws_js(TypeError, + () => new WebAssembly.Global(invalidArgument), + `new Global(${format_value(invalidArgument)})`); + } +}, "Invalid descriptor argument"); + +test(() => { + const invalidTypes = ["i16", "i128", "f16", "f128", "u32", "u64", "i32\0"]; + for (const value of invalidTypes) { + const argument = { value }; + assert_throws_js(TypeError, () => new WebAssembly.Global(argument)); + } +}, "Invalid type argument"); + +test(() => { + const argument = { "value": "i64" }; + const global = new WebAssembly.Global(argument); + assert_Global(global, 0n); +}, "i64 with default"); + +for (const type of ["i32", "f32", "f64"]) { + test(() => { + const argument = { "value": type }; + const global = new WebAssembly.Global(argument); + assert_Global(global, 0); + }, `Default value for type ${type}`); + + const valueArguments = [ + [undefined, 0], + [null, 0], + [true, 1], + [false, 0], + [2, 2], + ["3", 3], + [{ toString() { return "5" } }, 5, "object with toString returning string"], + [{ valueOf() { return "8" } }, 8, "object with valueOf returning string"], + [{ toString() { return 6 } }, 6, "object with toString returning number"], + [{ valueOf() { return 9 } }, 9, "object with valueOf returning number"], + ]; + for (const [value, expected, name = format_value(value)] of valueArguments) { + test(() => { + const argument = { "value": type }; + const global = new WebAssembly.Global(argument, value); + assert_Global(global, expected); + }, `Explicit value ${name} for type ${type}`); + } + + test(() => { + const argument = { "value": type }; + assert_throws_js(TypeError, () => new WebAssembly.Global(argument, 0n)); + }, `BigInt value for type ${type}`); +} + +const valueArguments = [ + [undefined, 0n], + [true, 1n], + [false, 0n], + ["3", 3n], + [123n, 123n], + [{ toString() { return "5" } }, 5n, "object with toString returning string"], + [{ valueOf() { return "8" } }, 8n, "object with valueOf returning string"], + [{ toString() { return 6n } }, 6n, "object with toString returning bigint"], + [{ valueOf() { return 9n } }, 9n, "object with valueOf returning bigint"], +]; +for (const [value, expected, name = format_value(value)] of valueArguments) { + test(() => { + const argument = { "value": "i64" }; + const global = new WebAssembly.Global(argument, value); + assert_Global(global, expected); + }, `Explicit value ${name} for type i64`); +} + +const invalidBigints = [ + null, + 666, + { toString() { return 5 } }, + { valueOf() { return 8 } }, + Symbol(), +]; +for (const invalidBigint of invalidBigints) { + test(() => { + var argument = { "value": "i64" }; + assert_throws_js(TypeError, () => new WebAssembly.Global(argument, invalidBigint)); + }, `Pass non-bigint as i64 Global value: ${format_value(invalidBigint)}`); +} + +test(() => { + const argument = { "value": "i32" }; + const global = new WebAssembly.Global(argument, 0, {}); + assert_Global(global, 0); +}, "Stray argument"); diff --git a/test/fixtures/wpt/wasm/jsapi/global/toString.any.js b/test/fixtures/wpt/wasm/jsapi/global/toString.any.js new file mode 100644 index 00000000000000..359c4273b5bd78 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/global/toString.any.js @@ -0,0 +1,17 @@ +// META: global=window,dedicatedworker,jsshell + +test(() => { + const argument = { "value": "i32" }; + const global = new WebAssembly.Global(argument); + assert_class_string(global, "WebAssembly.Global"); +}, "Object.prototype.toString on an Global"); + +test(() => { + assert_own_property(WebAssembly.Global.prototype, Symbol.toStringTag); + + const propDesc = Object.getOwnPropertyDescriptor(WebAssembly.Global.prototype, Symbol.toStringTag); + assert_equals(propDesc.value, "WebAssembly.Global", "value"); + assert_equals(propDesc.configurable, true, "configurable"); + assert_equals(propDesc.enumerable, false, "enumerable"); + assert_equals(propDesc.writable, false, "writable"); +}, "@@toStringTag exists on the prototype with the appropriate descriptor"); diff --git a/test/fixtures/wpt/wasm/jsapi/global/type.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/global/type.tentative.any.js new file mode 100644 index 00000000000000..173af647f27dc7 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/global/type.tentative.any.js @@ -0,0 +1,65 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/assertions.js + +function assert_type(argument) { + const myglobal = new WebAssembly.Global(argument); + const globaltype = myglobal.type(); + + assert_equals(globaltype.value, argument.value); + assert_equals(globaltype.mutable, argument.mutable); +} + +test(() => { + assert_type({ "value": "i32", "mutable": true}); +}, "i32, mutable"); + +test(() => { + assert_type({ "value": "i32", "mutable": false}); +}, "i32, immutable"); + +test(() => { + assert_type({ "value": "i64", "mutable": true}); +}, "i64, mutable"); + +test(() => { + assert_type({ "value": "i64", "mutable": false}); +}, "i64, immutable"); + +test(() => { + assert_type({ "value": "f32", "mutable": true}); +}, "f32, mutable"); + +test(() => { + assert_type({ "value": "f32", "mutable": false}); +}, "f32, immutable"); + +test(() => { + assert_type({ "value": "f64", "mutable": true}); +}, "f64, mutable"); + +test(() => { + assert_type({ "value": "f64", "mutable": false}); +}, "f64, immutable"); + +test(() => { + assert_type({"value": "externref", "mutable": true}) +}, "externref, mutable") + +test(() => { + assert_type({"value": "externref", "mutable": false}) +}, "externref, immutable") + +test(() => { + assert_type({"value": "anyfunc", "mutable": true}) +}, "anyfunc, mutable") + +test(() => { + assert_type({"value": "anyfunc", "mutable": false}) +}, "anyfunc, immutable") + +test(() => { + const myglobal = new WebAssembly.Global({"value": "i32", "mutable": true}); + const propertyNames = Object.getOwnPropertyNames(myglobal.type()); + assert_equals(propertyNames[0], "mutable"); + assert_equals(propertyNames[1], "value"); +}, "key ordering"); diff --git a/test/fixtures/wpt/wasm/jsapi/global/value-get-set.any.js b/test/fixtures/wpt/wasm/jsapi/global/value-get-set.any.js new file mode 100644 index 00000000000000..f95b7ca9e3f0d5 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/global/value-get-set.any.js @@ -0,0 +1,152 @@ +// META: global=window,dedicatedworker,jsshell + +test(() => { + const thisValues = [ + undefined, + null, + true, + "", + Symbol(), + 1, + {}, + WebAssembly.Global, + WebAssembly.Global.prototype, + ]; + + const desc = Object.getOwnPropertyDescriptor(WebAssembly.Global.prototype, "value"); + assert_equals(typeof desc, "object"); + + const getter = desc.get; + assert_equals(typeof getter, "function"); + + const setter = desc.set; + assert_equals(typeof setter, "function"); + + for (const thisValue of thisValues) { + assert_throws_js(TypeError, () => getter.call(thisValue), `getter with this=${format_value(thisValue)}`); + assert_throws_js(TypeError, () => setter.call(thisValue, 1), `setter with this=${format_value(thisValue)}`); + } +}, "Branding"); + +for (const type of ["i32", "i64", "f32", "f64"]) { + const [initial, value, invalid] = type === "i64" ? [0n, 1n, 2] : [0, 1, 2n]; + const immutableOptions = [ + [{}, "missing"], + [{ "mutable": undefined }, "undefined"], + [{ "mutable": null }, "null"], + [{ "mutable": false }, "false"], + [{ "mutable": "" }, "empty string"], + [{ "mutable": 0 }, "zero"], + ]; + for (const [opts, name] of immutableOptions) { + test(() => { + opts.value = type; + const global = new WebAssembly.Global(opts); + assert_equals(global.value, initial, "initial value"); + assert_equals(global.valueOf(), initial, "initial valueOf"); + + assert_throws_js(TypeError, () => global.value = value); + + assert_equals(global.value, initial, "post-set value"); + assert_equals(global.valueOf(), initial, "post-set valueOf"); + }, `Immutable ${type} (${name})`); + + test(t => { + opts.value = type; + const global = new WebAssembly.Global(opts); + assert_equals(global.value, initial, "initial value"); + assert_equals(global.valueOf(), initial, "initial valueOf"); + + const value = { + valueOf: t.unreached_func("should not call valueOf"), + toString: t.unreached_func("should not call toString"), + }; + assert_throws_js(TypeError, () => global.value = value); + + assert_equals(global.value, initial, "post-set value"); + assert_equals(global.valueOf(), initial, "post-set valueOf"); + }, `Immutable ${type} with ToNumber side-effects (${name})`); + } + + const mutableOptions = [ + [{ "mutable": true }, "true"], + [{ "mutable": 1 }, "one"], + [{ "mutable": "x" }, "string"], + [Object.create({ "mutable": true }), "true on prototype"], + ]; + for (const [opts, name] of mutableOptions) { + test(() => { + opts.value = type; + const global = new WebAssembly.Global(opts); + assert_equals(global.value, initial, "initial value"); + assert_equals(global.valueOf(), initial, "initial valueOf"); + + global.value = value; + + assert_throws_js(TypeError, () => global.value = invalid); + + assert_equals(global.value, value, "post-set value"); + assert_equals(global.valueOf(), value, "post-set valueOf"); + }, `Mutable ${type} (${name})`); + } +} + +test(() => { + const argument = { "value": "i64", "mutable": true }; + const global = new WebAssembly.Global(argument); + + assert_equals(global.value, 0n, "initial value using ToJSValue"); + + const valid = [ + [123n, 123n], + [2n ** 63n, - (2n ** 63n)], + [true, 1n], + [false, 0n], + ["456", 456n], + ]; + for (const [input, output] of valid) { + global.value = input; + assert_equals(global.valueOf(), output, "post-set valueOf"); + } + + const invalid = [ + undefined, + null, + 0, + 1, + 4.2, + Symbol(), + ]; + for (const input of invalid) { + assert_throws_js(TypeError, () => global.value = input); + } +}, "i64 mutability"); + +test(() => { + const argument = { "value": "i32", "mutable": true }; + const global = new WebAssembly.Global(argument); + const desc = Object.getOwnPropertyDescriptor(WebAssembly.Global.prototype, "value"); + assert_equals(typeof desc, "object"); + + const setter = desc.set; + assert_equals(typeof setter, "function"); + + assert_throws_js(TypeError, () => setter.call(global)); +}, "Calling setter without argument"); + +test(() => { + const argument = { "value": "i32", "mutable": true }; + const global = new WebAssembly.Global(argument); + const desc = Object.getOwnPropertyDescriptor(WebAssembly.Global.prototype, "value"); + assert_equals(typeof desc, "object"); + + const getter = desc.get; + assert_equals(typeof getter, "function"); + + const setter = desc.set; + assert_equals(typeof setter, "function"); + + assert_equals(getter.call(global, {}), 0); + assert_equals(setter.call(global, 1, {}), undefined); + assert_equals(global.value, 1); +}, "Stray argument"); diff --git a/test/fixtures/wpt/wasm/jsapi/global/valueOf.any.js b/test/fixtures/wpt/wasm/jsapi/global/valueOf.any.js new file mode 100644 index 00000000000000..0695a5a61fbc6e --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/global/valueOf.any.js @@ -0,0 +1,28 @@ +// META: global=window,dedicatedworker,jsshell + +test(() => { + const argument = { "value": "i32" }; + const thisValues = [ + undefined, + null, + true, + "", + Symbol(), + 1, + {}, + WebAssembly.Global, + WebAssembly.Global.prototype, + ]; + + const fn = WebAssembly.Global.prototype.valueOf; + + for (const thisValue of thisValues) { + assert_throws_js(TypeError, () => fn.call(thisValue), `this=${format_value(thisValue)}`); + } +}, "Branding"); + +test(() => { + const argument = { "value": "i32" }; + const global = new WebAssembly.Global(argument, 0); + assert_equals(global.valueOf({}), 0); +}, "Stray argument"); diff --git a/test/fixtures/wpt/wasm/jsapi/idlharness.any.js b/test/fixtures/wpt/wasm/jsapi/idlharness.any.js new file mode 100644 index 00000000000000..98713d4bf6e43a --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/idlharness.any.js @@ -0,0 +1,22 @@ +// META: script=/resources/WebIDLParser.js +// META: script=/resources/idlharness.js +// META: script=../resources/load_wasm.js + +'use strict'; + +// https://webassembly.github.io/spec/js-api/ + +idl_test( + ['wasm-js-api'], + [], + async idl_array => { + self.mod = await createWasmModule(); + self.instance = new WebAssembly.Instance(self.mod); + + idl_array.add_objects({ + Memory: [new WebAssembly.Memory({initial: 1024})], + Module: [self.mod], + Instance: [self.instance], + }); + } +); diff --git a/test/fixtures/wpt/wasm/jsapi/instance/constructor-bad-imports.any.js b/test/fixtures/wpt/wasm/jsapi/instance/constructor-bad-imports.any.js new file mode 100644 index 00000000000000..e4a5abb8eb2169 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/instance/constructor-bad-imports.any.js @@ -0,0 +1,13 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/wasm-module-builder.js +// META: script=/wasm/jsapi/bad-imports.js + +test_bad_imports((name, error, build, ...arguments) => { + test(() => { + const builder = new WasmModuleBuilder(); + build(builder); + const buffer = builder.toBuffer(); + const module = new WebAssembly.Module(buffer); + assert_throws_js(error, () => new WebAssembly.Instance(module, ...arguments)); + }, `new WebAssembly.Instance(module): ${name}`); +}); diff --git a/test/fixtures/wpt/wasm/jsapi/instance/constructor-caching.any.js b/test/fixtures/wpt/wasm/jsapi/instance/constructor-caching.any.js new file mode 100644 index 00000000000000..1aa4739b6294d0 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/instance/constructor-caching.any.js @@ -0,0 +1,54 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/wasm-module-builder.js + +function getExports() { + const builder = new WasmModuleBuilder(); + builder + .addFunction("fn", kSig_v_d) + .addBody([]) + .exportFunc(); + + builder.setTableBounds(1); + builder.addExportOfKind("table", kExternalTable, 0); + builder.addGlobal(kWasmI32, false).exportAs("global"); + builder.addMemory(4, 8, true); + + const buffer = builder.toBuffer(); + const module = new WebAssembly.Module(buffer); + const instance = new WebAssembly.Instance(module); + return instance.exports; +} + +test(() => { + const exports = getExports(); + + const builder = new WasmModuleBuilder(); + const functionIndex = builder.addImport("module", "imported", kSig_v_d); + builder.addExport("exportedFunction", functionIndex); + + const globalIndex = builder.addImportedGlobal("module", "global", kWasmI32); + builder.addExportOfKind("exportedGlobal", kExternalGlobal, globalIndex); + + builder.addImportedMemory("module", "memory", 4); + builder.exportMemoryAs("exportedMemory"); + + const tableIndex = builder.addImportedTable("module", "table", 1); + builder.addExportOfKind("exportedTable", kExternalTable, tableIndex); + + const buffer = builder.toBuffer(); + + const module = new WebAssembly.Module(buffer); + const instance = new WebAssembly.Instance(module, { + "module": { + "imported": exports.fn, + "global": exports.global, + "memory": exports.memory, + "table": exports.table, + } + }); + + assert_equals(instance.exports.exportedFunction, exports.fn); + assert_equals(instance.exports.exportedGlobal, exports.global); + assert_equals(instance.exports.exportedMemory, exports.memory); + assert_equals(instance.exports.exportedTable, exports.table); +}); diff --git a/test/fixtures/wpt/wasm/jsapi/instance/constructor.any.js b/test/fixtures/wpt/wasm/jsapi/instance/constructor.any.js new file mode 100644 index 00000000000000..26390ebd2cdb2e --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/instance/constructor.any.js @@ -0,0 +1,54 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/wasm-module-builder.js +// META: script=/wasm/jsapi/assertions.js +// META: script=/wasm/jsapi/instanceTestFactory.js + +let emptyModuleBinary; +setup(() => { + emptyModuleBinary = new WasmModuleBuilder().toBuffer(); +}); + +test(() => { + assert_function_name(WebAssembly.Instance, "Instance", "WebAssembly.Instance"); +}, "name"); + +test(() => { + assert_function_length(WebAssembly.Instance, 1, "WebAssembly.Instance"); +}, "length"); + +test(() => { + assert_throws_js(TypeError, () => new WebAssembly.Instance()); +}, "No arguments"); + +test(() => { + const invalidArguments = [ + undefined, + null, + true, + "", + Symbol(), + 1, + {}, + WebAssembly.Module, + WebAssembly.Module.prototype, + ]; + for (const argument of invalidArguments) { + assert_throws_js(TypeError, () => new WebAssembly.Instance(argument), + `new Instance(${format_value(argument)})`); + } +}, "Non-Module arguments"); + +test(() => { + const module = new WebAssembly.Module(emptyModuleBinary); + assert_throws_js(TypeError, () => WebAssembly.Instance(module)); +}, "Calling"); + +for (const [name, fn] of instanceTestFactory) { + test(() => { + const { buffer, args, exports, verify } = fn(); + const module = new WebAssembly.Module(buffer); + const instance = new WebAssembly.Instance(module, ...args); + assert_Instance(instance, exports); + verify(instance); + }, name); +} diff --git a/test/fixtures/wpt/wasm/jsapi/instance/exports.any.js b/test/fixtures/wpt/wasm/jsapi/instance/exports.any.js new file mode 100644 index 00000000000000..6dcfbcee950d87 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/instance/exports.any.js @@ -0,0 +1,66 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/wasm-module-builder.js + +let emptyModuleBinary; +setup(() => { + emptyModuleBinary = new WasmModuleBuilder().toBuffer(); +}); + +test(() => { + const thisValues = [ + undefined, + null, + true, + "", + Symbol(), + 1, + {}, + WebAssembly.Instance, + WebAssembly.Instance.prototype, + ]; + + const desc = Object.getOwnPropertyDescriptor(WebAssembly.Instance.prototype, "exports"); + assert_equals(typeof desc, "object"); + + const getter = desc.get; + assert_equals(typeof getter, "function"); + + assert_equals(typeof desc.set, "undefined"); + + for (const thisValue of thisValues) { + assert_throws_js(TypeError, () => getter.call(thisValue), `this=${format_value(thisValue)}`); + } +}, "Branding"); + +test(() => { + const module = new WebAssembly.Module(emptyModuleBinary); + const instance = new WebAssembly.Instance(module); + const exports = instance.exports; + + const desc = Object.getOwnPropertyDescriptor(WebAssembly.Instance.prototype, "exports"); + assert_equals(typeof desc, "object"); + + const getter = desc.get; + assert_equals(typeof getter, "function"); + + assert_equals(getter.call(instance, {}), exports); +}, "Stray argument"); + +test(() => { + const module = new WebAssembly.Module(emptyModuleBinary); + const instance = new WebAssembly.Instance(module); + const exports = instance.exports; + instance.exports = {}; + assert_equals(instance.exports, exports, "Should not change the exports"); +}, "Setting (sloppy mode)"); + +test(() => { + const module = new WebAssembly.Module(emptyModuleBinary); + const instance = new WebAssembly.Instance(module); + const exports = instance.exports; + assert_throws_js(TypeError, () => { + "use strict"; + instance.exports = {}; + }); + assert_equals(instance.exports, exports, "Should not change the exports"); +}, "Setting (strict mode)"); diff --git a/test/fixtures/wpt/wasm/jsapi/instance/toString.any.js b/test/fixtures/wpt/wasm/jsapi/instance/toString.any.js new file mode 100644 index 00000000000000..547a9ca8295f5b --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/instance/toString.any.js @@ -0,0 +1,19 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/wasm-module-builder.js + +test(() => { + const emptyModuleBinary = new WasmModuleBuilder().toBuffer(); + const module = new WebAssembly.Module(emptyModuleBinary); + const instance = new WebAssembly.Instance(module); + assert_class_string(instance, "WebAssembly.Instance"); +}, "Object.prototype.toString on an Instance"); + +test(() => { + assert_own_property(WebAssembly.Instance.prototype, Symbol.toStringTag); + + const propDesc = Object.getOwnPropertyDescriptor(WebAssembly.Instance.prototype, Symbol.toStringTag); + assert_equals(propDesc.value, "WebAssembly.Instance", "value"); + assert_equals(propDesc.configurable, true, "configurable"); + assert_equals(propDesc.enumerable, false, "enumerable"); + assert_equals(propDesc.writable, false, "writable"); +}, "@@toStringTag exists on the prototype with the appropriate descriptor"); diff --git a/test/fixtures/wpt/wasm/jsapi/instanceTestFactory.js b/test/fixtures/wpt/wasm/jsapi/instanceTestFactory.js new file mode 100644 index 00000000000000..ac468947ec22e2 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/instanceTestFactory.js @@ -0,0 +1,761 @@ +const instanceTestFactory = [ + [ + "Empty module without imports argument", + function() { + return { + buffer: emptyModuleBinary, + args: [], + exports: {}, + verify: () => {}, + }; + } + ], + + [ + "Empty module with undefined imports argument", + function() { + return { + buffer: emptyModuleBinary, + args: [undefined], + exports: {}, + verify: () => {}, + }; + } + ], + + [ + "Empty module with empty imports argument", + function() { + return { + buffer: emptyModuleBinary, + args: [{}], + exports: {}, + verify: () => {}, + }; + } + ], + + [ + "getter order for imports object", + function() { + const builder = new WasmModuleBuilder(); + builder.addImportedGlobal("module", "global1", kWasmI32); + builder.addImportedGlobal("module2", "global3", kWasmI32); + builder.addImportedMemory("module", "memory", 0, 128); + builder.addImportedGlobal("module", "global2", kWasmI32); + const buffer = builder.toBuffer(); + const order = []; + + const imports = { + get module() { + order.push("module getter"); + return { + get global1() { + order.push("global1 getter"); + return 0; + }, + get global2() { + order.push("global2 getter"); + return 0; + }, + get memory() { + order.push("memory getter"); + return new WebAssembly.Memory({ "initial": 64, maximum: 128 }); + }, + } + }, + get module2() { + order.push("module2 getter"); + return { + get global3() { + order.push("global3 getter"); + return 0; + }, + } + }, + }; + + const expected = [ + "module getter", + "global1 getter", + "module2 getter", + "global3 getter", + "module getter", + "memory getter", + "module getter", + "global2 getter", + ]; + return { + buffer, + args: [imports], + exports: {}, + verify: () => assert_array_equals(order, expected), + }; + } + ], + + [ + "imports", + function() { + const builder = new WasmModuleBuilder(); + + builder.addImport("module", "fn", kSig_v_v); + builder.addImportedGlobal("module", "global", kWasmI32); + builder.addImportedMemory("module", "memory", 0, 128); + builder.addImportedTable("module", "table", 0, 128); + + const buffer = builder.toBuffer(); + const imports = { + "module": { + "fn": function() {}, + "global": 0, + "memory": new WebAssembly.Memory({ "initial": 64, maximum: 128 }), + "table": new WebAssembly.Table({ "element": "anyfunc", "initial": 64, maximum: 128 }), + }, + get "module2"() { + assert_unreached("Should not get modules that are not imported"); + }, + }; + + return { + buffer, + args: [imports], + exports: {}, + verify: () => {}, + }; + } + ], + + [ + "imports with empty module names", + function() { + const builder = new WasmModuleBuilder(); + + builder.addImport("", "fn", kSig_v_v); + builder.addImportedGlobal("", "global", kWasmI32); + builder.addImportedMemory("", "memory", 0, 128); + builder.addImportedTable("", "table", 0, 128); + + const buffer = builder.toBuffer(); + const imports = { + "": { + "fn": function() {}, + "global": 0, + "memory": new WebAssembly.Memory({ "initial": 64, maximum: 128 }), + "table": new WebAssembly.Table({ "element": "anyfunc", "initial": 64, maximum: 128 }), + }, + }; + + return { + buffer, + args: [imports], + exports: {}, + verify: () => {}, + }; + } + ], + + [ + "imports with empty names", + function() { + const builder = new WasmModuleBuilder(); + + builder.addImport("a", "", kSig_v_v); + builder.addImportedGlobal("b", "", kWasmI32); + builder.addImportedMemory("c", "", 0, 128); + builder.addImportedTable("d", "", 0, 128); + + const buffer = builder.toBuffer(); + const imports = { + "a": { "": function() {} }, + "b": { "": 0 }, + "c": { "": new WebAssembly.Memory({ "initial": 64, maximum: 128 }) }, + "d": { "": new WebAssembly.Table({ "element": "anyfunc", "initial": 64, maximum: 128 }) }, + }; + + return { + buffer, + args: [imports], + exports: {}, + verify: () => {}, + }; + } + ], + + [ + "exports with empty name: function", + function() { + const builder = new WasmModuleBuilder(); + + builder + .addFunction("", kSig_v_d) + .addBody([]) + .exportFunc(); + + const buffer = builder.toBuffer(); + + const exports = { + "": { "kind": "function", "name": "0", "length": 1 }, + }; + + return { + buffer, + args: [], + exports, + verify: () => {}, + }; + } + ], + + [ + "exports with empty name: table", + function() { + const builder = new WasmModuleBuilder(); + + builder.setTableBounds(1); + builder.addExportOfKind("", kExternalTable, 0); + + const buffer = builder.toBuffer(); + + const exports = { + "": { "kind": "table", "length": 1 }, + }; + + return { + buffer, + args: [], + exports, + verify: () => {}, + }; + } + ], + + [ + "exports with empty name: global", + function() { + const builder = new WasmModuleBuilder(); + + builder.addGlobal(kWasmI32, true) + .exportAs("") + .init = 7; + + const buffer = builder.toBuffer(); + + const exports = { + "": { "kind": "global", "value": 7 }, + }; + + return { + buffer, + args: [], + exports, + verify: () => {}, + }; + } + ], + + [ + "No imports", + function() { + const builder = new WasmModuleBuilder(); + + builder + .addFunction("fn", kSig_v_d) + .addBody([]) + .exportFunc(); + builder + .addFunction("fn2", kSig_v_v) + .addBody([]) + .exportFunc(); + + builder.setTableBounds(1); + builder.addExportOfKind("table", kExternalTable, 0); + + builder.addGlobal(kWasmI32, true) + .exportAs("global") + .init = 7; + builder.addGlobal(kWasmF64, true) + .exportAs("global2") + .init = 1.2; + + builder.addMemory(4, 8, true); + + const buffer = builder.toBuffer(); + + const exports = { + "fn": { "kind": "function", "name": "0", "length": 1 }, + "fn2": { "kind": "function", "name": "1", "length": 0 }, + "table": { "kind": "table", "length": 1 }, + "global": { "kind": "global", "value": 7 }, + "global2": { "kind": "global", "value": 1.2 }, + "memory": { "kind": "memory", "size": 4 }, + }; + + return { + buffer, + args: [], + exports, + verify: () => {}, + }; + } + ], + + [ + "exports and imports", + function() { + const value = 102; + + const builder = new WasmModuleBuilder(); + + const index = builder.addImportedGlobal("module", "global", kWasmI32); + builder + .addFunction("fn", kSig_i_v) + .addBody([ + kExprGlobalGet, + index, + kExprReturn, + ]) + .exportFunc(); + + const buffer = builder.toBuffer(); + + const imports = { + "module": { + "global": value, + }, + }; + + const exports = { + "fn": { "kind": "function", "name": "0", "length": 0 }, + }; + + return { + buffer, + args: [imports], + exports, + verify: instance => assert_equals(instance.exports.fn(), value) + }; + } + ], + + [ + "i64 exports and imports", + function() { + const value = 102n; + + const builder = new WasmModuleBuilder(); + + const index = builder.addImportedGlobal("module", "global", kWasmI64); + builder + .addFunction("fn", kSig_l_v) + .addBody([ + kExprGlobalGet, + index, + kExprReturn, + ]) + .exportFunc(); + + const index2 = builder.addImportedGlobal("module", "global2", kWasmI64); + builder.addExportOfKind("global", kExternalGlobal, index2); + + const buffer = builder.toBuffer(); + + const imports = { + "module": { + "global": value, + "global2": 2n ** 63n, + }, + }; + + const exports = { + "fn": { "kind": "function", "name": "0", "length": 0 }, + "global": { "kind": "global", "value": -(2n ** 63n) }, + }; + + return { + buffer, + args: [imports], + exports, + verify: instance => assert_equals(instance.exports.fn(), value) + }; + } + ], + + [ + "import with i32-returning function", + function() { + const builder = new WasmModuleBuilder(); + + const fnIndex = builder.addImport("module", "fn", kSig_i_v); + const fn2 = builder + .addFunction("fn2", kSig_v_v) + .addBody([ + kExprCallFunction, + fnIndex, + kExprReturn, + ]) + .exportFunc(); + + const buffer = builder.toBuffer(); + + let called = false; + const imports = { + "module": { + "fn": function() { + called = true; + return 6n; + }, + }, + }; + + return { + buffer, + args: [imports], + exports: { + "fn2": { "kind": "function", "name": String(fn2.index), "length": 0 }, + }, + verify: instance => { + assert_throws_js(TypeError, () => instance.exports.fn2()); + assert_true(called, "Should have called into JS"); + } + }; + } + ], + + [ + "import with function that takes and returns i32", + function() { + const builder = new WasmModuleBuilder(); + + const fnIndex = builder.addImport("module", "fn", kSig_i_i); + const fn2 = builder + .addFunction("fn2", kSig_i_v) + .addBody([ + kExprI32Const, 0x66, + kExprCallFunction, + fnIndex, + kExprReturn, + ]) + .exportFunc(); + + const buffer = builder.toBuffer(); + + let called = false; + const imports = { + "module": { + "fn": function(n) { + called = true; + assert_equals(n, -26); + return { valueOf() { return 6; } }; + }, + }, + }; + + return { + buffer, + args: [imports], + exports: { + "fn2": { "kind": "function", "name": String(fn2.index), "length": 0 }, + }, + verify: instance => { + assert_equals(instance.exports.fn2(), 6); + assert_true(called, "Should have called into JS"); + } + }; + } + ], + + [ + "import with i64-returning function", + function() { + const builder = new WasmModuleBuilder(); + + const fnIndex = builder.addImport("module", "fn", kSig_l_v); + const fn2 = builder + .addFunction("fn2", kSig_v_v) + .addBody([ + kExprCallFunction, + fnIndex, + kExprReturn, + ]) + .exportFunc(); + + const buffer = builder.toBuffer(); + + let called = false; + const imports = { + "module": { + "fn": function() { + called = true; + return 6; + }, + }, + }; + + return { + buffer, + args: [imports], + exports: { + "fn2": { "kind": "function", "name": String(fn2.index), "length": 0 }, + }, + verify: instance => { + assert_throws_js(TypeError, () => instance.exports.fn2()); + assert_true(called, "Should have called into JS"); + } + }; + } + ], + + [ + "import with function that takes and returns i64", + function() { + const builder = new WasmModuleBuilder(); + + const fnIndex = builder.addImport("module", "fn", kSig_l_l); + const fn2 = builder + .addFunction("fn2", kSig_l_v) + .addBody([ + kExprI64Const, 0x66, + kExprCallFunction, + fnIndex, + kExprReturn, + ]) + .exportFunc(); + + const buffer = builder.toBuffer(); + + let called = false; + const imports = { + "module": { + "fn": function(n) { + called = true; + assert_equals(n, -26n); + return { valueOf() { return 6n; } }; + }, + }, + }; + + return { + buffer, + args: [imports], + exports: { + "fn2": { "kind": "function", "name": String(fn2.index), "length": 0 }, + }, + verify: instance => { + assert_equals(instance.exports.fn2(), 6n); + assert_true(called, "Should have called into JS"); + } + }; + } + ], + + [ + "import with i32-taking function", + function() { + const builder = new WasmModuleBuilder(); + + const fn = builder + .addFunction("fn", kSig_v_i) + .addBody([ + kExprReturn, + ]) + .exportFunc(); + + const buffer = builder.toBuffer(); + + return { + buffer, + args: [], + exports: { + "fn": { "kind": "function", "name": String(fn.index), "length": 1 }, + }, + verify: instance => assert_throws_js(TypeError, () => instance.exports.fn(6n)) + }; + } + ], + + [ + "import with i64-taking function", + function() { + const builder = new WasmModuleBuilder(); + + const fn = builder + .addFunction("fn", kSig_v_l) + .addBody([ + kExprReturn, + ]) + .exportFunc(); + + const buffer = builder.toBuffer(); + + return { + buffer, + args: [], + exports: { + "fn": { "kind": "function", "name": String(fn.index), "length": 1 }, + }, + verify: instance => assert_throws_js(TypeError, () => instance.exports.fn(6)) + }; + } + ], + + [ + "export i64-returning function", + function() { + const builder = new WasmModuleBuilder(); + + const fn = builder + .addFunction("fn", kSig_l_v) + .addBody([ + kExprI64Const, 0x66, + kExprReturn, + ]) + .exportFunc(); + + const buffer = builder.toBuffer(); + + return { + buffer, + args: [], + exports: { + "fn": { "kind": "function", "name": String(fn.index), "length": 0 }, + }, + verify: instance => assert_equals(instance.exports.fn(), -26n) + }; + } + ], + + [ + "i32 mutable WebAssembly.Global import", + function() { + const initial = 102; + const value = new WebAssembly.Global({ "value": "i32", "mutable": true }, initial); + + const builder = new WasmModuleBuilder(); + + const index = builder.addImportedGlobal("module", "global", kWasmI32, true); + const fn = builder + .addFunction("fn", kSig_i_v) + .addBody([ + kExprGlobalGet, + index, + kExprReturn, + ]) + .exportFunc(); + + const buffer = builder.toBuffer(); + + const imports = { + "module": { + "global": value, + }, + }; + + const exports = { + "fn": { "kind": "function", "name": String(fn.index), "length": 0 }, + }; + + return { + buffer, + args: [imports], + exports, + verify: instance => { + assert_equals(instance.exports.fn(), initial); + const after = 201; + value.value = after; + assert_equals(instance.exports.fn(), after); + } + }; + } + ], + + [ + "i64 mutable WebAssembly.Global import", + function() { + const initial = 102n; + const value = new WebAssembly.Global({ "value": "i64", "mutable": true }, initial); + + const builder = new WasmModuleBuilder(); + + const index = builder.addImportedGlobal("module", "global", kWasmI64, true); + const fn = builder + .addFunction("fn", kSig_l_v) + .addBody([ + kExprGlobalGet, + index, + kExprReturn, + ]) + .exportFunc(); + + const buffer = builder.toBuffer(); + + const imports = { + "module": { + "global": value, + }, + }; + + const exports = { + "fn": { "kind": "function", "name": String(fn.index), "length": 0 }, + }; + + return { + buffer, + args: [imports], + exports, + verify: instance => { + assert_equals(instance.exports.fn(), initial); + const after = 201n; + value.value = after; + assert_equals(instance.exports.fn(), after); + } + }; + } + ], + + [ + "Multiple i64 arguments", + function() { + const builder = new WasmModuleBuilder(); + + const fn = builder + .addFunction("fn", kSig_l_ll) + .addBody([ + kExprLocalGet, 1, + ]) + .exportFunc(); + + const buffer = builder.toBuffer(); + + const exports = { + "fn": { "kind": "function", "name": String(fn.index), "length": 2 }, + }; + + return { + buffer, + args: [], + exports, + verify: instance => { + const fn = instance.exports.fn; + assert_equals(fn(1n, 0n), 0n); + assert_equals(fn(1n, 123n), 123n); + assert_equals(fn(1n, -123n), -123n); + assert_equals(fn(1n, "5"), 5n); + assert_throws_js(TypeError, () => fn(1n, 5)); + } + }; + } + ], + + [ + "stray argument", + function() { + return { + buffer: emptyModuleBinary, + args: [{}, {}], + exports: {}, + verify: () => {} + }; + } + ], +]; diff --git a/test/fixtures/wpt/wasm/jsapi/interface.any.js b/test/fixtures/wpt/wasm/jsapi/interface.any.js new file mode 100644 index 00000000000000..19d29ead0a7264 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/interface.any.js @@ -0,0 +1,160 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/assertions.js + +function test_operations(object, object_name, operations) { + for (const [name, length] of operations) { + test(() => { + const propdesc = Object.getOwnPropertyDescriptor(object, name); + assert_equals(typeof propdesc, "object"); + assert_true(propdesc.writable, "writable"); + assert_true(propdesc.enumerable, "enumerable"); + assert_true(propdesc.configurable, "configurable"); + assert_equals(propdesc.value, object[name]); + }, `${object_name}.${name}`); + + test(() => { + assert_function_name(object[name], name, `${object_name}.${name}`); + }, `${object_name}.${name}: name`); + + test(() => { + assert_function_length(object[name], length, `${object_name}.${name}`); + }, `${object_name}.${name}: length`); + } +} + +function test_attributes(object, object_name, attributes) { + for (const [name, mutable] of attributes) { + test(() => { + const propdesc = Object.getOwnPropertyDescriptor(object, name); + assert_equals(typeof propdesc, "object"); + assert_true(propdesc.enumerable, "enumerable"); + assert_true(propdesc.configurable, "configurable"); + }, `${object_name}.${name}`); + + test(() => { + const propdesc = Object.getOwnPropertyDescriptor(object, name); + assert_equals(typeof propdesc, "object"); + assert_equals(typeof propdesc.get, "function"); + assert_function_name(propdesc.get, "get " + name, `getter for "${name}"`); + assert_function_length(propdesc.get, 0, `getter for "${name}"`); + }, `${object_name}.${name}: getter`); + + test(() => { + const propdesc = Object.getOwnPropertyDescriptor(object, name); + assert_equals(typeof propdesc, "object"); + if (mutable) { + assert_equals(typeof propdesc.set, "function"); + assert_function_name(propdesc.set, "set " + name, `setter for "${name}"`); + assert_function_length(propdesc.set, 1, `setter for "${name}"`); + } else { + assert_equals(typeof propdesc.set, "undefined"); + } + }, `${object_name}.${name}: setter`); + } +} + +test(() => { + const propdesc = Object.getOwnPropertyDescriptor(this, "WebAssembly"); + assert_equals(typeof propdesc, "object"); + assert_true(propdesc.writable, "writable"); + assert_false(propdesc.enumerable, "enumerable"); + assert_true(propdesc.configurable, "configurable"); + assert_equals(propdesc.value, this.WebAssembly); +}, "WebAssembly: property descriptor"); + +test(() => { + assert_throws_js(TypeError, () => WebAssembly()); +}, "WebAssembly: calling"); + +test(() => { + assert_throws_js(TypeError, () => new WebAssembly()); +}, "WebAssembly: constructing"); + +const interfaces = [ + "Module", + "Instance", + "Memory", + "Table", + "Global", + "CompileError", + "LinkError", + "RuntimeError", +]; + +for (const name of interfaces) { + test(() => { + const propdesc = Object.getOwnPropertyDescriptor(WebAssembly, name); + assert_equals(typeof propdesc, "object"); + assert_true(propdesc.writable, "writable"); + assert_false(propdesc.enumerable, "enumerable"); + assert_true(propdesc.configurable, "configurable"); + assert_equals(propdesc.value, WebAssembly[name]); + }, `WebAssembly.${name}: property descriptor`); + + test(() => { + const interface_object = WebAssembly[name]; + const propdesc = Object.getOwnPropertyDescriptor(interface_object, "prototype"); + assert_equals(typeof propdesc, "object"); + assert_false(propdesc.writable, "writable"); + assert_false(propdesc.enumerable, "enumerable"); + assert_false(propdesc.configurable, "configurable"); + }, `WebAssembly.${name}: prototype`); + + test(() => { + const interface_object = WebAssembly[name]; + const interface_prototype_object = interface_object.prototype; + const propdesc = Object.getOwnPropertyDescriptor(interface_prototype_object, "constructor"); + assert_equals(typeof propdesc, "object"); + assert_true(propdesc.writable, "writable"); + assert_false(propdesc.enumerable, "enumerable"); + assert_true(propdesc.configurable, "configurable"); + assert_equals(propdesc.value, interface_object); + }, `WebAssembly.${name}: prototype.constructor`); +} + +test_operations(WebAssembly, "WebAssembly", [ + ["validate", 1], + ["compile", 1], + ["instantiate", 1], +]); + + +test_operations(WebAssembly.Module, "WebAssembly.Module", [ + ["exports", 1], + ["imports", 1], + ["customSections", 2], +]); + + +test_attributes(WebAssembly.Instance.prototype, "WebAssembly.Instance", [ + ["exports", false], +]); + + +test_operations(WebAssembly.Memory.prototype, "WebAssembly.Memory", [ + ["grow", 1], +]); + +test_attributes(WebAssembly.Memory.prototype, "WebAssembly.Memory", [ + ["buffer", false], +]); + + +test_operations(WebAssembly.Table.prototype, "WebAssembly.Table", [ + ["grow", 1], + ["get", 1], + ["set", 1], +]); + +test_attributes(WebAssembly.Table.prototype, "WebAssembly.Table", [ + ["length", false], +]); + + +test_operations(WebAssembly.Global.prototype, "WebAssembly.Global", [ + ["valueOf", 0], +]); + +test_attributes(WebAssembly.Global.prototype, "WebAssembly.Global", [ + ["value", true], +]); diff --git a/test/fixtures/wpt/wasm/jsapi/memory/assertions.js b/test/fixtures/wpt/wasm/jsapi/memory/assertions.js new file mode 100644 index 00000000000000..b539513adcab7d --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/memory/assertions.js @@ -0,0 +1,38 @@ +function assert_ArrayBuffer(actual, { size=0, shared=false, detached=false }, message) { + // https://github.com/WebAssembly/spec/issues/840 + // See https://github.com/whatwg/html/issues/5380 for why not `self.SharedArrayBuffer` + const isShared = !("isView" in actual.constructor); + assert_equals(isShared, shared, `${message}: constructor`); + const sharedString = shared ? "Shared" : ""; + assert_equals(actual.toString(), `[object ${sharedString}ArrayBuffer]`, `${message}: toString()`); + assert_equals(Object.getPrototypeOf(actual).toString(), `[object ${sharedString}ArrayBuffer]`, `${message}: prototype toString()`); + if (detached) { + // https://github.com/tc39/ecma262/issues/678 + let byteLength; + try { + byteLength = actual.byteLength; + } catch (e) { + byteLength = 0; + } + assert_equals(byteLength, 0, `${message}: detached size`); + } else { + assert_equals(actual.byteLength, 0x10000 * size, `${message}: size`); + if (size > 0) { + const array = new Uint8Array(actual); + assert_equals(array[0], 0, `${message}: first element`); + assert_equals(array[array.byteLength - 1], 0, `${message}: last element`); + } + } + assert_equals(Object.isFrozen(actual), shared, "buffer frozen"); + assert_equals(Object.isExtensible(actual), !shared, "buffer extensibility"); +} + +function assert_Memory(memory, { size=0, shared=false }) { + assert_equals(Object.getPrototypeOf(memory), WebAssembly.Memory.prototype, + "prototype"); + assert_true(Object.isExtensible(memory), "extensible"); + + // https://github.com/WebAssembly/spec/issues/840 + assert_equals(memory.buffer, memory.buffer, "buffer should be idempotent"); + assert_ArrayBuffer(memory.buffer, { size, shared }); +} diff --git a/test/fixtures/wpt/wasm/jsapi/memory/buffer.any.js b/test/fixtures/wpt/wasm/jsapi/memory/buffer.any.js new file mode 100644 index 00000000000000..fb1d1200b892be --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/memory/buffer.any.js @@ -0,0 +1,64 @@ +// META: global=window,dedicatedworker,jsshell + +test(() => { + const thisValues = [ + undefined, + null, + true, + "", + Symbol(), + 1, + {}, + WebAssembly.Memory, + WebAssembly.Memory.prototype, + ]; + + const desc = Object.getOwnPropertyDescriptor(WebAssembly.Memory.prototype, "buffer"); + assert_equals(typeof desc, "object"); + + const getter = desc.get; + assert_equals(typeof getter, "function"); + + assert_equals(typeof desc.set, "undefined"); + + for (const thisValue of thisValues) { + assert_throws_js(TypeError, () => getter.call(thisValue), `this=${format_value(thisValue)}`); + } +}, "Branding"); + +test(() => { + const argument = { "initial": 0 }; + const memory = new WebAssembly.Memory(argument); + const buffer = memory.buffer; + + const desc = Object.getOwnPropertyDescriptor(WebAssembly.Memory.prototype, "buffer"); + assert_equals(typeof desc, "object"); + + const getter = desc.get; + assert_equals(typeof getter, "function"); + + assert_equals(getter.call(memory, {}), buffer); +}, "Stray argument"); + +test(() => { + const argument = { "initial": 0 }; + const memory = new WebAssembly.Memory(argument); + const memory2 = new WebAssembly.Memory(argument); + const buffer = memory.buffer; + assert_not_equals(buffer, memory2.buffer, "Need two distinct buffers"); + memory.buffer = memory2.buffer; + assert_equals(memory.buffer, buffer, "Should not change the buffer"); +}, "Setting (sloppy mode)"); + +test(() => { + const argument = { "initial": 0 }; + const memory = new WebAssembly.Memory(argument); + const memory2 = new WebAssembly.Memory(argument); + const buffer = memory.buffer; + assert_not_equals(buffer, memory2.buffer, "Need two distinct buffers"); + assert_throws_js(TypeError, () => { + "use strict"; + memory.buffer = memory2.buffer; + }); + assert_equals(memory.buffer, buffer, "Should not change the buffer"); +}, "Setting (strict mode)"); diff --git a/test/fixtures/wpt/wasm/jsapi/memory/constructor-shared.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/memory/constructor-shared.tentative.any.js new file mode 100644 index 00000000000000..216fc4ca55591f --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/memory/constructor-shared.tentative.any.js @@ -0,0 +1,54 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/assertions.js +// META: script=/wasm/jsapi/memory/assertions.js + +test(() => { + assert_throws_js(TypeError, () => new WebAssembly.Memory({ "initial": 10, "shared": true })); +}, "Shared memory without maximum"); + +test(t => { + const order = []; + + new WebAssembly.Memory({ + get maximum() { + order.push("maximum"); + return { + valueOf() { + order.push("maximum valueOf"); + return 1; + }, + }; + }, + + get initial() { + order.push("initial"); + return { + valueOf() { + order.push("initial valueOf"); + return 1; + }, + }; + }, + + get shared() { + order.push("shared"); + return { + valueOf: t.unreached_func("should not call shared valueOf"), + }; + }, + }); + + assert_array_equals(order, [ + "initial", + "initial valueOf", + "maximum", + "maximum valueOf", + "shared", + ]); +}, "Order of evaluation for descriptor (with shared)"); + +test(() => { + const argument = { "initial": 4, "maximum": 10, shared: true }; + const memory = new WebAssembly.Memory(argument); + assert_Memory(memory, { "size": 4, "shared": true }); +}, "Shared memory"); diff --git a/test/fixtures/wpt/wasm/jsapi/memory/constructor-types.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/memory/constructor-types.tentative.any.js new file mode 100644 index 00000000000000..d5378dbe82b00b --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/memory/constructor-types.tentative.any.js @@ -0,0 +1,20 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/assertions.js +// META: script=/wasm/jsapi/memory/assertions.js + +test(() => { + const argument = { initial: 5, minimum: 6 }; + assert_throws_js(TypeError, () => new WebAssembly.Memory(argument)); +}, "Initializing with both initial and minimum"); + +test(() => { + const argument = { minimum: 0 }; + const memory = new WebAssembly.Memory(argument); + assert_Memory(memory, { "size": 0 }); + }, "Zero minimum"); + +test(() => { + const argument = { minimum: 4 }; + const memory = new WebAssembly.Memory(argument); + assert_Memory(memory, { "size": 4 }); + }, "Non-zero minimum"); \ No newline at end of file diff --git a/test/fixtures/wpt/wasm/jsapi/memory/constructor.any.js b/test/fixtures/wpt/wasm/jsapi/memory/constructor.any.js new file mode 100644 index 00000000000000..0a0be11e370877 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/memory/constructor.any.js @@ -0,0 +1,139 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/assertions.js +// META: script=/wasm/jsapi/memory/assertions.js + +test(() => { + assert_function_name(WebAssembly.Memory, "Memory", "WebAssembly.Memory"); +}, "name"); + +test(() => { + assert_function_length(WebAssembly.Memory, 1, "WebAssembly.Memory"); +}, "length"); + +test(() => { + assert_throws_js(TypeError, () => new WebAssembly.Memory()); +}, "No arguments"); + +test(() => { + const argument = { "initial": 0 }; + assert_throws_js(TypeError, () => WebAssembly.Memory(argument)); +}, "Calling"); + +test(() => { + const invalidArguments = [ + undefined, + null, + false, + true, + "", + "test", + Symbol(), + 1, + NaN, + {}, + ]; + for (const invalidArgument of invalidArguments) { + assert_throws_js(TypeError, + () => new WebAssembly.Memory(invalidArgument), + `new Memory(${format_value(invalidArgument)})`); + } +}, "Invalid descriptor argument"); + +test(() => { + assert_throws_js(TypeError, () => new WebAssembly.Memory({ "initial": undefined })); +}, "Undefined initial value in descriptor"); + +const outOfRangeValues = [ + NaN, + Infinity, + -Infinity, + -1, + 0x100000000, + 0x1000000000, +]; + +for (const value of outOfRangeValues) { + test(() => { + assert_throws_js(TypeError, () => new WebAssembly.Memory({ "initial": value })); + }, `Out-of-range initial value in descriptor: ${format_value(value)}`); + + test(() => { + assert_throws_js(TypeError, () => new WebAssembly.Memory({ "initial": 0, "maximum": value })); + }, `Out-of-range maximum value in descriptor: ${format_value(value)}`); +} + +test(() => { + assert_throws_js(RangeError, () => new WebAssembly.Memory({ "initial": 10, "maximum": 9 })); +}, "Initial value exceeds maximum"); + +test(() => { + const proxy = new Proxy({}, { + has(o, x) { + assert_unreached(`Should not call [[HasProperty]] with ${x}`); + }, + get(o, x) { + // Due to the requirement not to supply both minimum and initial, we need to ignore one of them. + switch (x) { + case "shared": + return false; + case "initial": + case "maximum": + return 0; + default: + return undefined; + } + }, + }); + new WebAssembly.Memory(proxy); +}, "Proxy descriptor"); + +test(() => { + const order = []; + + new WebAssembly.Memory({ + get maximum() { + order.push("maximum"); + return { + valueOf() { + order.push("maximum valueOf"); + return 1; + }, + }; + }, + + get initial() { + order.push("initial"); + return { + valueOf() { + order.push("initial valueOf"); + return 1; + }, + }; + }, + }); + + assert_array_equals(order, [ + "initial", + "initial valueOf", + "maximum", + "maximum valueOf", + ]); +}, "Order of evaluation for descriptor"); + +test(() => { + const argument = { "initial": 0 }; + const memory = new WebAssembly.Memory(argument); + assert_Memory(memory, { "size": 0 }); +}, "Zero initial"); + +test(() => { + const argument = { "initial": 4 }; + const memory = new WebAssembly.Memory(argument); + assert_Memory(memory, { "size": 4 }); +}, "Non-zero initial"); + +test(() => { + const argument = { "initial": 0 }; + const memory = new WebAssembly.Memory(argument, {}); + assert_Memory(memory, { "size": 0 }); +}, "Stray argument"); diff --git a/test/fixtures/wpt/wasm/jsapi/memory/grow.any.js b/test/fixtures/wpt/wasm/jsapi/memory/grow.any.js new file mode 100644 index 00000000000000..c511129491f4de --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/memory/grow.any.js @@ -0,0 +1,189 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/memory/assertions.js + +test(() => { + const argument = { "initial": 0 }; + const memory = new WebAssembly.Memory(argument); + assert_throws_js(TypeError, () => memory.grow()); +}, "Missing arguments"); + +test(t => { + const thisValues = [ + undefined, + null, + true, + "", + Symbol(), + 1, + {}, + WebAssembly.Memory, + WebAssembly.Memory.prototype, + ]; + + const argument = { + valueOf: t.unreached_func("Should not touch the argument (valueOf)"), + toString: t.unreached_func("Should not touch the argument (toString)"), + }; + + const fn = WebAssembly.Memory.prototype.grow; + + for (const thisValue of thisValues) { + assert_throws_js(TypeError, () => fn.call(thisValue, argument), `this=${format_value(thisValue)}`); + } +}, "Branding"); + +test(() => { + const argument = { "initial": 0 }; + const memory = new WebAssembly.Memory(argument); + const oldMemory = memory.buffer; + assert_ArrayBuffer(oldMemory, { "size": 0 }, "Buffer before growing"); + + const result = memory.grow(2); + assert_equals(result, 0); + + const newMemory = memory.buffer; + assert_not_equals(oldMemory, newMemory); + assert_ArrayBuffer(oldMemory, { "detached": true }, "Old buffer after growing"); + assert_ArrayBuffer(newMemory, { "size": 2 }, "New buffer after growing"); +}, "Zero initial"); + +test(() => { + const argument = { "initial": { valueOf() { return 0 } } }; + const memory = new WebAssembly.Memory(argument); + const oldMemory = memory.buffer; + assert_ArrayBuffer(oldMemory, { "size": 0 }, "Buffer before growing"); + + const result = memory.grow({ valueOf() { return 2 } }); + assert_equals(result, 0); + + const newMemory = memory.buffer; + assert_not_equals(oldMemory, newMemory); + assert_ArrayBuffer(oldMemory, { "detached": true }, "Old buffer after growing"); + assert_ArrayBuffer(newMemory, { "size": 2 }, "New buffer after growing"); +}, "Zero initial with valueOf"); + +test(() => { + const argument = { "initial": 3 }; + const memory = new WebAssembly.Memory(argument); + const oldMemory = memory.buffer; + assert_ArrayBuffer(oldMemory, { "size": 3 }, "Buffer before growing"); + + const result = memory.grow(2); + assert_equals(result, 3); + + const newMemory = memory.buffer; + assert_not_equals(oldMemory, newMemory); + assert_ArrayBuffer(oldMemory, { "detached": true }, "Old buffer after growing"); + assert_ArrayBuffer(newMemory, { "size": 5 }, "New buffer after growing"); +}, "Non-zero initial"); + +test(() => { + const argument = { "initial": 0, "maximum": 2 }; + const memory = new WebAssembly.Memory(argument); + const oldMemory = memory.buffer; + assert_ArrayBuffer(oldMemory, { "size": 0 }, "Buffer before growing"); + + const result = memory.grow(2); + assert_equals(result, 0); + + const newMemory = memory.buffer; + assert_not_equals(oldMemory, newMemory); + assert_ArrayBuffer(oldMemory, { "detached": true }, "Old buffer after growing"); + assert_ArrayBuffer(newMemory, { "size": 2 }, "New buffer after growing"); +}, "Zero initial with respected maximum"); + +test(() => { + const argument = { "initial": 0, "maximum": 2 }; + const memory = new WebAssembly.Memory(argument); + const oldMemory = memory.buffer; + assert_ArrayBuffer(oldMemory, { "size": 0 }, "Buffer before growing"); + + const result = memory.grow(1); + assert_equals(result, 0); + + const newMemory = memory.buffer; + assert_not_equals(oldMemory, newMemory); + assert_ArrayBuffer(oldMemory, { "detached": true }, "Old buffer after growing once"); + assert_ArrayBuffer(newMemory, { "size": 1 }, "New buffer after growing once"); + + const result2 = memory.grow(1); + assert_equals(result2, 1); + + const newestMemory = memory.buffer; + assert_not_equals(newMemory, newestMemory); + assert_ArrayBuffer(oldMemory, { "detached": true }, "New buffer after growing twice"); + assert_ArrayBuffer(newMemory, { "detached": true }, "New buffer after growing twice"); + assert_ArrayBuffer(newestMemory, { "size": 2 }, "Newest buffer after growing twice"); +}, "Zero initial with respected maximum grown twice"); + +test(() => { + const argument = { "initial": 1, "maximum": 2 }; + const memory = new WebAssembly.Memory(argument); + const oldMemory = memory.buffer; + assert_ArrayBuffer(oldMemory, { "size": 1 }, "Buffer before growing"); + + assert_throws_js(RangeError, () => memory.grow(2)); + assert_equals(memory.buffer, oldMemory); + assert_ArrayBuffer(memory.buffer, { "size": 1 }, "Buffer before trying to grow"); +}, "Zero initial growing too much"); + +const outOfRangeValues = [ + undefined, + NaN, + Infinity, + -Infinity, + -1, + 0x100000000, + 0x1000000000, + "0x100000000", + { valueOf() { return 0x100000000; } }, +]; + +for (const value of outOfRangeValues) { + test(() => { + const argument = { "initial": 0 }; + const memory = new WebAssembly.Memory(argument); + assert_throws_js(TypeError, () => memory.grow(value)); + }, `Out-of-range argument: ${format_value(value)}`); +} + +test(() => { + const argument = { "initial": 0 }; + const memory = new WebAssembly.Memory(argument); + const oldMemory = memory.buffer; + assert_ArrayBuffer(oldMemory, { "size": 0 }, "Buffer before growing"); + + const result = memory.grow(2, {}); + assert_equals(result, 0); + + const newMemory = memory.buffer; + assert_not_equals(oldMemory, newMemory); + assert_ArrayBuffer(oldMemory, { "detached": true }, "Old buffer after growing"); + assert_ArrayBuffer(newMemory, { "size": 2 }, "New buffer after growing"); +}, "Stray argument"); + +test(() => { + const argument = { "initial": 1, "maximum": 2, "shared": true }; + const memory = new WebAssembly.Memory(argument); + const oldMemory = memory.buffer; + assert_ArrayBuffer(oldMemory, { "size": 1, "shared": true }, "Buffer before growing"); + + const result = memory.grow(1); + assert_equals(result, 1); + + const newMemory = memory.buffer; + assert_not_equals(oldMemory, newMemory); + assert_ArrayBuffer(oldMemory, { "size": 1, "shared": true }, "Old buffer after growing"); + assert_ArrayBuffer(newMemory, { "size": 2, "shared": true }, "New buffer after growing"); + + // The old and new buffers must have the same value for the + // [[ArrayBufferData]] internal slot. + const oldArray = new Uint8Array(oldMemory); + const newArray = new Uint8Array(newMemory); + assert_equals(oldArray[0], 0, "old first element"); + assert_equals(newArray[0], 0, "new first element"); + oldArray[0] = 1; + assert_equals(oldArray[0], 1, "old first element"); + assert_equals(newArray[0], 1, "new first element"); + +}, "Growing shared memory does not detach old buffer"); diff --git a/test/fixtures/wpt/wasm/jsapi/memory/toString.any.js b/test/fixtures/wpt/wasm/jsapi/memory/toString.any.js new file mode 100644 index 00000000000000..f4059f76577227 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/memory/toString.any.js @@ -0,0 +1,17 @@ +// META: global=window,dedicatedworker,jsshell + +test(() => { + const argument = { "initial": 0 }; + const memory = new WebAssembly.Memory(argument); + assert_class_string(memory, "WebAssembly.Memory"); +}, "Object.prototype.toString on an Memory"); + +test(() => { + assert_own_property(WebAssembly.Memory.prototype, Symbol.toStringTag); + + const propDesc = Object.getOwnPropertyDescriptor(WebAssembly.Memory.prototype, Symbol.toStringTag); + assert_equals(propDesc.value, "WebAssembly.Memory", "value"); + assert_equals(propDesc.configurable, true, "configurable"); + assert_equals(propDesc.enumerable, false, "enumerable"); + assert_equals(propDesc.writable, false, "writable"); +}, "@@toStringTag exists on the prototype with the appropriate descriptor"); diff --git a/test/fixtures/wpt/wasm/jsapi/memory/type.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/memory/type.tentative.any.js new file mode 100644 index 00000000000000..a96a3227adca7f --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/memory/type.tentative.any.js @@ -0,0 +1,37 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/assertions.js + +function assert_type(argument) { + const memory = new WebAssembly.Memory(argument); + const memorytype = memory.type() + + assert_equals(memorytype.minimum, argument.minimum); + assert_equals(memorytype.maximum, argument.maximum); + if (argument.shared !== undefined) { + assert_equals(memorytype.shared, argument.shared); + } +} + +test(() => { + assert_type({ "minimum": 0 }); +}, "Zero initial, no maximum"); + +test(() => { + assert_type({ "minimum": 5 }); +}, "Non-zero initial, no maximum"); + +test(() => { + assert_type({ "minimum": 0, "maximum": 0 }); +}, "Zero maximum"); + +test(() => { + assert_type({ "minimum": 0, "maximum": 5 }); +}, "None-zero maximum"); + +test(() => { + assert_type({ "minimum": 0, "maximum": 10, "shared": false}); +}, "non-shared memory"); + +test(() => { + assert_type({ "minimum": 0, "maximum": 10, "shared": true}); +}, "shared memory"); \ No newline at end of file diff --git a/test/fixtures/wpt/wasm/jsapi/module/constructor.any.js b/test/fixtures/wpt/wasm/jsapi/module/constructor.any.js new file mode 100644 index 00000000000000..9978f7e6ac8f2b --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/module/constructor.any.js @@ -0,0 +1,69 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/wasm-module-builder.js +// META: script=/wasm/jsapi/assertions.js + +let emptyModuleBinary; +setup(() => { + emptyModuleBinary = new WasmModuleBuilder().toBuffer(); +}); + +test(() => { + assert_function_name(WebAssembly.Module, "Module", "WebAssembly.Module"); +}, "name"); + +test(() => { + assert_function_length(WebAssembly.Module, 1, "WebAssembly.Module"); +}, "length"); + +test(() => { + assert_throws_js(TypeError, () => new WebAssembly.Module()); +}, "No arguments"); + +test(() => { + assert_throws_js(TypeError, () => WebAssembly.Module(emptyModuleBinary)); +}, "Calling"); + +test(() => { + const invalidArguments = [ + undefined, + null, + true, + "test", + Symbol(), + 7, + NaN, + {}, + ArrayBuffer, + ArrayBuffer.prototype, + Array.from(emptyModuleBinary), + ]; + for (const argument of invalidArguments) { + assert_throws_js(TypeError, () => new WebAssembly.Module(argument), + `new Module(${format_value(argument)})`); + } +}, "Invalid arguments"); + +test(() => { + const buffer = new Uint8Array(); + assert_throws_js(WebAssembly.CompileError, () => new WebAssembly.Module(buffer)); +}, "Empty buffer"); + +test(() => { + const buffer = new Uint8Array(Array.from(emptyModuleBinary).concat([0, 0])); + assert_throws_js(WebAssembly.CompileError, () => new WebAssembly.Module(buffer)); +}, "Invalid code"); + +test(() => { + const module = new WebAssembly.Module(emptyModuleBinary); + assert_equals(Object.getPrototypeOf(module), WebAssembly.Module.prototype); +}, "Prototype"); + +test(() => { + const module = new WebAssembly.Module(emptyModuleBinary); + assert_true(Object.isExtensible(module)); +}, "Extensibility"); + +test(() => { + const module = new WebAssembly.Module(emptyModuleBinary, {}); + assert_equals(Object.getPrototypeOf(module), WebAssembly.Module.prototype); +}, "Stray argument"); diff --git a/test/fixtures/wpt/wasm/jsapi/module/customSections.any.js b/test/fixtures/wpt/wasm/jsapi/module/customSections.any.js new file mode 100644 index 00000000000000..4029877e92c7b8 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/module/customSections.any.js @@ -0,0 +1,140 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/wasm-module-builder.js + +function assert_ArrayBuffer(buffer, expected) { + assert_equals(Object.getPrototypeOf(buffer), ArrayBuffer.prototype, "Prototype"); + assert_true(Object.isExtensible(buffer), "isExtensible"); + assert_array_equals(new Uint8Array(buffer), expected); +} + +function assert_sections(sections, expected) { + assert_true(Array.isArray(sections), "Should be array"); + assert_equals(Object.getPrototypeOf(sections), Array.prototype, "Prototype"); + assert_true(Object.isExtensible(sections), "isExtensible"); + + assert_equals(sections.length, expected.length); + for (let i = 0; i < expected.length; ++i) { + assert_ArrayBuffer(sections[i], expected[i]); + } +} + +let emptyModuleBinary; +setup(() => { + emptyModuleBinary = new WasmModuleBuilder().toBuffer(); +}); + +test(() => { + assert_throws_js(TypeError, () => WebAssembly.Module.customSections()); + const module = new WebAssembly.Module(emptyModuleBinary); + assert_throws_js(TypeError, () => WebAssembly.Module.customSections(module)); +}, "Missing arguments"); + +test(() => { + const invalidArguments = [ + undefined, + null, + true, + "", + Symbol(), + 1, + {}, + WebAssembly.Module, + WebAssembly.Module.prototype, + ]; + for (const argument of invalidArguments) { + assert_throws_js(TypeError, () => WebAssembly.Module.customSections(argument, ""), + `customSections(${format_value(argument)})`); + } +}, "Non-Module arguments"); + +test(() => { + const module = new WebAssembly.Module(emptyModuleBinary); + const fn = WebAssembly.Module.customSections; + const thisValues = [ + undefined, + null, + true, + "", + Symbol(), + 1, + {}, + WebAssembly.Module, + WebAssembly.Module.prototype, + ]; + for (const thisValue of thisValues) { + assert_sections(fn.call(thisValue, module, ""), []); + } +}, "Branding"); + +test(() => { + const module = new WebAssembly.Module(emptyModuleBinary); + assert_sections(WebAssembly.Module.customSections(module, ""), []); +}, "Empty module"); + +test(() => { + const module = new WebAssembly.Module(emptyModuleBinary); + assert_not_equals(WebAssembly.Module.customSections(module, ""), + WebAssembly.Module.customSections(module, "")); +}, "Empty module: array caching"); + +test(() => { + const bytes1 = [87, 101, 98, 65, 115, 115, 101, 109, 98, 108, 121]; + const bytes2 = [74, 83, 65, 80, 73]; + + const builder = new WasmModuleBuilder(); + builder.addCustomSection("name", bytes1); + builder.addCustomSection("name", bytes2); + builder.addCustomSection("foo", bytes1); + const buffer = builder.toBuffer() + const module = new WebAssembly.Module(buffer); + + assert_sections(WebAssembly.Module.customSections(module, "name"), [ + bytes1, + bytes2, + ]) + + assert_sections(WebAssembly.Module.customSections(module, "foo"), [ + bytes1, + ]) + + assert_sections(WebAssembly.Module.customSections(module, ""), []) + assert_sections(WebAssembly.Module.customSections(module, "\0"), []) + assert_sections(WebAssembly.Module.customSections(module, "name\0"), []) + assert_sections(WebAssembly.Module.customSections(module, "foo\0"), []) +}, "Custom sections"); + +test(() => { + const bytes = [87, 101, 98, 65, 115, 115, 101, 109, 98, 108, 121]; + const name = "yee\uD801\uDC37eey" + + const builder = new WasmModuleBuilder(); + builder.addCustomSection(name, bytes); + const buffer = builder.toBuffer(); + const module = new WebAssembly.Module(buffer); + + assert_sections(WebAssembly.Module.customSections(module, name), [ + bytes, + ]); + assert_sections(WebAssembly.Module.customSections(module, "yee\uFFFDeey"), []); + assert_sections(WebAssembly.Module.customSections(module, "yee\uFFFD\uFFFDeey"), []); +}, "Custom sections with surrogate pairs"); + +test(() => { + const bytes = [87, 101, 98, 65, 115, 115, 101, 109, 98, 108, 121]; + + const builder = new WasmModuleBuilder(); + builder.addCustomSection("na\uFFFDme", bytes); + const buffer = builder.toBuffer(); + const module = new WebAssembly.Module(buffer); + + assert_sections(WebAssembly.Module.customSections(module, "name"), []); + assert_sections(WebAssembly.Module.customSections(module, "na\uFFFDme"), [ + bytes, + ]); + assert_sections(WebAssembly.Module.customSections(module, "na\uDC01me"), []); +}, "Custom sections with U+FFFD"); + +test(() => { + const module = new WebAssembly.Module(emptyModuleBinary); + assert_sections(WebAssembly.Module.customSections(module, "", {}), []); +}, "Stray argument"); diff --git a/test/fixtures/wpt/wasm/jsapi/module/exports.any.js b/test/fixtures/wpt/wasm/jsapi/module/exports.any.js new file mode 100644 index 00000000000000..40a3935a4a23ba --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/module/exports.any.js @@ -0,0 +1,185 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/wasm-module-builder.js + +let emptyModuleBinary; +setup(() => { + emptyModuleBinary = new WasmModuleBuilder().toBuffer(); +}); + +function assert_ModuleExportDescriptor(export_, expected) { + assert_equals(Object.getPrototypeOf(export_), Object.prototype, "Prototype"); + assert_true(Object.isExtensible(export_), "isExtensible"); + + const name = Object.getOwnPropertyDescriptor(export_, "name"); + assert_true(name.writable, "name: writable"); + assert_true(name.enumerable, "name: enumerable"); + assert_true(name.configurable, "name: configurable"); + assert_equals(name.value, expected.name); + + const kind = Object.getOwnPropertyDescriptor(export_, "kind"); + assert_true(kind.writable, "kind: writable"); + assert_true(kind.enumerable, "kind: enumerable"); + assert_true(kind.configurable, "kind: configurable"); + assert_equals(kind.value, expected.kind); +} + +function assert_exports(exports, expected) { + assert_true(Array.isArray(exports), "Should be array"); + assert_equals(Object.getPrototypeOf(exports), Array.prototype, "Prototype"); + assert_true(Object.isExtensible(exports), "isExtensible"); + + assert_equals(exports.length, expected.length); + for (let i = 0; i < expected.length; ++i) { + assert_ModuleExportDescriptor(exports[i], expected[i]); + } +} + +test(() => { + assert_throws_js(TypeError, () => WebAssembly.Module.exports()); +}, "Missing arguments"); + +test(() => { + const invalidArguments = [ + undefined, + null, + true, + "", + Symbol(), + 1, + {}, + WebAssembly.Module, + WebAssembly.Module.prototype, + ]; + for (const argument of invalidArguments) { + assert_throws_js(TypeError, () => WebAssembly.Module.exports(argument), + `exports(${format_value(argument)})`); + } +}, "Non-Module arguments"); + +test(() => { + const module = new WebAssembly.Module(emptyModuleBinary); + const fn = WebAssembly.Module.exports; + const thisValues = [ + undefined, + null, + true, + "", + Symbol(), + 1, + {}, + WebAssembly.Module, + WebAssembly.Module.prototype, + ]; + for (const thisValue of thisValues) { + assert_array_equals(fn.call(thisValue, module), []); + } +}, "Branding"); + +test(() => { + const module = new WebAssembly.Module(emptyModuleBinary); + const exports = WebAssembly.Module.exports(module); + assert_true(Array.isArray(exports)); +}, "Return type"); + +test(() => { + const module = new WebAssembly.Module(emptyModuleBinary); + const exports = WebAssembly.Module.exports(module); + assert_exports(exports, []); +}, "Empty module"); + +test(() => { + const module = new WebAssembly.Module(emptyModuleBinary); + assert_not_equals(WebAssembly.Module.exports(module), WebAssembly.Module.exports(module)); +}, "Empty module: array caching"); + +test(() => { + const builder = new WasmModuleBuilder(); + + builder + .addFunction("fn", kSig_v_v) + .addBody([]) + .exportFunc(); + builder + .addFunction("fn2", kSig_v_v) + .addBody([]) + .exportFunc(); + + builder.setTableBounds(1); + builder.addExportOfKind("table", kExternalTable, 0); + + builder.addGlobal(kWasmI32, true) + .exportAs("global") + .init = 7; + builder.addGlobal(kWasmF64, true) + .exportAs("global2") + .init = 1.2; + + builder.addMemory(0, 256, true); + + const buffer = builder.toBuffer() + const module = new WebAssembly.Module(buffer); + const exports = WebAssembly.Module.exports(module); + const expected = [ + { "kind": "function", "name": "fn" }, + { "kind": "function", "name": "fn2" }, + { "kind": "table", "name": "table" }, + { "kind": "global", "name": "global" }, + { "kind": "global", "name": "global2" }, + { "kind": "memory", "name": "memory" }, + ]; + assert_exports(exports, expected); +}, "exports"); + +test(() => { + const builder = new WasmModuleBuilder(); + + builder + .addFunction("", kSig_v_v) + .addBody([]) + .exportFunc(); + + const buffer = builder.toBuffer() + const module = new WebAssembly.Module(buffer); + const exports = WebAssembly.Module.exports(module); + const expected = [ + { "kind": "function", "name": "" }, + ]; + assert_exports(exports, expected); +}, "exports with empty name: function"); + +test(() => { + const builder = new WasmModuleBuilder(); + + builder.setTableBounds(1); + builder.addExportOfKind("", kExternalTable, 0); + + const buffer = builder.toBuffer() + const module = new WebAssembly.Module(buffer); + const exports = WebAssembly.Module.exports(module); + const expected = [ + { "kind": "table", "name": "" }, + ]; + assert_exports(exports, expected); +}, "exports with empty name: table"); + +test(() => { + const builder = new WasmModuleBuilder(); + + builder.addGlobal(kWasmI32, true) + .exportAs("") + .init = 7; + + const buffer = builder.toBuffer() + const module = new WebAssembly.Module(buffer); + const exports = WebAssembly.Module.exports(module); + const expected = [ + { "kind": "global", "name": "" }, + ]; + assert_exports(exports, expected); +}, "exports with empty name: global"); + +test(() => { + const module = new WebAssembly.Module(emptyModuleBinary); + const exports = WebAssembly.Module.exports(module, {}); + assert_exports(exports, []); +}, "Stray argument"); diff --git a/test/fixtures/wpt/wasm/jsapi/module/imports.any.js b/test/fixtures/wpt/wasm/jsapi/module/imports.any.js new file mode 100644 index 00000000000000..ec550ce6c41af1 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/module/imports.any.js @@ -0,0 +1,185 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/wasm-module-builder.js + +function assert_ModuleImportDescriptor(import_, expected) { + assert_equals(Object.getPrototypeOf(import_), Object.prototype, "Prototype"); + assert_true(Object.isExtensible(import_), "isExtensible"); + + const module = Object.getOwnPropertyDescriptor(import_, "module"); + assert_true(module.writable, "module: writable"); + assert_true(module.enumerable, "module: enumerable"); + assert_true(module.configurable, "module: configurable"); + assert_equals(module.value, expected.module); + + const name = Object.getOwnPropertyDescriptor(import_, "name"); + assert_true(name.writable, "name: writable"); + assert_true(name.enumerable, "name: enumerable"); + assert_true(name.configurable, "name: configurable"); + assert_equals(name.value, expected.name); + + const kind = Object.getOwnPropertyDescriptor(import_, "kind"); + assert_true(kind.writable, "kind: writable"); + assert_true(kind.enumerable, "kind: enumerable"); + assert_true(kind.configurable, "kind: configurable"); + assert_equals(kind.value, expected.kind); +} + +function assert_imports(imports, expected) { + assert_true(Array.isArray(imports), "Should be array"); + assert_equals(Object.getPrototypeOf(imports), Array.prototype, "Prototype"); + assert_true(Object.isExtensible(imports), "isExtensible"); + + assert_equals(imports.length, expected.length); + for (let i = 0; i < expected.length; ++i) { + assert_ModuleImportDescriptor(imports[i], expected[i]); + } +} + +let emptyModuleBinary; +setup(() => { + emptyModuleBinary = new WasmModuleBuilder().toBuffer(); +}); + +test(() => { + assert_throws_js(TypeError, () => WebAssembly.Module.imports()); +}, "Missing arguments"); + +test(() => { + const invalidArguments = [ + undefined, + null, + true, + "", + Symbol(), + 1, + {}, + WebAssembly.Module, + WebAssembly.Module.prototype, + ]; + for (const argument of invalidArguments) { + assert_throws_js(TypeError, () => WebAssembly.Module.imports(argument), + `imports(${format_value(argument)})`); + } +}, "Non-Module arguments"); + +test(() => { + const module = new WebAssembly.Module(emptyModuleBinary); + const fn = WebAssembly.Module.imports; + const thisValues = [ + undefined, + null, + true, + "", + Symbol(), + 1, + {}, + WebAssembly.Module, + WebAssembly.Module.prototype, + ]; + for (const thisValue of thisValues) { + assert_array_equals(fn.call(thisValue, module), []); + } +}, "Branding"); + +test(() => { + const module = new WebAssembly.Module(emptyModuleBinary); + const imports = WebAssembly.Module.imports(module); + assert_true(Array.isArray(imports)); +}, "Return type"); + +test(() => { + const module = new WebAssembly.Module(emptyModuleBinary); + const imports = WebAssembly.Module.imports(module); + assert_imports(imports, []); +}, "Empty module"); + +test(() => { + const module = new WebAssembly.Module(emptyModuleBinary); + assert_not_equals(WebAssembly.Module.imports(module), WebAssembly.Module.imports(module)); +}, "Empty module: array caching"); + +test(() => { + const builder = new WasmModuleBuilder(); + + builder.addImport("module", "fn", kSig_v_v); + builder.addImportedGlobal("module", "global", kWasmI32); + builder.addImportedMemory("module", "memory", 0, 128); + builder.addImportedTable("module", "table", 0, 128); + + const buffer = builder.toBuffer() + const module = new WebAssembly.Module(buffer); + const imports = WebAssembly.Module.imports(module); + const expected = [ + { "module": "module", "kind": "function", "name": "fn" }, + { "module": "module", "kind": "global", "name": "global" }, + { "module": "module", "kind": "memory", "name": "memory" }, + { "module": "module", "kind": "table", "name": "table" }, + ]; + assert_imports(imports, expected); +}, "imports"); + +test(() => { + const builder = new WasmModuleBuilder(); + + builder.addImport("", "fn", kSig_v_v); + builder.addImportedGlobal("", "global", kWasmI32); + builder.addImportedMemory("", "memory", 0, 128); + builder.addImportedTable("", "table", 0, 128); + + const buffer = builder.toBuffer() + const module = new WebAssembly.Module(buffer); + const imports = WebAssembly.Module.imports(module); + const expected = [ + { "module": "", "kind": "function", "name": "fn" }, + { "module": "", "kind": "global", "name": "global" }, + { "module": "", "kind": "memory", "name": "memory" }, + { "module": "", "kind": "table", "name": "table" }, + ]; + assert_imports(imports, expected); +}, "imports with empty module name"); + +test(() => { + const builder = new WasmModuleBuilder(); + + builder.addImport("a", "", kSig_v_v); + builder.addImportedGlobal("b", "", kWasmI32); + builder.addImportedMemory("c", "", 0, 128); + builder.addImportedTable("d", "", 0, 128); + + const buffer = builder.toBuffer() + const module = new WebAssembly.Module(buffer); + const imports = WebAssembly.Module.imports(module); + const expected = [ + { "module": "a", "kind": "function", "name": "" }, + { "module": "b", "kind": "global", "name": "" }, + { "module": "c", "kind": "memory", "name": "" }, + { "module": "d", "kind": "table", "name": "" }, + ]; + assert_imports(imports, expected); +}, "imports with empty names"); + +test(() => { + const builder = new WasmModuleBuilder(); + + builder.addImport("", "", kSig_v_v); + builder.addImportedGlobal("", "", kWasmI32); + builder.addImportedMemory("", "", 0, 128); + builder.addImportedTable("", "", 0, 128); + + const buffer = builder.toBuffer() + const module = new WebAssembly.Module(buffer); + const imports = WebAssembly.Module.imports(module); + const expected = [ + { "module": "", "kind": "function", "name": "" }, + { "module": "", "kind": "global", "name": "" }, + { "module": "", "kind": "memory", "name": "" }, + { "module": "", "kind": "table", "name": "" }, + ]; + assert_imports(imports, expected); +}, "imports with empty module names and names"); + +test(() => { + const module = new WebAssembly.Module(emptyModuleBinary); + const imports = WebAssembly.Module.imports(module, {}); + assert_imports(imports, []); +}, "Stray argument"); diff --git a/test/fixtures/wpt/wasm/jsapi/module/toString.any.js b/test/fixtures/wpt/wasm/jsapi/module/toString.any.js new file mode 100644 index 00000000000000..1c20cd6108c84e --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/module/toString.any.js @@ -0,0 +1,18 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/wasm-module-builder.js + +test(() => { + const emptyModuleBinary = new WasmModuleBuilder().toBuffer(); + const module = new WebAssembly.Module(emptyModuleBinary); + assert_class_string(module, "WebAssembly.Module"); +}, "Object.prototype.toString on an Module"); + +test(() => { + assert_own_property(WebAssembly.Module.prototype, Symbol.toStringTag); + + const propDesc = Object.getOwnPropertyDescriptor(WebAssembly.Module.prototype, Symbol.toStringTag); + assert_equals(propDesc.value, "WebAssembly.Module", "value"); + assert_equals(propDesc.configurable, true, "configurable"); + assert_equals(propDesc.enumerable, false, "enumerable"); + assert_equals(propDesc.writable, false, "writable"); +}, "@@toStringTag exists on the prototype with the appropriate descriptor"); diff --git a/test/fixtures/wpt/wasm/jsapi/proto-from-ctor-realm.html b/test/fixtures/wpt/wasm/jsapi/proto-from-ctor-realm.html new file mode 100644 index 00000000000000..45405b52900bf9 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/proto-from-ctor-realm.html @@ -0,0 +1,95 @@ + + +WebAssembly JS API: Default [[Prototype]] value is from NewTarget's Realm + + + + + + + + + + + diff --git a/test/fixtures/wpt/wasm/jsapi/prototypes.any.js b/test/fixtures/wpt/wasm/jsapi/prototypes.any.js new file mode 100644 index 00000000000000..714f4f8430e5eb --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/prototypes.any.js @@ -0,0 +1,43 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/assertions.js +// META: script=/wasm/jsapi/wasm-module-builder.js + +let emptyModuleBinary; +setup(() => { + emptyModuleBinary = new WasmModuleBuilder().toBuffer(); +}); + +test(() => { + class _Module extends WebAssembly.Module {} + let module = new _Module(emptyModuleBinary); + assert_true(module instanceof _Module, "_Module instanceof _Module"); + assert_true(module instanceof WebAssembly.Module, "_Module instanceof WebAssembly.Module"); +}, "_Module"); + +test(() => { + class _Instance extends WebAssembly.Instance {} + let instance = new _Instance(new WebAssembly.Module(emptyModuleBinary)); + assert_true(instance instanceof _Instance, "_Instance instanceof _Instance"); + assert_true(instance instanceof WebAssembly.Instance, "_Instance instanceof WebAssembly.Instance"); +}, "_Instance"); + +test(() => { + class _Memory extends WebAssembly.Memory {} + let memory = new _Memory({initial: 0, maximum: 1}); + assert_true(memory instanceof _Memory, "_Memory instanceof _Memory"); + assert_true(memory instanceof WebAssembly.Memory, "_Memory instanceof WebAssembly.Memory"); +}, "_Memory"); + +test(() => { + class _Table extends WebAssembly.Table {} + let table = new _Table({initial: 0, element: "anyfunc"}); + assert_true(table instanceof _Table, "_Table instanceof _Table"); + assert_true(table instanceof WebAssembly.Table, "_Table instanceof WebAssembly.Table"); +}, "_Table"); + +test(() => { + class _Global extends WebAssembly.Global {} + let global = new _Global({value: "i32", mutable: false}, 0); + assert_true(global instanceof _Global, "_Global instanceof _Global"); + assert_true(global instanceof WebAssembly.Global, "_Global instanceof WebAssembly.Global"); +}, "_Global"); diff --git a/test/fixtures/wpt/wasm/jsapi/table/assertions.js b/test/fixtures/wpt/wasm/jsapi/table/assertions.js new file mode 100644 index 00000000000000..19cc5c3b92d6fa --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/table/assertions.js @@ -0,0 +1,24 @@ +function assert_equal_to_array(table, expected, message) { + assert_equals(table.length, expected.length, `${message}: length`); + // The argument check in get() happens before the range check, and negative numbers + // are illegal, hence will throw TypeError per spec. + assert_throws_js(TypeError, () => table.get(-1), `${message}: table.get(-1)`); + for (let i = 0; i < expected.length; ++i) { + assert_equals(table.get(i), expected[i], `${message}: table.get(${i} of ${expected.length})`); + } + assert_throws_js(RangeError, () => table.get(expected.length), + `${message}: table.get(${expected.length} of ${expected.length})`); + assert_throws_js(RangeError, () => table.get(expected.length + 1), + `${message}: table.get(${expected.length + 1} of ${expected.length})`); +} + +function assert_Table(actual, expected) { + assert_equals(Object.getPrototypeOf(actual), WebAssembly.Table.prototype, + "prototype"); + assert_true(Object.isExtensible(actual), "extensible"); + + assert_equals(actual.length, expected.length, "length"); + for (let i = 0; i < expected.length; ++i) { + assert_equals(actual.get(i), null, `actual.get(${i})`); + } +} diff --git a/test/fixtures/wpt/wasm/jsapi/table/constructor-types.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/table/constructor-types.tentative.any.js new file mode 100644 index 00000000000000..99ca41b55a9152 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/table/constructor-types.tentative.any.js @@ -0,0 +1,20 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/assertions.js +// META: script=/wasm/jsapi/table/assertions.js + +test(() => { + const argument = { "element": "anyfunc", "initial": 0, "minimum": 0 }; + assert_throws_js(TypeError, () => new WebAssembly.Table(argument)); +}, "Initializing with both initial and minimum"); + +test(() => { + const argument = { "element": "anyfunc", "minimum": 0 }; + const table = new WebAssembly.Table(argument); + assert_Table(table, { "length": 0 }); +}, "Zero minimum"); + +test(() => { + const argument = { "element": "anyfunc", "minimum": 5 }; + const table = new WebAssembly.Table(argument); + assert_Table(table, { "length": 5 }); +}, "Non-zero minimum"); \ No newline at end of file diff --git a/test/fixtures/wpt/wasm/jsapi/table/constructor.any.js b/test/fixtures/wpt/wasm/jsapi/table/constructor.any.js new file mode 100644 index 00000000000000..6d38d04e4f5050 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/table/constructor.any.js @@ -0,0 +1,208 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/wasm-module-builder.js +// META: script=/wasm/jsapi/assertions.js +// META: script=/wasm/jsapi/table/assertions.js + +test(() => { + assert_function_name(WebAssembly.Table, "Table", "WebAssembly.Table"); +}, "name"); + +test(() => { + assert_function_length(WebAssembly.Table, 1, "WebAssembly.Table"); +}, "length"); + +test(() => { + assert_throws_js(TypeError, () => new WebAssembly.Table()); +}, "No arguments"); + +test(() => { + const argument = { "element": "anyfunc", "initial": 0 }; + assert_throws_js(TypeError, () => WebAssembly.Table(argument)); +}, "Calling"); + +test(() => { + assert_throws_js(TypeError, () => new WebAssembly.Table({})); +}, "Empty descriptor"); + +test(() => { + const invalidArguments = [ + undefined, + null, + false, + true, + "", + "test", + Symbol(), + 1, + NaN, + {}, + ]; + for (const invalidArgument of invalidArguments) { + assert_throws_js(TypeError, + () => new WebAssembly.Table(invalidArgument), + `new Table(${format_value(invalidArgument)})`); + } +}, "Invalid descriptor argument"); + +test(() => { + assert_throws_js(TypeError, () => new WebAssembly.Table({ "element": "anyfunc", "initial": undefined })); +}, "Undefined initial value in descriptor"); + +test(() => { + assert_throws_js(TypeError, () => new WebAssembly.Table({ "element": undefined, "initial": 0 })); +}, "Undefined element value in descriptor"); + +const outOfRangeValues = [ + NaN, + Infinity, + -Infinity, + -1, + 0x100000000, + 0x1000000000, +]; + +for (const value of outOfRangeValues) { + test(() => { + assert_throws_js(TypeError, () => new WebAssembly.Table({ "element": "anyfunc", "initial": value })); + }, `Out-of-range initial value in descriptor: ${format_value(value)}`); + + test(() => { + assert_throws_js(TypeError, () => new WebAssembly.Table({ "element": "anyfunc", "initial": 0, "maximum": value })); + }, `Out-of-range maximum value in descriptor: ${format_value(value)}`); +} + +test(() => { + assert_throws_js(RangeError, () => new WebAssembly.Table({ "element": "anyfunc", "initial": 10, "maximum": 9 })); +}, "Initial value exceeds maximum"); + +test(() => { + const argument = { "element": "anyfunc", "initial": 0 }; + const table = new WebAssembly.Table(argument); + assert_Table(table, { "length": 0 }); +}, "Basic (zero)"); + +test(() => { + const argument = { "element": "anyfunc", "initial": 5 }; + const table = new WebAssembly.Table(argument); + assert_Table(table, { "length": 5 }); +}, "Basic (non-zero)"); + +test(() => { + const argument = { "element": "anyfunc", "initial": 0 }; + const table = new WebAssembly.Table(argument, null, {}); + assert_Table(table, { "length": 0 }); +}, "Stray argument"); + +test(() => { + const proxy = new Proxy({}, { + has(o, x) { + assert_unreached(`Should not call [[HasProperty]] with ${x}`); + }, + get(o, x) { + switch (x) { + case "element": + return "anyfunc"; + case "initial": + case "maximum": + return 0; + default: + return undefined; + } + }, + }); + const table = new WebAssembly.Table(proxy); + assert_Table(table, { "length": 0 }); +}, "Proxy descriptor"); + +test(() => { + const table = new WebAssembly.Table({ + "element": { + toString() { return "anyfunc"; }, + }, + "initial": 1, + }); + assert_Table(table, { "length": 1 }); +}, "Type conversion for descriptor.element"); + +test(() => { + const order = []; + + new WebAssembly.Table({ + get maximum() { + order.push("maximum"); + return { + valueOf() { + order.push("maximum valueOf"); + return 1; + }, + }; + }, + + get initial() { + order.push("initial"); + return { + valueOf() { + order.push("initial valueOf"); + return 1; + }, + }; + }, + + get element() { + order.push("element"); + return { + toString() { + order.push("element toString"); + return "anyfunc"; + }, + }; + }, + }); + + assert_array_equals(order, [ + "element", + "element toString", + "initial", + "initial valueOf", + "maximum", + "maximum valueOf", + ]); +}, "Order of evaluation for descriptor"); + +test(() => { + const testObject = {}; + const argument = { "element": "externref", "initial": 3 }; + const table = new WebAssembly.Table(argument, testObject); + assert_equals(table.length, 3); + assert_equals(table.get(0), testObject); + assert_equals(table.get(1), testObject); + assert_equals(table.get(2), testObject); +}, "initialize externref table with default value"); + +test(() => { + const argument = { "element": "i32", "initial": 3 }; + assert_throws_js(TypeError, () => new WebAssembly.Table(argument)); +}, "initialize table with a wrong element value"); + +test(() => { + const builder = new WasmModuleBuilder(); + builder + .addFunction("fn", kSig_v_v) + .addBody([]) + .exportFunc(); + const bin = builder.toBuffer(); + const fn = new WebAssembly.Instance(new WebAssembly.Module(bin)).exports.fn; + const argument = { "element": "anyfunc", "initial": 3 }; + const table = new WebAssembly.Table(argument, fn); + assert_equals(table.length, 3); + assert_equals(table.get(0), fn); + assert_equals(table.get(1), fn); + assert_equals(table.get(2), fn); +}, "initialize anyfunc table with default value"); + +test(() => { + const argument = { "element": "anyfunc", "initial": 3 }; + assert_throws_js(TypeError, () => new WebAssembly.Table(argument, {})); + assert_throws_js(TypeError, () => new WebAssembly.Table(argument, "cannot be used as a wasm function")); + assert_throws_js(TypeError, () => new WebAssembly.Table(argument, 37)); +}, "initialize anyfunc table with a bad default value"); diff --git a/test/fixtures/wpt/wasm/jsapi/table/get-set.any.js b/test/fixtures/wpt/wasm/jsapi/table/get-set.any.js new file mode 100644 index 00000000000000..9301057a533ed4 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/table/get-set.any.js @@ -0,0 +1,263 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/wasm-module-builder.js +// META: script=assertions.js + +let functions = {}; +setup(() => { + const builder = new WasmModuleBuilder(); + + builder + .addFunction("fn", kSig_v_d) + .addBody([]) + .exportFunc(); + builder + .addFunction("fn2", kSig_v_v) + .addBody([]) + .exportFunc(); + + const buffer = builder.toBuffer() + const module = new WebAssembly.Module(buffer); + const instance = new WebAssembly.Instance(module, {}); + functions = instance.exports; +}); + +test(() => { + const argument = { "element": "anyfunc", "initial": 5 }; + const table = new WebAssembly.Table(argument); + assert_throws_js(TypeError, () => table.get()); +}, "Missing arguments: get"); + +test(t => { + const thisValues = [ + undefined, + null, + true, + "", + Symbol(), + 1, + {}, + WebAssembly.Table, + WebAssembly.Table.prototype, + ]; + + const argument = { + valueOf: t.unreached_func("Should not touch the argument (valueOf)"), + toString: t.unreached_func("Should not touch the argument (toString)"), + }; + + const fn = WebAssembly.Table.prototype.get; + + for (const thisValue of thisValues) { + assert_throws_js(TypeError, () => fn.call(thisValue, argument), `this=${format_value(thisValue)}`); + } +}, "Branding: get"); + +test(() => { + const argument = { "element": "anyfunc", "initial": 5 }; + const table = new WebAssembly.Table(argument); + assert_throws_js(TypeError, () => table.set()); +}, "Missing arguments: set"); + +test(t => { + const thisValues = [ + undefined, + null, + true, + "", + Symbol(), + 1, + {}, + WebAssembly.Table, + WebAssembly.Table.prototype, + ]; + + const argument = { + valueOf: t.unreached_func("Should not touch the argument (valueOf)"), + toString: t.unreached_func("Should not touch the argument (toString)"), + }; + + const fn = WebAssembly.Table.prototype.set; + + for (const thisValue of thisValues) { + assert_throws_js(TypeError, () => fn.call(thisValue, argument, null), `this=${format_value(thisValue)}`); + } +}, "Branding: set"); + +test(() => { + const argument = { "element": "anyfunc", "initial": 5 }; + const table = new WebAssembly.Table(argument); + assert_equal_to_array(table, [null, null, null, null, null]); + + const {fn, fn2} = functions; + + assert_equals(table.set(0, fn), undefined, "set() returns undefined."); + table.set(2, fn2); + table.set(4, fn); + + assert_equal_to_array(table, [fn, null, fn2, null, fn]); + + table.set(0, null); + assert_equal_to_array(table, [null, null, fn2, null, fn]); +}, "Basic"); + +test(() => { + const argument = { "element": "anyfunc", "initial": 5 }; + const table = new WebAssembly.Table(argument); + assert_equal_to_array(table, [null, null, null, null, null]); + + const {fn, fn2} = functions; + + table.set(0, fn); + table.set(2, fn2); + table.set(4, fn); + + assert_equal_to_array(table, [fn, null, fn2, null, fn]); + + table.grow(4); + + assert_equal_to_array(table, [fn, null, fn2, null, fn, null, null, null, null]); +}, "Growing"); + +test(() => { + const argument = { "element": "anyfunc", "initial": 5 }; + const table = new WebAssembly.Table(argument); + assert_equal_to_array(table, [null, null, null, null, null]); + + const {fn} = functions; + + // -1 is the wrong type hence the type check on entry gets this + // before the range check does. + assert_throws_js(TypeError, () => table.set(-1, fn)); + assert_throws_js(RangeError, () => table.set(5, fn)); + assert_equal_to_array(table, [null, null, null, null, null]); +}, "Setting out-of-bounds"); + +test(() => { + const argument = { "element": "anyfunc", "initial": 1 }; + const table = new WebAssembly.Table(argument); + assert_equal_to_array(table, [null]); + + const invalidArguments = [ + undefined, + true, + false, + "test", + Symbol(), + 7, + NaN, + {}, + ]; + for (const argument of invalidArguments) { + assert_throws_js(TypeError, () => table.set(0, argument), + `set(${format_value(argument)})`); + } + assert_equal_to_array(table, [null]); +}, "Setting non-function"); + +test(() => { + const argument = { "element": "anyfunc", "initial": 1 }; + const table = new WebAssembly.Table(argument); + assert_equal_to_array(table, [null]); + + const fn = function() {}; + assert_throws_js(TypeError, () => table.set(0, fn)); + assert_equal_to_array(table, [null]); +}, "Setting non-wasm function"); + +test(() => { + const argument = { "element": "anyfunc", "initial": 1 }; + const table = new WebAssembly.Table(argument); + assert_equal_to_array(table, [null]); + + const fn = () => {}; + assert_throws_js(TypeError, () => table.set(0, fn)); + assert_equal_to_array(table, [null]); +}, "Setting non-wasm arrow function"); + +const outOfRangeValues = [ + undefined, + NaN, + Infinity, + -Infinity, + -1, + 0x100000000, + 0x1000000000, + "0x100000000", + { valueOf() { return 0x100000000; } }, +]; + +for (const value of outOfRangeValues) { + test(() => { + const argument = { "element": "anyfunc", "initial": 1 }; + const table = new WebAssembly.Table(argument); + assert_throws_js(TypeError, () => table.get(value)); + }, `Getting out-of-range argument: ${format_value(value)}`); + + test(() => { + const argument = { "element": "anyfunc", "initial": 1 }; + const table = new WebAssembly.Table(argument); + assert_throws_js(TypeError, () => table.set(value, null)); + }, `Setting out-of-range argument: ${format_value(value)}`); +} + +test(() => { + const argument = { "element": "anyfunc", "initial": 1 }; + const table = new WebAssembly.Table(argument); + let called = 0; + const value = { + valueOf() { + called++; + return 0; + }, + }; + assert_throws_js(TypeError, () => table.set(value, {})); + assert_equals(called, 1); +}, "Order of argument conversion"); + +test(() => { + const {fn} = functions; + const argument = { "element": "anyfunc", "initial": 1 }; + const table = new WebAssembly.Table(argument); + + assert_equals(table.get(0, {}), null); + assert_equals(table.set(0, fn, {}), undefined); +}, "Stray argument"); + +test(() => { + const builder = new WasmModuleBuilder(); + builder + .addFunction("fn", kSig_v_v) + .addBody([]) + .exportFunc(); + const bin = builder.toBuffer(); + const fn = new WebAssembly.Instance(new WebAssembly.Module(bin)).exports.fn; + + const argument = { "element": "anyfunc", "initial": 1 }; + const table = new WebAssembly.Table(argument, fn); + + assert_equals(table.get(0), fn); + table.set(0); + assert_equals(table.get(0), null); + + table.set(0, fn); + assert_equals(table.get(0), fn); + + assert_throws_js(TypeError, () => table.set(0, {})); + assert_throws_js(TypeError, () => table.set(0, 37)); +}, "Arguments for anyfunc table set"); + +test(() => { + const testObject = {}; + const argument = { "element": "externref", "initial": 1 }; + const table = new WebAssembly.Table(argument, testObject); + + assert_equals(table.get(0), testObject); + table.set(0); + assert_equals(table.get(0), undefined); + + table.set(0, testObject); + assert_equals(table.get(0), testObject); + + table.set(0, 37); + assert_equals(table.get(0), 37); +}, "Arguments for externref table set"); diff --git a/test/fixtures/wpt/wasm/jsapi/table/grow.any.js b/test/fixtures/wpt/wasm/jsapi/table/grow.any.js new file mode 100644 index 00000000000000..520d24bf4bafbb --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/table/grow.any.js @@ -0,0 +1,126 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/wasm-module-builder.js +// META: script=assertions.js + +function nulls(n) { + return Array(n).fill(null); +} + +test(() => { + const argument = { "element": "anyfunc", "initial": 5 }; + const table = new WebAssembly.Table(argument); + assert_throws_js(TypeError, () => table.grow()); +}, "Missing arguments"); + +test(t => { + const thisValues = [ + undefined, + null, + true, + "", + Symbol(), + 1, + {}, + WebAssembly.Table, + WebAssembly.Table.prototype, + ]; + + const argument = { + valueOf: t.unreached_func("Should not touch the argument (valueOf)"), + toString: t.unreached_func("Should not touch the argument (toString)"), + }; + + const fn = WebAssembly.Table.prototype.grow; + + for (const thisValue of thisValues) { + assert_throws_js(TypeError, () => fn.call(thisValue, argument), `this=${format_value(thisValue)}`); + } +}, "Branding"); + +test(() => { + const argument = { "element": "anyfunc", "initial": 5 }; + const table = new WebAssembly.Table(argument); + assert_equal_to_array(table, nulls(5), "before"); + + const result = table.grow(3); + assert_equals(result, 5); + assert_equal_to_array(table, nulls(8), "after"); +}, "Basic"); + +test(() => { + const argument = { "element": "anyfunc", "initial": 3, "maximum": 5 }; + const table = new WebAssembly.Table(argument); + assert_equal_to_array(table, nulls(3), "before"); + + const result = table.grow(2); + assert_equals(result, 3); + assert_equal_to_array(table, nulls(5), "after"); +}, "Reached maximum"); + +test(() => { + const argument = { "element": "anyfunc", "initial": 2, "maximum": 5 }; + const table = new WebAssembly.Table(argument); + assert_equal_to_array(table, nulls(2), "before"); + + assert_throws_js(RangeError, () => table.grow(4)); + assert_equal_to_array(table, nulls(2), "after"); +}, "Exceeded maximum"); + +const outOfRangeValues = [ + undefined, + NaN, + Infinity, + -Infinity, + -1, + 0x100000000, + 0x1000000000, + "0x100000000", + { valueOf() { return 0x100000000; } }, +]; + +for (const value of outOfRangeValues) { + test(() => { + const argument = { "element": "anyfunc", "initial": 1 }; + const table = new WebAssembly.Table(argument); + assert_throws_js(TypeError, () => table.grow(value)); + }, `Out-of-range argument: ${format_value(value)}`); +} + +test(() => { + const argument = { "element": "anyfunc", "initial": 5 }; + const table = new WebAssembly.Table(argument); + assert_equal_to_array(table, nulls(5), "before"); + + const result = table.grow(3, null, {}); + assert_equals(result, 5); + assert_equal_to_array(table, nulls(8), "after"); +}, "Stray argument"); + +test(() => { + const builder = new WasmModuleBuilder(); + builder + .addFunction("fn", kSig_v_v) + .addBody([]) + .exportFunc(); + const bin = builder.toBuffer() + const argument = { "element": "anyfunc", "initial": 1 }; + const table = new WebAssembly.Table(argument); + const fn = new WebAssembly.Instance(new WebAssembly.Module(bin)).exports.fn; + const result = table.grow(2, fn); + assert_equals(result, 1); + assert_equals(table.get(0), null); + assert_equals(table.get(1), fn); + assert_equals(table.get(2), fn); +}, "Grow with exported-function argument"); + +test(() => { + const argument = { "element": "anyfunc", "initial": 1 }; + const table = new WebAssembly.Table(argument); + assert_throws_js(TypeError, () => table.grow(2, {})); +}, "Grow with non-function argument"); + +test(() => { + const argument = { "element": "anyfunc", "initial": 1 }; + const table = new WebAssembly.Table(argument); + assert_throws_js(TypeError, () => table.grow(2, () => true)); +}, "Grow with JS-function argument"); diff --git a/test/fixtures/wpt/wasm/jsapi/table/length.any.js b/test/fixtures/wpt/wasm/jsapi/table/length.any.js new file mode 100644 index 00000000000000..a9ef095ded4458 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/table/length.any.js @@ -0,0 +1,60 @@ +// META: global=window,dedicatedworker,jsshell + +test(() => { + const thisValues = [ + undefined, + null, + true, + "", + Symbol(), + 1, + {}, + WebAssembly.Table, + WebAssembly.Table.prototype, + ]; + + const desc = Object.getOwnPropertyDescriptor(WebAssembly.Table.prototype, "length"); + assert_equals(typeof desc, "object"); + + const getter = desc.get; + assert_equals(typeof getter, "function"); + + assert_equals(typeof desc.set, "undefined"); + + for (const thisValue of thisValues) { + assert_throws_js(TypeError, () => getter.call(thisValue), `this=${format_value(thisValue)}`); + } +}, "Branding"); + +test(() => { + const argument = { "element": "anyfunc", "initial": 2 }; + const table = new WebAssembly.Table(argument); + assert_equals(table.length, 2, "Initial length"); + + const desc = Object.getOwnPropertyDescriptor(WebAssembly.Table.prototype, "length"); + assert_equals(typeof desc, "object"); + + const getter = desc.get; + assert_equals(typeof getter, "function"); + + assert_equals(getter.call(table, {}), 2); +}, "Stray argument"); + +test(() => { + const argument = { "element": "anyfunc", "initial": 2 }; + const table = new WebAssembly.Table(argument); + assert_equals(table.length, 2, "Initial length"); + table.length = 4; + assert_equals(table.length, 2, "Should not change the length"); +}, "Setting (sloppy mode)"); + +test(() => { + const argument = { "element": "anyfunc", "initial": 2 }; + const table = new WebAssembly.Table(argument); + assert_equals(table.length, 2, "Initial length"); + assert_throws_js(TypeError, () => { + "use strict"; + table.length = 4; + }); + assert_equals(table.length, 2, "Should not change the length"); +}, "Setting (strict mode)"); diff --git a/test/fixtures/wpt/wasm/jsapi/table/toString.any.js b/test/fixtures/wpt/wasm/jsapi/table/toString.any.js new file mode 100644 index 00000000000000..8a09f2832c1d64 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/table/toString.any.js @@ -0,0 +1,17 @@ +// META: global=window,dedicatedworker,jsshell + +test(() => { + const argument = { "element": "anyfunc", "initial": 0 }; + const table = new WebAssembly.Table(argument); + assert_class_string(table, "WebAssembly.Table"); +}, "Object.prototype.toString on an Table"); + +test(() => { + assert_own_property(WebAssembly.Table.prototype, Symbol.toStringTag); + + const propDesc = Object.getOwnPropertyDescriptor(WebAssembly.Table.prototype, Symbol.toStringTag); + assert_equals(propDesc.value, "WebAssembly.Table", "value"); + assert_equals(propDesc.configurable, true, "configurable"); + assert_equals(propDesc.enumerable, false, "enumerable"); + assert_equals(propDesc.writable, false, "writable"); +}, "@@toStringTag exists on the prototype with the appropriate descriptor"); diff --git a/test/fixtures/wpt/wasm/jsapi/table/type.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/table/type.tentative.any.js new file mode 100644 index 00000000000000..596e10b6bf548e --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/table/type.tentative.any.js @@ -0,0 +1,26 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/assertions.js + +function assert_type(argument) { + const mytable = new WebAssembly.Table(argument); + const tabletype = mytable.type() + assert_equals(tabletype.minimum, argument.minimum); + assert_equals(tabletype.maximum, argument.maximum); + assert_equals(tabletype.element, argument.element); +} + +test(() => { + assert_type({ "minimum": 0, "element": "anyfunc"}); +}, "Zero initial, no maximum"); + +test(() => { + assert_type({ "minimum": 5, "element": "anyfunc" }); +}, "Non-zero initial, no maximum"); + +test(() => { + assert_type({ "minimum": 0, "maximum": 0, "element": "anyfunc" }); +}, "Zero maximum"); + +test(() => { + assert_type({ "minimum": 0, "maximum": 5, "element": "anyfunc" }); +}, "Non-zero maximum"); diff --git a/test/fixtures/wpt/wasm/jsapi/tag/constructor.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/tag/constructor.tentative.any.js new file mode 100644 index 00000000000000..de63e7bf46d1e8 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/tag/constructor.tentative.any.js @@ -0,0 +1,49 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/assertions.js + +test(() => { + assert_function_name(WebAssembly.Tag, "Tag", "WebAssembly.Tag"); +}, "name"); + +test(() => { + assert_function_length(WebAssembly.Tag, 1, "WebAssembly.Tag"); +}, "length"); + +test(() => { + assert_throws_js(TypeError, () => new WebAssembly.Tag()); +}, "No arguments"); + +test(() => { + const argument = { parameters: [] }; + assert_throws_js(TypeError, () => WebAssembly.Tag(argument)); +}, "Calling"); + +test(() => { + const invalidArguments = [ + undefined, + null, + false, + true, + "", + "test", + Symbol(), + 1, + NaN, + {}, + ]; + for (const invalidArgument of invalidArguments) { + assert_throws_js( + TypeError, + () => new WebAssembly.Tag(invalidArgument), + `new Tag(${format_value(invalidArgument)})` + ); + } +}, "Invalid descriptor argument"); + +test(() => { + const invalidTypes = ["i16", "i128", "f16", "f128", "u32", "u64", "i32\0"]; + for (const value of invalidTypes) { + const argument = { parameters: [value] }; + assert_throws_js(TypeError, () => new WebAssembly.Tag(argument)); + } +}, "Invalid type parameter"); diff --git a/test/fixtures/wpt/wasm/jsapi/tag/toString.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/tag/toString.tentative.any.js new file mode 100644 index 00000000000000..ad9a4ba152f4f8 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/tag/toString.tentative.any.js @@ -0,0 +1,20 @@ +// META: global=window,dedicatedworker,jsshell + +test(() => { + const argument = { parameters: [] }; + const tag = new WebAssembly.Tag(argument); + assert_class_string(tag, "WebAssembly.Tag"); +}, "Object.prototype.toString on a Tag"); + +test(() => { + assert_own_property(WebAssembly.Tag.prototype, Symbol.toStringTag); + + const propDesc = Object.getOwnPropertyDescriptor( + WebAssembly.Tag.prototype, + Symbol.toStringTag + ); + assert_equals(propDesc.value, "WebAssembly.Tag", "value"); + assert_equals(propDesc.configurable, true, "configurable"); + assert_equals(propDesc.enumerable, false, "enumerable"); + assert_equals(propDesc.writable, false, "writable"); +}, "@@toStringTag exists on the prototype with the appropriate descriptor"); diff --git a/test/fixtures/wpt/wasm/jsapi/tag/type.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/tag/type.tentative.any.js new file mode 100644 index 00000000000000..9d2f0de1a00f20 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/tag/type.tentative.any.js @@ -0,0 +1,21 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/assertions.js + +function assert_type(argument) { + const tag = new WebAssembly.Tag(argument); + const tagtype = tag.type(); + + assert_array_equals(tagtype.parameters, argument.parameters); +} + +test(() => { + assert_type({ parameters: [] }); +}, "[]"); + +test(() => { + assert_type({ parameters: ["i32", "i64"] }); +}, "[i32 i64]"); + +test(() => { + assert_type({ parameters: ["i32", "i64", "f32", "f64"] }); +}, "[i32 i64 f32 f64]"); diff --git a/test/fixtures/wpt/wasm/jsapi/wasm-module-builder.js b/test/fixtures/wpt/wasm/jsapi/wasm-module-builder.js new file mode 100644 index 00000000000000..7be72f86dae752 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/wasm-module-builder.js @@ -0,0 +1,1323 @@ +// Copyright 2016 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Used for encoding f32 and double constants to bits. +let byte_view = new Uint8Array(8); +let data_view = new DataView(byte_view.buffer); + +// The bytes function receives one of +// - several arguments, each of which is either a number or a string of length +// 1; if it's a string, the charcode of the contained character is used. +// - a single array argument containing the actual arguments +// - a single string; the returned buffer will contain the char codes of all +// contained characters. +function bytes(...input) { + if (input.length == 1 && typeof input[0] == 'array') input = input[0]; + if (input.length == 1 && typeof input[0] == 'string') { + let len = input[0].length; + let view = new Uint8Array(len); + for (let i = 0; i < len; i++) view[i] = input[0].charCodeAt(i); + return view.buffer; + } + let view = new Uint8Array(input.length); + for (let i = 0; i < input.length; i++) { + let val = input[i]; + if (typeof val == 'string') { + assertEquals(1, val.length, 'string inputs must have length 1'); + val = val.charCodeAt(0); + } + view[i] = val | 0; + } + return view.buffer; +} + +// Header declaration constants +var kWasmH0 = 0; +var kWasmH1 = 0x61; +var kWasmH2 = 0x73; +var kWasmH3 = 0x6d; + +var kWasmV0 = 0x1; +var kWasmV1 = 0; +var kWasmV2 = 0; +var kWasmV3 = 0; + +var kHeaderSize = 8; +var kPageSize = 65536; +var kSpecMaxPages = 65535; +var kMaxVarInt32Size = 5; +var kMaxVarInt64Size = 10; + +let kDeclNoLocals = 0; + +// Section declaration constants +let kUnknownSectionCode = 0; +let kTypeSectionCode = 1; // Function signature declarations +let kImportSectionCode = 2; // Import declarations +let kFunctionSectionCode = 3; // Function declarations +let kTableSectionCode = 4; // Indirect function table and other tables +let kMemorySectionCode = 5; // Memory attributes +let kGlobalSectionCode = 6; // Global declarations +let kExportSectionCode = 7; // Exports +let kStartSectionCode = 8; // Start function declaration +let kElementSectionCode = 9; // Elements section +let kCodeSectionCode = 10; // Function code +let kDataSectionCode = 11; // Data segments +let kDataCountSectionCode = 12; // Data segment count (between Element & Code) +let kExceptionSectionCode = 13; // Exception section (between Global & Export) + +// Name section types +let kModuleNameCode = 0; +let kFunctionNamesCode = 1; +let kLocalNamesCode = 2; + +let kWasmFunctionTypeForm = 0x60; +let kWasmAnyFunctionTypeForm = 0x70; + +let kHasMaximumFlag = 1; +let kSharedHasMaximumFlag = 3; + +// Segment flags +let kActiveNoIndex = 0; +let kPassive = 1; +let kActiveWithIndex = 2; +let kPassiveWithElements = 5; + +// Function declaration flags +let kDeclFunctionName = 0x01; +let kDeclFunctionImport = 0x02; +let kDeclFunctionLocals = 0x04; +let kDeclFunctionExport = 0x08; + +// Local types +let kWasmStmt = 0x40; +let kWasmI32 = 0x7f; +let kWasmI64 = 0x7e; +let kWasmF32 = 0x7d; +let kWasmF64 = 0x7c; +let kWasmS128 = 0x7b; +let kWasmAnyRef = 0x6f; +let kWasmAnyFunc = 0x70; + +let kExternalFunction = 0; +let kExternalTable = 1; +let kExternalMemory = 2; +let kExternalGlobal = 3; +let kExternalException = 4; + +let kTableZero = 0; +let kMemoryZero = 0; +let kSegmentZero = 0; + +let kExceptionAttribute = 0; + +// Useful signatures +let kSig_i_i = makeSig([kWasmI32], [kWasmI32]); +let kSig_l_l = makeSig([kWasmI64], [kWasmI64]); +let kSig_i_l = makeSig([kWasmI64], [kWasmI32]); +let kSig_i_ii = makeSig([kWasmI32, kWasmI32], [kWasmI32]); +let kSig_i_iii = makeSig([kWasmI32, kWasmI32, kWasmI32], [kWasmI32]); +let kSig_v_iiii = makeSig([kWasmI32, kWasmI32, kWasmI32, kWasmI32], []); +let kSig_f_ff = makeSig([kWasmF32, kWasmF32], [kWasmF32]); +let kSig_d_dd = makeSig([kWasmF64, kWasmF64], [kWasmF64]); +let kSig_l_ll = makeSig([kWasmI64, kWasmI64], [kWasmI64]); +let kSig_i_dd = makeSig([kWasmF64, kWasmF64], [kWasmI32]); +let kSig_v_v = makeSig([], []); +let kSig_i_v = makeSig([], [kWasmI32]); +let kSig_l_v = makeSig([], [kWasmI64]); +let kSig_f_v = makeSig([], [kWasmF32]); +let kSig_d_v = makeSig([], [kWasmF64]); +let kSig_v_i = makeSig([kWasmI32], []); +let kSig_v_ii = makeSig([kWasmI32, kWasmI32], []); +let kSig_v_iii = makeSig([kWasmI32, kWasmI32, kWasmI32], []); +let kSig_v_l = makeSig([kWasmI64], []); +let kSig_v_d = makeSig([kWasmF64], []); +let kSig_v_dd = makeSig([kWasmF64, kWasmF64], []); +let kSig_v_ddi = makeSig([kWasmF64, kWasmF64, kWasmI32], []); +let kSig_ii_v = makeSig([], [kWasmI32, kWasmI32]); +let kSig_iii_v = makeSig([], [kWasmI32, kWasmI32, kWasmI32]); +let kSig_ii_i = makeSig([kWasmI32], [kWasmI32, kWasmI32]); +let kSig_iii_i = makeSig([kWasmI32], [kWasmI32, kWasmI32, kWasmI32]); +let kSig_ii_ii = makeSig([kWasmI32, kWasmI32], [kWasmI32, kWasmI32]); +let kSig_iii_ii = makeSig([kWasmI32, kWasmI32], [kWasmI32, kWasmI32, kWasmI32]); + +let kSig_v_f = makeSig([kWasmF32], []); +let kSig_f_f = makeSig([kWasmF32], [kWasmF32]); +let kSig_f_d = makeSig([kWasmF64], [kWasmF32]); +let kSig_d_d = makeSig([kWasmF64], [kWasmF64]); +let kSig_r_r = makeSig([kWasmAnyRef], [kWasmAnyRef]); +let kSig_a_a = makeSig([kWasmAnyFunc], [kWasmAnyFunc]); +let kSig_i_r = makeSig([kWasmAnyRef], [kWasmI32]); +let kSig_v_r = makeSig([kWasmAnyRef], []); +let kSig_v_a = makeSig([kWasmAnyFunc], []); +let kSig_v_rr = makeSig([kWasmAnyRef, kWasmAnyRef], []); +let kSig_v_aa = makeSig([kWasmAnyFunc, kWasmAnyFunc], []); +let kSig_r_v = makeSig([], [kWasmAnyRef]); +let kSig_a_v = makeSig([], [kWasmAnyFunc]); +let kSig_a_i = makeSig([kWasmI32], [kWasmAnyFunc]); + +function makeSig(params, results) { + return {params: params, results: results}; +} + +function makeSig_v_x(x) { + return makeSig([x], []); +} + +function makeSig_v_xx(x) { + return makeSig([x, x], []); +} + +function makeSig_r_v(r) { + return makeSig([], [r]); +} + +function makeSig_r_x(r, x) { + return makeSig([x], [r]); +} + +function makeSig_r_xx(r, x) { + return makeSig([x, x], [r]); +} + +// Opcodes +let kExprUnreachable = 0x00; +let kExprNop = 0x01; +let kExprBlock = 0x02; +let kExprLoop = 0x03; +let kExprIf = 0x04; +let kExprElse = 0x05; +let kExprTry = 0x06; +let kExprCatch = 0x07; +let kExprCatchAll = 0x19; +let kExprThrow = 0x08; +let kExprRethrow = 0x09; +let kExprBrOnExn = 0x0a; +let kExprEnd = 0x0b; +let kExprBr = 0x0c; +let kExprBrIf = 0x0d; +let kExprBrTable = 0x0e; +let kExprReturn = 0x0f; +let kExprCallFunction = 0x10; +let kExprCallIndirect = 0x11; +let kExprReturnCall = 0x12; +let kExprReturnCallIndirect = 0x13; +let kExprDrop = 0x1a; +let kExprSelect = 0x1b; +let kExprLocalGet = 0x20; +let kExprLocalSet = 0x21; +let kExprLocalTee = 0x22; +let kExprGlobalGet = 0x23; +let kExprGlobalSet = 0x24; +let kExprTableGet = 0x25; +let kExprTableSet = 0x26; +let kExprI32LoadMem = 0x28; +let kExprI64LoadMem = 0x29; +let kExprF32LoadMem = 0x2a; +let kExprF64LoadMem = 0x2b; +let kExprI32LoadMem8S = 0x2c; +let kExprI32LoadMem8U = 0x2d; +let kExprI32LoadMem16S = 0x2e; +let kExprI32LoadMem16U = 0x2f; +let kExprI64LoadMem8S = 0x30; +let kExprI64LoadMem8U = 0x31; +let kExprI64LoadMem16S = 0x32; +let kExprI64LoadMem16U = 0x33; +let kExprI64LoadMem32S = 0x34; +let kExprI64LoadMem32U = 0x35; +let kExprI32StoreMem = 0x36; +let kExprI64StoreMem = 0x37; +let kExprF32StoreMem = 0x38; +let kExprF64StoreMem = 0x39; +let kExprI32StoreMem8 = 0x3a; +let kExprI32StoreMem16 = 0x3b; +let kExprI64StoreMem8 = 0x3c; +let kExprI64StoreMem16 = 0x3d; +let kExprI64StoreMem32 = 0x3e; +let kExprMemorySize = 0x3f; +let kExprMemoryGrow = 0x40; +let kExprI32Const = 0x41; +let kExprI64Const = 0x42; +let kExprF32Const = 0x43; +let kExprF64Const = 0x44; +let kExprI32Eqz = 0x45; +let kExprI32Eq = 0x46; +let kExprI32Ne = 0x47; +let kExprI32LtS = 0x48; +let kExprI32LtU = 0x49; +let kExprI32GtS = 0x4a; +let kExprI32GtU = 0x4b; +let kExprI32LeS = 0x4c; +let kExprI32LeU = 0x4d; +let kExprI32GeS = 0x4e; +let kExprI32GeU = 0x4f; +let kExprI64Eqz = 0x50; +let kExprI64Eq = 0x51; +let kExprI64Ne = 0x52; +let kExprI64LtS = 0x53; +let kExprI64LtU = 0x54; +let kExprI64GtS = 0x55; +let kExprI64GtU = 0x56; +let kExprI64LeS = 0x57; +let kExprI64LeU = 0x58; +let kExprI64GeS = 0x59; +let kExprI64GeU = 0x5a; +let kExprF32Eq = 0x5b; +let kExprF32Ne = 0x5c; +let kExprF32Lt = 0x5d; +let kExprF32Gt = 0x5e; +let kExprF32Le = 0x5f; +let kExprF32Ge = 0x60; +let kExprF64Eq = 0x61; +let kExprF64Ne = 0x62; +let kExprF64Lt = 0x63; +let kExprF64Gt = 0x64; +let kExprF64Le = 0x65; +let kExprF64Ge = 0x66; +let kExprI32Clz = 0x67; +let kExprI32Ctz = 0x68; +let kExprI32Popcnt = 0x69; +let kExprI32Add = 0x6a; +let kExprI32Sub = 0x6b; +let kExprI32Mul = 0x6c; +let kExprI32DivS = 0x6d; +let kExprI32DivU = 0x6e; +let kExprI32RemS = 0x6f; +let kExprI32RemU = 0x70; +let kExprI32And = 0x71; +let kExprI32Ior = 0x72; +let kExprI32Xor = 0x73; +let kExprI32Shl = 0x74; +let kExprI32ShrS = 0x75; +let kExprI32ShrU = 0x76; +let kExprI32Rol = 0x77; +let kExprI32Ror = 0x78; +let kExprI64Clz = 0x79; +let kExprI64Ctz = 0x7a; +let kExprI64Popcnt = 0x7b; +let kExprI64Add = 0x7c; +let kExprI64Sub = 0x7d; +let kExprI64Mul = 0x7e; +let kExprI64DivS = 0x7f; +let kExprI64DivU = 0x80; +let kExprI64RemS = 0x81; +let kExprI64RemU = 0x82; +let kExprI64And = 0x83; +let kExprI64Ior = 0x84; +let kExprI64Xor = 0x85; +let kExprI64Shl = 0x86; +let kExprI64ShrS = 0x87; +let kExprI64ShrU = 0x88; +let kExprI64Rol = 0x89; +let kExprI64Ror = 0x8a; +let kExprF32Abs = 0x8b; +let kExprF32Neg = 0x8c; +let kExprF32Ceil = 0x8d; +let kExprF32Floor = 0x8e; +let kExprF32Trunc = 0x8f; +let kExprF32NearestInt = 0x90; +let kExprF32Sqrt = 0x91; +let kExprF32Add = 0x92; +let kExprF32Sub = 0x93; +let kExprF32Mul = 0x94; +let kExprF32Div = 0x95; +let kExprF32Min = 0x96; +let kExprF32Max = 0x97; +let kExprF32CopySign = 0x98; +let kExprF64Abs = 0x99; +let kExprF64Neg = 0x9a; +let kExprF64Ceil = 0x9b; +let kExprF64Floor = 0x9c; +let kExprF64Trunc = 0x9d; +let kExprF64NearestInt = 0x9e; +let kExprF64Sqrt = 0x9f; +let kExprF64Add = 0xa0; +let kExprF64Sub = 0xa1; +let kExprF64Mul = 0xa2; +let kExprF64Div = 0xa3; +let kExprF64Min = 0xa4; +let kExprF64Max = 0xa5; +let kExprF64CopySign = 0xa6; +let kExprI32ConvertI64 = 0xa7; +let kExprI32SConvertF32 = 0xa8; +let kExprI32UConvertF32 = 0xa9; +let kExprI32SConvertF64 = 0xaa; +let kExprI32UConvertF64 = 0xab; +let kExprI64SConvertI32 = 0xac; +let kExprI64UConvertI32 = 0xad; +let kExprI64SConvertF32 = 0xae; +let kExprI64UConvertF32 = 0xaf; +let kExprI64SConvertF64 = 0xb0; +let kExprI64UConvertF64 = 0xb1; +let kExprF32SConvertI32 = 0xb2; +let kExprF32UConvertI32 = 0xb3; +let kExprF32SConvertI64 = 0xb4; +let kExprF32UConvertI64 = 0xb5; +let kExprF32ConvertF64 = 0xb6; +let kExprF64SConvertI32 = 0xb7; +let kExprF64UConvertI32 = 0xb8; +let kExprF64SConvertI64 = 0xb9; +let kExprF64UConvertI64 = 0xba; +let kExprF64ConvertF32 = 0xbb; +let kExprI32ReinterpretF32 = 0xbc; +let kExprI64ReinterpretF64 = 0xbd; +let kExprF32ReinterpretI32 = 0xbe; +let kExprF64ReinterpretI64 = 0xbf; +let kExprI32SExtendI8 = 0xc0; +let kExprI32SExtendI16 = 0xc1; +let kExprI64SExtendI8 = 0xc2; +let kExprI64SExtendI16 = 0xc3; +let kExprI64SExtendI32 = 0xc4; +let kExprRefNull = 0xd0; +let kExprRefIsNull = 0xd1; +let kExprRefFunc = 0xd2; + +// Prefix opcodes +let kNumericPrefix = 0xfc; +let kSimdPrefix = 0xfd; +let kAtomicPrefix = 0xfe; + +// Numeric opcodes. +let kExprMemoryInit = 0x08; +let kExprDataDrop = 0x09; +let kExprMemoryCopy = 0x0a; +let kExprMemoryFill = 0x0b; +let kExprTableInit = 0x0c; +let kExprElemDrop = 0x0d; +let kExprTableCopy = 0x0e; +let kExprTableGrow = 0x0f; +let kExprTableSize = 0x10; +let kExprTableFill = 0x11; + +// Atomic opcodes. +let kExprAtomicNotify = 0x00; +let kExprI32AtomicWait = 0x01; +let kExprI64AtomicWait = 0x02; +let kExprI32AtomicLoad = 0x10; +let kExprI32AtomicLoad8U = 0x12; +let kExprI32AtomicLoad16U = 0x13; +let kExprI32AtomicStore = 0x17; +let kExprI32AtomicStore8U = 0x19; +let kExprI32AtomicStore16U = 0x1a; +let kExprI32AtomicAdd = 0x1e; +let kExprI32AtomicAdd8U = 0x20; +let kExprI32AtomicAdd16U = 0x21; +let kExprI32AtomicSub = 0x25; +let kExprI32AtomicSub8U = 0x27; +let kExprI32AtomicSub16U = 0x28; +let kExprI32AtomicAnd = 0x2c; +let kExprI32AtomicAnd8U = 0x2e; +let kExprI32AtomicAnd16U = 0x2f; +let kExprI32AtomicOr = 0x33; +let kExprI32AtomicOr8U = 0x35; +let kExprI32AtomicOr16U = 0x36; +let kExprI32AtomicXor = 0x3a; +let kExprI32AtomicXor8U = 0x3c; +let kExprI32AtomicXor16U = 0x3d; +let kExprI32AtomicExchange = 0x41; +let kExprI32AtomicExchange8U = 0x43; +let kExprI32AtomicExchange16U = 0x44; +let kExprI32AtomicCompareExchange = 0x48; +let kExprI32AtomicCompareExchange8U = 0x4a; +let kExprI32AtomicCompareExchange16U = 0x4b; + +let kExprI64AtomicLoad = 0x11; +let kExprI64AtomicLoad8U = 0x14; +let kExprI64AtomicLoad16U = 0x15; +let kExprI64AtomicLoad32U = 0x16; +let kExprI64AtomicStore = 0x18; +let kExprI64AtomicStore8U = 0x1b; +let kExprI64AtomicStore16U = 0x1c; +let kExprI64AtomicStore32U = 0x1d; +let kExprI64AtomicAdd = 0x1f; +let kExprI64AtomicAdd8U = 0x22; +let kExprI64AtomicAdd16U = 0x23; +let kExprI64AtomicAdd32U = 0x24; +let kExprI64AtomicSub = 0x26; +let kExprI64AtomicSub8U = 0x29; +let kExprI64AtomicSub16U = 0x2a; +let kExprI64AtomicSub32U = 0x2b; +let kExprI64AtomicAnd = 0x2d; +let kExprI64AtomicAnd8U = 0x30; +let kExprI64AtomicAnd16U = 0x31; +let kExprI64AtomicAnd32U = 0x32; +let kExprI64AtomicOr = 0x34; +let kExprI64AtomicOr8U = 0x37; +let kExprI64AtomicOr16U = 0x38; +let kExprI64AtomicOr32U = 0x39; +let kExprI64AtomicXor = 0x3b; +let kExprI64AtomicXor8U = 0x3e; +let kExprI64AtomicXor16U = 0x3f; +let kExprI64AtomicXor32U = 0x40; +let kExprI64AtomicExchange = 0x42; +let kExprI64AtomicExchange8U = 0x45; +let kExprI64AtomicExchange16U = 0x46; +let kExprI64AtomicExchange32U = 0x47; +let kExprI64AtomicCompareExchange = 0x49 +let kExprI64AtomicCompareExchange8U = 0x4c; +let kExprI64AtomicCompareExchange16U = 0x4d; +let kExprI64AtomicCompareExchange32U = 0x4e; + +// Simd opcodes. +let kExprS128LoadMem = 0x00; +let kExprS128StoreMem = 0x01; +let kExprI32x4Splat = 0x0c; +let kExprI32x4Eq = 0x2c; +let kExprS1x4AllTrue = 0x75; +let kExprF32x4Min = 0x9e; + +class Binary { + constructor() { + this.length = 0; + this.buffer = new Uint8Array(8192); + } + + ensure_space(needed) { + if (this.buffer.length - this.length >= needed) return; + let new_capacity = this.buffer.length * 2; + while (new_capacity - this.length < needed) new_capacity *= 2; + let new_buffer = new Uint8Array(new_capacity); + new_buffer.set(this.buffer); + this.buffer = new_buffer; + } + + trunc_buffer() { + return new Uint8Array(this.buffer.buffer, 0, this.length); + } + + reset() { + this.length = 0; + } + + emit_u8(val) { + this.ensure_space(1); + this.buffer[this.length++] = val; + } + + emit_u16(val) { + this.ensure_space(2); + this.buffer[this.length++] = val; + this.buffer[this.length++] = val >> 8; + } + + emit_u32(val) { + this.ensure_space(4); + this.buffer[this.length++] = val; + this.buffer[this.length++] = val >> 8; + this.buffer[this.length++] = val >> 16; + this.buffer[this.length++] = val >> 24; + } + + emit_leb_u(val, max_len) { + this.ensure_space(max_len); + for (let i = 0; i < max_len; ++i) { + let v = val & 0xff; + val = val >>> 7; + if (val == 0) { + this.buffer[this.length++] = v; + return; + } + this.buffer[this.length++] = v | 0x80; + } + throw new Error("Leb value exceeds maximum length of " + max_len); + } + + emit_u32v(val) { + this.emit_leb_u(val, kMaxVarInt32Size); + } + + emit_u64v(val) { + this.emit_leb_u(val, kMaxVarInt64Size); + } + + emit_bytes(data) { + this.ensure_space(data.length); + this.buffer.set(data, this.length); + this.length += data.length; + } + + emit_string(string) { + // When testing illegal names, we pass a byte array directly. + if (string instanceof Array) { + this.emit_u32v(string.length); + this.emit_bytes(string); + return; + } + + // This is the hacky way to convert a JavaScript string to a UTF8 encoded + // string only containing single-byte characters. + let string_utf8 = unescape(encodeURIComponent(string)); + this.emit_u32v(string_utf8.length); + for (let i = 0; i < string_utf8.length; i++) { + this.emit_u8(string_utf8.charCodeAt(i)); + } + } + + emit_header() { + this.emit_bytes([ + kWasmH0, kWasmH1, kWasmH2, kWasmH3, kWasmV0, kWasmV1, kWasmV2, kWasmV3 + ]); + } + + emit_section(section_code, content_generator) { + // Emit section name. + this.emit_u8(section_code); + // Emit the section to a temporary buffer: its full length isn't know yet. + const section = new Binary; + content_generator(section); + // Emit section length. + this.emit_u32v(section.length); + // Copy the temporary buffer. + // Avoid spread because {section} can be huge. + this.emit_bytes(section.trunc_buffer()); + } +} + +class WasmFunctionBuilder { + constructor(module, name, type_index) { + this.module = module; + this.name = name; + this.type_index = type_index; + this.body = []; + this.locals = []; + this.local_names = []; + } + + numLocalNames() { + let num_local_names = 0; + for (let loc_name of this.local_names) { + if (loc_name !== undefined) ++num_local_names; + } + return num_local_names; + } + + exportAs(name) { + this.module.addExport(name, this.index); + return this; + } + + exportFunc() { + this.exportAs(this.name); + return this; + } + + addBody(body) { + for (let b of body) { + if (typeof b !== 'number' || (b & (~0xFF)) !== 0 ) + throw new Error('invalid body (entries must be 8 bit numbers): ' + body); + } + this.body = body.slice(); + // Automatically add the end for the function block to the body. + this.body.push(kExprEnd); + return this; + } + + addBodyWithEnd(body) { + this.body = body; + return this; + } + + getNumLocals() { + let total_locals = 0; + for (let l of this.locals) { + for (let type of ["i32", "i64", "f32", "f64", "s128"]) { + total_locals += l[type + "_count"] || 0; + } + } + return total_locals; + } + + addLocals(locals, names) { + const old_num_locals = this.getNumLocals(); + this.locals.push(locals); + if (names) { + const missing_names = old_num_locals - this.local_names.length; + this.local_names.push(...new Array(missing_names), ...names); + } + return this; + } + + end() { + return this.module; + } +} + +class WasmGlobalBuilder { + constructor(module, type, mutable) { + this.module = module; + this.type = type; + this.mutable = mutable; + this.init = 0; + } + + exportAs(name) { + this.module.exports.push({name: name, kind: kExternalGlobal, + index: this.index}); + return this; + } +} + +class WasmTableBuilder { + constructor(module, type, initial_size, max_size) { + this.module = module; + this.type = type; + this.initial_size = initial_size; + this.has_max = max_size != undefined; + this.max_size = max_size; + } + + exportAs(name) { + this.module.exports.push({name: name, kind: kExternalTable, + index: this.index}); + return this; + } +} + +class WasmModuleBuilder { + constructor() { + this.types = []; + this.imports = []; + this.exports = []; + this.globals = []; + this.tables = []; + this.exceptions = []; + this.functions = []; + this.element_segments = []; + this.data_segments = []; + this.explicit = []; + this.num_imported_funcs = 0; + this.num_imported_globals = 0; + this.num_imported_tables = 0; + this.num_imported_exceptions = 0; + return this; + } + + addStart(start_index) { + this.start_index = start_index; + return this; + } + + addMemory(min, max, exp, shared) { + this.memory = {min: min, max: max, exp: exp, shared: shared}; + return this; + } + + addExplicitSection(bytes) { + this.explicit.push(bytes); + return this; + } + + stringToBytes(name) { + var result = new Binary(); + result.emit_string(name); + return result.trunc_buffer() + } + + createCustomSection(name, bytes) { + name = this.stringToBytes(name); + var section = new Binary(); + section.emit_u8(kUnknownSectionCode); + section.emit_u32v(name.length + bytes.length); + section.emit_bytes(name); + section.emit_bytes(bytes); + return section.trunc_buffer(); + } + + addCustomSection(name, bytes) { + this.explicit.push(this.createCustomSection(name, bytes)); + } + + addType(type) { + this.types.push(type); + var pl = type.params.length; // should have params + var rl = type.results.length; // should have results + return this.types.length - 1; + } + + addGlobal(local_type, mutable) { + let glob = new WasmGlobalBuilder(this, local_type, mutable); + glob.index = this.globals.length + this.num_imported_globals; + this.globals.push(glob); + return glob; + } + + addTable(type, initial_size, max_size = undefined) { + if (type != kWasmAnyRef && type != kWasmAnyFunc) { + throw new Error('Tables must be of type kWasmAnyRef or kWasmAnyFunc'); + } + let table = new WasmTableBuilder(this, type, initial_size, max_size); + table.index = this.tables.length + this.num_imported_tables; + this.tables.push(table); + return table; + } + + addException(type) { + let type_index = (typeof type) == "number" ? type : this.addType(type); + let except_index = this.exceptions.length + this.num_imported_exceptions; + this.exceptions.push(type_index); + return except_index; + } + + addFunction(name, type) { + let type_index = (typeof type) == "number" ? type : this.addType(type); + let func = new WasmFunctionBuilder(this, name, type_index); + func.index = this.functions.length + this.num_imported_funcs; + this.functions.push(func); + return func; + } + + addImport(module, name, type) { + if (this.functions.length != 0) { + throw new Error('Imported functions must be declared before local ones'); + } + let type_index = (typeof type) == "number" ? type : this.addType(type); + this.imports.push({module: module, name: name, kind: kExternalFunction, + type: type_index}); + return this.num_imported_funcs++; + } + + addImportedGlobal(module, name, type, mutable = false) { + if (this.globals.length != 0) { + throw new Error('Imported globals must be declared before local ones'); + } + let o = {module: module, name: name, kind: kExternalGlobal, type: type, + mutable: mutable}; + this.imports.push(o); + return this.num_imported_globals++; + } + + addImportedMemory(module, name, initial = 0, maximum, shared) { + let o = {module: module, name: name, kind: kExternalMemory, + initial: initial, maximum: maximum, shared: shared}; + this.imports.push(o); + return this; + } + + addImportedTable(module, name, initial, maximum, type) { + if (this.tables.length != 0) { + throw new Error('Imported tables must be declared before local ones'); + } + let o = {module: module, name: name, kind: kExternalTable, initial: initial, + maximum: maximum, type: type || kWasmAnyFunctionTypeForm}; + this.imports.push(o); + return this.num_imported_tables++; + } + + addImportedException(module, name, type) { + if (this.exceptions.length != 0) { + throw new Error('Imported exceptions must be declared before local ones'); + } + let type_index = (typeof type) == "number" ? type : this.addType(type); + let o = {module: module, name: name, kind: kExternalException, type: type_index}; + this.imports.push(o); + return this.num_imported_exceptions++; + } + + addExport(name, index) { + this.exports.push({name: name, kind: kExternalFunction, index: index}); + return this; + } + + addExportOfKind(name, kind, index) { + this.exports.push({name: name, kind: kind, index: index}); + return this; + } + + addDataSegment(addr, data, is_global = false) { + this.data_segments.push( + {addr: addr, data: data, is_global: is_global, is_active: true}); + return this.data_segments.length - 1; + } + + addPassiveDataSegment(data) { + this.data_segments.push({data: data, is_active: false}); + return this.data_segments.length - 1; + } + + exportMemoryAs(name) { + this.exports.push({name: name, kind: kExternalMemory, index: 0}); + } + + addElementSegment(table, base, is_global, array) { + this.element_segments.push({table: table, base: base, is_global: is_global, + array: array, is_active: true}); + return this; + } + + addPassiveElementSegment(array, is_import = false) { + this.element_segments.push({array: array, is_active: false}); + return this; + } + + appendToTable(array) { + for (let n of array) { + if (typeof n != 'number') + throw new Error('invalid table (entries have to be numbers): ' + array); + } + if (this.tables.length == 0) { + this.addTable(kWasmAnyFunc, 0); + } + // Adjust the table to the correct size. + let table = this.tables[0]; + const base = table.initial_size; + const table_size = base + array.length; + table.initial_size = table_size; + if (table.has_max && table_size > table.max_size) { + table.max_size = table_size; + } + return this.addElementSegment(0, base, false, array); + } + + setTableBounds(min, max = undefined) { + if (this.tables.length != 0) { + throw new Error("The table bounds of table '0' have already been set."); + } + this.addTable(kWasmAnyFunc, min, max); + return this; + } + + setName(name) { + this.name = name; + return this; + } + + toBuffer(debug = false) { + let binary = new Binary; + let wasm = this; + + // Add header + binary.emit_header(); + + // Add type section + if (wasm.types.length > 0) { + if (debug) print("emitting types @ " + binary.length); + binary.emit_section(kTypeSectionCode, section => { + section.emit_u32v(wasm.types.length); + for (let type of wasm.types) { + section.emit_u8(kWasmFunctionTypeForm); + section.emit_u32v(type.params.length); + for (let param of type.params) { + section.emit_u8(param); + } + section.emit_u32v(type.results.length); + for (let result of type.results) { + section.emit_u8(result); + } + } + }); + } + + // Add imports section + if (wasm.imports.length > 0) { + if (debug) print("emitting imports @ " + binary.length); + binary.emit_section(kImportSectionCode, section => { + section.emit_u32v(wasm.imports.length); + for (let imp of wasm.imports) { + section.emit_string(imp.module); + section.emit_string(imp.name || ''); + section.emit_u8(imp.kind); + if (imp.kind == kExternalFunction) { + section.emit_u32v(imp.type); + } else if (imp.kind == kExternalGlobal) { + section.emit_u32v(imp.type); + section.emit_u8(imp.mutable); + } else if (imp.kind == kExternalMemory) { + var has_max = (typeof imp.maximum) != "undefined"; + var is_shared = (typeof imp.shared) != "undefined"; + if (is_shared) { + section.emit_u8(has_max ? 3 : 2); // flags + } else { + section.emit_u8(has_max ? 1 : 0); // flags + } + section.emit_u32v(imp.initial); // initial + if (has_max) section.emit_u32v(imp.maximum); // maximum + } else if (imp.kind == kExternalTable) { + section.emit_u8(imp.type); + var has_max = (typeof imp.maximum) != "undefined"; + section.emit_u8(has_max ? 1 : 0); // flags + section.emit_u32v(imp.initial); // initial + if (has_max) section.emit_u32v(imp.maximum); // maximum + } else if (imp.kind == kExternalException) { + section.emit_u32v(kExceptionAttribute); + section.emit_u32v(imp.type); + } else { + throw new Error("unknown/unsupported import kind " + imp.kind); + } + } + }); + } + + // Add functions declarations + if (wasm.functions.length > 0) { + if (debug) print("emitting function decls @ " + binary.length); + binary.emit_section(kFunctionSectionCode, section => { + section.emit_u32v(wasm.functions.length); + for (let func of wasm.functions) { + section.emit_u32v(func.type_index); + } + }); + } + + // Add table section + if (wasm.tables.length > 0) { + if (debug) print ("emitting tables @ " + binary.length); + binary.emit_section(kTableSectionCode, section => { + section.emit_u32v(wasm.tables.length); + for (let table of wasm.tables) { + section.emit_u8(table.type); + section.emit_u8(table.has_max); + section.emit_u32v(table.initial_size); + if (table.has_max) section.emit_u32v(table.max_size); + } + }); + } + + // Add memory section + if (wasm.memory !== undefined) { + if (debug) print("emitting memory @ " + binary.length); + binary.emit_section(kMemorySectionCode, section => { + section.emit_u8(1); // one memory entry + const has_max = wasm.memory.max !== undefined; + const is_shared = wasm.memory.shared !== undefined; + // Emit flags (bit 0: reszeable max, bit 1: shared memory) + if (is_shared) { + section.emit_u8(has_max ? kSharedHasMaximumFlag : 2); + } else { + section.emit_u8(has_max ? kHasMaximumFlag : 0); + } + section.emit_u32v(wasm.memory.min); + if (has_max) section.emit_u32v(wasm.memory.max); + }); + } + + // Add global section. + if (wasm.globals.length > 0) { + if (debug) print ("emitting globals @ " + binary.length); + binary.emit_section(kGlobalSectionCode, section => { + section.emit_u32v(wasm.globals.length); + for (let global of wasm.globals) { + section.emit_u8(global.type); + section.emit_u8(global.mutable); + if ((typeof global.init_index) == "undefined") { + // Emit a constant initializer. + switch (global.type) { + case kWasmI32: + section.emit_u8(kExprI32Const); + section.emit_u32v(global.init); + break; + case kWasmI64: + section.emit_u8(kExprI64Const); + section.emit_u64v(global.init); + break; + case kWasmF32: + section.emit_bytes(wasmF32Const(global.init)); + break; + case kWasmF64: + section.emit_bytes(wasmF64Const(global.init)); + break; + case kWasmAnyFunc: + case kWasmAnyRef: + if (global.function_index !== undefined) { + section.emit_u8(kExprRefFunc); + section.emit_u32v(global.function_index); + } else { + section.emit_u8(kExprRefNull); + } + break; + } + } else { + // Emit a global-index initializer. + section.emit_u8(kExprGlobalGet); + section.emit_u32v(global.init_index); + } + section.emit_u8(kExprEnd); // end of init expression + } + }); + } + + // Add exceptions. + if (wasm.exceptions.length > 0) { + if (debug) print("emitting exceptions @ " + binary.length); + binary.emit_section(kExceptionSectionCode, section => { + section.emit_u32v(wasm.exceptions.length); + for (let type of wasm.exceptions) { + section.emit_u32v(kExceptionAttribute); + section.emit_u32v(type); + } + }); + } + + // Add export table. + var mem_export = (wasm.memory !== undefined && wasm.memory.exp); + var exports_count = wasm.exports.length + (mem_export ? 1 : 0); + if (exports_count > 0) { + if (debug) print("emitting exports @ " + binary.length); + binary.emit_section(kExportSectionCode, section => { + section.emit_u32v(exports_count); + for (let exp of wasm.exports) { + section.emit_string(exp.name); + section.emit_u8(exp.kind); + section.emit_u32v(exp.index); + } + if (mem_export) { + section.emit_string("memory"); + section.emit_u8(kExternalMemory); + section.emit_u8(0); + } + }); + } + + // Add start function section. + if (wasm.start_index !== undefined) { + if (debug) print("emitting start function @ " + binary.length); + binary.emit_section(kStartSectionCode, section => { + section.emit_u32v(wasm.start_index); + }); + } + + // Add element segments + if (wasm.element_segments.length > 0) { + if (debug) print("emitting element segments @ " + binary.length); + binary.emit_section(kElementSectionCode, section => { + var inits = wasm.element_segments; + section.emit_u32v(inits.length); + + for (let init of inits) { + if (init.is_active) { + // Active segment. + if (init.table == 0) { + section.emit_u32v(kActiveNoIndex); + } else { + section.emit_u32v(kActiveWithIndex); + section.emit_u32v(init.table); + } + if (init.is_global) { + section.emit_u8(kExprGlobalGet); + } else { + section.emit_u8(kExprI32Const); + } + section.emit_u32v(init.base); + section.emit_u8(kExprEnd); + if (init.table != 0) { + section.emit_u8(kExternalFunction); + } + section.emit_u32v(init.array.length); + for (let index of init.array) { + section.emit_u32v(index); + } + } else { + // Passive segment. + section.emit_u8(kPassiveWithElements); // flags + section.emit_u8(kWasmAnyFunc); + section.emit_u32v(init.array.length); + for (let index of init.array) { + if (index === null) { + section.emit_u8(kExprRefNull); + section.emit_u8(kExprEnd); + } else { + section.emit_u8(kExprRefFunc); + section.emit_u32v(index); + section.emit_u8(kExprEnd); + } + } + } + } + }); + } + + // If there are any passive data segments, add the DataCount section. + if (wasm.data_segments.some(seg => !seg.is_active)) { + binary.emit_section(kDataCountSectionCode, section => { + section.emit_u32v(wasm.data_segments.length); + }); + } + + // Add function bodies. + if (wasm.functions.length > 0) { + // emit function bodies + if (debug) print("emitting code @ " + binary.length); + binary.emit_section(kCodeSectionCode, section => { + section.emit_u32v(wasm.functions.length); + let header = new Binary; + for (let func of wasm.functions) { + header.reset(); + // Function body length will be patched later. + let local_decls = []; + for (let l of func.locals || []) { + if (l.i32_count > 0) { + local_decls.push({count: l.i32_count, type: kWasmI32}); + } + if (l.i64_count > 0) { + local_decls.push({count: l.i64_count, type: kWasmI64}); + } + if (l.f32_count > 0) { + local_decls.push({count: l.f32_count, type: kWasmF32}); + } + if (l.f64_count > 0) { + local_decls.push({count: l.f64_count, type: kWasmF64}); + } + if (l.s128_count > 0) { + local_decls.push({count: l.s128_count, type: kWasmS128}); + } + if (l.anyref_count > 0) { + local_decls.push({count: l.anyref_count, type: kWasmAnyRef}); + } + if (l.anyfunc_count > 0) { + local_decls.push({count: l.anyfunc_count, type: kWasmAnyFunc}); + } + } + + header.emit_u32v(local_decls.length); + for (let decl of local_decls) { + header.emit_u32v(decl.count); + header.emit_u8(decl.type); + } + + section.emit_u32v(header.length + func.body.length); + section.emit_bytes(header.trunc_buffer()); + section.emit_bytes(func.body); + } + }); + } + + // Add data segments. + if (wasm.data_segments.length > 0) { + if (debug) print("emitting data segments @ " + binary.length); + binary.emit_section(kDataSectionCode, section => { + section.emit_u32v(wasm.data_segments.length); + for (let seg of wasm.data_segments) { + if (seg.is_active) { + section.emit_u8(0); // linear memory index 0 / flags + if (seg.is_global) { + // initializer is a global variable + section.emit_u8(kExprGlobalGet); + section.emit_u32v(seg.addr); + } else { + // initializer is a constant + section.emit_u8(kExprI32Const); + section.emit_u32v(seg.addr); + } + section.emit_u8(kExprEnd); + } else { + section.emit_u8(kPassive); // flags + } + section.emit_u32v(seg.data.length); + section.emit_bytes(seg.data); + } + }); + } + + // Add any explicitly added sections + for (let exp of wasm.explicit) { + if (debug) print("emitting explicit @ " + binary.length); + binary.emit_bytes(exp); + } + + // Add names. + let num_function_names = 0; + let num_functions_with_local_names = 0; + for (let func of wasm.functions) { + if (func.name !== undefined) ++num_function_names; + if (func.numLocalNames() > 0) ++num_functions_with_local_names; + } + if (num_function_names > 0 || num_functions_with_local_names > 0 || + wasm.name !== undefined) { + if (debug) print('emitting names @ ' + binary.length); + binary.emit_section(kUnknownSectionCode, section => { + section.emit_string('name'); + // Emit module name. + if (wasm.name !== undefined) { + section.emit_section(kModuleNameCode, name_section => { + name_section.emit_string(wasm.name); + }); + } + // Emit function names. + if (num_function_names > 0) { + section.emit_section(kFunctionNamesCode, name_section => { + name_section.emit_u32v(num_function_names); + for (let func of wasm.functions) { + if (func.name === undefined) continue; + name_section.emit_u32v(func.index); + name_section.emit_string(func.name); + } + }); + } + // Emit local names. + if (num_functions_with_local_names > 0) { + section.emit_section(kLocalNamesCode, name_section => { + name_section.emit_u32v(num_functions_with_local_names); + for (let func of wasm.functions) { + if (func.numLocalNames() == 0) continue; + name_section.emit_u32v(func.index); + name_section.emit_u32v(func.numLocalNames()); + for (let i = 0; i < func.local_names.length; ++i) { + if (func.local_names[i] === undefined) continue; + name_section.emit_u32v(i); + name_section.emit_string(func.local_names[i]); + } + } + }); + } + }); + } + + return binary.trunc_buffer(); + } + + toArray(debug = false) { + return Array.from(this.toBuffer(debug)); + } + + instantiate(ffi) { + let module = this.toModule(); + let instance = new WebAssembly.Instance(module, ffi); + return instance; + } + + asyncInstantiate(ffi) { + return WebAssembly.instantiate(this.toBuffer(), ffi) + .then(({module, instance}) => instance); + } + + toModule(debug = false) { + return new WebAssembly.Module(this.toBuffer(debug)); + } +} + +function wasmSignedLeb(val, max_len = 5) { + let res = []; + for (let i = 0; i < max_len; ++i) { + let v = val & 0x7f; + // If {v} sign-extended from 7 to 32 bits is equal to val, we are done. + if (((v << 25) >> 25) == val) { + res.push(v); + return res; + } + res.push(v | 0x80); + val = val >> 7; + } + throw new Error( + 'Leb value <' + val + '> exceeds maximum length of ' + max_len); +} + +function wasmI32Const(val) { + return [kExprI32Const, ...wasmSignedLeb(val, 5)]; +} + +function wasmF32Const(f) { + // Write in little-endian order at offset 0. + data_view.setFloat32(0, f, true); + return [ + kExprF32Const, byte_view[0], byte_view[1], byte_view[2], byte_view[3] + ]; +} + +function wasmF64Const(f) { + // Write in little-endian order at offset 0. + data_view.setFloat64(0, f, true); + return [ + kExprF64Const, byte_view[0], byte_view[1], byte_view[2], + byte_view[3], byte_view[4], byte_view[5], byte_view[6], byte_view[7] + ]; +} diff --git a/test/fixtures/wpt/wasm/webapi/META.yml b/test/fixtures/wpt/wasm/webapi/META.yml new file mode 100644 index 00000000000000..69715cd7c8dcf5 --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/META.yml @@ -0,0 +1 @@ +spec: https://webassembly.github.io/spec/web-api/ diff --git a/test/fixtures/wpt/wasm/webapi/abort.any.js b/test/fixtures/wpt/wasm/webapi/abort.any.js new file mode 100644 index 00000000000000..f5ddd353aa9e35 --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/abort.any.js @@ -0,0 +1,37 @@ +const methods = [ + "compileStreaming", + "instantiateStreaming", +]; + +for (const method of methods) { + promise_test(async t => { + const controller = new AbortController(); + const signal = controller.signal; + controller.abort(); + const request = fetch('../incrementer.wasm', { signal }); + return promise_rejects_dom(t, 'AbortError', WebAssembly[method](request), + `${method} should reject`); + }, `${method}() on an already-aborted request should reject with AbortError`); + + promise_test(async t => { + const controller = new AbortController(); + const signal = controller.signal; + const request = fetch('../incrementer.wasm', { signal }); + const promise = WebAssembly[method](request); + controller.abort(); + return promise_rejects_dom(t, 'AbortError', promise, `${method} should reject`); + }, `${method}() synchronously followed by abort should reject with AbortError`); + + promise_test(async t => { + const controller = new AbortController(); + const signal = controller.signal; + return fetch('../incrementer.wasm', { signal }) + .then(response => { + Promise.resolve().then(() => controller.abort()); + return WebAssembly[method](response); + }) + .catch(err => { + assert_equals(err.name, "AbortError"); + }); + }, `${method}() asynchronously racing with abort should succeed or reject with AbortError`); +} diff --git a/test/fixtures/wpt/wasm/webapi/body.any.js b/test/fixtures/wpt/wasm/webapi/body.any.js new file mode 100644 index 00000000000000..4db7e8d123cfb6 --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/body.any.js @@ -0,0 +1,19 @@ +// META: global=window,worker +// META: script=/wasm/jsapi/wasm-module-builder.js + +for (const method of ["compileStreaming", "instantiateStreaming"]) { + promise_test(t => { + const buffer = new WasmModuleBuilder().toBuffer(); + const argument = new Response(buffer, { headers: { "Content-Type": "application/wasm" } }); + argument.arrayBuffer(); + return promise_rejects_js(t, TypeError, WebAssembly[method](argument)); + }, `${method} after consumption`); + + promise_test(t => { + const buffer = new WasmModuleBuilder().toBuffer(); + const argument = new Response(buffer, { headers: { "Content-Type": "application/wasm" } }); + const promise = WebAssembly[method](argument); + argument.arrayBuffer(); + return promise_rejects_js(t, TypeError, promise); + }, `${method} before consumption`); +} diff --git a/test/fixtures/wpt/wasm/webapi/contenttype.any.js b/test/fixtures/wpt/wasm/webapi/contenttype.any.js new file mode 100644 index 00000000000000..0a2f5f1122ce46 --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/contenttype.any.js @@ -0,0 +1,64 @@ +// META: global=window,worker +// META: script=/wasm/jsapi/assertions.js + +promise_test(t => { + const response = fetch("/wasm/incrementer.wasm").then(res => new Response(res.body)); + return promise_rejects_js(t, TypeError, WebAssembly.compileStreaming(response)); +}, "Response with no Content-Type: compileStreaming"); + +promise_test(t => { + const response = fetch("/wasm/incrementer.wasm").then(res => new Response(res.body)); + return promise_rejects_js(t, TypeError, WebAssembly.instantiateStreaming(response)); +}, "Response with no Content-Type: instantiateStreaming"); + +const invalidContentTypes = [ + "", + "application/javascript", + "application/octet-stream", + "text/wasm", + "application/wasm;", + "application/wasm;x", + "application/wasm;charset=UTF-8", +]; + +for (const contenttype of invalidContentTypes) { + promise_test(t => { + const response = fetch(`/wasm/incrementer.wasm?pipe=header(Content-Type,${encodeURIComponent(contenttype)})`); + return promise_rejects_js(t, TypeError, WebAssembly.compileStreaming(response)); + }, `Response with Content-Type ${format_value(contenttype)}: compileStreaming`); + + promise_test(t => { + const response = fetch(`/wasm/incrementer.wasm?pipe=header(Content-Type,${encodeURIComponent(contenttype)})`); + return promise_rejects_js(t, TypeError, WebAssembly.instantiateStreaming(response)); + }, `Response with Content-Type ${format_value(contenttype)}: instantiateStreaming`); +} + +const validContentTypes = [ + "application/wasm", + "APPLICATION/wasm", + "APPLICATION/WASM", +]; + +for (const contenttype of validContentTypes) { + promise_test(async t => { + const response = fetch(`/wasm/incrementer.wasm?pipe=header(Content-Type,${encodeURIComponent(contenttype)})`); + const module = await WebAssembly.compileStreaming(response); + assert_equals(Object.getPrototypeOf(module), WebAssembly.Module.prototype, + "prototype"); + }, `Response with Content-Type ${format_value(contenttype)}: compileStreaming`); + + promise_test(async t => { + const response = fetch(`/wasm/incrementer.wasm?pipe=header(Content-Type,${encodeURIComponent(contenttype)})`); + const result = await WebAssembly.instantiateStreaming(response); + assert_WebAssemblyInstantiatedSource( + result, + { + "increment": { + "kind": "function", + "name": "0", + "length": 1 + } + } + ); + }, `Response with Content-Type ${format_value(contenttype)}: instantiateStreaming`); +} diff --git a/test/fixtures/wpt/wasm/webapi/empty-body.any.js b/test/fixtures/wpt/wasm/webapi/empty-body.any.js new file mode 100644 index 00000000000000..0771647b708ec3 --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/empty-body.any.js @@ -0,0 +1,20 @@ +// META: global=window,worker + +const invalidArguments = [ + [() => new Response(undefined, { headers: { "Content-Type": "application/wasm" } }), "no body"], + [() => new Response("", { headers: { "Content-Type": "application/wasm" } }), "empty body"], +]; + +for (const method of ["compileStreaming", "instantiateStreaming"]) { + for (const [argumentFactory, name] of invalidArguments) { + promise_test(t => { + const argument = argumentFactory(); + return promise_rejects_js(t, WebAssembly.CompileError, WebAssembly[method](argument)); + }, `${method}: ${name}`); + + promise_test(t => { + const argument = Promise.resolve(argumentFactory()); + return promise_rejects_js(t, WebAssembly.CompileError, WebAssembly[method](argument)); + }, `${method}: ${name} in a promise`); + } +} diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/execute-start.tentative.html b/test/fixtures/wpt/wasm/webapi/esm-integration/execute-start.tentative.html new file mode 100644 index 00000000000000..a35adbe8ebfc1c --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/esm-integration/execute-start.tentative.html @@ -0,0 +1,23 @@ + +Check execution of WebAssembly start function + + + + + diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/exported-names.tentative.html b/test/fixtures/wpt/wasm/webapi/esm-integration/exported-names.tentative.html new file mode 100644 index 00000000000000..16a9c59787bc94 --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/esm-integration/exported-names.tentative.html @@ -0,0 +1,17 @@ + +Exported names from a WebAssembly module + + + + diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/invalid-bytecode.tentative.html b/test/fixtures/wpt/wasm/webapi/esm-integration/invalid-bytecode.tentative.html new file mode 100644 index 00000000000000..0e447dbee51042 --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/esm-integration/invalid-bytecode.tentative.html @@ -0,0 +1,24 @@ + +Handling of importing invalid WebAssembly modules + + + + + + diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/js-wasm-cycle-errors.tentative.html b/test/fixtures/wpt/wasm/webapi/esm-integration/js-wasm-cycle-errors.tentative.html new file mode 100644 index 00000000000000..f45e06ece5f3bc --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/esm-integration/js-wasm-cycle-errors.tentative.html @@ -0,0 +1,38 @@ + +Cyclic linking between JavaScript and WebAssembly (JS higher) + + + + + + + + + diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/js-wasm-cycle.tentative.html b/test/fixtures/wpt/wasm/webapi/esm-integration/js-wasm-cycle.tentative.html new file mode 100644 index 00000000000000..38b0d3203c27d8 --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/esm-integration/js-wasm-cycle.tentative.html @@ -0,0 +1,11 @@ + +Check bindings in JavaScript and WebAssembly cycle (JS higher) + + + + diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/module-parse-error.tentative.html b/test/fixtures/wpt/wasm/webapi/esm-integration/module-parse-error.tentative.html new file mode 100644 index 00000000000000..0e447dbee51042 --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/esm-integration/module-parse-error.tentative.html @@ -0,0 +1,24 @@ + +Handling of importing invalid WebAssembly modules + + + + + + diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resolve-export.js b/test/fixtures/wpt/wasm/webapi/esm-integration/resolve-export.js new file mode 100644 index 00000000000000..e0dcf493f8e780 --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resolve-export.js @@ -0,0 +1 @@ +export { f } from "./resources/resolve-export.wasm"; diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resolve-export.tentative.html b/test/fixtures/wpt/wasm/webapi/esm-integration/resolve-export.tentative.html new file mode 100644 index 00000000000000..14688221021d53 --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resolve-export.tentative.html @@ -0,0 +1,25 @@ + +Check ResolveExport on invalid re-export from WebAssembly + + + + + diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/execute-start.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/execute-start.wasm new file mode 100644 index 00000000000000..ecfdda1f9af82c Binary files /dev/null and b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/execute-start.wasm differ diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/exported-names.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/exported-names.wasm new file mode 100644 index 00000000000000..ebffad193c342c Binary files /dev/null and b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/exported-names.wasm differ diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/invalid-bytecode.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/invalid-bytecode.wasm new file mode 100644 index 00000000000000..1ae8b721f3be9c Binary files /dev/null and b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/invalid-bytecode.wasm differ diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/invalid-module.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/invalid-module.wasm new file mode 100644 index 00000000000000..dd711f0953bf8f Binary files /dev/null and b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/invalid-module.wasm differ diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-function-error.js b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-function-error.js new file mode 100644 index 00000000000000..06cb8a0ad9f328 --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-function-error.js @@ -0,0 +1,2 @@ +export const func = 42; +import { f } from "./js-wasm-cycle-function-error.wasm"; diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-function-error.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-function-error.wasm new file mode 100644 index 00000000000000..b89d94dde74dc7 Binary files /dev/null and b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-function-error.wasm differ diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-global.js b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-global.js new file mode 100644 index 00000000000000..1f375b8ce1576a --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-global.js @@ -0,0 +1,2 @@ +export const glob = new WebAssembly.Global({ value: "i32" }, 42); +import { f } from "./js-wasm-cycle-global.wasm"; diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-global.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-global.wasm new file mode 100644 index 00000000000000..2a9017f87b123e Binary files /dev/null and b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-global.wasm differ diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-memory.js b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-memory.js new file mode 100644 index 00000000000000..92e37a86acd866 --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-memory.js @@ -0,0 +1,2 @@ +export const mem = new WebAssembly.Memory({ initial: 10 }); +import { f } from "./js-wasm-cycle-memory.wasm"; diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-memory.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-memory.wasm new file mode 100644 index 00000000000000..e699a9b3c47c49 Binary files /dev/null and b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-memory.wasm differ diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-table.js b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-table.js new file mode 100644 index 00000000000000..5d6794489f0e16 --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-table.js @@ -0,0 +1,2 @@ +export const tab = new WebAssembly.Table({ element: "anyfunc" }); +import { f } from "./js-wasm-cycle-table.wasm"; diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-table.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-table.wasm new file mode 100644 index 00000000000000..ec4883e652e468 Binary files /dev/null and b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-table.wasm differ diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-value.js b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-value.js new file mode 100644 index 00000000000000..f7b0d62080b31d --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-value.js @@ -0,0 +1,2 @@ +export const val = 42; +import { f } from "./js-wasm-cycle-value.wasm"; diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-value.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-value.wasm new file mode 100644 index 00000000000000..083409e2606b67 Binary files /dev/null and b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-value.wasm differ diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle.js b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle.js new file mode 100644 index 00000000000000..8ee579e2ad3421 --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle.js @@ -0,0 +1,13 @@ +function f() { return 42; } +export { f }; + +import { mem, tab, glob, func } from "./js-wasm-cycle.wasm"; +assert_true(glob instanceof WebAssembly.Global); +assert_equals(glob.valueOf(), 1); +assert_true(mem instanceof WebAssembly.Memory); +assert_true(tab instanceof WebAssembly.Table); +assert_true(func instanceof Function); + +f = () => { return 24 }; + +assert_equals(func(), 42); diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle.wasm new file mode 100644 index 00000000000000..77a3b86ab67528 Binary files /dev/null and b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle.wasm differ diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/log.js b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/log.js new file mode 100644 index 00000000000000..0c4f5ed519b0fd --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/log.js @@ -0,0 +1 @@ +export function logExec() { log.push("executed"); } diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/resolve-export.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/resolve-export.wasm new file mode 100644 index 00000000000000..d8fc92d022fbf4 Binary files /dev/null and b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/resolve-export.wasm differ diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-export-i64-global.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-export-i64-global.wasm new file mode 100644 index 00000000000000..f9f0cf27992d4b Binary files /dev/null and b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-export-i64-global.wasm differ diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-export-to-wasm.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-export-to-wasm.wasm new file mode 100644 index 00000000000000..0ee948f96fdfbb Binary files /dev/null and b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-export-to-wasm.wasm differ diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-error-from-wasm.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-error-from-wasm.wasm new file mode 100644 index 00000000000000..c27bcb068de86e Binary files /dev/null and b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-error-from-wasm.wasm differ diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-from-wasm.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-from-wasm.wasm new file mode 100644 index 00000000000000..652ff143100f83 Binary files /dev/null and b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-from-wasm.wasm differ diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-func.js b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-func.js new file mode 100644 index 00000000000000..78982c32dc69d7 --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-func.js @@ -0,0 +1 @@ +export let f = 5; diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-func.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-func.wasm new file mode 100644 index 00000000000000..2f23c58520fe59 Binary files /dev/null and b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-func.wasm differ diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-global.js b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-global.js new file mode 100644 index 00000000000000..4258cd2d7d1e95 --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-global.js @@ -0,0 +1 @@ +export let g = 5; diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-global.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-global.wasm new file mode 100644 index 00000000000000..2f8bd77940cc21 Binary files /dev/null and b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-global.wasm differ diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-memory.js b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-memory.js new file mode 100644 index 00000000000000..4cee8898383784 --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-memory.js @@ -0,0 +1 @@ +export let m = 5; diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-memory.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-memory.wasm new file mode 100644 index 00000000000000..d9474047cd334b Binary files /dev/null and b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-memory.wasm differ diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-table.js b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-table.js new file mode 100644 index 00000000000000..ca823646cbb365 --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-table.js @@ -0,0 +1 @@ +export let t = 5; diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-table.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-table.wasm new file mode 100644 index 00000000000000..8ccc8be7f21382 Binary files /dev/null and b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-table.wasm differ diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-js-cycle.js b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-js-cycle.js new file mode 100644 index 00000000000000..161edab4f6f1f1 --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-js-cycle.js @@ -0,0 +1,15 @@ +import * as mod from "./wasm-js-cycle.wasm"; + +let jsGlob = new WebAssembly.Global({ value: "i32", mutable: true }, 42); +let jsMem = new WebAssembly.Memory({ initial: 10 }); +let jsTab = new WebAssembly.Table({ initial: 10, element: "anyfunc" }); +let jsFunc = () => { return 42; }; + +export { jsGlob, jsMem, jsTab, jsFunc }; + +export function mutateBindings() { + jsGlob = 0; + jsMem = 0; + jsTab = 0; + jsFunc = 0; +} diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-js-cycle.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-js-cycle.wasm new file mode 100644 index 00000000000000..b700377b279031 Binary files /dev/null and b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-js-cycle.wasm differ diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/worker-helper.js b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/worker-helper.js new file mode 100644 index 00000000000000..277bb4c1ea5b79 --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/worker-helper.js @@ -0,0 +1 @@ +export function pm(x) { postMessage(x); } diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/worker.js b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/worker.js new file mode 100644 index 00000000000000..c72464f71a4561 --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/worker.js @@ -0,0 +1 @@ +import * as mod from "./worker.wasm" diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/worker.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/worker.wasm new file mode 100644 index 00000000000000..e942dc54acf77e Binary files /dev/null and b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/worker.wasm differ diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/wasm-import-wasm-export.tentative.html b/test/fixtures/wpt/wasm/webapi/esm-integration/wasm-import-wasm-export.tentative.html new file mode 100644 index 00000000000000..3761a22f218db0 --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/esm-integration/wasm-import-wasm-export.tentative.html @@ -0,0 +1,14 @@ + +Check import and export between WebAssembly modules + + + + diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/wasm-import.tentative.html b/test/fixtures/wpt/wasm/webapi/esm-integration/wasm-import.tentative.html new file mode 100644 index 00000000000000..243cfd46e4bdf0 --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/esm-integration/wasm-import.tentative.html @@ -0,0 +1,34 @@ + +Errors for imports of WebAssembly modules + + + + + + + + diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/wasm-js-cycle.tentative.html b/test/fixtures/wpt/wasm/webapi/esm-integration/wasm-js-cycle.tentative.html new file mode 100644 index 00000000000000..298d4d40b0006e --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/esm-integration/wasm-js-cycle.tentative.html @@ -0,0 +1,32 @@ + +Check bindings in JavaScript and WebAssembly cycle (Wasm higher) + + + + diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/wasm-to-wasm-link-error.tentative.html b/test/fixtures/wpt/wasm/webapi/esm-integration/wasm-to-wasm-link-error.tentative.html new file mode 100644 index 00000000000000..6c43e72b09bfe7 --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/esm-integration/wasm-to-wasm-link-error.tentative.html @@ -0,0 +1,26 @@ + +Errors for linking WebAssembly module scripts + + + + + diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/worker-import.tentative.html b/test/fixtures/wpt/wasm/webapi/esm-integration/worker-import.tentative.html new file mode 100644 index 00000000000000..739f2d3f28cc68 --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/esm-integration/worker-import.tentative.html @@ -0,0 +1,13 @@ + +Testing import of WebAssembly from JavaScript worker + + + + diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/worker.tentative.html b/test/fixtures/wpt/wasm/webapi/esm-integration/worker.tentative.html new file mode 100644 index 00000000000000..8002e07ce7f1cf --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/esm-integration/worker.tentative.html @@ -0,0 +1,13 @@ + +Testing WebAssembly worker + + + + diff --git a/test/fixtures/wpt/wasm/webapi/historical.any.js b/test/fixtures/wpt/wasm/webapi/historical.any.js new file mode 100644 index 00000000000000..257112c4160f54 --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/historical.any.js @@ -0,0 +1,29 @@ +// META: global=window,worker + +promise_test(async t => { + const db_name = "WebAssembly"; + const obj_store = "store"; + const module_key = "module"; + + await new Promise((resolve, reject) => { + const delete_request = indexedDB.deleteDatabase(db_name); + delete_request.onsuccess = resolve; + delete_request.onerror = reject; + }); + + const db = await new Promise((resolve, reject) => { + const open_request = indexedDB.open(db_name); + open_request.onupgradeneeded = function() { + open_request.result.createObjectStore(obj_store); + }; + open_request.onsuccess = function() { + resolve(open_request.result); + }; + open_request.onerror = reject; + }); + + const mod = await WebAssembly.compileStreaming(fetch('../incrementer.wasm')); + const tx = db.transaction(obj_store, 'readwrite'); + const store = tx.objectStore(obj_store); + assert_throws_dom("DataCloneError", () => store.put(mod, module_key)); +}); diff --git a/test/fixtures/wpt/wasm/webapi/idlharness.any.js b/test/fixtures/wpt/wasm/webapi/idlharness.any.js new file mode 100644 index 00000000000000..0c4669e6caa7b2 --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/idlharness.any.js @@ -0,0 +1,10 @@ +// META: script=/resources/WebIDLParser.js +// META: script=/resources/idlharness.js + +"use strict"; + +idl_test( + ["wasm-web-api"], + ["wasm-js-api"], + idl_array => {} +); diff --git a/test/fixtures/wpt/wasm/webapi/instantiateStreaming-bad-imports.any.js b/test/fixtures/wpt/wasm/webapi/instantiateStreaming-bad-imports.any.js new file mode 100644 index 00000000000000..38ecc40252e51d --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/instantiateStreaming-bad-imports.any.js @@ -0,0 +1,13 @@ +// META: global=window,worker +// META: script=/wasm/jsapi/wasm-module-builder.js +// META: script=/wasm/jsapi/bad-imports.js + +test_bad_imports((name, error, build, ...args) => { + promise_test(t => { + const builder = new WasmModuleBuilder(); + build(builder); + const buffer = builder.toBuffer(); + const response = new Response(buffer, { "headers": { "Content-Type": "application/wasm" } }); + return promise_rejects_js(t, error, WebAssembly.instantiateStreaming(response, ...args)); + }, name); +}); diff --git a/test/fixtures/wpt/wasm/webapi/instantiateStreaming.any.js b/test/fixtures/wpt/wasm/webapi/instantiateStreaming.any.js new file mode 100644 index 00000000000000..cf3a5e7331f30e --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/instantiateStreaming.any.js @@ -0,0 +1,49 @@ +// META: global=window,worker +// META: script=/wasm/jsapi/wasm-module-builder.js +// META: script=/wasm/jsapi/assertions.js +// META: script=/wasm/jsapi/instanceTestFactory.js + +let emptyModuleBinary; +setup(() => { + emptyModuleBinary = new WasmModuleBuilder().toBuffer(); +}); + +for (const [name, fn] of instanceTestFactory) { + promise_test(async () => { + const { buffer, args, exports, verify } = fn(); + const response = new Response(buffer, { "headers": { "Content-Type": "application/wasm" } }); + const result = await WebAssembly.instantiateStreaming(response, ...args); + assert_WebAssemblyInstantiatedSource(result, exports); + verify(result.instance); + }, name); +} + +promise_test(async () => { + const builder = new WasmModuleBuilder(); + builder.addImportedGlobal("module", "global", kWasmI32); + const buffer = builder.toBuffer(); + const response = new Response(buffer, { "headers": { "Content-Type": "application/wasm" } }); + const order = []; + + const imports = { + get module() { + order.push("module getter"); + return { + get global() { + order.push("global getter"); + return 0; + }, + } + }, + }; + + const expected = [ + "module getter", + "global getter", + ]; + const p = WebAssembly.instantiateStreaming(response, imports); + assert_array_equals(order, []); + const result = await p; + assert_WebAssemblyInstantiatedSource(result, {}); + assert_array_equals(order, expected); +}, "Synchronous options handling"); diff --git a/test/fixtures/wpt/wasm/webapi/invalid-args.any.js b/test/fixtures/wpt/wasm/webapi/invalid-args.any.js new file mode 100644 index 00000000000000..b27e018a984e39 --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/invalid-args.any.js @@ -0,0 +1,28 @@ +// META: global=window,worker + +const invalidArguments = [ + [undefined], + [null], + [true], + ["test"], + [Symbol()], + [0], + [0.1], + [NaN], + [{}, "Empty object"], + [Response, "Response interface object"], + [Response.prototype, "Response interface prototype object"], +]; + +for (const method of ["compileStreaming", "instantiateStreaming"]) { + for (const [argument, name = format_value(argument)] of invalidArguments) { + promise_test(t => { + return promise_rejects_js(t, TypeError, WebAssembly[method](argument)); + }, `${method}: ${name}`); + + promise_test(t => { + const promise = Promise.resolve(argument); + return promise_rejects_js(t, TypeError, WebAssembly[method](argument)); + }, `${method}: ${name} in a promise`); + } +} diff --git a/test/fixtures/wpt/wasm/webapi/invalid-code.any.js b/test/fixtures/wpt/wasm/webapi/invalid-code.any.js new file mode 100644 index 00000000000000..37373d49971a3d --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/invalid-code.any.js @@ -0,0 +1,21 @@ +// META: global=window,worker +// META: script=/wasm/jsapi/wasm-module-builder.js + +let emptyModuleBinary; +setup(() => { + emptyModuleBinary = new WasmModuleBuilder().toBuffer(); +}); + +for (const method of ["compileStreaming", "instantiateStreaming"]) { + promise_test(t => { + const buffer = new Uint8Array(Array.from(emptyModuleBinary).concat([0, 0])); + const response = new Response(buffer, { headers: { "Content-Type": "application/wasm" } }); + return promise_rejects_js(t, WebAssembly.CompileError, WebAssembly[method](response)); + }, `Invalid code (0x0000): ${method}`); + + promise_test(t => { + const buffer = new Uint8Array(Array.from(emptyModuleBinary).concat([0xCA, 0xFE])); + const response = new Response(buffer, { headers: { "Content-Type": "application/wasm" } }); + return promise_rejects_js(t, WebAssembly.CompileError, WebAssembly[method](response)); + }, `Invalid code (0xCAFE): ${method}`); +} diff --git a/test/fixtures/wpt/wasm/webapi/modified-contenttype.any.js b/test/fixtures/wpt/wasm/webapi/modified-contenttype.any.js new file mode 100644 index 00000000000000..354930517c73ea --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/modified-contenttype.any.js @@ -0,0 +1,24 @@ +// META: global=window,worker +// META: script=/wasm/jsapi/wasm-module-builder.js + +["compileStreaming", "instantiateStreaming"].forEach(method => { + promise_test(async t => { + const buffer = new WasmModuleBuilder().toBuffer(); + const argument = new Response(buffer, { headers: { "Content-Type": "test/test" } }); + argument.headers.set("Content-Type", "application/wasm"); + // This should resolve successfully + await WebAssembly[method](argument); + // Ensure body can only be read once + return promise_rejects_js(t, TypeError, argument.blob()); + }, `${method} with Content-Type set late`); + + promise_test(async t => { + const buffer = new WasmModuleBuilder().toBuffer(); + const argument = new Response(buffer, { headers: { "Content-Type": "application/wasm" } }); + argument.headers.delete("Content-Type"); + // Ensure Wasm cannot be created + await promise_rejects_js(t, TypeError, WebAssembly[method](argument)); + // This should resolve successfully + await argument.arrayBuffer(); + }, `${method} with Content-Type removed late`); +}); diff --git a/test/fixtures/wpt/wasm/webapi/origin.sub.any.js b/test/fixtures/wpt/wasm/webapi/origin.sub.any.js new file mode 100644 index 00000000000000..bf7901eeddee7c --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/origin.sub.any.js @@ -0,0 +1,15 @@ +// META: global=window,worker + +for (const method of ["compileStreaming", "instantiateStreaming"]) { + promise_test(t => { + const url = "http://{{domains[www]}}:{{ports[http][0]}}/wasm/incrementer.wasm"; + const response = fetch(url, { "mode": "no-cors" }); + return promise_rejects_js(t, TypeError, WebAssembly[method](response)); + }, `Opaque response: ${method}`); + + promise_test(t => { + const url = "/fetch/api/resources/redirect.py?redirect_status=301&location=/wasm/incrementer.wasm"; + const response = fetch(url, { "mode": "no-cors", "redirect": "manual" }); + return promise_rejects_js(t, TypeError, WebAssembly[method](response)); + }, `Opaque redirect response: ${method}`); +} diff --git a/test/fixtures/wpt/wasm/webapi/rejected-arg.any.js b/test/fixtures/wpt/wasm/webapi/rejected-arg.any.js new file mode 100644 index 00000000000000..49018db5e89eb0 --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/rejected-arg.any.js @@ -0,0 +1,9 @@ +// META: global=window,worker + +for (const method of ["compileStreaming", "instantiateStreaming"]) { + promise_test(t => { + const error = { "name": "custom error" }; + const promise = Promise.reject(error); + return promise_rejects_exactly(t, error, WebAssembly[method](promise)); + }, `${method}`); +} diff --git a/test/fixtures/wpt/wasm/webapi/resources/incrementer.no_mime_type.wasm b/test/fixtures/wpt/wasm/webapi/resources/incrementer.no_mime_type.wasm new file mode 100644 index 00000000000000..47afcdef2a2812 Binary files /dev/null and b/test/fixtures/wpt/wasm/webapi/resources/incrementer.no_mime_type.wasm differ diff --git a/test/fixtures/wpt/wasm/webapi/resources/incrementer.wasm b/test/fixtures/wpt/wasm/webapi/resources/incrementer.wasm new file mode 100644 index 00000000000000..47afcdef2a2812 Binary files /dev/null and b/test/fixtures/wpt/wasm/webapi/resources/incrementer.wasm differ diff --git a/test/fixtures/wpt/wasm/webapi/resources/incrementer.wasm.headers b/test/fixtures/wpt/wasm/webapi/resources/incrementer.wasm.headers new file mode 100644 index 00000000000000..76b9c163b6cd33 --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/resources/incrementer.wasm.headers @@ -0,0 +1,2 @@ +Content-Type: application/wasm +Cache-Control: max-age=3600 diff --git a/test/fixtures/wpt/wasm/webapi/resources/incrementer.wrong_mime_type.wasm b/test/fixtures/wpt/wasm/webapi/resources/incrementer.wrong_mime_type.wasm new file mode 100644 index 00000000000000..47afcdef2a2812 Binary files /dev/null and b/test/fixtures/wpt/wasm/webapi/resources/incrementer.wrong_mime_type.wasm differ diff --git a/test/fixtures/wpt/wasm/webapi/resources/incrementer.wrong_mime_type.wasm.headers b/test/fixtures/wpt/wasm/webapi/resources/incrementer.wrong_mime_type.wasm.headers new file mode 100644 index 00000000000000..833ee71634def2 --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/resources/incrementer.wrong_mime_type.wasm.headers @@ -0,0 +1,2 @@ +Content-Type: text/css +Cache-Control: max-age=3600 diff --git a/test/fixtures/wpt/wasm/webapi/status.any.js b/test/fixtures/wpt/wasm/webapi/status.any.js new file mode 100644 index 00000000000000..f3859646cc54b9 --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/status.any.js @@ -0,0 +1,21 @@ +// META: global=window,worker + +const statuses = [ + 0, + 300, + 400, + 404, + 500, + 600, + 700, + 999, +]; + +for (const method of ["compileStreaming", "instantiateStreaming"]) { + for (const status of statuses) { + promise_test(t => { + const response = fetch(`status.py?status=${status}`); + return promise_rejects_js(t, TypeError, WebAssembly[method](response)); + }, `Response with status ${status}: ${method}`); + } +} diff --git a/test/fixtures/wpt/wasm/webapi/wasm_stream_compile_test.html b/test/fixtures/wpt/wasm/webapi/wasm_stream_compile_test.html new file mode 100644 index 00000000000000..790410e425befd --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/wasm_stream_compile_test.html @@ -0,0 +1,115 @@ + + +WebAssembly.compileStreaming + + + + diff --git a/test/fixtures/wpt/wasm/webapi/wasm_stream_instantiate_test.html b/test/fixtures/wpt/wasm/webapi/wasm_stream_instantiate_test.html new file mode 100644 index 00000000000000..f39f6504953f5e --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/wasm_stream_instantiate_test.html @@ -0,0 +1,115 @@ + + +WebAssembly.instantiateStreaming + + + + diff --git a/test/internet/test-dgram-broadcast-multi-process.js b/test/internet/test-dgram-broadcast-multi-process.js index 5972c5e24e537c..daa19ada43d0da 100644 --- a/test/internet/test-dgram-broadcast-multi-process.js +++ b/test/internet/test-dgram-broadcast-multi-process.js @@ -47,7 +47,7 @@ get_bindAddress: for (const name in networkInterfaces) { const interfaces = networkInterfaces[name]; for (let i = 0; i < interfaces.length; i++) { const localInterface = interfaces[i]; - if (!localInterface.internal && localInterface.family === 'IPv4') { + if (!localInterface.internal && localInterface.family === 4) { bindAddress = localInterface.address; break get_bindAddress; } diff --git a/test/internet/test-dgram-multicast-set-interface-lo.js b/test/internet/test-dgram-multicast-set-interface-lo.js index 11c3d47cff0637..0d93ebb1227530 100644 --- a/test/internet/test-dgram-multicast-set-interface-lo.js +++ b/test/internet/test-dgram-multicast-set-interface-lo.js @@ -49,7 +49,7 @@ const TMPL = (tail) => `${NOW} - ${tail}`; const interfaceAddress = ((networkInterfaces) => { for (const name in networkInterfaces) { for (const localInterface of networkInterfaces[name]) { - if (!localInterface.internal && localInterface.family === FAM) { + if (!localInterface.internal && `IPv${localInterface.family}` === FAM) { let interfaceAddress = localInterface.address; // On Windows, IPv6 would need: `%${localInterface.scopeid}` if (FAM === 'IPv6') diff --git a/test/internet/test-dgram-multicast-ssm-multi-process.js b/test/internet/test-dgram-multicast-ssm-multi-process.js index 324c989a180fa0..a6919ca57c3300 100644 --- a/test/internet/test-dgram-multicast-ssm-multi-process.js +++ b/test/internet/test-dgram-multicast-ssm-multi-process.js @@ -28,7 +28,7 @@ get_sourceAddress: for (const name in networkInterfaces) { const interfaces = networkInterfaces[name]; for (let i = 0; i < interfaces.length; i++) { const localInterface = interfaces[i]; - if (!localInterface.internal && localInterface.family === 'IPv4') { + if (!localInterface.internal && localInterface.family === 4) { sourceAddress = localInterface.address; break get_sourceAddress; } diff --git a/test/internet/test-dgram-multicast-ssmv6-multi-process.js b/test/internet/test-dgram-multicast-ssmv6-multi-process.js index fd7b8dcd4ce011..a23b1236f4656e 100644 --- a/test/internet/test-dgram-multicast-ssmv6-multi-process.js +++ b/test/internet/test-dgram-multicast-ssmv6-multi-process.js @@ -28,7 +28,7 @@ get_sourceAddress: for (const name in networkInterfaces) { const interfaces = networkInterfaces[name]; for (let i = 0; i < interfaces.length; i++) { const localInterface = interfaces[i]; - if (!localInterface.internal && localInterface.family === 'IPv6') { + if (!localInterface.internal && localInterface.family === 6) { sourceAddress = localInterface.address; break get_sourceAddress; } diff --git a/test/internet/test-dns-ipv4.js b/test/internet/test-dns-ipv4.js index a84f7e644fc9cf..b673e2f920d0d4 100644 --- a/test/internet/test-dns-ipv4.js +++ b/test/internet/test-dns-ipv4.js @@ -1,3 +1,5 @@ +// Flags: --dns-result-order=ipv4first + 'use strict'; const common = require('../common'); const { addresses } = require('../common/internet'); diff --git a/test/internet/test-dns-lookup.js b/test/internet/test-dns-lookup.js index 6efa946f9d7522..d4e3d6d1eb762b 100644 --- a/test/internet/test-dns-lookup.js +++ b/test/internet/test-dns-lookup.js @@ -45,17 +45,10 @@ dns.lookup(addresses.NOT_FOUND, { assert.strictEqual(error.hostname, addresses.NOT_FOUND); })); -common.expectWarning('DeprecationWarning', - 'Type coercion of dns.lookup options is deprecated', - 'DEP0153'); - -assert.rejects( - dnsPromises.lookup(addresses.NOT_FOUND, { +assert.throws( + () => dnsPromises.lookup(addresses.NOT_FOUND, { family: 'IPv4', all: 'all' }), - { - code: 'ENOTFOUND', - message: `getaddrinfo ENOTFOUND ${addresses.NOT_FOUND}` - } + { code: 'ERR_INVALID_ARG_VALUE' } ); diff --git a/test/js-native-api/common.h b/test/js-native-api/common.h index 73f60906630140..46784059a1f70a 100644 --- a/test/js-native-api/common.h +++ b/test/js-native-api/common.h @@ -62,6 +62,9 @@ #define DECLARE_NODE_API_GETTER(name, func) \ { (name), NULL, NULL, (func), NULL, NULL, napi_default, NULL } +#define DECLARE_NODE_API_PROPERTY_VALUE(name, value) \ + { (name), NULL, NULL, NULL, NULL, (value), napi_default, NULL } + void add_returned_status(napi_env env, const char* key, napi_value object, diff --git a/test/js-native-api/test_exception/test_exception.c b/test/js-native-api/test_exception/test_exception.c index 844f4475ac4d4c..053f048466d930 100644 --- a/test/js-native-api/test_exception/test_exception.c +++ b/test/js-native-api/test_exception/test_exception.c @@ -2,6 +2,7 @@ #include "../common.h" static bool exceptionWasPending = false; +static int num = 0x23432; static napi_value returnException(napi_env env, napi_callback_info info) { size_t argc = 1; @@ -83,7 +84,7 @@ static napi_value createExternal(napi_env env, napi_callback_info info) { napi_value external; NODE_API_CALL(env, - napi_create_external(env, NULL, finalizer, NULL, &external)); + napi_create_external(env, &num, finalizer, NULL, &external)); return external; } diff --git a/test/message/async_error_nexttick_main.out b/test/message/async_error_nexttick_main.out index 8d11dea63d4191..9669e9b5102ff9 100644 --- a/test/message/async_error_nexttick_main.out +++ b/test/message/async_error_nexttick_main.out @@ -1,7 +1,7 @@ Error: test at one (*fixtures*async-error.js:4:9) at two (*fixtures*async-error.js:17:9) - at processTicksAndRejections (node:internal/process/task_queues:*:*) + at process.processTicksAndRejections (node:internal/process/task_queues:*:*) at async three (*fixtures*async-error.js:20:3) at async four (*fixtures*async-error.js:24:3) at async main (*message*async_error_nexttick_main.js:7:5) diff --git a/test/message/core_line_numbers.out b/test/message/core_line_numbers.out index 54b3b0ac2dc832..bdba01a223c08f 100644 --- a/test/message/core_line_numbers.out +++ b/test/message/core_line_numbers.out @@ -7,9 +7,9 @@ RangeError: Invalid input at Object.decode (node:punycode:*:*) at Object. (*test*message*core_line_numbers.js:*:*) at Module._compile (node:internal/modules/cjs/loader:*:*) - at Object.Module._extensions..js (node:internal/modules/cjs/loader:*:*) + at Module._extensions..js (node:internal/modules/cjs/loader:*:*) at Module.load (node:internal/modules/cjs/loader:*:*) - at Function.Module._load (node:internal/modules/cjs/loader:*:*) + at Module._load (node:internal/modules/cjs/loader:*:*) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:*:*) at node:internal/main/run_main_module:*:* diff --git a/test/message/error_aggregateTwoErrors.out b/test/message/error_aggregateTwoErrors.out index fef3953451ece9..34f56f450deb94 100644 --- a/test/message/error_aggregateTwoErrors.out +++ b/test/message/error_aggregateTwoErrors.out @@ -4,9 +4,9 @@ throw aggregateTwoErrors(err, originalError); AggregateError: original at Object. (*test*message*error_aggregateTwoErrors.js:*:*) at Module._compile (node:internal/modules/cjs/loader:*:*) - at Object.Module._extensions..js (node:internal/modules/cjs/loader:*:*) + at Module._extensions..js (node:internal/modules/cjs/loader:*:*) at Module.load (node:internal/modules/cjs/loader:*:*) - at Function.Module._load (node:internal/modules/cjs/loader:*:*) + at Module._load (node:internal/modules/cjs/loader:*:*) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:*:*) at node:internal/main/run_main_module:*:* { code: 'ERR0' diff --git a/test/message/error_exit.out b/test/message/error_exit.out index da535b05c105e9..d10abedb0880ae 100644 --- a/test/message/error_exit.out +++ b/test/message/error_exit.out @@ -9,9 +9,9 @@ AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: at Object. (*test*message*error_exit.js:*:*) at Module._compile (node:internal/modules/cjs/loader:*:*) - at Object.Module._extensions..js (node:internal/modules/cjs/loader:*:*) + at Module._extensions..js (node:internal/modules/cjs/loader:*:*) at Module.load (node:internal/modules/cjs/loader:*:*) - at Function.Module._load (node:internal/modules/cjs/loader:*:*) + at Module._load (node:internal/modules/cjs/loader:*:*) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:*:*) at node:internal/main/run_main_module:*:* { generatedMessage: true, diff --git a/test/message/error_with_nul.out b/test/message/error_with_nul.out index 4d3172439a915f..91b22a240231aa 100644 Binary files a/test/message/error_with_nul.out and b/test/message/error_with_nul.out differ diff --git a/test/message/events_unhandled_error_common_trace.out b/test/message/events_unhandled_error_common_trace.out index 0c10dd53fa2fdf..cfff44a7b3305f 100644 --- a/test/message/events_unhandled_error_common_trace.out +++ b/test/message/events_unhandled_error_common_trace.out @@ -7,9 +7,9 @@ Error: foo:bar at foo (*events_unhandled_error_common_trace.js:*:*) at Object. (*events_unhandled_error_common_trace.js:*:*) at Module._compile (node:internal/modules/cjs/loader:*:*) - at Object.Module._extensions..js (node:internal/modules/cjs/loader:*:*) + at Module._extensions..js (node:internal/modules/cjs/loader:*:*) at Module.load (node:internal/modules/cjs/loader:*:*) - at Function.Module._load (node:internal/modules/cjs/loader:*:*) + at Module._load (node:internal/modules/cjs/loader:*:*) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:*:*) at node:internal/main/run_main_module:*:* Emitted 'error' event at: diff --git a/test/message/events_unhandled_error_nexttick.out b/test/message/events_unhandled_error_nexttick.out index 533b3ae463e560..a5a220da44f94b 100644 --- a/test/message/events_unhandled_error_nexttick.out +++ b/test/message/events_unhandled_error_nexttick.out @@ -5,13 +5,13 @@ node:events:* Error at Object. (*events_unhandled_error_nexttick.js:*:*) at Module._compile (node:internal/modules/cjs/loader:*:*) - at Object.Module._extensions..js (node:internal/modules/cjs/loader:*:*) + at Module._extensions..js (node:internal/modules/cjs/loader:*:*) at Module.load (node:internal/modules/cjs/loader:*:*) - at Function.Module._load (node:internal/modules/cjs/loader:*:*) + at Module._load (node:internal/modules/cjs/loader:*:*) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:*:*) at node:internal/main/run_main_module:*:* Emitted 'error' event at: at *events_unhandled_error_nexttick.js:*:* - at processTicksAndRejections (node:internal/process/task_queues:*:*) + at process.processTicksAndRejections (node:internal/process/task_queues:*:*) Node.js * diff --git a/test/message/events_unhandled_error_sameline.out b/test/message/events_unhandled_error_sameline.out index 62feb784663565..f60b0f8572dd5e 100644 --- a/test/message/events_unhandled_error_sameline.out +++ b/test/message/events_unhandled_error_sameline.out @@ -5,9 +5,9 @@ node:events:* Error at Object. (*events_unhandled_error_sameline.js:*:*) at Module._compile (node:internal/modules/cjs/loader:*:*) - at Object.Module._extensions..js (node:internal/modules/cjs/loader:*:*) + at Module._extensions..js (node:internal/modules/cjs/loader:*:*) at Module.load (node:internal/modules/cjs/loader:*:*) - at Function.Module._load (node:internal/modules/cjs/loader:*:*) + at Module._load (node:internal/modules/cjs/loader:*:*) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:*:*) at node:internal/main/run_main_module:*:* Emitted 'error' event at: diff --git a/test/message/events_unhandled_error_subclass.out b/test/message/events_unhandled_error_subclass.out index 4eddce9baf9858..6733809a8ccaab 100644 --- a/test/message/events_unhandled_error_subclass.out +++ b/test/message/events_unhandled_error_subclass.out @@ -5,9 +5,9 @@ node:events:* Error at Object. (*events_unhandled_error_subclass.js:*:*) at Module._compile (node:internal/modules/cjs/loader:*:*) - at Object.Module._extensions..js (node:internal/modules/cjs/loader:*:*) + at Module._extensions..js (node:internal/modules/cjs/loader:*:*) at Module.load (node:internal/modules/cjs/loader:*:*) - at Function.Module._load (node:internal/modules/cjs/loader:*:*) + at Module._load (node:internal/modules/cjs/loader:*:*) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:*:*) at node:internal/main/run_main_module:*:* Emitted 'error' event on Foo instance at: diff --git a/test/message/if-error-has-good-stack.out b/test/message/if-error-has-good-stack.out index 32ec6de3e596c4..6ded3280d20aa1 100644 --- a/test/message/if-error-has-good-stack.out +++ b/test/message/if-error-has-good-stack.out @@ -12,9 +12,9 @@ AssertionError [ERR_ASSERTION]: ifError got unwanted exception: test error at a (*if-error-has-good-stack.js:*:*) at Object. (*if-error-has-good-stack.js:*:*) at Module._compile (node:internal/modules/cjs/loader:*:*) - at Object.Module._extensions..js (node:internal/modules/cjs/loader:*:*) + at Module._extensions..js (node:internal/modules/cjs/loader:*:*) at Module.load (node:internal/modules/cjs/loader:*:*) - at Function.Module._load (node:internal/modules/cjs/loader:*:*) + at Module._load (node:internal/modules/cjs/loader:*:*) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:*:*) at node:internal/main/run_main_module:*:* { generatedMessage: false, @@ -25,9 +25,9 @@ AssertionError [ERR_ASSERTION]: ifError got unwanted exception: test error at a (*if-error-has-good-stack.js:*:*) at Object. (*if-error-has-good-stack.js:*:*) at Module._compile (node:internal/modules/cjs/loader:*:*) - at Object.Module._extensions..js (node:internal/modules/cjs/loader:*:*) + at Module._extensions..js (node:internal/modules/cjs/loader:*:*) at Module.load (node:internal/modules/cjs/loader:*:*) - at Function.Module._load (node:internal/modules/cjs/loader:*:*) + at Module._load (node:internal/modules/cjs/loader:*:*) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:*:*) at node:internal/main/run_main_module:*:* expected: null, diff --git a/test/message/nexttick_throw.out b/test/message/nexttick_throw.out index 4efaed978a57f8..2d105a5c422abc 100644 --- a/test/message/nexttick_throw.out +++ b/test/message/nexttick_throw.out @@ -4,6 +4,6 @@ ^ ReferenceError: undefined_reference_error_maker is not defined at *test*message*nexttick_throw.js:*:* - at processTicksAndRejections (node:internal/process/task_queues:*:*) + at process.processTicksAndRejections (node:internal/process/task_queues:*:*) Node.js * diff --git a/test/message/source_map_disabled_by_api.out b/test/message/source_map_disabled_by_api.out index 1b8d3d7a2e06b6..70979849795e11 100644 --- a/test/message/source_map_disabled_by_api.out +++ b/test/message/source_map_disabled_by_api.out @@ -5,9 +5,9 @@ Error: an error! at functionA (*enclosing-call-site-min.js:1:26) at Object. (*enclosing-call-site-min.js:1:199) at Module._compile (node:internal/modules/cjs/loader:*) - at Object.Module._extensions..js (node:internal/modules/cjs/loader:*) + at Module._extensions..js (node:internal/modules/cjs/loader:*) at Module.load (node:internal/modules/cjs/loader:*) - at Function.Module._load (node:internal/modules/cjs/loader:*) + at Module._load (node:internal/modules/cjs/loader:*) at Module.require (node:internal/modules/cjs/loader:*) *enclosing-call-site.js:16 throw new Error('an error!') @@ -20,9 +20,9 @@ Error: an error! at functionA (*enclosing-call-site.js:2:3) at Object. (*enclosing-call-site.js:24:3) at Module._compile (node:internal/modules/cjs/loader:*) - at Object.Module._extensions..js (node:internal/modules/cjs/loader:*) + at Module._extensions..js (node:internal/modules/cjs/loader:*) at Module.load (node:internal/modules/cjs/loader:*) - at Function.Module._load (node:internal/modules/cjs/loader:*) + at Module._load (node:internal/modules/cjs/loader:*) at Module.require (node:internal/modules/cjs/loader:*) Node.js * diff --git a/test/message/source_map_enabled_by_api.out b/test/message/source_map_enabled_by_api.out index 2f902e90371ad6..9af1810020cf3f 100644 --- a/test/message/source_map_enabled_by_api.out +++ b/test/message/source_map_enabled_by_api.out @@ -9,9 +9,9 @@ Error: an error! at functionA (*enclosing-call-site.js:2:3) at Object. (*enclosing-call-site.js:24:3) at Module._compile (node:internal/modules/cjs/loader:*) - at Object.Module._extensions..js (node:internal/modules/cjs/loader:*) + at Module._extensions..js (node:internal/modules/cjs/loader:*) at Module.load (node:internal/modules/cjs/loader:*) - at Function.Module._load (node:internal/modules/cjs/loader:*) + at Module._load (node:internal/modules/cjs/loader:*) at Module.require (node:internal/modules/cjs/loader:*) *enclosing-call-site-min.js:1 var functionA=function(){functionB()};function functionB(){functionC()}var functionC=function(){functionD()},functionD=function(){if(0 (*enclosing-call-site-min.js:1:199) at Module._compile (node:internal/modules/cjs/loader:*) - at Object.Module._extensions..js (node:internal/modules/cjs/loader:*) + at Module._extensions..js (node:internal/modules/cjs/loader:*) at Module.load (node:internal/modules/cjs/loader:*) - at Function.Module._load (node:internal/modules/cjs/loader:*) + at Module._load (node:internal/modules/cjs/loader:*) at Module.require (node:internal/modules/cjs/loader:*) Node.js * diff --git a/test/message/source_map_enclosing_function.out b/test/message/source_map_enclosing_function.out index 50e8e553494b53..90482b6cb2a5dd 100644 --- a/test/message/source_map_enclosing_function.out +++ b/test/message/source_map_enclosing_function.out @@ -9,9 +9,9 @@ Error: an error! at functionA (*enclosing-call-site.js:2:3) at Object. (*enclosing-call-site.js:24:3) at Module._compile (node:internal/modules/cjs/loader:*) - at Object.Module._extensions..js (node:internal/modules/cjs/loader:*) + at Module._extensions..js (node:internal/modules/cjs/loader:*) at Module.load (node:internal/modules/cjs/loader:*) - at Function.Module._load (node:internal/modules/cjs/loader:*) + at Module._load (node:internal/modules/cjs/loader:*) at Module.require (node:internal/modules/cjs/loader:*) Node.js * diff --git a/test/message/source_map_reference_error_tabs.out b/test/message/source_map_reference_error_tabs.out index 9dfcf846372098..be31c07810edef 100644 --- a/test/message/source_map_reference_error_tabs.out +++ b/test/message/source_map_reference_error_tabs.out @@ -6,9 +6,9 @@ ReferenceError: alert is not defined at *tabs.coffee:26:2* at *tabs.coffee:1:14* at Module._compile (node:internal/modules/cjs/loader:* - at Object.Module._extensions..js (node:internal/modules/cjs/loader:* + at Module._extensions..js (node:internal/modules/cjs/loader:* at Module.load (node:internal/modules/cjs/loader:* - at Function.Module._load (node:internal/modules/cjs/loader:* + at Module._load (node:internal/modules/cjs/loader:* at Module.require (node:internal/modules/cjs/loader:* at require (node:internal/modules/cjs/helpers:* at Object. (*source_map_reference_error_tabs.js:* diff --git a/test/message/source_map_throw_catch.out b/test/message/source_map_throw_catch.out index 95bba5eee3e9dc..9a98aa59e76759 100644 --- a/test/message/source_map_throw_catch.out +++ b/test/message/source_map_throw_catch.out @@ -6,9 +6,9 @@ Error: an exception at *typescript-throw.ts:18:11* at *typescript-throw.ts:24:1* at Module._compile (node:internal/modules/cjs/loader:*) - at Object.Module._extensions..js (node:internal/modules/cjs/loader:*) + at Module._extensions..js (node:internal/modules/cjs/loader:*) at Module.load (node:internal/modules/cjs/loader:*) - at Function.Module._load (node:internal/modules/cjs/loader:*) + at Module._load (node:internal/modules/cjs/loader:*) at Module.require (node:internal/modules/cjs/loader:*) at require (node:internal/modules/cjs/helpers:*) at Object. (*source_map_throw_catch.js:6:3) diff --git a/test/message/source_map_throw_first_tick.out b/test/message/source_map_throw_first_tick.out index c4eb64aa8e3f9d..8eff61425c9b1a 100644 --- a/test/message/source_map_throw_first_tick.out +++ b/test/message/source_map_throw_first_tick.out @@ -6,9 +6,9 @@ Error: an exception at *typescript-throw.ts:18:11* at *typescript-throw.ts:24:1* at Module._compile (node:internal/modules/cjs/loader:*) - at Object.Module._extensions..js (node:internal/modules/cjs/loader:*) + at Module._extensions..js (node:internal/modules/cjs/loader:*) at Module.load (node:internal/modules/cjs/loader:*) - at Function.Module._load (node:internal/modules/cjs/loader:*) + at Module._load (node:internal/modules/cjs/loader:*) at Module.require (node:internal/modules/cjs/loader:*) at require (node:internal/modules/cjs/helpers:*) at Object. (*source_map_throw_first_tick.js:5:1) diff --git a/test/message/source_map_throw_icu.out b/test/message/source_map_throw_icu.out index 0e95691a23d339..6a52cb5d53a619 100644 --- a/test/message/source_map_throw_icu.out +++ b/test/message/source_map_throw_icu.out @@ -6,9 +6,9 @@ Error: an error at *icu.jsx:3:23* at *icu.jsx:9:5* at Module._compile (node:internal/modules/cjs/loader:* - at Object.Module._extensions..js (node:internal/modules/cjs/loader:* + at Module._extensions..js (node:internal/modules/cjs/loader:* at Module.load (node:internal/modules/cjs/loader:* - at Function.Module._load (node:internal/modules/cjs/loader:* + at Module._load (node:internal/modules/cjs/loader:* at Module.require (node:internal/modules/cjs/loader:* at require (node:internal/modules/cjs/helpers:* at Object. (*source_map_throw_icu.js:* diff --git a/test/message/source_map_throw_set_immediate.out b/test/message/source_map_throw_set_immediate.out index 42a595c302ed6a..5f27ae2b5295e9 100644 --- a/test/message/source_map_throw_set_immediate.out +++ b/test/message/source_map_throw_set_immediate.out @@ -5,6 +5,6 @@ Error: goodbye at Hello *uglify-throw-original.js:5:9* at *uglify-throw-original.js:9:3* - at processImmediate (node:internal/timers:*) + at process.processImmediate (node:internal/timers:*) Node.js * diff --git a/test/message/test-no-extra-info-on-fatal-exception.out b/test/message/test-no-extra-info-on-fatal-exception.out index 840ee0e65a75d2..0390dacb8d5f42 100644 --- a/test/message/test-no-extra-info-on-fatal-exception.out +++ b/test/message/test-no-extra-info-on-fatal-exception.out @@ -5,8 +5,8 @@ throw new Error('foo'); Error: foo at Object. (*:6:7) at Module._compile (node:internal/modules/cjs/loader:*:*) - at Object.Module._extensions..js (node:internal/modules/cjs/loader:*:*) + at Module._extensions..js (node:internal/modules/cjs/loader:*:*) at Module.load (node:internal/modules/cjs/loader:*:*) - at Function.Module._load (node:internal/modules/cjs/loader:*:*) + at Module._load (node:internal/modules/cjs/loader:*:*) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:*:*) at node:internal/main/run_main_module:*:* diff --git a/test/message/test_runner_no_refs.js b/test/message/test_runner_no_refs.js new file mode 100644 index 00000000000000..8f2815f067af15 --- /dev/null +++ b/test/message/test_runner_no_refs.js @@ -0,0 +1,13 @@ +// Flags: --no-warnings +'use strict'; +require('../common'); +const test = require('node:test'); + +// When run alone, the test below does not keep the event loop alive. +test('does not keep event loop alive', async (t) => { + await t.test('+does not keep event loop alive', async (t) => { + return new Promise((resolve) => { + setTimeout(resolve, 1000).unref(); + }); + }); +}); diff --git a/test/message/test_runner_no_refs.out b/test/message/test_runner_no_refs.out new file mode 100644 index 00000000000000..c5407e3bd330c0 --- /dev/null +++ b/test/message/test_runner_no_refs.out @@ -0,0 +1,27 @@ +TAP version 13 + not ok 1 - +does not keep event loop alive + --- + duration_ms: * + failureType: 'cancelledByParent' + error: 'test did not finish before its parent and was cancelled' + code: 'ERR_TEST_FAILURE' + stack: |- + * + ... + 1..1 +not ok 1 - does not keep event loop alive + --- + duration_ms: * + failureType: 'cancelledByParent' + error: 'test did not finish before its parent and was cancelled' + code: 'ERR_TEST_FAILURE' + stack: |- + * + ... +1..1 +# tests 1 +# pass 0 +# fail 1 +# skipped 0 +# todo 0 +# duration_ms * diff --git a/test/message/test_runner_no_tests.js b/test/message/test_runner_no_tests.js new file mode 100644 index 00000000000000..c4d226c0bd27d6 --- /dev/null +++ b/test/message/test_runner_no_tests.js @@ -0,0 +1,7 @@ +// Flags: --no-warnings +'use strict'; +require('../common'); +const test = require('node:test'); + +// No TAP output should be generated. +console.log(test.name); diff --git a/test/message/test_runner_no_tests.out b/test/message/test_runner_no_tests.out new file mode 100644 index 00000000000000..9f84e58dc125f8 --- /dev/null +++ b/test/message/test_runner_no_tests.out @@ -0,0 +1 @@ +bound test diff --git a/test/message/test_runner_only_tests.js b/test/message/test_runner_only_tests.js new file mode 100644 index 00000000000000..b6aeb580af94b4 --- /dev/null +++ b/test/message/test_runner_only_tests.js @@ -0,0 +1,48 @@ +// Flags: --no-warnings --test-only +'use strict'; +require('../common'); +const test = require('node:test'); + +// These tests should be skipped based on the 'only' option. +test('only = undefined'); +test('only = undefined, skip = string', { skip: 'skip message' }); +test('only = undefined, skip = true', { skip: true }); +test('only = undefined, skip = false', { skip: false }); +test('only = false', { only: false }); +test('only = false, skip = string', { only: false, skip: 'skip message' }); +test('only = false, skip = true', { only: false, skip: true }); +test('only = false, skip = false', { only: false, skip: false }); + +// These tests should be skipped based on the 'skip' option. +test('only = true, skip = string', { only: true, skip: 'skip message' }); +test('only = true, skip = true', { only: true, skip: true }); + +// An 'only' test with subtests. +test('only = true, with subtests', { only: true }, async (t) => { + // These subtests should run. + await t.test('running subtest 1'); + await t.test('running subtest 2'); + + // Switch the context to only execute 'only' tests. + t.runOnly(true); + await t.test('skipped subtest 1'); + await t.test('skipped subtest 2'); + await t.test('running subtest 3', { only: true }); + + // Switch the context back to execute all tests. + t.runOnly(false); + await t.test('running subtest 4', async (t) => { + // These subtests should run. + await t.test('running sub-subtest 1'); + await t.test('running sub-subtest 2'); + + // Switch the context to only execute 'only' tests. + t.runOnly(true); + await t.test('skipped sub-subtest 1'); + await t.test('skipped sub-subtest 2'); + }); + + // Explicitly do not run these tests. + await t.test('skipped subtest 3', { only: false }); + await t.test('skipped subtest 4', { skip: true }); +}); diff --git a/test/message/test_runner_only_tests.out b/test/message/test_runner_only_tests.out new file mode 100644 index 00000000000000..76e6337bb8953d --- /dev/null +++ b/test/message/test_runner_only_tests.out @@ -0,0 +1,102 @@ +TAP version 13 +ok 1 - only = undefined # SKIP 'only' option not set + --- + duration_ms: * + ... +ok 2 - only = undefined, skip = string # SKIP 'only' option not set + --- + duration_ms: * + ... +ok 3 - only = undefined, skip = true # SKIP 'only' option not set + --- + duration_ms: * + ... +ok 4 - only = undefined, skip = false # SKIP 'only' option not set + --- + duration_ms: * + ... +ok 5 - only = false # SKIP 'only' option not set + --- + duration_ms: * + ... +ok 6 - only = false, skip = string # SKIP 'only' option not set + --- + duration_ms: * + ... +ok 7 - only = false, skip = true # SKIP 'only' option not set + --- + duration_ms: * + ... +ok 8 - only = false, skip = false # SKIP 'only' option not set + --- + duration_ms: * + ... +ok 9 - only = true, skip = string # SKIP skip message + --- + duration_ms: * + ... +ok 10 - only = true, skip = true # SKIP + --- + duration_ms: * + ... + ok 1 - running subtest 1 + --- + duration_ms: * + ... + ok 2 - running subtest 2 + --- + duration_ms: * + ... + ok 3 - skipped subtest 1 # SKIP 'only' option not set + --- + duration_ms: * + ... + ok 4 - skipped subtest 2 # SKIP 'only' option not set + --- + duration_ms: * + ... + ok 5 - running subtest 3 + --- + duration_ms: * + ... + ok 1 - running sub-subtest 1 + --- + duration_ms: * + ... + ok 2 - running sub-subtest 2 + --- + duration_ms: * + ... + ok 3 - skipped sub-subtest 1 # SKIP 'only' option not set + --- + duration_ms: * + ... + ok 4 - skipped sub-subtest 2 # SKIP 'only' option not set + --- + duration_ms: * + ... + 1..4 + ok 6 - running subtest 4 + --- + duration_ms: * + ... + ok 7 - skipped subtest 3 # SKIP 'only' option not set + --- + duration_ms: * + ... + ok 8 - skipped subtest 4 # SKIP + --- + duration_ms: * + ... + 1..8 +ok 11 - only = true, with subtests + --- + duration_ms: * + ... +1..11 +# tests 11 +# pass 1 +# fail 0 +# skipped 10 +# todo 0 +# duration_ms * diff --git a/test/message/test_runner_output.js b/test/message/test_runner_output.js new file mode 100644 index 00000000000000..d397f1ee7a252c --- /dev/null +++ b/test/message/test_runner_output.js @@ -0,0 +1,321 @@ +// Flags: --no-warnings +'use strict'; +require('../common'); +const assert = require('node:assert'); +const test = require('node:test'); +const util = require('util'); + +test('sync pass todo', (t) => { + t.todo(); +}); + +test('sync pass todo with message', (t) => { + t.todo('this is a passing todo'); +}); + +test('sync fail todo', (t) => { + t.todo(); + throw new Error('thrown from sync fail todo'); +}); + +test('sync fail todo with message', (t) => { + t.todo('this is a failing todo'); + throw new Error('thrown from sync fail todo with message'); +}); + +test('sync skip pass', (t) => { + t.skip(); +}); + +test('sync skip pass with message', (t) => { + t.skip('this is skipped'); +}); + +test('sync pass', (t) => { + t.diagnostic('this test should pass'); +}); + +test('sync throw fail', () => { + throw new Error('thrown from sync throw fail'); +}); + +test('async skip pass', async (t) => { + t.skip(); +}); + +test('async pass', async () => { + +}); + +test('async throw fail', async () => { + throw new Error('thrown from async throw fail'); +}); + +test('async skip fail', async (t) => { + t.skip(); + throw new Error('thrown from async throw fail'); +}); + +test('async assertion fail', async () => { + // Make sure the assert module is handled. + assert.strictEqual(true, false); +}); + +test('resolve pass', () => { + return Promise.resolve(); +}); + +test('reject fail', () => { + return Promise.reject(new Error('rejected from reject fail')); +}); + +test('unhandled rejection - passes but warns', () => { + Promise.reject(new Error('rejected from unhandled rejection fail')); +}); + +test('async unhandled rejection - passes but warns', async () => { + Promise.reject(new Error('rejected from async unhandled rejection fail')); +}); + +test('immediate throw - passes but warns', () => { + setImmediate(() => { + throw new Error('thrown from immediate throw fail'); + }); +}); + +test('immediate reject - passes but warns', () => { + setImmediate(() => { + Promise.reject(new Error('rejected from immediate reject fail')); + }); +}); + +test('immediate resolve pass', () => { + return new Promise((resolve) => { + setImmediate(() => { + resolve(); + }); + }); +}); + +test('subtest sync throw fail', async (t) => { + await t.test('+sync throw fail', (t) => { + t.diagnostic('this subtest should make its parent test fail'); + throw new Error('thrown from subtest sync throw fail'); + }); +}); + +test('sync throw non-error fail', async (t) => { + throw Symbol('thrown symbol from sync throw non-error fail'); +}); + +test('level 0a', { concurrency: 4 }, async (t) => { + t.test('level 1a', async (t) => { + const p1a = new Promise((resolve) => { + setTimeout(() => { + resolve(); + }, 1000); + }); + + return p1a; + }); + + t.test('level 1b', async (t) => { + const p1b = new Promise((resolve) => { + resolve(); + }); + + return p1b; + }); + + t.test('level 1c', async (t) => { + const p1c = new Promise((resolve) => { + setTimeout(() => { + resolve(); + }, 2000); + }); + + return p1c; + }); + + t.test('level 1d', async (t) => { + const p1c = new Promise((resolve) => { + setTimeout(() => { + resolve(); + }, 1500); + }); + + return p1c; + }); + + const p0a = new Promise((resolve) => { + setTimeout(() => { + resolve(); + }, 3000); + }); + + return p0a; +}); + +test('top level', { concurrency: 2 }, async (t) => { + t.test('+long running', async (t) => { + return new Promise((resolve, reject) => { + setTimeout(resolve, 3000).unref(); + }); + }); + + t.test('+short running', async (t) => { + t.test('++short running', async (t) => {}); + }); +}); + +test('invalid subtest - pass but subtest fails', (t) => { + setImmediate(() => { + t.test('invalid subtest fail', () => { + throw new Error('this should not be thrown'); + }); + }); +}); + +test('sync skip option', { skip: true }, (t) => { + throw new Error('this should not be executed'); +}); + +test('sync skip option with message', { skip: 'this is skipped' }, (t) => { + throw new Error('this should not be executed'); +}); + +test('sync skip option is false fail', { skip: false }, (t) => { + throw new Error('this should be executed'); +}); + +// A test with no arguments provided. +test(); + +// A test with only a named function provided. +test(function functionOnly() {}); + +// A test with only an anonymous function provided. +test(() => {}); + +// A test with only a name provided. +test('test with only a name provided'); + +// A test with an empty string name. +test(''); + +// A test with only options provided. +test({ skip: true }); + +// A test with only a name and options provided. +test('test with a name and options provided', { skip: true }); + +// A test with only options and a function provided. +test({ skip: true }, function functionAndOptions() {}); + +// A test whose description needs to be escaped. +test('escaped description \\ # \\#\\'); + +// A test whose skip message needs to be escaped. +test('escaped skip message', { skip: '#skip' }); + +// A test whose todo message needs to be escaped. +test('escaped todo message', { todo: '#todo' }); + +// A test with a diagnostic message that needs to be escaped. +test('escaped diagnostic', (t) => { + t.diagnostic('#diagnostic'); +}); + +test('callback pass', (t, done) => { + setImmediate(done); +}); + +test('callback fail', (t, done) => { + setImmediate(() => { + done(new Error('callback failure')); + }); +}); + +test('sync t is this in test', function(t) { + assert.strictEqual(this, t); +}); + +test('async t is this in test', async function(t) { + assert.strictEqual(this, t); +}); + +test('callback t is this in test', function(t, done) { + assert.strictEqual(this, t); + done(); +}); + +test('callback also returns a Promise', async (t, done) => { + throw new Error('thrown from callback also returns a Promise'); +}); + +test('callback throw', (t, done) => { + throw new Error('thrown from callback throw'); +}); + +test('callback called twice', (t, done) => { + done(); + done(); +}); + +test('callback called twice in different ticks', (t, done) => { + setImmediate(done); + done(); +}); + +test('callback called twice in future tick', (t, done) => { + setImmediate(() => { + done(); + done(); + }); +}); + +test('callback async throw', (t, done) => { + setImmediate(() => { + throw new Error('thrown from callback async throw'); + }); +}); + +test('callback async throw after done', (t, done) => { + setImmediate(() => { + throw new Error('thrown from callback async throw after done'); + }); + + done(); +}); + +test('only is set but not in only mode', { only: true }, async (t) => { + // All of these subtests should run. + await t.test('running subtest 1'); + t.runOnly(true); + await t.test('running subtest 2'); + await t.test('running subtest 3', { only: true }); + t.runOnly(false); + await t.test('running subtest 4'); +}); + +test('custom inspect symbol fail', () => { + const obj = { + [util.inspect.custom]() { + return 'customized'; + }, + foo: 1 + }; + + throw obj; +}); + +test('custom inspect symbol that throws fail', () => { + const obj = { + [util.inspect.custom]() { + throw new Error('bad-inspect'); + }, + foo: 1 + }; + + throw obj; +}); diff --git a/test/message/test_runner_output.out b/test/message/test_runner_output.out new file mode 100644 index 00000000000000..866b498deab105 --- /dev/null +++ b/test/message/test_runner_output.out @@ -0,0 +1,472 @@ +TAP version 13 +ok 1 - sync pass todo # TODO + --- + duration_ms: * + ... +ok 2 - sync pass todo with message # TODO this is a passing todo + --- + duration_ms: * + ... +not ok 3 - sync fail todo # TODO + --- + duration_ms: * + failureType: 'testCodeFailure' + error: 'thrown from sync fail todo' + code: 'ERR_TEST_FAILURE' + stack: |- + * + * + * + * + * + * + * + * + ... +not ok 4 - sync fail todo with message # TODO this is a failing todo + --- + duration_ms: * + failureType: 'testCodeFailure' + error: 'thrown from sync fail todo with message' + code: 'ERR_TEST_FAILURE' + stack: |- + * + * + * + * + * + * + * + * + * + * + ... +ok 5 - sync skip pass # SKIP + --- + duration_ms: * + ... +ok 6 - sync skip pass with message # SKIP this is skipped + --- + duration_ms: * + ... +ok 7 - sync pass + --- + duration_ms: * + ... +# this test should pass +not ok 8 - sync throw fail + --- + duration_ms: * + failureType: 'testCodeFailure' + error: 'thrown from sync throw fail' + code: 'ERR_TEST_FAILURE' + stack: |- + * + * + * + * + * + * + * + * + ... +ok 9 - async skip pass # SKIP + --- + duration_ms: * + ... +ok 10 - async pass + --- + duration_ms: * + ... +not ok 11 - async throw fail + --- + duration_ms: * + failureType: 'testCodeFailure' + error: 'thrown from async throw fail' + code: 'ERR_TEST_FAILURE' + stack: |- + * + * + * + * + * + * + * + * + ... +not ok 12 - async skip fail # SKIP + --- + duration_ms: * + failureType: 'testCodeFailure' + error: 'thrown from async throw fail' + code: 'ERR_TEST_FAILURE' + stack: |- + * + * + * + * + * + * + * + * + ... +not ok 13 - async assertion fail + --- + duration_ms: * + failureType: 'testCodeFailure' + error: |- + Expected values to be strictly equal: + + true !== false + + code: 'ERR_ASSERTION' + stack: |- + * + * + * + * + * + * + * + * + ... +ok 14 - resolve pass + --- + duration_ms: * + ... +not ok 15 - reject fail + --- + duration_ms: * + failureType: 'testCodeFailure' + error: 'rejected from reject fail' + code: 'ERR_TEST_FAILURE' + stack: |- + * + * + * + * + * + * + * + * + ... +ok 16 - unhandled rejection - passes but warns + --- + duration_ms: * + ... +ok 17 - async unhandled rejection - passes but warns + --- + duration_ms: * + ... +ok 18 - immediate throw - passes but warns + --- + duration_ms: * + ... +ok 19 - immediate reject - passes but warns + --- + duration_ms: * + ... +ok 20 - immediate resolve pass + --- + duration_ms: * + ... + not ok 1 - +sync throw fail + --- + duration_ms: * + failureType: 'testCodeFailure' + error: 'thrown from subtest sync throw fail' + code: 'ERR_TEST_FAILURE' + stack: |- + * + * + * + * + * + * + * + * + * + * + ... + # this subtest should make its parent test fail + 1..1 +not ok 21 - subtest sync throw fail + --- + duration_ms: * + failureType: 'subtestsFailed' + error: '1 subtest failed' + code: 'ERR_TEST_FAILURE' + ... +not ok 22 - sync throw non-error fail + --- + duration_ms: * + failureType: 'testCodeFailure' + error: 'Symbol(thrown symbol from sync throw non-error fail)' + code: 'ERR_TEST_FAILURE' + ... + ok 1 - level 1a + --- + duration_ms: * + ... + ok 2 - level 1b + --- + duration_ms: * + ... + ok 3 - level 1c + --- + duration_ms: * + ... + ok 4 - level 1d + --- + duration_ms: * + ... + 1..4 +ok 23 - level 0a + --- + duration_ms: * + ... + not ok 1 - +long running + --- + duration_ms: * + failureType: 'cancelledByParent' + error: 'test did not finish before its parent and was cancelled' + code: 'ERR_TEST_FAILURE' + ... + ok 1 - ++short running + --- + duration_ms: * + ... + 1..1 + ok 2 - +short running + --- + duration_ms: * + ... + 1..2 +not ok 24 - top level + --- + duration_ms: * + failureType: 'subtestsFailed' + error: '1 subtest failed' + code: 'ERR_TEST_FAILURE' + ... +ok 25 - invalid subtest - pass but subtest fails + --- + duration_ms: * + ... +ok 26 - sync skip option # SKIP + --- + duration_ms: * + ... +ok 27 - sync skip option with message # SKIP this is skipped + --- + duration_ms: * + ... +not ok 28 - sync skip option is false fail + --- + duration_ms: * + failureType: 'testCodeFailure' + error: 'this should be executed' + code: 'ERR_TEST_FAILURE' + stack: |- + * + * + * + * + * + * + * + ... +ok 29 - + --- + duration_ms: * + ... +ok 30 - functionOnly + --- + duration_ms: * + ... +ok 31 - + --- + duration_ms: * + ... +ok 32 - test with only a name provided + --- + duration_ms: * + ... +ok 33 - + --- + duration_ms: * + ... +ok 34 - # SKIP + --- + duration_ms: * + ... +ok 35 - test with a name and options provided # SKIP + --- + duration_ms: * + ... +ok 36 - functionAndOptions # SKIP + --- + duration_ms: * + ... +ok 37 - escaped description \\ \# \\\#\\ + --- + duration_ms: * + ... +ok 38 - escaped skip message # SKIP \#skip + --- + duration_ms: * + ... +ok 39 - escaped todo message # TODO \#todo + --- + duration_ms: * + ... +ok 40 - escaped diagnostic + --- + duration_ms: * + ... +# \#diagnostic +ok 41 - callback pass + --- + duration_ms: * + ... +not ok 42 - callback fail + --- + duration_ms: * + failureType: 'testCodeFailure' + error: 'callback failure' + code: 'ERR_TEST_FAILURE' + stack: |- + * + * + ... +ok 43 - sync t is this in test + --- + duration_ms: * + ... +ok 44 - async t is this in test + --- + duration_ms: * + ... +ok 45 - callback t is this in test + --- + duration_ms: * + ... +not ok 46 - callback also returns a Promise + --- + duration_ms: * + failureType: 'callbackAndPromisePresent' + error: 'passed a callback but also returned a Promise' + code: 'ERR_TEST_FAILURE' + ... +not ok 47 - callback throw + --- + duration_ms: * + failureType: 'testCodeFailure' + error: 'thrown from callback throw' + code: 'ERR_TEST_FAILURE' + stack: |- + * + * + * + * + * + * + * + ... +not ok 48 - callback called twice + --- + duration_ms: * + failureType: 'multipleCallbackInvocations' + error: 'callback invoked multiple times' + code: 'ERR_TEST_FAILURE' + stack: |- + * + * + ... +ok 49 - callback called twice in different ticks + --- + duration_ms: * + ... +not ok 50 - callback called twice in future tick + --- + duration_ms: * + failureType: 'uncaughtException' + error: 'callback invoked multiple times' + code: 'ERR_TEST_FAILURE' + stack: |- + * + ... +not ok 51 - callback async throw + --- + duration_ms: * + failureType: 'uncaughtException' + error: 'thrown from callback async throw' + code: 'ERR_TEST_FAILURE' + stack: |- + * + ... +ok 52 - callback async throw after done + --- + duration_ms: * + ... + ok 1 - running subtest 1 + --- + duration_ms: * + ... + ok 2 - running subtest 2 + --- + duration_ms: * + ... + ok 3 - running subtest 3 + --- + duration_ms: * + ... + ok 4 - running subtest 4 + --- + duration_ms: * + ... + 1..4 +ok 53 - only is set but not in only mode + --- + duration_ms: * + ... +not ok 54 - custom inspect symbol fail + --- + duration_ms: * + failureType: 'testCodeFailure' + error: 'customized' + code: 'ERR_TEST_FAILURE' + ... +not ok 55 - custom inspect symbol that throws fail + --- + duration_ms: * + failureType: 'testCodeFailure' + error: |- + { + foo: 1, + [Symbol(nodejs.util.inspect.custom)]: [Function: [nodejs.util.inspect.custom]] + } + code: 'ERR_TEST_FAILURE' + ... +not ok 56 - invalid subtest fail + --- + duration_ms: * + failureType: 'parentAlreadyFinished' + error: 'test could not be started because its parent finished' + code: 'ERR_TEST_FAILURE' + stack: |- + * + ... +1..56 +# Warning: Test "unhandled rejection - passes but warns" generated asynchronous activity after the test ended. This activity created the error "Error: rejected from unhandled rejection fail" and would have caused the test to fail, but instead triggered an unhandledRejection event. +# Warning: Test "async unhandled rejection - passes but warns" generated asynchronous activity after the test ended. This activity created the error "Error: rejected from async unhandled rejection fail" and would have caused the test to fail, but instead triggered an unhandledRejection event. +# Warning: Test "immediate throw - passes but warns" generated asynchronous activity after the test ended. This activity created the error "Error: thrown from immediate throw fail" and would have caused the test to fail, but instead triggered an uncaughtException event. +# Warning: Test "immediate reject - passes but warns" generated asynchronous activity after the test ended. This activity created the error "Error: rejected from immediate reject fail" and would have caused the test to fail, but instead triggered an unhandledRejection event. +# Warning: Test "callback called twice in different ticks" generated asynchronous activity after the test ended. This activity created the error "Error [ERR_TEST_FAILURE]: callback invoked multiple times" and would have caused the test to fail, but instead triggered an uncaughtException event. +# Warning: Test "callback async throw after done" generated asynchronous activity after the test ended. This activity created the error "Error: thrown from callback async throw after done" and would have caused the test to fail, but instead triggered an uncaughtException event. +# tests 56 +# pass 24 +# fail 17 +# skipped 10 +# todo 5 +# duration_ms * diff --git a/test/message/test_runner_unresolved_promise.js b/test/message/test_runner_unresolved_promise.js new file mode 100644 index 00000000000000..00d231be34b6df --- /dev/null +++ b/test/message/test_runner_unresolved_promise.js @@ -0,0 +1,8 @@ +// Flags: --no-warnings +'use strict'; +require('../common'); +const test = require('node:test'); + +test('pass'); +test('never resolving promise', () => new Promise(() => {})); +test('fail'); diff --git a/test/message/test_runner_unresolved_promise.out b/test/message/test_runner_unresolved_promise.out new file mode 100644 index 00000000000000..98f52966c33bcb --- /dev/null +++ b/test/message/test_runner_unresolved_promise.out @@ -0,0 +1,30 @@ +TAP version 13 +ok 1 - pass + --- + duration_ms: * + ... +not ok 2 - never resolving promise + --- + duration_ms: * + failureType: 'cancelledByParent' + error: 'test did not finish before its parent and was cancelled' + code: 'ERR_TEST_FAILURE' + stack: |- + * + ... +not ok 3 - fail + --- + duration_ms: * + failureType: 'cancelledByParent' + error: 'test did not finish before its parent and was cancelled' + code: 'ERR_TEST_FAILURE' + stack: |- + * + ... +1..3 +# tests 3 +# pass 1 +# fail 2 +# skipped 0 +# todo 0 +# duration_ms * diff --git a/test/message/timeout_throw.out b/test/message/timeout_throw.out index bc20cc7762f844..83b861caa7ca29 100644 --- a/test/message/timeout_throw.out +++ b/test/message/timeout_throw.out @@ -4,6 +4,6 @@ ReferenceError: undefined_reference_error_maker is not defined at Timeout._onTimeout (*test*message*timeout_throw.js:*:*) at listOnTimeout (node:internal/timers:*:*) - at processTimers (node:internal/timers:*:*) + at process.processTimers (node:internal/timers:*:*) Node.js * diff --git a/test/message/undefined_reference_in_new_context.out b/test/message/undefined_reference_in_new_context.out index 259bb6d1bc1362..3bfa1c5b967dd8 100644 --- a/test/message/undefined_reference_in_new_context.out +++ b/test/message/undefined_reference_in_new_context.out @@ -12,7 +12,7 @@ ReferenceError: foo is not defined at Module._compile (node:internal/modules/cjs/loader:*) at *..js (node:internal/modules/cjs/loader:*) at Module.load (node:internal/modules/cjs/loader:*) - at Function.Module._load (node:internal/modules/cjs/loader:*:*) + at Module._load (node:internal/modules/cjs/loader:*:*) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:*:*) Node.js * diff --git a/test/message/vm_display_runtime_error.out b/test/message/vm_display_runtime_error.out index 403ad365451e96..857525e4a4768a 100644 --- a/test/message/vm_display_runtime_error.out +++ b/test/message/vm_display_runtime_error.out @@ -9,9 +9,9 @@ Error: boo! at Object.runInThisContext (node:vm:*) at Object. (*test*message*vm_display_runtime_error.js:*) at Module._compile (node:internal/modules/cjs/loader:*) - at Object.Module._extensions..js (node:internal/modules/cjs/loader:*) + at Module._extensions..js (node:internal/modules/cjs/loader:*) at Module.load (node:internal/modules/cjs/loader:*) - at Function.Module._load (node:internal/modules/cjs/loader:*) + at Module._load (node:internal/modules/cjs/loader:*) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:*) at node:internal/main/run_main_module:*:* test.vm:1 @@ -24,9 +24,9 @@ Error: spooky! at Object.runInThisContext (node:vm:*) at Object. (*test*message*vm_display_runtime_error.js:*) at Module._compile (node:internal/modules/cjs/loader:*) - at Object.Module._extensions..js (node:internal/modules/cjs/loader:*) + at Module._extensions..js (node:internal/modules/cjs/loader:*) at Module.load (node:internal/modules/cjs/loader:*) - at Function.Module._load (node:internal/modules/cjs/loader:*) + at Module._load (node:internal/modules/cjs/loader:*) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:*) at node:internal/main/run_main_module:*:* diff --git a/test/message/vm_display_syntax_error.out b/test/message/vm_display_syntax_error.out index 4501ae7e81f272..427f593e9e9a21 100644 --- a/test/message/vm_display_syntax_error.out +++ b/test/message/vm_display_syntax_error.out @@ -8,9 +8,9 @@ SyntaxError: Unexpected number at Object.runInThisContext (node:vm:*) at Object. (*test*message*vm_display_syntax_error.js:*) at Module._compile (node:internal/modules/cjs/loader:*) - at Object.Module._extensions..js (node:internal/modules/cjs/loader:*) + at Module._extensions..js (node:internal/modules/cjs/loader:*) at Module.load (node:internal/modules/cjs/loader:*) - at Function.Module._load (node:internal/modules/cjs/loader:*) + at Module._load (node:internal/modules/cjs/loader:*) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:*) at node:internal/main/run_main_module:*:* test.vm:1 @@ -22,9 +22,9 @@ SyntaxError: Unexpected number at Object.runInThisContext (node:vm:*) at Object. (*test*message*vm_display_syntax_error.js:*) at Module._compile (node:internal/modules/cjs/loader:*) - at Object.Module._extensions..js (node:internal/modules/cjs/loader:*) + at Module._extensions..js (node:internal/modules/cjs/loader:*) at Module.load (node:internal/modules/cjs/loader:*) - at Function.Module._load (node:internal/modules/cjs/loader:*) + at Module._load (node:internal/modules/cjs/loader:*) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:*) at node:internal/main/run_main_module:*:* diff --git a/test/message/vm_dont_display_runtime_error.out b/test/message/vm_dont_display_runtime_error.out index 4ffe3cf4dc2e6b..a2811489275b17 100644 --- a/test/message/vm_dont_display_runtime_error.out +++ b/test/message/vm_dont_display_runtime_error.out @@ -10,9 +10,9 @@ Error: boo! at Object.runInThisContext (node:vm:*) at Object. (*test*message*vm_dont_display_runtime_error.js:*) at Module._compile (node:internal/modules/cjs/loader:*) - at Object.Module._extensions..js (node:internal/modules/cjs/loader:*) + at Module._extensions..js (node:internal/modules/cjs/loader:*) at Module.load (node:internal/modules/cjs/loader:*) - at Function.Module._load (node:internal/modules/cjs/loader:*) + at Module._load (node:internal/modules/cjs/loader:*) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:*) at node:internal/main/run_main_module:*:* diff --git a/test/message/vm_dont_display_syntax_error.out b/test/message/vm_dont_display_syntax_error.out index b9e8c24dd97791..2219ee7f2722ad 100644 --- a/test/message/vm_dont_display_syntax_error.out +++ b/test/message/vm_dont_display_syntax_error.out @@ -10,9 +10,9 @@ SyntaxError: Unexpected number at Object.runInThisContext (node:vm:*) at Object. (*test*message*vm_dont_display_syntax_error.js:*) at Module._compile (node:internal/modules/cjs/loader:*) - at Object.Module._extensions..js (node:internal/modules/cjs/loader:*) + at Module._extensions..js (node:internal/modules/cjs/loader:*) at Module.load (node:internal/modules/cjs/loader:*) - at Function.Module._load (node:internal/modules/cjs/loader:*) + at Module._load (node:internal/modules/cjs/loader:*) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:*) at node:internal/main/run_main_module:*:* diff --git a/test/node-api/test_general/test.js b/test/node-api/test_general/test.js index dd409f010a3ada..77550c02927fb4 100644 --- a/test/node-api/test_general/test.js +++ b/test/node-api/test_general/test.js @@ -1,15 +1,46 @@ 'use strict'; const common = require('../../common'); +const tmpdir = require('../../common/tmpdir'); +const child_process = require('child_process'); +const fs = require('fs'); +const path = require('path'); +const url = require('url'); const filename = require.resolve(`./build/${common.buildType}/test_general`); const test_general = require(filename); const assert = require('assert'); -// TODO(gabrielschulhof): This test may need updating if/when the filename -// becomes a full-fledged URL. -assert.strictEqual(test_general.filename, `file://${filename}`); +tmpdir.refresh(); -const [ major, minor, patch, release ] = test_general.testGetNodeVersion(); -assert.strictEqual(process.version.split('-')[0], - `v${major}.${minor}.${patch}`); -assert.strictEqual(release, process.release.name); +{ + // TODO(gabrielschulhof): This test may need updating if/when the filename + // becomes a full-fledged URL. + assert.strictEqual(test_general.filename, url.pathToFileURL(filename).href); +} + +{ + const urlTestDir = path.join(tmpdir.path, 'foo%#bar'); + const urlTestFile = path.join(urlTestDir, path.basename(filename)); + fs.mkdirSync(urlTestDir, { recursive: true }); + fs.copyFileSync(filename, urlTestFile); + // Use a child process as indirection so that the native module is not loaded + // into this process and can be removed here. + const reportedFilename = child_process.spawnSync( + process.execPath, + ['-p', `require(${JSON.stringify(urlTestFile)}).filename`], + { encoding: 'utf8' }).stdout.trim(); + assert.doesNotMatch(reportedFilename, /foo%#bar/); + assert.strictEqual(reportedFilename, url.pathToFileURL(urlTestFile).href); + fs.rmSync(urlTestDir, { + force: true, + recursive: true, + maxRetries: 256 + }); +} + +{ + const [ major, minor, patch, release ] = test_general.testGetNodeVersion(); + assert.strictEqual(process.version.split('-')[0], + `v${major}.${minor}.${patch}`); + assert.strictEqual(release, process.release.name); +} diff --git a/test/node-api/test_init_order/binding.gyp b/test/node-api/test_init_order/binding.gyp new file mode 100644 index 00000000000000..2ffb81838d1189 --- /dev/null +++ b/test/node-api/test_init_order/binding.gyp @@ -0,0 +1,8 @@ +{ + "targets": [ + { + "target_name": "test_init_order", + "sources": [ "test_init_order.cc" ] + } + ] +} diff --git a/test/node-api/test_init_order/test.js b/test/node-api/test_init_order/test.js new file mode 100644 index 00000000000000..0bfeb8f94afebb --- /dev/null +++ b/test/node-api/test_init_order/test.js @@ -0,0 +1,10 @@ +'use strict'; + +// This test verifies that C++ static variable dynamic initialization is called +// correctly and does not interfere with the module initialization. +const common = require('../../common'); +const test_init_order = require(`./build/${common.buildType}/test_init_order`); +const assert = require('assert'); + +assert.strictEqual(test_init_order.cppIntValue, 42); +assert.strictEqual(test_init_order.cppStringValue, '123'); diff --git a/test/node-api/test_init_order/test_init_order.cc b/test/node-api/test_init_order/test_init_order.cc new file mode 100644 index 00000000000000..fc4174c09d8dba --- /dev/null +++ b/test/node-api/test_init_order/test_init_order.cc @@ -0,0 +1,52 @@ +#include +#include +#include +#include "../../js-native-api/common.h" + +// This test verifies that use of the NAPI_MODULE in C++ code does not +// interfere with the C++ dynamic static initializers. + +namespace { + +// This class uses dynamic static initializers for the test. +// In production code developers must avoid dynamic static initializers because +// they affect the start up time. They must prefer static initialization such as +// use of constexpr functions or classes with constexpr constructors. E.g. +// instead of using std::string, it is preferrable to use const char[], or +// constexpr std::string_view starting with C++17, or even constexpr +// std::string starting with C++20. +struct MyClass { + static const std::unique_ptr valueHolder; + static const std::string testString; +}; + +const std::unique_ptr MyClass::valueHolder = + std::unique_ptr(new int(42)); +// NOLINTNEXTLINE(runtime/string) +const std::string MyClass::testString = std::string("123"); + +} // namespace + +EXTERN_C_START +napi_value Init(napi_env env, napi_value exports) { + napi_value cppIntValue, cppStringValue; + NODE_API_CALL(env, + napi_create_int32(env, *MyClass::valueHolder, &cppIntValue)); + NODE_API_CALL( + env, + napi_create_string_utf8( + env, MyClass::testString.c_str(), NAPI_AUTO_LENGTH, &cppStringValue)); + + napi_property_descriptor descriptors[] = { + DECLARE_NODE_API_PROPERTY_VALUE("cppIntValue", cppIntValue), + DECLARE_NODE_API_PROPERTY_VALUE("cppStringValue", cppStringValue)}; + + NODE_API_CALL(env, + napi_define_properties( + env, exports, std::size(descriptors), descriptors)); + + return exports; +} +EXTERN_C_END + +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/parallel/test-asyncresource-bind.js b/test/parallel/test-asyncresource-bind.js index a9f613d9302edf..29de9bbb0f10bb 100644 --- a/test/parallel/test-asyncresource-bind.js +++ b/test/parallel/test-asyncresource-bind.js @@ -41,7 +41,7 @@ const fn3 = asyncResource.bind(common.mustCall(function() { fn3(); const fn4 = asyncResource.bind(common.mustCall(function() { - assert.strictEqual(this, asyncResource); + assert.strictEqual(this, undefined); })); fn4(); @@ -49,3 +49,8 @@ const fn5 = asyncResource.bind(common.mustCall(function() { assert.strictEqual(this, false); }), false); fn5(); + +const fn6 = asyncResource.bind(common.mustCall(function() { + assert.strictEqual(this, 'test'); +})); +fn6.call('test'); diff --git a/test/parallel/test-blob-buffer-too-large.js b/test/parallel/test-blob-buffer-too-large.js new file mode 100644 index 00000000000000..2fd8b8754bd593 --- /dev/null +++ b/test/parallel/test-blob-buffer-too-large.js @@ -0,0 +1,24 @@ +// Flags: --no-warnings +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { Blob } = require('buffer'); + +if (common.isFreeBSD) + common.skip('Oversized buffer make the FreeBSD CI runner crash'); + +try { + new Blob([new Uint8Array(0xffffffff), [1]]); +} catch (e) { + if ( + e.message === 'Array buffer allocation failed' || + e.message === 'Invalid typed array length: 4294967295' + ) { + common.skip( + 'Insufficient memory on this platform for oversized buffer test.' + ); + } else { + assert.strictEqual(e.code, 'ERR_BUFFER_TOO_LARGE'); + } +} diff --git a/test/parallel/test-blob-createobjecturl.js b/test/parallel/test-blob-createobjecturl.js index a8fd377dd3ef70..70c64b138db1ac 100644 --- a/test/parallel/test-blob-createobjecturl.js +++ b/test/parallel/test-blob-createobjecturl.js @@ -29,6 +29,10 @@ const assert = require('assert'); Buffer.from(await otherBlob.arrayBuffer()).toString(), 'hello'); URL.revokeObjectURL(id); + + // should do nothing + URL.revokeObjectURL(id); + assert.strictEqual(resolveObjectURL(id), undefined); // Leaving a Blob registered should not cause an assert diff --git a/test/parallel/test-blob.js b/test/parallel/test-blob.js index 53b9ddb0cb3e81..fe66ff08f945e1 100644 --- a/test/parallel/test-blob.js +++ b/test/parallel/test-blob.js @@ -198,6 +198,8 @@ assert.throws(() => new Blob({}), { const b = new Blob(); assert.strictEqual(inspect(b, { depth: null }), 'Blob { size: 0, type: \'\' }'); + assert.strictEqual(inspect(b, { depth: 1 }), + 'Blob { size: 0, type: \'\' }'); assert.strictEqual(inspect(b, { depth: -1 }), '[Blob]'); } @@ -230,6 +232,30 @@ assert.throws(() => new Blob({}), { }); } +{ + assert.throws(() => Reflect.get(Blob.prototype, 'type', {}), { + code: 'ERR_INVALID_THIS', + }); + assert.throws(() => Reflect.get(Blob.prototype, 'size', {}), { + code: 'ERR_INVALID_THIS', + }); + assert.throws(() => Blob.prototype.slice(Blob.prototype, 0, 1), { + code: 'ERR_INVALID_THIS', + }); + assert.throws(() => Blob.prototype.stream.call(), { + code: 'ERR_INVALID_THIS', + }); +} + +(async () => { + assert.rejects(async () => Blob.prototype.arrayBuffer.call(), { + code: 'ERR_INVALID_THIS', + }); + assert.rejects(async () => Blob.prototype.text.call(), { + code: 'ERR_INVALID_THIS', + }); +})().then(common.mustCall()); + (async () => { const blob = new Blob([ new Uint8Array([0x50, 0x41, 0x53, 0x53]), diff --git a/test/parallel/test-bootstrap-modules.js b/test/parallel/test-bootstrap-modules.js index d29e2de66bdf4d..d5bf6e133ebc99 100644 --- a/test/parallel/test-bootstrap-modules.js +++ b/test/parallel/test-bootstrap-modules.js @@ -42,6 +42,7 @@ const expectedModules = new Set([ 'Internal Binding util', 'Internal Binding uv', 'Internal Binding v8', + 'Internal Binding wasm_web_api', 'Internal Binding worker', 'NativeModule buffer', 'NativeModule events', @@ -140,8 +141,11 @@ const expectedModules = new Set([ 'NativeModule internal/validators', 'NativeModule internal/vm/module', 'NativeModule internal/webstreams/adapters', + 'NativeModule internal/webstreams/compression', + 'NativeModule internal/webstreams/encoding', 'NativeModule internal/webstreams/queuingstrategies', 'NativeModule internal/webstreams/readablestream', + 'NativeModule internal/webstreams/transformstream', 'NativeModule internal/webstreams/util', 'NativeModule internal/webstreams/writablestream', 'NativeModule internal/worker/io', @@ -206,6 +210,26 @@ if (process.env.NODE_V8_COVERAGE) { expectedModules.add('Internal Binding profiler'); } +if (common.hasCrypto) { + expectedModules.add('Internal Binding crypto'); + expectedModules.add('NativeModule crypto'); + expectedModules.add('NativeModule internal/crypto/certificate'); + expectedModules.add('NativeModule internal/crypto/cipher'); + expectedModules.add('NativeModule internal/crypto/diffiehellman'); + expectedModules.add('NativeModule internal/crypto/hash'); + expectedModules.add('NativeModule internal/crypto/hashnames'); + expectedModules.add('NativeModule internal/crypto/hkdf'); + expectedModules.add('NativeModule internal/crypto/keygen'); + expectedModules.add('NativeModule internal/crypto/keys'); + expectedModules.add('NativeModule internal/crypto/pbkdf2'); + expectedModules.add('NativeModule internal/crypto/random'); + expectedModules.add('NativeModule internal/crypto/scrypt'); + expectedModules.add('NativeModule internal/crypto/sig'); + expectedModules.add('NativeModule internal/crypto/util'); + expectedModules.add('NativeModule internal/crypto/x509'); + expectedModules.add('NativeModule internal/streams/lazy_transform'); +} + const { internalBinding } = require('internal/test/binding'); if (internalBinding('config').hasDtrace) { expectedModules.add('Internal Binding dtrace'); diff --git a/test/parallel/test-btoa-atob.js b/test/parallel/test-btoa-atob.js index 162406dd9f6b50..abf05adeef1042 100644 --- a/test/parallel/test-btoa-atob.js +++ b/test/parallel/test-btoa-atob.js @@ -12,3 +12,28 @@ strictEqual(globalThis.btoa, buffer.btoa); // Throws type error on no argument passed throws(() => buffer.atob(), /TypeError/); throws(() => buffer.btoa(), /TypeError/); + +strictEqual(atob(' '), ''); +strictEqual(atob(' Y\fW\tJ\njZ A=\r= '), 'abcd'); + +strictEqual(atob(null), '\x9Eée'); +strictEqual(atob(NaN), '5£'); +strictEqual(atob(Infinity), '"wâ\x9E+r'); +strictEqual(atob(true), '¶»\x9E'); +strictEqual(atob(1234), '×mø'); +strictEqual(atob([]), ''); +strictEqual(atob({ toString: () => '' }), ''); +strictEqual(atob({ [Symbol.toPrimitive]: () => '' }), ''); + +throws(() => atob(Symbol()), /TypeError/); +[ + undefined, false, () => {}, {}, [1], + 0, 1, 0n, 1n, -Infinity, + 'a', 'a\n\n\n', '\ra\r\r', ' a ', '\t\t\ta', 'a\f\f\f', '\ta\r \n\f', +].forEach((value) => + // See #2 - https://html.spec.whatwg.org/multipage/webappapis.html#dom-atob + throws(() => atob(value), { + constructor: DOMException, + name: 'InvalidCharacterError', + code: 5, + })); diff --git a/test/parallel/test-buffer-bytelength.js b/test/parallel/test-buffer-bytelength.js index 33cbb3268440a1..95d54d425bc252 100644 --- a/test/parallel/test-buffer-bytelength.js +++ b/test/parallel/test-buffer-bytelength.js @@ -23,8 +23,6 @@ const vm = require('vm'); ); }); -assert.strictEqual(Buffer.byteLength('', undefined, true), -1); - assert(ArrayBuffer.isView(new Buffer(10))); assert(ArrayBuffer.isView(new SlowBuffer(10))); assert(ArrayBuffer.isView(Buffer.alloc(10))); diff --git a/test/parallel/test-code-cache.js b/test/parallel/test-code-cache.js index 1b151e269dcfaf..f61ed9f5c54077 100644 --- a/test/parallel/test-code-cache.js +++ b/test/parallel/test-code-cache.js @@ -16,7 +16,7 @@ const { } = internalBinding('native_module'); for (const key of canBeRequired) { - require(key); + require(`node:${key}`); } // The computation has to be delayed until we have done loading modules diff --git a/test/parallel/test-common-expect-warning.js b/test/parallel/test-common-expect-warning.js index be3e385b11e0f4..dff32037fbb5b1 100644 --- a/test/parallel/test-common-expect-warning.js +++ b/test/parallel/test-common-expect-warning.js @@ -36,9 +36,11 @@ if (process.argv[2] !== 'child') { child.stderr.on('data', (data) => { stderr += data; }); + child.stderr.on('end', common.mustCall(() => { + assert.match(stderr, /Unexpected extra warning received/); + })); child.on('exit', common.mustCall((status) => { assert.notStrictEqual(status, 0); - assert.match(stderr, /Unexpected extra warning received/); })); } } else { diff --git a/test/parallel/test-crypto-authenticated.js b/test/parallel/test-crypto-authenticated.js index 21c5af6cfe3e5e..162b451c5b459c 100644 --- a/test/parallel/test-crypto-authenticated.js +++ b/test/parallel/test-crypto-authenticated.js @@ -44,7 +44,7 @@ const errMessages = { state: / state/, FIPS: /not supported in FIPS mode/, length: /Invalid initialization vector/, - authTagLength: /Invalid authentication tag/ + authTagLength: /Invalid authentication tag length/ }; const ciphers = crypto.getCiphers(); @@ -96,10 +96,9 @@ for (const test of TEST_CASES) { const isCCM = /^aes-(128|192|256)-ccm$/.test(test.algo); const isOCB = /^aes-(128|192|256)-ocb$/.test(test.algo); - const isChacha20Poly1305 = test.algo === 'chacha20-poly1305'; let options; - if (isCCM || isOCB || isChacha20Poly1305) + if (isCCM || isOCB) options = { authTagLength: test.tag.length / 2 }; const inputEncoding = test.plainIsHex ? 'hex' : 'ascii'; @@ -659,8 +658,7 @@ for (const test of TEST_CASES) { assert.throws(() => crypto.createCipheriv( valid.algo, Buffer.from(valid.key, 'hex'), - Buffer.from(H(prefix) + valid.iv, 'hex'), - { authTagLength: valid.tag.length / 2 } + Buffer.from(H(prefix) + valid.iv, 'hex') ), errMessages.length, `iv length ${ivLength} was not rejected`); function H(length) { return '00'.repeat(length); } @@ -687,3 +685,104 @@ for (const test of TEST_CASES) { }); } } + +{ + const key = Buffer.alloc(32); + const iv = Buffer.alloc(12); + + for (const authTagLength of [0, 17]) { + assert.throws(() => { + crypto.createCipheriv('chacha20-poly1305', key, iv, { authTagLength }); + }, { + code: 'ERR_CRYPTO_INVALID_AUTH_TAG', + message: errMessages.authTagLength + }); + } +} + +// ChaCha20-Poly1305 should respect the authTagLength option and should not +// require the authentication tag before calls to update() during decryption. +{ + const key = Buffer.alloc(32); + const iv = Buffer.alloc(12); + + for (let authTagLength = 1; authTagLength <= 16; authTagLength++) { + const cipher = + crypto.createCipheriv('chacha20-poly1305', key, iv, { authTagLength }); + const ciphertext = Buffer.concat([cipher.update('foo'), cipher.final()]); + const authTag = cipher.getAuthTag(); + assert.strictEqual(authTag.length, authTagLength); + + // The decipher operation should reject all authentication tags other than + // that of the expected length. + for (let other = 1; other <= 16; other++) { + const decipher = crypto.createDecipheriv('chacha20-poly1305', key, iv, { + authTagLength: other + }); + // ChaCha20 is a stream cipher so we do not need to call final() to obtain + // the full plaintext. + const plaintext = decipher.update(ciphertext); + assert.strictEqual(plaintext.toString(), 'foo'); + if (other === authTagLength) { + // The authentication tag length is as expected and the tag itself is + // correct, so this should work. + decipher.setAuthTag(authTag); + decipher.final(); + } else { + // The authentication tag that we are going to pass to setAuthTag is + // either too short or too long. If other < authTagLength, the + // authentication tag is still correct, but it should still be rejected + // because its security assurance is lower than expected. + assert.throws(() => { + decipher.setAuthTag(authTag); + }, { + code: 'ERR_CRYPTO_INVALID_AUTH_TAG', + message: `Invalid authentication tag length: ${authTagLength}` + }); + } + } + } +} + +// ChaCha20-Poly1305 should default to an authTagLength of 16. When encrypting, +// this matches the behavior of GCM ciphers. When decrypting, however, it is +// stricter than GCM in that it only allows authentication tags that are exactly +// 16 bytes long, whereas, when no authTagLength was specified, GCM would accept +// shorter tags as long as their length was valid according to NIST SP 800-38D. +// For ChaCha20-Poly1305, we intentionally deviate from that because there are +// no recommended or approved authentication tag lengths below 16 bytes. +{ + const rfcTestCases = TEST_CASES.filter(({ algo, tampered }) => { + return algo === 'chacha20-poly1305' && tampered === false; + }); + assert.strictEqual(rfcTestCases.length, 1); + + const [testCase] = rfcTestCases; + const key = Buffer.from(testCase.key, 'hex'); + const iv = Buffer.from(testCase.iv, 'hex'); + const aad = Buffer.from(testCase.aad, 'hex'); + + for (const opt of [ + undefined, + { authTagLength: undefined }, + { authTagLength: 16 }, + ]) { + const cipher = crypto.createCipheriv('chacha20-poly1305', key, iv, opt); + const ciphertext = Buffer.concat([ + cipher.setAAD(aad).update(testCase.plain, 'hex'), + cipher.final(), + ]); + const authTag = cipher.getAuthTag(); + + assert.strictEqual(ciphertext.toString('hex'), testCase.ct); + assert.strictEqual(authTag.toString('hex'), testCase.tag); + + const decipher = crypto.createDecipheriv('chacha20-poly1305', key, iv, opt); + const plaintext = Buffer.concat([ + decipher.setAAD(aad).update(ciphertext), + decipher.setAuthTag(authTag).final(), + ]); + + assert.strictEqual(plaintext.toString('hex'), testCase.plain); + } +} diff --git a/test/parallel/test-crypto-key-objects.js b/test/parallel/test-crypto-key-objects.js index c564c4dcb43fac..40a982ea7b6cd6 100644 --- a/test/parallel/test-crypto-key-objects.js +++ b/test/parallel/test-crypto-key-objects.js @@ -21,6 +21,7 @@ const { privateDecrypt, privateEncrypt, getCurves, + generateKeySync, generateKeyPairSync, webcrypto, } = require('crypto'); @@ -545,9 +546,7 @@ const privateDsa = fixtures.readKey('dsa_private_encrypted_1025.pem', format: 'pem', passphrase: Buffer.alloc(1024, 'a') }), { - message: common.hasOpenSSL3 ? - 'error:07880109:common libcrypto routines::interrupted or cancelled' : - /bad decrypt/ + message: /bad decrypt/ }); const publicKey = createPublicKey(publicDsa); @@ -846,3 +845,51 @@ const privateDsa = fixtures.readKey('dsa_private_encrypted_1025.pem', assert(!isKeyObject(cryptoKey)); }); } + +{ + const first = Buffer.from('Hello'); + const second = Buffer.from('World'); + const keyObject = createSecretKey(first); + assert(createSecretKey(first).equals(createSecretKey(first))); + assert(!createSecretKey(first).equals(createSecretKey(second))); + + assert.throws(() => keyObject.equals(0), { + name: 'TypeError', + code: 'ERR_INVALID_ARG_TYPE', + message: 'The "otherKeyObject" argument must be an instance of KeyObject. Received type number (0)' + }); + + assert(keyObject.equals(keyObject)); + assert(!keyObject.equals(createPublicKey(publicPem))); + assert(!keyObject.equals(createPrivateKey(privatePem))); +} + +{ + const first = generateKeyPairSync('ed25519'); + const second = generateKeyPairSync('ed25519'); + const secret = generateKeySync('aes', { length: 128 }); + + assert(first.publicKey.equals(first.publicKey)); + assert(first.publicKey.equals(createPublicKey( + first.publicKey.export({ format: 'pem', type: 'spki' })))); + assert(!first.publicKey.equals(second.publicKey)); + assert(!first.publicKey.equals(second.privateKey)); + assert(!first.publicKey.equals(secret)); + + assert(first.privateKey.equals(first.privateKey)); + assert(first.privateKey.equals(createPrivateKey( + first.privateKey.export({ format: 'pem', type: 'pkcs8' })))); + assert(!first.privateKey.equals(second.privateKey)); + assert(!first.privateKey.equals(second.publicKey)); + assert(!first.privateKey.equals(secret)); +} + +{ + const first = generateKeyPairSync('ed25519'); + const second = generateKeyPairSync('ed448'); + + assert(!first.publicKey.equals(second.publicKey)); + assert(!first.publicKey.equals(second.privateKey)); + assert(!first.privateKey.equals(second.privateKey)); + assert(!first.privateKey.equals(second.publicKey)); +} diff --git a/test/parallel/test-crypto-keygen.js b/test/parallel/test-crypto-keygen.js index e930db92158005..6db4bf1f957ce7 100644 --- a/test/parallel/test-crypto-keygen.js +++ b/test/parallel/test-crypto-keygen.js @@ -117,7 +117,7 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher); { // Test sync key generation with key objects with a non-standard - // publicExpononent + // publicExponent const { publicKey, privateKey } = generateKeyPairSync('rsa', { publicExponent: 3, modulusLength: 512 @@ -1122,57 +1122,189 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher); } } +function addNumericalSeparator(val) { + val = String(val); + let res = ''; + let i = val.length; + const start = val[0] === '-' ? 1 : 0; + for (; i >= start + 4; i -= 3) { + res = `_${val.slice(i - 3, i)}${res}`; + } + return `${val.slice(0, i)}${res}`; +} + + // Test RSA parameters. { - // Test invalid modulus lengths. - for (const modulusLength of [undefined, null, 'a', true, {}, [], 512.1, -1]) { + // Test invalid modulus lengths. (non-number) + for (const modulusLength of [undefined, null, 'a', true, {}, []]) { assert.throws(() => generateKeyPair('rsa', { modulusLength }, common.mustNotCall()), { name: 'TypeError', - code: 'ERR_INVALID_ARG_VALUE', - message: "The property 'options.modulusLength' is invalid. " + + code: 'ERR_INVALID_ARG_TYPE', + message: + 'The "options.modulusLength" property must be of type number.' + + common.invalidArgTypeHelper(modulusLength) + }); + } + + // Test invalid modulus lengths. (non-integer) + for (const modulusLength of [512.1, 1.3, 1.1, 5000.9, 100.5]) { + assert.throws(() => generateKeyPair('rsa', { + modulusLength + }, common.mustNotCall()), { + name: 'RangeError', + code: 'ERR_OUT_OF_RANGE', + message: + 'The value of "options.modulusLength" is out of range. ' + + 'It must be an integer. ' + `Received ${inspect(modulusLength)}` }); } - // Test invalid exponents. - for (const publicExponent of ['a', true, {}, [], 3.5, -1]) { + // Test invalid modulus lengths. (out of range) + for (const modulusLength of [-1, -9, 4294967297]) { + assert.throws(() => generateKeyPair('rsa', { + modulusLength + }, common.mustNotCall()), { + name: 'RangeError', + code: 'ERR_OUT_OF_RANGE', + message: + 'The value of "options.modulusLength" is out of range. ' + + 'It must be >= 0 && < 4294967296. ' + + `Received ${addNumericalSeparator(modulusLength)}` + }); + } + + // Test invalid exponents. (non-number) + for (const publicExponent of ['a', true, {}, []]) { assert.throws(() => generateKeyPair('rsa', { modulusLength: 4096, publicExponent }, common.mustNotCall()), { name: 'TypeError', - code: 'ERR_INVALID_ARG_VALUE', - message: "The property 'options.publicExponent' is invalid. " + + code: 'ERR_INVALID_ARG_TYPE', + message: + 'The "options.publicExponent" property must be of type number.' + + common.invalidArgTypeHelper(publicExponent) + }); + } + + // Test invalid exponents. (non-integer) + for (const publicExponent of [3.5, 1.1, 50.5, 510.5]) { + assert.throws(() => generateKeyPair('rsa', { + modulusLength: 4096, + publicExponent + }, common.mustNotCall()), { + name: 'RangeError', + code: 'ERR_OUT_OF_RANGE', + message: + 'The value of "options.publicExponent" is out of range. ' + + 'It must be an integer. ' + `Received ${inspect(publicExponent)}` }); } + + // Test invalid exponents. (out of range) + for (const publicExponent of [-5, -3, 4294967297]) { + assert.throws(() => generateKeyPair('rsa', { + modulusLength: 4096, + publicExponent + }, common.mustNotCall()), { + name: 'RangeError', + code: 'ERR_OUT_OF_RANGE', + message: + 'The value of "options.publicExponent" is out of range. ' + + 'It must be >= 0 && < 4294967296. ' + + `Received ${addNumericalSeparator(publicExponent)}` + }); + } } // Test DSA parameters. { - // Test invalid modulus lengths. - for (const modulusLength of [undefined, null, 'a', true, {}, [], 4096.1]) { + // Test invalid modulus lengths. (non-number) + for (const modulusLength of [undefined, null, 'a', true, {}, []]) { assert.throws(() => generateKeyPair('dsa', { modulusLength }, common.mustNotCall()), { name: 'TypeError', - code: 'ERR_INVALID_ARG_VALUE', - message: "The property 'options.modulusLength' is invalid. " + + code: 'ERR_INVALID_ARG_TYPE', + message: + 'The "options.modulusLength" property must be of type number.' + + common.invalidArgTypeHelper(modulusLength) + }); + } + + // Test invalid modulus lengths. (non-integer) + for (const modulusLength of [512.1, 1.3, 1.1, 5000.9, 100.5]) { + assert.throws(() => generateKeyPair('dsa', { + modulusLength + }, common.mustNotCall()), { + name: 'RangeError', + code: 'ERR_OUT_OF_RANGE', + message: + 'The value of "options.modulusLength" is out of range. ' + + 'It must be an integer. ' + `Received ${inspect(modulusLength)}` }); } - // Test invalid divisor lengths. - for (const divisorLength of ['a', true, {}, [], 4096.1, 2147483648, -1]) { + // Test invalid modulus lengths. (out of range) + for (const modulusLength of [-1, -9, 4294967297]) { + assert.throws(() => generateKeyPair('dsa', { + modulusLength + }, common.mustNotCall()), { + name: 'RangeError', + code: 'ERR_OUT_OF_RANGE', + message: + 'The value of "options.modulusLength" is out of range. ' + + 'It must be >= 0 && < 4294967296. ' + + `Received ${addNumericalSeparator(modulusLength)}` + }); + } + + // Test invalid divisor lengths. (non-number) + for (const divisorLength of ['a', true, {}, []]) { assert.throws(() => generateKeyPair('dsa', { modulusLength: 2048, divisorLength }, common.mustNotCall()), { name: 'TypeError', - code: 'ERR_INVALID_ARG_VALUE', - message: "The property 'options.divisorLength' is invalid. " + + code: 'ERR_INVALID_ARG_TYPE', + message: + 'The "options.divisorLength" property must be of type number.' + + common.invalidArgTypeHelper(divisorLength) + }); + } + + // Test invalid divisor lengths. (non-integer) + for (const divisorLength of [4096.1, 5.1, 6.9, 9.5]) { + assert.throws(() => generateKeyPair('dsa', { + modulusLength: 2048, + divisorLength + }, common.mustNotCall()), { + name: 'RangeError', + code: 'ERR_OUT_OF_RANGE', + message: + 'The value of "options.divisorLength" is out of range. ' + + 'It must be an integer. ' + + `Received ${inspect(divisorLength)}` + }); + } + + // Test invalid divisor lengths. (out of range) + for (const divisorLength of [-6, -9, 2147483648]) { + assert.throws(() => generateKeyPair('dsa', { + modulusLength: 2048, + divisorLength + }, common.mustNotCall()), { + name: 'RangeError', + code: 'ERR_OUT_OF_RANGE', + message: + 'The value of "options.divisorLength" is out of range. ' + + 'It must be >= 0 && <= 2147483647. ' + `Received ${inspect(divisorLength)}` }); } @@ -1202,9 +1334,10 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher); }); }, { name: 'TypeError', - code: 'ERR_INVALID_ARG_VALUE', - message: "The property 'options.namedCurve' is invalid. " + - `Received ${inspect(namedCurve)}` + code: 'ERR_INVALID_ARG_TYPE', + message: + 'The "options.namedCurve" property must be of type string.' + + common.invalidArgTypeHelper(namedCurve) }); } @@ -1293,9 +1426,10 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher); primeLength: 2147483648 }, common.mustNotCall()); }, { - name: 'TypeError', - code: 'ERR_INVALID_ARG_VALUE', - message: "The property 'options.primeLength' is invalid. " + + name: 'RangeError', + code: 'ERR_OUT_OF_RANGE', + message: 'The value of "options.primeLength" is out of range. ' + + 'It must be >= 0 && <= 2147483647. ' + 'Received 2147483648', }); @@ -1304,9 +1438,10 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher); primeLength: -1 }, common.mustNotCall()); }, { - name: 'TypeError', - code: 'ERR_INVALID_ARG_VALUE', - message: "The property 'options.primeLength' is invalid. " + + name: 'RangeError', + code: 'ERR_OUT_OF_RANGE', + message: 'The value of "options.primeLength" is out of range. ' + + 'It must be >= 0 && <= 2147483647. ' + 'Received -1', }); @@ -1316,9 +1451,10 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher); generator: 2147483648, }, common.mustNotCall()); }, { - name: 'TypeError', - code: 'ERR_INVALID_ARG_VALUE', - message: "The property 'options.generator' is invalid. " + + name: 'RangeError', + code: 'ERR_OUT_OF_RANGE', + message: 'The value of "options.generator" is out of range. ' + + 'It must be >= 0 && <= 2147483647. ' + 'Received 2147483648', }); @@ -1328,9 +1464,10 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher); generator: -1, }, common.mustNotCall()); }, { - name: 'TypeError', - code: 'ERR_INVALID_ARG_VALUE', - message: "The property 'options.generator' is invalid. " + + name: 'RangeError', + code: 'ERR_OUT_OF_RANGE', + message: 'The value of "options.generator" is out of range. ' + + 'It must be >= 0 && <= 2147483647. ' + 'Received -1', }); @@ -1389,9 +1526,10 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher); }); }, { name: 'TypeError', - code: 'ERR_INVALID_ARG_VALUE', - message: "The property 'options.hashAlgorithm' is invalid. " + - `Received ${inspect(hashValue)}` + code: 'ERR_INVALID_ARG_TYPE', + message: + 'The "options.hashAlgorithm" property must be of type string.' + + common.invalidArgTypeHelper(hashValue) }); } @@ -1404,9 +1542,10 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher); mgf1HashAlgorithm: 'sha256' }, common.mustNotCall()); }, { - name: 'TypeError', - code: 'ERR_INVALID_ARG_VALUE', - message: "The property 'options.saltLength' is invalid. " + + name: 'RangeError', + code: 'ERR_OUT_OF_RANGE', + message: 'The value of "options.saltLength" is out of range. ' + + 'It must be >= 0 && <= 2147483647. ' + 'Received 2147483648' }); @@ -1418,9 +1557,10 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher); mgf1HashAlgorithm: 'sha256' }, common.mustNotCall()); }, { - name: 'TypeError', - code: 'ERR_INVALID_ARG_VALUE', - message: "The property 'options.saltLength' is invalid. " + + name: 'RangeError', + code: 'ERR_OUT_OF_RANGE', + message: 'The value of "options.saltLength" is out of range. ' + + 'It must be >= 0 && <= 2147483647. ' + 'Received -1' }); @@ -1534,53 +1674,75 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher); }, { name: 'TypeError', - code: 'ERR_INVALID_ARG_VALUE', - message: "The property 'options.mgf1HashAlgorithm' is invalid. " + - `Received ${inspect(mgf1HashAlgorithm)}` + code: 'ERR_INVALID_ARG_TYPE', + message: + 'The "options.mgf1HashAlgorithm" property must be of type string.' + + common.invalidArgTypeHelper(mgf1HashAlgorithm) } ); } } -if (!common.hasOpenSSL3) { - // Passing an empty passphrase string should not cause OpenSSL's default - // passphrase prompt in the terminal. - // See https://github.com/nodejs/node/issues/35898. +// Passing an empty passphrase string should not cause OpenSSL's default +// passphrase prompt in the terminal. +// See https://github.com/nodejs/node/issues/35898. - for (const type of ['pkcs1', 'pkcs8']) { - generateKeyPair('rsa', { - modulusLength: 1024, - privateKeyEncoding: { - type, - format: 'pem', - cipher: 'aes-256-cbc', - passphrase: '' - } - }, common.mustSucceed((publicKey, privateKey) => { - assert.strictEqual(publicKey.type, 'public'); - - for (const passphrase of ['', Buffer.alloc(0)]) { - const privateKeyObject = createPrivateKey({ - passphrase, - key: privateKey - }); - assert.strictEqual(privateKeyObject.asymmetricKeyType, 'rsa'); - } +for (const type of ['pkcs1', 'pkcs8']) { + generateKeyPair('rsa', { + modulusLength: 1024, + privateKeyEncoding: { + type, + format: 'pem', + cipher: 'aes-256-cbc', + passphrase: '' + } + }, common.mustSucceed((publicKey, privateKey) => { + assert.strictEqual(publicKey.type, 'public'); - // Encrypting with an empty passphrase is not the same as not encrypting - // the key, and not specifying a passphrase should fail when decoding it. - assert.throws(() => { - return testSignVerify(publicKey, privateKey); - }, { - name: 'TypeError', - code: 'ERR_MISSING_PASSPHRASE', - message: 'Passphrase required for encrypted key' + for (const passphrase of ['', Buffer.alloc(0)]) { + const privateKeyObject = createPrivateKey({ + passphrase, + key: privateKey }); - })); - } + assert.strictEqual(privateKeyObject.asymmetricKeyType, 'rsa'); + } + + // Encrypting with an empty passphrase is not the same as not encrypting + // the key, and not specifying a passphrase should fail when decoding it. + assert.throws(() => { + return testSignVerify(publicKey, privateKey); + }, common.hasOpenSSL3 ? { + name: 'Error', + code: 'ERR_OSSL_CRYPTO_INTERRUPTED_OR_CANCELLED', + message: 'error:07880109:common libcrypto routines::interrupted or cancelled' + } : { + name: 'TypeError', + code: 'ERR_MISSING_PASSPHRASE', + message: 'Passphrase required for encrypted key' + }); + })); } +// Passing an empty passphrase string should not throw ERR_OSSL_CRYPTO_MALLOC_FAILURE even on OpenSSL 3. +// Regression test for https://github.com/nodejs/node/issues/41428. +generateKeyPair('rsa', { + modulusLength: 4096, + publicKeyEncoding: { + type: 'spki', + format: 'pem' + }, + privateKeyEncoding: { + type: 'pkcs8', + format: 'pem', + cipher: 'aes-256-cbc', + passphrase: '' + } +}, common.mustSucceed((publicKey, privateKey) => { + assert.strictEqual(typeof publicKey, 'string'); + assert.strictEqual(typeof privateKey, 'string'); +})); + { // Proprietary Web Cryptography API ECDH/ECDSA namedCurve parameters // should not be recognized in this API. diff --git a/test/parallel/test-crypto-prime.js b/test/parallel/test-crypto-prime.js index 6cd9290877d3c9..88eca54bb90560 100644 --- a/test/parallel/test-crypto-prime.js +++ b/test/parallel/test-crypto-prime.js @@ -41,12 +41,14 @@ const pCheckPrime = promisify(checkPrime); }); }); -[-1, 0].forEach((i) => { - assert.throws(() => generatePrime(i, common.mustNotCall()), { - code: 'ERR_OUT_OF_RANGE' +[-1, 0, 2 ** 31, 2 ** 31 + 1, 2 ** 32 - 1, 2 ** 32].forEach((size) => { + assert.throws(() => generatePrime(size, common.mustNotCall()), { + code: 'ERR_OUT_OF_RANGE', + message: />= 1 && <= 2147483647/ }); - assert.throws(() => generatePrimeSync(i), { - code: 'ERR_OUT_OF_RANGE' + assert.throws(() => generatePrimeSync(size), { + code: 'ERR_OUT_OF_RANGE', + message: />= 1 && <= 2147483647/ }); }); diff --git a/test/parallel/test-crypto-random.js b/test/parallel/test-crypto-random.js index afe2edfc4fb4b3..7b8c2c2b7a59ea 100644 --- a/test/parallel/test-crypto-random.js +++ b/test/parallel/test-crypto-random.js @@ -338,7 +338,6 @@ assert.throws( const desc = Object.getOwnPropertyDescriptor(crypto, f); assert.ok(desc); assert.strictEqual(desc.configurable, true); - assert.strictEqual(desc.writable, true); assert.strictEqual(desc.enumerable, false); }); diff --git a/test/parallel/test-crypto-scrypt.js b/test/parallel/test-crypto-scrypt.js index 5f860c75bbebf0..9e5e46164fb05c 100644 --- a/test/parallel/test-crypto-scrypt.js +++ b/test/parallel/test-crypto-scrypt.js @@ -24,7 +24,7 @@ const good = [ }, // Test vectors from https://tools.ietf.org/html/rfc7914#page-13 that // should pass. Note that the test vector with N=1048576 is omitted - // because it takes too long to complete and uses over 1 GB of memory. + // because it takes too long to complete and uses over 1 GiB of memory. { pass: '', salt: '', diff --git a/test/parallel/test-crypto-x509.js b/test/parallel/test-crypto-x509.js index d1782359277dc5..03f7c209679d9c 100644 --- a/test/parallel/test-crypto-x509.js +++ b/test/parallel/test-crypto-x509.js @@ -198,27 +198,28 @@ const der = Buffer.from( // Verify that legacy encoding works const legacyObjectCheck = { - subject: 'C=US\n' + - 'ST=CA\n' + - 'L=SF\n' + - 'O=Joyent\n' + - 'OU=Node.js\n' + - 'CN=agent1\n' + - 'emailAddress=ry@tinyclouds.org', - issuer: - 'C=US\n' + - 'ST=CA\n' + - 'L=SF\n' + - 'O=Joyent\n' + - 'OU=Node.js\n' + - 'CN=ca1\n' + - 'emailAddress=ry@tinyclouds.org', - infoAccess: - common.hasOpenSSL3 ? - 'OCSP - URI:http://ocsp.nodejs.org/\n' + - 'CA Issuers - URI:http://ca.nodejs.org/ca.cert' : - 'OCSP - URI:http://ocsp.nodejs.org/\n' + - 'CA Issuers - URI:http://ca.nodejs.org/ca.cert\n', + subject: Object.assign(Object.create(null), { + C: 'US', + ST: 'CA', + L: 'SF', + O: 'Joyent', + OU: 'Node.js', + CN: 'agent1', + emailAddress: 'ry@tinyclouds.org', + }), + issuer: Object.assign(Object.create(null), { + C: 'US', + ST: 'CA', + L: 'SF', + O: 'Joyent', + OU: 'Node.js', + CN: 'ca1', + emailAddress: 'ry@tinyclouds.org', + }), + infoAccess: Object.assign(Object.create(null), { + 'OCSP - URI': ['http://ocsp.nodejs.org/'], + 'CA Issuers - URI': ['http://ca.nodejs.org/ca.cert'] + }), modulus: 'EF5440701637E28ABB038E5641F828D834C342A9D25EDBB86A2BF' + '6FBD809CB8E037A98B71708E001242E4DEB54C6164885F599DD87' + 'A23215745955BE20417E33C4D0D1B80C9DA3DE419A2607195D2FB' + @@ -243,9 +244,9 @@ const der = Buffer.from( const legacyObject = x509.toLegacyObject(); assert.deepStrictEqual(legacyObject.raw, x509.raw); - assert.strictEqual(legacyObject.subject, legacyObjectCheck.subject); - assert.strictEqual(legacyObject.issuer, legacyObjectCheck.issuer); - assert.strictEqual(legacyObject.infoAccess, legacyObjectCheck.infoAccess); + assert.deepStrictEqual(legacyObject.subject, legacyObjectCheck.subject); + assert.deepStrictEqual(legacyObject.issuer, legacyObjectCheck.issuer); + assert.deepStrictEqual(legacyObject.infoAccess, legacyObjectCheck.infoAccess); assert.strictEqual(legacyObject.modulus, legacyObjectCheck.modulus); assert.strictEqual(legacyObject.bits, legacyObjectCheck.bits); assert.strictEqual(legacyObject.exponent, legacyObjectCheck.exponent); diff --git a/test/parallel/test-debugger-invalid-json.js b/test/parallel/test-debugger-invalid-json.js new file mode 100644 index 00000000000000..9bad8ed36949b2 --- /dev/null +++ b/test/parallel/test-debugger-invalid-json.js @@ -0,0 +1,42 @@ +'use strict'; +const common = require('../common'); +const startCLI = require('../common/debugger'); + +common.skipIfInspectorDisabled(); + +const assert = require('assert'); +const http = require('http'); + +const host = '127.0.0.1'; + +{ + const server = http.createServer((req, res) => { + res.statusCode = 400; + res.end('Bad Request'); + }); + server.listen(0, common.mustCall(() => { + const port = server.address().port; + const cli = startCLI([`${host}:${port}`]); + cli.quit().then(common.mustCall((code) => { + assert.strictEqual(code, 1); + })).finally(() => { + server.close(); + }); + })); +} + +{ + const server = http.createServer((req, res) => { + res.statusCode = 200; + res.end('some data that is invalid json'); + }); + server.listen(0, host, common.mustCall(() => { + const port = server.address().port; + const cli = startCLI([`${host}:${port}`]); + cli.quit().then(common.mustCall((code) => { + assert.strictEqual(code, 1); + })).finally(() => { + server.close(); + }); + })); +} diff --git a/test/parallel/test-dgram-address.js b/test/parallel/test-dgram-address.js index 2a41755e1c2ea6..e320f121a3a831 100644 --- a/test/parallel/test-dgram-address.js +++ b/test/parallel/test-dgram-address.js @@ -35,7 +35,7 @@ const dgram = require('dgram'); assert.strictEqual(typeof address.port, 'number'); assert.ok(isFinite(address.port)); assert.ok(address.port > 0); - assert.strictEqual(address.family, 'IPv4'); + assert.strictEqual(address.family, 4); socket.close(); })); @@ -59,7 +59,7 @@ if (common.hasIPv6) { assert.strictEqual(typeof address.port, 'number'); assert.ok(isFinite(address.port)); assert.ok(address.port > 0); - assert.strictEqual(address.family, 'IPv6'); + assert.strictEqual(address.family, 6); socket.close(); })); diff --git a/test/parallel/test-dns-lookup-promises-options-deprecated.js b/test/parallel/test-dns-lookup-promises-options-deprecated.js index 6f3b9314b9db13..2761b616620cca 100644 --- a/test/parallel/test-dns-lookup-promises-options-deprecated.js +++ b/test/parallel/test-dns-lookup-promises-options-deprecated.js @@ -15,18 +15,21 @@ common.expectWarning({ 'internal/test/binding': [ 'These APIs are for internal testing only. Do not use them.', ], - 'DeprecationWarning': { - DEP0153: 'Type coercion of dns.lookup options is deprecated' - } }); assert.throws(() => { dnsPromises.lookup('127.0.0.1', { hints: '-1' }); }, { - code: 'ERR_INVALID_ARG_VALUE', + code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError' }); -dnsPromises.lookup('127.0.0.1', { family: '6' }); -dnsPromises.lookup('127.0.0.1', { all: 'true' }); -dnsPromises.lookup('127.0.0.1', { verbatim: 'true' }); -dnsPromises.lookup('127.0.0.1', '6'); +assert.throws(() => dnsPromises.lookup('127.0.0.1', { hints: -1 }), + { code: 'ERR_INVALID_ARG_VALUE' }); +assert.throws(() => dnsPromises.lookup('127.0.0.1', { family: '6' }), + { code: 'ERR_INVALID_ARG_VALUE' }); +assert.throws(() => dnsPromises.lookup('127.0.0.1', { all: 'true' }), + { code: 'ERR_INVALID_ARG_TYPE' }); +assert.throws(() => dnsPromises.lookup('127.0.0.1', { verbatim: 'true' }), + { code: 'ERR_INVALID_ARG_TYPE' }); +assert.throws(() => dnsPromises.lookup('127.0.0.1', '6'), + { code: 'ERR_INVALID_ARG_TYPE' }); diff --git a/test/parallel/test-dns-lookup.js b/test/parallel/test-dns-lookup.js index 461e705a5e3cb6..a847a91d655196 100644 --- a/test/parallel/test-dns-lookup.js +++ b/test/parallel/test-dns-lookup.js @@ -69,14 +69,15 @@ assert.throws(() => { } { + const family = 20; const err = { code: 'ERR_INVALID_ARG_VALUE', name: 'TypeError', - message: "The argument 'family' must be one of: 0, 4, 6. Received 20" + message: `The property 'options.family' must be one of: 0, 4, 6. Received ${family}` }; const options = { hints: 0, - family: 20, + family, all: false }; @@ -86,6 +87,52 @@ assert.throws(() => { }, err); } +[1, 0n, 1n, '', '0', Symbol(), true, false, {}, [], () => {}] + .forEach((family) => { + const err = { code: 'ERR_INVALID_ARG_VALUE' }; + const options = { family }; + assert.throws(() => { dnsPromises.lookup(false, options); }, err); + assert.throws(() => { + dns.lookup(false, options, common.mustNotCall()); + }, err); + }); +[0n, 1n, '', '0', Symbol(), true, false].forEach((family) => { + const err = { code: 'ERR_INVALID_ARG_TYPE' }; + assert.throws(() => { dnsPromises.lookup(false, family); }, err); + assert.throws(() => { + dns.lookup(false, family, common.mustNotCall()); + }, err); +}); +assert.throws(() => dnsPromises.lookup(false, () => {}), + { code: 'ERR_INVALID_ARG_TYPE' }); + +[0n, 1n, '', '0', Symbol(), true, false, {}, [], () => {}].forEach((hints) => { + const err = { code: 'ERR_INVALID_ARG_TYPE' }; + const options = { hints }; + assert.throws(() => { dnsPromises.lookup(false, options); }, err); + assert.throws(() => { + dns.lookup(false, options, common.mustNotCall()); + }, err); +}); + +[0, 1, 0n, 1n, '', '0', Symbol(), {}, [], () => {}].forEach((all) => { + const err = { code: 'ERR_INVALID_ARG_TYPE' }; + const options = { all }; + assert.throws(() => { dnsPromises.lookup(false, options); }, err); + assert.throws(() => { + dns.lookup(false, options, common.mustNotCall()); + }, err); +}); + +[0, 1, 0n, 1n, '', '0', Symbol(), {}, [], () => {}].forEach((verbatim) => { + const err = { code: 'ERR_INVALID_ARG_TYPE' }; + const options = { verbatim }; + assert.throws(() => { dnsPromises.lookup(false, options); }, err); + assert.throws(() => { + dns.lookup(false, options, common.mustNotCall()); + }, err); +}); + (async function() { let res; diff --git a/test/parallel/test-dns-lookupService-promises.js b/test/parallel/test-dns-lookupService-promises.js index d7e50f194da8a1..4052139c922389 100644 --- a/test/parallel/test-dns-lookupService-promises.js +++ b/test/parallel/test-dns-lookupService-promises.js @@ -6,7 +6,7 @@ const assert = require('assert'); const dnsPromises = require('dns').promises; dnsPromises.lookupService('127.0.0.1', 22).then(common.mustCall((result) => { - assert.strictEqual(result.service, 'ssh'); + assert(['ssh', '22'].includes(result.service)); assert.strictEqual(typeof result.hostname, 'string'); assert.notStrictEqual(result.hostname.length, 0); })); diff --git a/test/parallel/test-dns-perf_hooks.js b/test/parallel/test-dns-perf_hooks.js new file mode 100644 index 00000000000000..d90b7dfe3dea95 --- /dev/null +++ b/test/parallel/test-dns-perf_hooks.js @@ -0,0 +1,26 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const dns = require('dns'); +const { PerformanceObserver } = require('perf_hooks'); + +const entries = []; +const obs = new PerformanceObserver(common.mustCallAtLeast((items) => { + entries.push(...items.getEntries()); +})); + +obs.observe({ type: 'dns' }); + +dns.lookup('localhost', () => {}); + +process.on('exit', () => { + assert.strictEqual(entries.length, 1); + entries.forEach((entry) => { + assert.strictEqual(!!entry.name, true); + assert.strictEqual(entry.entryType, 'dns'); + assert.strictEqual(typeof entry.startTime, 'number'); + assert.strictEqual(typeof entry.duration, 'number'); + assert.strictEqual(typeof entry.detail, 'object'); + }); +}); diff --git a/test/parallel/test-domain-thrown-error-handler-stack.js b/test/parallel/test-domain-thrown-error-handler-stack.js index da5f6318c0fb15..a1ca2c44cdc234 100644 --- a/test/parallel/test-domain-thrown-error-handler-stack.js +++ b/test/parallel/test-domain-thrown-error-handler-stack.js @@ -3,7 +3,7 @@ const common = require('../common'); const domain = require('domain'); -// Make sure that when an erorr is thrown from a nested domain, its error +// Make sure that when an error is thrown from a nested domain, its error // handler runs outside of that domain, but within the context of any parent // domain. diff --git a/test/parallel/test-fetch-disabled.mjs b/test/parallel/test-fetch-disabled.mjs index f06d484701c3ec..ea6b6807d8dbb5 100644 --- a/test/parallel/test-fetch-disabled.mjs +++ b/test/parallel/test-fetch-disabled.mjs @@ -8,3 +8,6 @@ assert.strictEqual(typeof globalThis.FormData, 'undefined'); assert.strictEqual(typeof globalThis.Headers, 'undefined'); assert.strictEqual(typeof globalThis.Request, 'undefined'); assert.strictEqual(typeof globalThis.Response, 'undefined'); + +assert.strictEqual(typeof WebAssembly.compileStreaming, 'undefined'); +assert.strictEqual(typeof WebAssembly.instantiateStreaming, 'undefined'); diff --git a/test/parallel/test-fs-constants.js b/test/parallel/test-fs-constants.js new file mode 100644 index 00000000000000..49bcabd80873b4 --- /dev/null +++ b/test/parallel/test-fs-constants.js @@ -0,0 +1,8 @@ +'use strict'; +require('../common'); +const fs = require('fs'); +const assert = require('assert'); + +// Check if the two constants accepted by chmod() on Windows are defined. +assert.notStrictEqual(fs.constants.S_IRUSR, undefined); +assert.notStrictEqual(fs.constants.S_IWUSR, undefined); diff --git a/test/parallel/test-fs-promises-file-handle-write.js b/test/parallel/test-fs-promises-file-handle-write.js index 3c25842d8bf9cc..7f3d12d4817042 100644 --- a/test/parallel/test-fs-promises-file-handle-write.js +++ b/test/parallel/test-fs-promises-file-handle-write.js @@ -53,7 +53,12 @@ async function validateNonUint8ArrayWrite() { async function validateNonStringValuesWrite() { const filePathForHandle = path.resolve(tmpDir, 'tmp-non-string-write.txt'); const fileHandle = await open(filePathForHandle, 'w+'); - const nonStringValues = [123, {}, new Map()]; + const nonStringValues = [ + 123, {}, new Map(), null, undefined, 0n, () => {}, Symbol(), true, + new String('notPrimitive'), + { toString() { return 'amObject'; } }, + { [Symbol.toPrimitive]: (hint) => 'amObject' }, + ]; for (const nonStringValue of nonStringValues) { await assert.rejects( fileHandle.write(nonStringValue), diff --git a/test/parallel/test-fs-readSync-optional-params.js b/test/parallel/test-fs-readSync-optional-params.js index 37d3d24911db51..00f1a5531cf6ea 100644 --- a/test/parallel/test-fs-readSync-optional-params.js +++ b/test/parallel/test-fs-readSync-optional-params.js @@ -5,23 +5,53 @@ const fixtures = require('../common/fixtures'); const fs = require('fs'); const assert = require('assert'); const filepath = fixtures.path('x.txt'); -const fd = fs.openSync(filepath, 'r'); const expected = Buffer.from('xyz\n'); function runTest(defaultBuffer, options) { - const result = fs.readSync(fd, defaultBuffer, options); - assert.strictEqual(result, expected.length); - assert.deepStrictEqual(defaultBuffer, expected); + let fd; + try { + fd = fs.openSync(filepath, 'r'); + const result = fs.readSync(fd, defaultBuffer, options); + assert.strictEqual(result, expected.length); + assert.deepStrictEqual(defaultBuffer, expected); + } finally { + if (fd != null) fs.closeSync(fd); + } } -// Test passing in an empty options object -runTest(Buffer.allocUnsafe(expected.length), { position: 0 }); +for (const options of [ -// Test not passing in any options object -runTest(Buffer.allocUnsafe(expected.length)); + // Test options object + { offset: 0 }, + { length: expected.length }, + { position: 0 }, + { offset: 0, length: expected.length }, + { offset: 0, position: 0 }, + { length: expected.length, position: 0 }, + { offset: 0, length: expected.length, position: 0 }, -// Test passing in options -runTest(Buffer.allocUnsafe(expected.length), { offset: 0, - length: expected.length, - position: 0 }); + { offset: null }, + { position: null }, + { position: -1 }, + { position: 0n }, + + // Test default params + {}, + null, + undefined, + + // Test if bad params are interpreted as default (not mandatory) + false, + true, + Infinity, + 42n, + Symbol(), + + // Test even more malicious corner cases + '4'.repeat(expected.length), + new String('4444'), + [4, 4, 4, 4], +]) { + runTest(Buffer.allocUnsafe(expected.length), options); +} diff --git a/test/parallel/test-fs-rm.js b/test/parallel/test-fs-rm.js index e0e3fd88544fff..82a5273f28e775 100644 --- a/test/parallel/test-fs-rm.js +++ b/test/parallel/test-fs-rm.js @@ -16,6 +16,15 @@ let count = 0; const nextDirPath = (name = 'rm') => path.join(tmpdir.path, `${name}-${count++}`); +const isGitPresent = (() => { + try { execSync('git --version'); return true; } catch { return false; } +})(); + +function gitInit(gitDirectory) { + fs.mkdirSync(gitDirectory); + execSync('git init', { cwd: gitDirectory }); +} + function makeNonEmptyDirectory(depth, files, folders, dirname, createSymLinks) { fs.mkdirSync(dirname, { recursive: true }); fs.writeFileSync(path.join(dirname, 'text.txt'), 'hello', 'utf8'); @@ -130,6 +139,16 @@ function removeAsync(dir) { })); } +// Removing a .git directory should not throw an EPERM. +// Refs: https://github.com/isaacs/rimraf/issues/21. +if (isGitPresent) { + const gitDirectory = nextDirPath(); + gitInit(gitDirectory); + fs.rm(gitDirectory, { recursive: true }, common.mustSucceed(() => { + assert.strictEqual(fs.existsSync(gitDirectory), false); + })); +} + // Test the synchronous version. { const dir = nextDirPath(); @@ -179,6 +198,15 @@ function removeAsync(dir) { assert.throws(() => fs.rmSync(dir), { syscall: 'stat' }); } +// Removing a .git directory should not throw an EPERM. +// Refs: https://github.com/isaacs/rimraf/issues/21. +if (isGitPresent) { + const gitDirectory = nextDirPath(); + gitInit(gitDirectory); + fs.rmSync(gitDirectory, { recursive: true }); + assert.strictEqual(fs.existsSync(gitDirectory), false); +} + // Test the Promises based version. (async () => { const dir = nextDirPath(); @@ -230,6 +258,17 @@ function removeAsync(dir) { } })().then(common.mustCall()); +// Removing a .git directory should not throw an EPERM. +// Refs: https://github.com/isaacs/rimraf/issues/21. +if (isGitPresent) { + (async () => { + const gitDirectory = nextDirPath(); + gitInit(gitDirectory); + await fs.promises.rm(gitDirectory, { recursive: true }); + assert.strictEqual(fs.existsSync(gitDirectory), false); + })().then(common.mustCall()); +} + // Test input validation. { const dir = nextDirPath(); diff --git a/test/parallel/test-fs-symlink-longpath.js b/test/parallel/test-fs-symlink-longpath.js index ac15b841df9c3a..f3586317c27ede 100644 --- a/test/parallel/test-fs-symlink-longpath.js +++ b/test/parallel/test-fs-symlink-longpath.js @@ -12,10 +12,10 @@ const longPath = path.join(...[tmpDir].concat(Array(30).fill('1234567890'))); fs.mkdirSync(longPath, { recursive: true }); // Test if we can have symlinks to files and folders with long filenames -const targetDirtectory = path.join(longPath, 'target-directory'); -fs.mkdirSync(targetDirtectory); +const targetDirectory = path.join(longPath, 'target-directory'); +fs.mkdirSync(targetDirectory); const pathDirectory = path.join(tmpDir, 'new-directory'); -fs.symlink(targetDirtectory, pathDirectory, 'dir', common.mustSucceed(() => { +fs.symlink(targetDirectory, pathDirectory, 'dir', common.mustSucceed(() => { assert(fs.existsSync(pathDirectory)); })); diff --git a/test/parallel/test-fs-util-validateoffsetlength.js b/test/parallel/test-fs-util-validateoffsetlength.js index 28e087d33aec7b..bda20f86683723 100644 --- a/test/parallel/test-fs-util-validateoffsetlength.js +++ b/test/parallel/test-fs-util-validateoffsetlength.js @@ -50,7 +50,7 @@ const { ); } -// Most platforms don't allow reads or writes >= 2 GB. +// Most platforms don't allow reads or writes >= 2 GiB. // See https://github.com/libuv/libuv/pull/1501. const kIoMaxLength = 2 ** 31 - 1; diff --git a/test/parallel/test-fs-write-file-sync.js b/test/parallel/test-fs-write-file-sync.js index 2ef7d14365b781..950b571c79eb39 100644 --- a/test/parallel/test-fs-write-file-sync.js +++ b/test/parallel/test-fs-write-file-sync.js @@ -104,6 +104,10 @@ tmpdir.refresh(); // Test writeFileSync with an object with an own toString function { + // Runtime deprecated by DEP0162 + common.expectWarning('DeprecationWarning', + 'Implicit coercion of objects with own toString property is deprecated.', + 'DEP0162'); const file = path.join(tmpdir.path, 'testWriteFileSyncStringify.txt'); const data = { toString() { diff --git a/test/parallel/test-fs-write.js b/test/parallel/test-fs-write.js index 30e25b15808dd2..a321f1b27adaa3 100644 --- a/test/parallel/test-fs-write.js +++ b/test/parallel/test-fs-write.js @@ -126,6 +126,12 @@ fs.open(fn3, 'w', 0o644, common.mustSucceed((fd) => { fs.write(fd, expected, done); })); + +// Test write with an object with an own toString function +// Runtime deprecated by DEP0162 +common.expectWarning('DeprecationWarning', + 'Implicit coercion of objects with own toString property is deprecated.', + 'DEP0162'); fs.open(fn4, 'w', 0o644, common.mustSucceed((fd) => { const done = common.mustSucceed((written) => { assert.strictEqual(written, Buffer.byteLength(expected)); @@ -155,7 +161,11 @@ fs.open(fn4, 'w', 0o644, common.mustSucceed((fd) => { ); }); -[false, 5, {}, [], null, undefined].forEach((data) => { +[ + false, 5, {}, [], null, undefined, + new String('notPrimitive'), + { [Symbol.toPrimitive]: (hint) => 'amObject' }, +].forEach((data) => { assert.throws( () => fs.write(1, data, common.mustNotCall()), { diff --git a/test/parallel/test-global-webstreams.js b/test/parallel/test-global-webstreams.js new file mode 100644 index 00000000000000..ab20e376b718ab --- /dev/null +++ b/test/parallel/test-global-webstreams.js @@ -0,0 +1,24 @@ +'use strict'; + +require('../common'); + +const assert = require('assert'); +const webstreams = require('stream/web'); + +assert.strictEqual(ReadableStream, webstreams.ReadableStream); +assert.strictEqual(ReadableStreamDefaultReader, webstreams.ReadableStreamDefaultReader); +assert.strictEqual(ReadableStreamBYOBReader, webstreams.ReadableStreamBYOBReader); +assert.strictEqual(ReadableStreamBYOBRequest, webstreams.ReadableStreamBYOBRequest); +assert.strictEqual(ReadableByteStreamController, webstreams.ReadableByteStreamController); +assert.strictEqual(ReadableStreamDefaultController, webstreams.ReadableStreamDefaultController); +assert.strictEqual(TransformStream, webstreams.TransformStream); +assert.strictEqual(TransformStreamDefaultController, webstreams.TransformStreamDefaultController); +assert.strictEqual(WritableStream, webstreams.WritableStream); +assert.strictEqual(WritableStreamDefaultWriter, webstreams.WritableStreamDefaultWriter); +assert.strictEqual(WritableStreamDefaultController, webstreams.WritableStreamDefaultController); +assert.strictEqual(ByteLengthQueuingStrategy, webstreams.ByteLengthQueuingStrategy); +assert.strictEqual(CountQueuingStrategy, webstreams.CountQueuingStrategy); +assert.strictEqual(TextEncoderStream, webstreams.TextEncoderStream); +assert.strictEqual(TextDecoderStream, webstreams.TextDecoderStream); +assert.strictEqual(CompressionStream, webstreams.CompressionStream); +assert.strictEqual(DecompressionStream, webstreams.DecompressionStream); diff --git a/test/parallel/test-http-agent-getname.js b/test/parallel/test-http-agent-getname.js index ab946a4bde3ddf..14cfcfaabdf6f4 100644 --- a/test/parallel/test-http-agent-getname.js +++ b/test/parallel/test-http-agent-getname.js @@ -18,7 +18,13 @@ assert.strictEqual( 'localhost:80:192.168.1.1' ); -// empty +// empty argument +assert.strictEqual( + agent.getName(), + 'localhost::' +); + +// empty options assert.strictEqual( agent.getName({}), 'localhost::' diff --git a/test/parallel/test-http-keep-alive-max-requests.js b/test/parallel/test-http-keep-alive-max-requests.js index 657b59ae6d9369..9412bceffb63aa 100644 --- a/test/parallel/test-http-keep-alive-max-requests.js +++ b/test/parallel/test-http-keep-alive-max-requests.js @@ -91,7 +91,7 @@ server.listen(0, common.mustCall((res) => { anotherSocket.on('ready', common.mustCall(() => { // Do another 2 requests with another socket - // enusre that this will not affect the first socket + // ensure that this will not affect the first socket initialRequests(anotherSocket, 2, common.mustCall(() => { let buffer = ''; diff --git a/test/parallel/test-http-nodelay.js b/test/parallel/test-http-nodelay.js new file mode 100644 index 00000000000000..7b259b73c2080d --- /dev/null +++ b/test/parallel/test-http-nodelay.js @@ -0,0 +1,37 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); +const net = require('net'); + +const originalConnect = net.Socket.prototype.connect; + +net.Socket.prototype.connect = common.mustCall(function(args) { + assert.strictEqual(args[0].noDelay, true); + return originalConnect.call(this, args); +}); + +const server = http.createServer(common.mustCall((req, res) => { + res.writeHead(200); + res.end(); + server.close(); +})); + +server.listen(0, common.mustCall(() => { + assert.strictEqual(server.noDelay, true); + + const req = http.request({ + method: 'GET', + port: server.address().port + }, common.mustCall((res) => { + res.on('end', () => { + server.close(); + res.req.socket.end(); + }); + + res.resume(); + })); + + req.end(); +})); diff --git a/test/parallel/test-http-outgoing-internal-headernames-getter.js b/test/parallel/test-http-outgoing-internal-headernames-getter.js index 4a56a1301050b5..2d8238066e0cd4 100644 --- a/test/parallel/test-http-outgoing-internal-headernames-getter.js +++ b/test/parallel/test-http-outgoing-internal-headernames-getter.js @@ -2,6 +2,7 @@ const common = require('../common'); const { OutgoingMessage } = require('http'); +const assert = require('assert'); const warn = 'OutgoingMessage.prototype._headerNames is deprecated'; common.expectWarning('DeprecationWarning', warn, 'DEP0066'); @@ -11,3 +12,12 @@ common.expectWarning('DeprecationWarning', warn, 'DEP0066'); const outgoingMessage = new OutgoingMessage(); outgoingMessage._headerNames; // eslint-disable-line no-unused-expressions } + +{ + // Tests _headerNames getter result after setting a header. + const outgoingMessage = new OutgoingMessage(); + outgoingMessage.setHeader('key', 'value'); + const expect = Object.create(null); + expect.key = 'key'; + assert.deepStrictEqual(outgoingMessage._headerNames, expect); +} diff --git a/test/parallel/test-http-parser-timeout-reset.js b/test/parallel/test-http-parser-timeout-reset.js index ffc3d81c381b7b..c51daffaafb056 100644 --- a/test/parallel/test-http-parser-timeout-reset.js +++ b/test/parallel/test-http-parser-timeout-reset.js @@ -27,7 +27,6 @@ const server = net.createServer((socket) => { {}, 0, 0, - 1e3 ); parser[HTTPParser.kOnTimeout] = common.mustNotCall(); diff --git a/test/parallel/test-http-perf_hooks.js b/test/parallel/test-http-perf_hooks.js index 826211472f222c..0708a1e8c06f5a 100644 --- a/test/parallel/test-http-perf_hooks.js +++ b/test/parallel/test-http-perf_hooks.js @@ -5,13 +5,9 @@ const assert = require('assert'); const http = require('http'); const { PerformanceObserver } = require('perf_hooks'); - +const entries = []; const obs = new PerformanceObserver(common.mustCallAtLeast((items) => { - items.getEntries().forEach((entry) => { - assert.strictEqual(entry.entryType, 'http'); - assert.strictEqual(typeof entry.startTime, 'number'); - assert.strictEqual(typeof entry.duration, 'number'); - }); + entries.push(...items.getEntries()); })); obs.observe({ type: 'http' }); @@ -57,3 +53,20 @@ server.listen(0, common.mustCall(async () => { ]); server.close(); })); + +process.on('exit', () => { + let numberOfHttpClients = 0; + let numberOfHttpRequests = 0; + entries.forEach((entry) => { + assert.strictEqual(entry.entryType, 'http'); + assert.strictEqual(typeof entry.startTime, 'number'); + assert.strictEqual(typeof entry.duration, 'number'); + if (entry.name === 'HttpClient') { + numberOfHttpClients++; + } else if (entry.name === 'HttpRequest') { + numberOfHttpRequests++; + } + }); + assert.strictEqual(numberOfHttpClients, 2); + assert.strictEqual(numberOfHttpRequests, 2); +}); diff --git a/test/parallel/test-http-server-headers-timeout-delayed-headers.js b/test/parallel/test-http-server-headers-timeout-delayed-headers.js new file mode 100644 index 00000000000000..5c18492384911e --- /dev/null +++ b/test/parallel/test-http-server-headers-timeout-delayed-headers.js @@ -0,0 +1,61 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { createServer } = require('http'); +const { connect } = require('net'); + +// This test validates that the server returns 408 +// after server.headersTimeout if the client +// pauses before start sending the request. + +let sendDelayedRequestHeaders; +const headersTimeout = common.platformTimeout(1000); +const server = createServer({ + headersTimeout, + requestTimeout: 0, + keepAliveTimeout: 0, + connectionsCheckingInterval: common.platformTimeout(250), +}, common.mustNotCall()); +server.on('connection', common.mustCall(() => { + assert.strictEqual(typeof sendDelayedRequestHeaders, 'function'); + sendDelayedRequestHeaders(); +})); + +assert.strictEqual(server.headersTimeout, headersTimeout); + +// Check that timeout event is not triggered +server.once('timeout', common.mustNotCall((socket) => { + socket.destroy(); +})); + +server.listen(0, common.mustCall(() => { + const client = connect(server.address().port); + let response = ''; + + client.on('data', common.mustCall((chunk) => { + response += chunk.toString('utf-8'); + })); + + const errOrEnd = common.mustSucceed(function(err) { + assert.strictEqual( + response, + 'HTTP/1.1 408 Request Timeout\r\nConnection: close\r\n\r\n' + ); + server.close(); + }); + + client.on('end', errOrEnd); + client.on('error', errOrEnd); + + client.resume(); + + sendDelayedRequestHeaders = common.mustCall(() => { + setTimeout(() => { + client.write('POST / HTTP/1.1\r\n'); + client.write('Content-Length: 20\r\n'); + client.write('Connection: close\r\n\r\n'); + client.write('12345678901234567890\r\n\r\n'); + }, common.platformTimeout(headersTimeout * 2)).unref(); + }); +})); diff --git a/test/parallel/test-http-server-headers-timeout-interrupted-headers.js b/test/parallel/test-http-server-headers-timeout-interrupted-headers.js new file mode 100644 index 00000000000000..ea47ae8e19e12e --- /dev/null +++ b/test/parallel/test-http-server-headers-timeout-interrupted-headers.js @@ -0,0 +1,61 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { createServer } = require('http'); +const { connect } = require('net'); + +// This test validates that the server returns 408 +// after server.headersTimeout if the client +// pauses sending in the middle of a header. + +let sendDelayedRequestHeaders; +const headersTimeout = common.platformTimeout(1000); +const server = createServer({ + headersTimeout, + requestTimeout: 0, + keepAliveTimeout: 0, + connectionsCheckingInterval: common.platformTimeout(250), +}, common.mustNotCall()); +server.on('connection', common.mustCall(() => { + assert.strictEqual(typeof sendDelayedRequestHeaders, 'function'); + sendDelayedRequestHeaders(); +})); + +assert.strictEqual(server.headersTimeout, headersTimeout); + +// Check that timeout event is not triggered +server.once('timeout', common.mustNotCall((socket) => { + socket.destroy(); +})); + +server.listen(0, common.mustCall(() => { + const client = connect(server.address().port); + let response = ''; + + client.on('data', common.mustCall((chunk) => { + response += chunk.toString('utf-8'); + })); + + const errOrEnd = common.mustSucceed(function(err) { + assert.strictEqual( + response, + 'HTTP/1.1 408 Request Timeout\r\nConnection: close\r\n\r\n' + ); + server.close(); + }); + + client.on('end', errOrEnd); + client.on('error', errOrEnd); + + client.resume(); + client.write('GET / HTTP/1.1\r\n'); + client.write('Connection: close\r\n'); + client.write('X-CRASH: '); + + sendDelayedRequestHeaders = common.mustCall(() => { + setTimeout(() => { + client.write('1234567890\r\n\r\n'); + }, common.platformTimeout(headersTimeout * 2)).unref(); + }); +})); diff --git a/test/parallel/test-http-server-headers-timeout-keepalive.js b/test/parallel/test-http-server-headers-timeout-keepalive.js new file mode 100644 index 00000000000000..05531087ed8321 --- /dev/null +++ b/test/parallel/test-http-server-headers-timeout-keepalive.js @@ -0,0 +1,100 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { createServer } = require('http'); +const { connect } = require('net'); + +// This test validates that the server returns 408 +// after server.headersTimeout if the client +// does not send headers before the timeout, and +// that keep alive works properly. + +function performRequestWithDelay(client, firstDelay, secondDelay, closeAfter) { + client.resume(); + client.write('GET / HTTP/1.1\r\n'); + + setTimeout(() => { + client.write('Connection: '); + }, firstDelay).unref(); + + // Complete the request + setTimeout(() => { + client.write(`${closeAfter ? 'close' : 'keep-alive'}\r\n\r\n`); + }, firstDelay + secondDelay).unref(); +} + +const headersTimeout = common.platformTimeout(2000); +const server = createServer({ + headersTimeout, + requestTimeout: 0, + keepAliveTimeout: 0, + connectionsCheckingInterval: headersTimeout / 4, +}, common.mustCallAtLeast((req, res) => { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.end(); +})); + +assert.strictEqual(server.headersTimeout, headersTimeout); + +// Check that timeout event is not triggered +server.once('timeout', common.mustNotCall((socket) => { + socket.destroy(); +})); + +server.listen(0, common.mustCall(() => { + const client = connect(server.address().port); + let second = false; + let response = ''; + + client.on('data', common.mustCallAtLeast((chunk) => { + response += chunk.toString('utf-8'); + + // First response has ended + if (!second && response.endsWith('\r\n\r\n')) { + assert.strictEqual( + response.split('\r\n')[0], + 'HTTP/1.1 200 OK' + ); + + const defer = headersTimeout * 1.5; + + // Wait some time to make sure headersTimeout + // does not interfere with keep alive + setTimeout(() => { + response = ''; + second = true; + + // Perform a second request expected to finish after headersTimeout + performRequestWithDelay( + client, + headersTimeout / 5, + headersTimeout, + true + ); + }, defer).unref(); + } + }, 1)); + + const errOrEnd = common.mustCall(function(err) { + assert.strictEqual(second, true); + assert.strictEqual( + response, + // Empty because of https://github.com/nodejs/node/commit/e8d7fedf7cad6e612e4f2e0456e359af57608ac7 + // 'HTTP/1.1 408 Request Timeout\r\nConnection: close\r\n\r\n' + '' + ); + server.close(); + }); + + client.on('error', errOrEnd); + client.on('end', errOrEnd); + + // Perform a second request expected to finish before headersTimeout + performRequestWithDelay( + client, + headersTimeout / 5, + headersTimeout / 5, + false + ); +})); diff --git a/test/parallel/test-http-server-headers-timeout-pipelining.js b/test/parallel/test-http-server-headers-timeout-pipelining.js new file mode 100644 index 00000000000000..13f89c60c05cf5 --- /dev/null +++ b/test/parallel/test-http-server-headers-timeout-pipelining.js @@ -0,0 +1,76 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { createServer } = require('http'); +const { connect } = require('net'); + +// This test validates that the server returns 408 +// after server.requestTimeout if the client +// does not complete a request when using pipelining. + +const headersTimeout = common.platformTimeout(1000); +const server = createServer({ + headersTimeout, + requestTimeout: 0, + keepAliveTimeout: 0, + connectionsCheckingInterval: common.platformTimeout(250), +}, common.mustCallAtLeast((req, res) => { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.end(); +})); + +// 0 seconds is the default +assert.strictEqual(server.headersTimeout, headersTimeout); + +// Make sure requestTimeout and keepAliveTimeout +// are big enough for the headersTimeout. +server.requestTimeout = 0; +server.keepAliveTimeout = 0; + +server.listen(0, common.mustCall(() => { + const client = connect(server.address().port); + let second = false; + let response = ''; + + client.on('data', common.mustCallAtLeast((chunk) => { + response += chunk.toString('utf-8'); + + // First response has ended + if (!second && response.endsWith('\r\n\r\n')) { + assert.strictEqual( + response.split('\r\n')[0], + 'HTTP/1.1 200 OK' + ); + + response = ''; + second = true; + } + }, 1)); + + const errOrEnd = common.mustCall(function(err) { + if (!second) { + return; + } + + assert.strictEqual( + response, + // Empty because of https://github.com/nodejs/node/commit/e8d7fedf7cad6e612e4f2e0456e359af57608ac7 + // 'HTTP/1.1 408 Request Timeout\r\nConnection: close\r\n\r\n' + '' + ); + server.close(); + }); + + client.on('error', errOrEnd); + client.on('end', errOrEnd); + + // Send two requests using pipelining. Delay before finishing the second one + client.resume(); + client.write('GET / HTTP/1.1\r\nConnection: keep-alive\r\n\r\nGET / HTTP/1.1\r\nConnection: '); + + // Complete the request + setTimeout(() => { + client.write('close\r\n\r\n'); + }, headersTimeout * 1.5).unref(); +})); diff --git a/test/parallel/test-http-server-request-timeout-delayed-body.js b/test/parallel/test-http-server-request-timeout-delayed-body.js index ec8ebbb5cd18eb..c5ecb720e47eaf 100644 --- a/test/parallel/test-http-server-request-timeout-delayed-body.js +++ b/test/parallel/test-http-server-request-timeout-delayed-body.js @@ -10,7 +10,13 @@ const { connect } = require('net'); // pauses before start sending the body. let sendDelayedRequestBody; -const server = createServer(common.mustCall((req, res) => { +const requestTimeout = common.platformTimeout(1000); +const server = createServer({ + headersTimeout: 0, + requestTimeout, + keepAliveTimeout: 0, + connectionsCheckingInterval: common.platformTimeout(250), +}, common.mustCall((req, res) => { let body = ''; req.setEncoding('utf-8'); @@ -28,10 +34,6 @@ const server = createServer(common.mustCall((req, res) => { sendDelayedRequestBody(); })); -// 0 seconds is the default -assert.strictEqual(server.requestTimeout, 0); -const requestTimeout = common.platformTimeout(1000); -server.requestTimeout = requestTimeout; assert.strictEqual(server.requestTimeout, requestTimeout); server.listen(0, common.mustCall(() => { @@ -51,11 +53,10 @@ server.listen(0, common.mustCall(() => { sendDelayedRequestBody = common.mustCall(() => { setTimeout(() => { client.write('12345678901234567890\r\n\r\n'); - }, common.platformTimeout(2000)).unref(); + }, common.platformTimeout(requestTimeout * 2)).unref(); }); - const errOrEnd = common.mustCall(function(err) { - console.log(err); + const errOrEnd = common.mustSucceed(function(err) { assert.strictEqual( response, 'HTTP/1.1 408 Request Timeout\r\nConnection: close\r\n\r\n' diff --git a/test/parallel/test-http-server-request-timeout-delayed-headers.js b/test/parallel/test-http-server-request-timeout-delayed-headers.js index f5d85b70514950..7174afec47fcc0 100644 --- a/test/parallel/test-http-server-request-timeout-delayed-headers.js +++ b/test/parallel/test-http-server-request-timeout-delayed-headers.js @@ -8,16 +8,20 @@ const { connect } = require('net'); // This test validates that the server returns 408 // after server.requestTimeout if the client // pauses before start sending the request. + let sendDelayedRequestHeaders; -const server = createServer(common.mustNotCall()); +const requestTimeout = common.platformTimeout(1000); +const server = createServer({ + headersTimeout: 0, + requestTimeout, + keepAliveTimeout: 0, + connectionsCheckingInterval: common.platformTimeout(250), +}, common.mustNotCall()); server.on('connection', common.mustCall(() => { assert.strictEqual(typeof sendDelayedRequestHeaders, 'function'); sendDelayedRequestHeaders(); })); -// 0 seconds is the default -assert.strictEqual(server.requestTimeout, 0); -const requestTimeout = common.platformTimeout(1000); -server.requestTimeout = requestTimeout; + assert.strictEqual(server.requestTimeout, requestTimeout); server.listen(0, common.mustCall(() => { @@ -28,8 +32,7 @@ server.listen(0, common.mustCall(() => { response += chunk.toString('utf-8'); })); - const errOrEnd = common.mustCall(function(err) { - console.log(err); + const errOrEnd = common.mustSucceed(function(err) { assert.strictEqual( response, 'HTTP/1.1 408 Request Timeout\r\nConnection: close\r\n\r\n' @@ -48,6 +51,6 @@ server.listen(0, common.mustCall(() => { client.write('Content-Length: 20\r\n'); client.write('Connection: close\r\n\r\n'); client.write('12345678901234567890\r\n\r\n'); - }, common.platformTimeout(2000)).unref(); + }, common.platformTimeout(requestTimeout * 2)).unref(); }); })); diff --git a/test/parallel/test-http-server-request-timeout-interrupted-body.js b/test/parallel/test-http-server-request-timeout-interrupted-body.js index 7d70351fdc0833..ee087719e4ee05 100644 --- a/test/parallel/test-http-server-request-timeout-interrupted-body.js +++ b/test/parallel/test-http-server-request-timeout-interrupted-body.js @@ -8,8 +8,15 @@ const { connect } = require('net'); // This test validates that the server returns 408 // after server.requestTimeout if the client // pauses sending in the middle of the body. + let sendDelayedRequestBody; -const server = createServer(common.mustCall((req, res) => { +const requestTimeout = common.platformTimeout(1000); +const server = createServer({ + headersTimeout: 0, + requestTimeout, + keepAliveTimeout: 0, + connectionsCheckingInterval: common.platformTimeout(250), +}, common.mustCall((req, res) => { let body = ''; req.setEncoding('utf-8'); @@ -27,10 +34,6 @@ const server = createServer(common.mustCall((req, res) => { sendDelayedRequestBody(); })); -// 0 seconds is the default -assert.strictEqual(server.requestTimeout, 0); -const requestTimeout = common.platformTimeout(1000); -server.requestTimeout = requestTimeout; assert.strictEqual(server.requestTimeout, requestTimeout); server.listen(0, common.mustCall(() => { @@ -41,8 +44,7 @@ server.listen(0, common.mustCall(() => { response += chunk.toString('utf-8'); })); - const errOrEnd = common.mustCall(function(err) { - console.log(err); + const errOrEnd = common.mustSucceed(function(err) { assert.strictEqual( response, 'HTTP/1.1 408 Request Timeout\r\nConnection: close\r\n\r\n' @@ -63,6 +65,6 @@ server.listen(0, common.mustCall(() => { sendDelayedRequestBody = common.mustCall(() => { setTimeout(() => { client.write('1234567890\r\n\r\n'); - }, common.platformTimeout(2000)).unref(); + }, common.platformTimeout(requestTimeout * 2)).unref(); }); })); diff --git a/test/parallel/test-http-server-request-timeout-interrupted-headers.js b/test/parallel/test-http-server-request-timeout-interrupted-headers.js index 1fa8946ffae70f..64511c6b50ce3a 100644 --- a/test/parallel/test-http-server-request-timeout-interrupted-headers.js +++ b/test/parallel/test-http-server-request-timeout-interrupted-headers.js @@ -8,17 +8,20 @@ const { connect } = require('net'); // This test validates that the server returns 408 // after server.requestTimeout if the client // pauses sending in the middle of a header. + let sendDelayedRequestHeaders; -const server = createServer(common.mustNotCall()); +const requestTimeout = common.platformTimeout(1000); +const server = createServer({ + headersTimeout: 0, + requestTimeout, + keepAliveTimeout: 0, + connectionsCheckingInterval: common.platformTimeout(250), +}, common.mustNotCall()); server.on('connection', common.mustCall(() => { assert.strictEqual(typeof sendDelayedRequestHeaders, 'function'); sendDelayedRequestHeaders(); })); -// 120 seconds is the default -assert.strictEqual(server.requestTimeout, 0); -const requestTimeout = common.platformTimeout(1000); -server.requestTimeout = requestTimeout; assert.strictEqual(server.requestTimeout, requestTimeout); server.listen(0, common.mustCall(() => { @@ -29,8 +32,7 @@ server.listen(0, common.mustCall(() => { response += chunk.toString('utf-8'); })); - const errOrEnd = common.mustCall(function(err) { - console.log(err); + const errOrEnd = common.mustSucceed(function(err) { assert.strictEqual( response, 'HTTP/1.1 408 Request Timeout\r\nConnection: close\r\n\r\n' @@ -49,6 +51,6 @@ server.listen(0, common.mustCall(() => { sendDelayedRequestHeaders = common.mustCall(() => { setTimeout(() => { client.write('1234567890\r\n\r\n'); - }, common.platformTimeout(2000)).unref(); + }, common.platformTimeout(requestTimeout * 2)).unref(); }); })); diff --git a/test/parallel/test-http-server-request-timeout-keepalive.js b/test/parallel/test-http-server-request-timeout-keepalive.js index 77fde867e9b540..2466e1ee7a953c 100644 --- a/test/parallel/test-http-server-request-timeout-keepalive.js +++ b/test/parallel/test-http-server-request-timeout-keepalive.js @@ -8,36 +8,33 @@ const { connect } = require('net'); // This test validates that the server returns 408 // after server.requestTimeout if the client // does not complete a request, and that keep alive -// works properly +// works properly. -function performRequestWithDelay(client, firstDelay, secondDelay) { +function performRequestWithDelay(client, firstDelay, secondDelay, closeAfter) { client.resume(); client.write('GET / HTTP/1.1\r\n'); - firstDelay = common.platformTimeout(firstDelay); - secondDelay = common.platformTimeout(secondDelay); - - console.log('performRequestWithDelay', firstDelay, secondDelay); - setTimeout(() => { client.write('Connection: '); }, firstDelay).unref(); // Complete the request setTimeout(() => { - client.write('keep-alive\r\n\r\n'); + client.write(`${closeAfter ? 'close' : 'keep-alive'}\r\n\r\n`); }, firstDelay + secondDelay).unref(); } -const server = createServer(common.mustCallAtLeast((req, res) => { +const requestTimeout = common.platformTimeout(2000); +const server = createServer({ + headersTimeout: 0, + requestTimeout, + keepAliveTimeout: 0, + connectionsCheckingInterval: requestTimeout / 4 +}, common.mustCallAtLeast((req, res) => { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.end(); })); -// 0 seconds is the default -assert.strictEqual(server.requestTimeout, 0); -const requestTimeout = common.platformTimeout(1000); -server.requestTimeout = requestTimeout; assert.strictEqual(server.requestTimeout, requestTimeout); // Make sure keepAliveTimeout is big enough for the requestTimeout. @@ -58,9 +55,7 @@ server.listen(0, common.mustCall(() => { 'HTTP/1.1 200 OK' ); - const defer = common.platformTimeout(server.requestTimeout * 1.5); - - console.log('defer by', defer); + const defer = requestTimeout * 1.5; // Wait some time to make sure requestTimeout // does not interfere with keep alive @@ -69,13 +64,17 @@ server.listen(0, common.mustCall(() => { second = true; // Perform a second request expected to finish after requestTimeout - performRequestWithDelay(client, 1000, 3000); + performRequestWithDelay( + client, + requestTimeout / 5, + requestTimeout, + true + ); }, defer).unref(); } }, 1)); const errOrEnd = common.mustCall(function(err) { - console.log(err); assert.strictEqual(second, true); assert.strictEqual( response, @@ -90,5 +89,10 @@ server.listen(0, common.mustCall(() => { client.on('end', errOrEnd); // Perform a second request expected to finish before requestTimeout - performRequestWithDelay(client, 50, 500); + performRequestWithDelay( + client, + requestTimeout / 5, + requestTimeout / 5, + false + ); })); diff --git a/test/parallel/test-http-server-request-timeout-pipelining.js b/test/parallel/test-http-server-request-timeout-pipelining.js new file mode 100644 index 00000000000000..4e6977b3270dab --- /dev/null +++ b/test/parallel/test-http-server-request-timeout-pipelining.js @@ -0,0 +1,70 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { createServer } = require('http'); +const { connect } = require('net'); + +// This test validates that the server returns 408 +// after server.requestTimeout if the client +// does not complete a request when using pipelining. + +const requestTimeout = common.platformTimeout(1000); +const server = createServer({ + headersTimeout: 0, + requestTimeout, + keepAliveTimeout: 0, + connectionsCheckingInterval: common.platformTimeout(250), +}, common.mustCallAtLeast((req, res) => { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.end(); +})); + +assert.strictEqual(server.requestTimeout, requestTimeout); + +server.listen(0, common.mustCall(() => { + const client = connect(server.address().port); + let second = false; + let response = ''; + + client.on('data', common.mustCallAtLeast((chunk) => { + response += chunk.toString('utf-8'); + + // First response has ended + if (!second && response.endsWith('\r\n\r\n')) { + assert.strictEqual( + response.split('\r\n')[0], + 'HTTP/1.1 200 OK' + ); + + response = ''; + second = true; + } + }, 1)); + + const errOrEnd = common.mustCall(function(err) { + if (!second) { + return; + } + + assert.strictEqual( + response, + // Empty because of https://github.com/nodejs/node/commit/e8d7fedf7cad6e612e4f2e0456e359af57608ac7 + // 'HTTP/1.1 408 Request Timeout\r\nConnection: close\r\n\r\n' + '' + ); + server.close(); + }); + + client.on('error', errOrEnd); + client.on('end', errOrEnd); + + // Send two requests using pipelining. Delay before finishing the second one + client.resume(); + client.write('GET / HTTP/1.1\r\nConnection: keep-alive\r\n\r\nGET / HTTP/1.1\r\nConnection: '); + + // Complete the request + setTimeout(() => { + client.write('close\r\n\r\n'); + }, requestTimeout * 1.5).unref(); +})); diff --git a/test/parallel/test-http-server-request-timeout-upgrade.js b/test/parallel/test-http-server-request-timeout-upgrade.js index 87e8dbab131631..b1974a128b9df3 100644 --- a/test/parallel/test-http-server-request-timeout-upgrade.js +++ b/test/parallel/test-http-server-request-timeout-upgrade.js @@ -8,16 +8,18 @@ const { connect } = require('net'); // This test validates that the requestTimeoout // is disabled after the connection is upgraded. let sendDelayedRequestHeaders; -const server = createServer(common.mustNotCall()); +const requestTimeout = common.platformTimeout(1000); +const server = createServer({ + headersTimeout: 0, + requestTimeout, + keepAliveTimeout: 0, + connectionsCheckingInterval: common.platformTimeout(250), +}, common.mustNotCall()); server.on('connection', common.mustCall(() => { assert.strictEqual(typeof sendDelayedRequestHeaders, 'function'); sendDelayedRequestHeaders(); })); -// 0 seconds is the default -assert.strictEqual(server.requestTimeout, 0); -const requestTimeout = common.platformTimeout(1000); -server.requestTimeout = requestTimeout; assert.strictEqual(server.requestTimeout, requestTimeout); server.on('upgrade', common.mustCall((req, socket, head) => { diff --git a/test/parallel/test-http-slow-headers-keepalive-multiple-requests.js b/test/parallel/test-http-slow-headers-keepalive-multiple-requests.js deleted file mode 100644 index 9ea76e8e56e952..00000000000000 --- a/test/parallel/test-http-slow-headers-keepalive-multiple-requests.js +++ /dev/null @@ -1,51 +0,0 @@ -'use strict'; - -const common = require('../common'); -const http = require('http'); -const net = require('net'); -const { finished } = require('stream'); - -const headers = - 'GET / HTTP/1.1\r\n' + - 'Host: localhost\r\n' + - 'Connection: keep-alive\r\n' + - 'Agent: node\r\n'; - -const baseTimeout = 1000; - -const server = http.createServer(common.mustCall((req, res) => { - req.resume(); - res.writeHead(200); - res.end(); -}, 2)); - -server.keepAliveTimeout = 10 * baseTimeout; -server.headersTimeout = baseTimeout; - -server.once('timeout', common.mustNotCall((socket) => { - socket.destroy(); -})); - -server.listen(0, () => { - const client = net.connect(server.address().port); - - // first request - client.write(headers); - client.write('\r\n'); - - setTimeout(() => { - // second request - client.write(headers); - // `headersTimeout` doesn't seem to fire if request - // is sent altogether. - setTimeout(() => { - client.write('\r\n'); - client.end(); - }, 10); - }, baseTimeout + 10); - - client.resume(); - finished(client, common.mustCall((err) => { - server.close(); - })); -}); diff --git a/test/parallel/test-http-slow-headers-keepalive.js b/test/parallel/test-http-slow-headers-keepalive.js deleted file mode 100644 index 4958d534ef77d4..00000000000000 --- a/test/parallel/test-http-slow-headers-keepalive.js +++ /dev/null @@ -1,51 +0,0 @@ -'use strict'; - -const common = require('../common'); -const http = require('http'); -const net = require('net'); -const { finished } = require('stream'); - -const headers = - 'GET / HTTP/1.1\r\n' + - 'Host: localhost\r\n' + - 'Connection: keep-alive\r\n' + - 'Agent: node\r\n'; - -let sendCharEvery = 1000; - -const server = http.createServer(common.mustCall((req, res) => { - res.writeHead(200); - res.end(); -})); - -// Pass a REAL env variable to shortening up the default -// value which is 40s otherwise this is useful for manual -// testing -if (!process.env.REAL) { - sendCharEvery = common.platformTimeout(10); - server.headersTimeout = 2 * sendCharEvery; -} - -server.once('timeout', common.mustCall((socket) => { - socket.destroy(); -})); - -server.listen(0, () => { - const client = net.connect(server.address().port); - client.write(headers); - // Finish the first request - client.write('\r\n'); - // second request - client.write(headers); - client.write('X-CRASH: '); - - const interval = setInterval(() => { - client.write('a'); - }, sendCharEvery); - - client.resume(); - finished(client, common.mustCall((err) => { - clearInterval(interval); - server.close(); - })); -}); diff --git a/test/parallel/test-http-slow-headers.js b/test/parallel/test-http-slow-headers.js deleted file mode 100644 index 25ee5b63e8c0e2..00000000000000 --- a/test/parallel/test-http-slow-headers.js +++ /dev/null @@ -1,50 +0,0 @@ -'use strict'; - -const common = require('../common'); -const assert = require('assert'); -const { createServer } = require('http'); -const { connect } = require('net'); -const { finished } = require('stream'); - -// This test validates that the 'timeout' event fires -// after server.headersTimeout. - -const headers = - 'GET / HTTP/1.1\r\n' + - 'Host: localhost\r\n' + - 'Agent: node\r\n'; - -const server = createServer(common.mustNotCall()); -let sendCharEvery = 1000; - -// 60 seconds is the default -assert.strictEqual(server.headersTimeout, 60 * 1000); - -// Pass a REAL env variable to shortening up the default -// value which is 40s otherwise this is useful for manual -// testing -if (!process.env.REAL) { - sendCharEvery = common.platformTimeout(10); - server.headersTimeout = 2 * sendCharEvery; -} - -server.once('timeout', common.mustCall((socket) => { - socket.destroy(); -})); - -server.listen(0, common.mustCall(() => { - const client = connect(server.address().port); - client.write(headers); - client.write('X-CRASH: '); - - const interval = setInterval(() => { - client.write('a'); - }, sendCharEvery); - - client.resume(); - - finished(client, common.mustCall((err) => { - clearInterval(interval); - server.close(); - })); -})); diff --git a/test/parallel/test-http-write-callbacks.js b/test/parallel/test-http-write-callbacks.js index 3730e57936b4a6..390fddf1dc2911 100644 --- a/test/parallel/test-http-write-callbacks.js +++ b/test/parallel/test-http-write-callbacks.js @@ -20,7 +20,7 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. 'use strict'; -require('../common'); +const common = require('../common'); const assert = require('assert'); const http = require('http'); @@ -53,15 +53,13 @@ server.on('checkContinue', (req, res) => { res.writeContinue(() => { // Continue has been written req.on('end', () => { - res.write('asdf', (er) => { - assert.ifError(er); - res.write('foo', 'ascii', (er) => { - assert.ifError(er); - res.end(Buffer.from('bar'), 'buffer', (er) => { + res.write('asdf', common.mustSucceed(() => { + res.write('foo', 'ascii', common.mustSucceed(() => { + res.end(Buffer.from('bar'), 'buffer', common.mustSucceed(() => { serverEndCb = true; - }); - }); - }); + })); + })); + })); }); }); @@ -79,16 +77,13 @@ server.listen(0, function() { }); req.on('continue', () => { // ok, good to go. - req.write('YmF6', 'base64', (er) => { - assert.ifError(er); - req.write(Buffer.from('quux'), (er) => { - assert.ifError(er); - req.end('626c657267', 'hex', (er) => { - assert.ifError(er); + req.write('YmF6', 'base64', common.mustSucceed(() => { + req.write(Buffer.from('quux'), common.mustSucceed(() => { + req.end('626c657267', 'hex', common.mustSucceed(() => { clientEndCb = true; - }); - }); - }); + })); + })); + })); }); req.on('response', (res) => { // This should not come until after the end is flushed out diff --git a/test/parallel/test-http2-exceeds-server-trailer-size.js b/test/parallel/test-http2-exceeds-server-trailer-size.js new file mode 100644 index 00000000000000..87c1070afbb7a4 --- /dev/null +++ b/test/parallel/test-http2-exceeds-server-trailer-size.js @@ -0,0 +1,51 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { createServer, constants, connect } = require('http2'); + +const server = createServer(); + +server.on('stream', (stream, headers) => { + stream.respond(undefined, { waitForTrailers: true }); + + stream.on('data', common.mustNotCall()); + + stream.on('wantTrailers', common.mustCall(() => { + // Trigger a frame error by sending a trailer that is too large + stream.sendTrailers({ 'test-trailer': 'X'.repeat(64 * 1024) }); + })); + + stream.on('frameError', common.mustCall((frameType, errorCode) => { + assert.strictEqual(errorCode, constants.NGHTTP2_FRAME_SIZE_ERROR); + })); + + stream.on('error', common.expectsError({ + code: 'ERR_HTTP2_STREAM_ERROR', + })); + + stream.on('close', common.mustCall()); + + stream.end(); +}); + +server.listen(0, () => { + const clientSession = connect(`http://localhost:${server.address().port}`); + + clientSession.on('frameError', common.mustNotCall()); + clientSession.on('close', common.mustCall(() => { + server.close(); + })); + + const clientStream = clientSession.request(); + + clientStream.on('close', common.mustCall()); + // These events mustn't be called once the frame size error is from the server + clientStream.on('frameError', common.mustNotCall()); + clientStream.on('error', common.mustNotCall()); + + clientStream.end(); +}); diff --git a/test/parallel/test-http2-goaway-delayed-request.js b/test/parallel/test-http2-goaway-delayed-request.js new file mode 100644 index 00000000000000..7afadbe80186ee --- /dev/null +++ b/test/parallel/test-http2-goaway-delayed-request.js @@ -0,0 +1,22 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const http2 = require('http2'); + +const server = http2.createServer(); + +server.listen(0, () => { + const client = http2.connect(`http://localhost:${server.address().port}`); + client.on('close', common.mustCall(() => { + server.close(); + })); + + // The client.close() is executed before the socket is able to make request + const stream = client.request(); + stream.on('error', common.expectsError({ code: 'ERR_HTTP2_GOAWAY_SESSION' })); + + setImmediate(() => client.close()); +}); diff --git a/test/parallel/test-http2-max-session-memory-leak.js b/test/parallel/test-http2-max-session-memory-leak.js index b066ca80bc5eab..476c605783c81c 100644 --- a/test/parallel/test-http2-max-session-memory-leak.js +++ b/test/parallel/test-http2-max-session-memory-leak.js @@ -9,7 +9,7 @@ const http2 = require('http2'); // mechanism. const bodyLength = 8192; -const maxSessionMemory = 1; // 1 MB +const maxSessionMemory = 1; // 1 MiB const requestCount = 1000; const server = http2.createServer({ maxSessionMemory }); diff --git a/test/parallel/test-http2-options-max-headers-block-length.js b/test/parallel/test-http2-options-max-headers-block-length.js index 11632c6e825c53..af1cc6f9bc4860 100644 --- a/test/parallel/test-http2-options-max-headers-block-length.js +++ b/test/parallel/test-http2-options-max-headers-block-length.js @@ -32,12 +32,12 @@ server.listen(0, common.mustCall(() => { })); req.on('frameError', common.mustCall((type, code) => { - assert.strictEqual(code, h2.constants.NGHTTP2_ERR_FRAME_SIZE_ERROR); + assert.strictEqual(code, h2.constants.NGHTTP2_FRAME_SIZE_ERROR); })); req.on('error', common.expectsError({ code: 'ERR_HTTP2_STREAM_ERROR', name: 'Error', - message: 'Stream closed with error code NGHTTP2_REFUSED_STREAM' + message: 'Stream closed with error code NGHTTP2_FRAME_SIZE_ERROR' })); })); diff --git a/test/parallel/test-http2-server-stream-session-destroy.js b/test/parallel/test-http2-server-stream-session-destroy.js index a03569cdee7825..34b22fdfbd1334 100644 --- a/test/parallel/test-http2-server-stream-session-destroy.js +++ b/test/parallel/test-http2-server-stream-session-destroy.js @@ -34,7 +34,7 @@ server.on('stream', common.mustCall((stream) => { name: 'Error' } ); - // When session is detroyed all streams are destroyed and no further + // When session is destroyed all streams are destroyed and no further // error should be emitted. stream.on('error', common.mustNotCall()); assert.strictEqual(stream.write('data', common.expectsError({ diff --git a/test/parallel/test-https-agent-getname.js b/test/parallel/test-https-agent-getname.js index 6f8c32b299a669..2a13ab1c6f47ee 100644 --- a/test/parallel/test-https-agent-getname.js +++ b/test/parallel/test-https-agent-getname.js @@ -9,6 +9,12 @@ const https = require('https'); const agent = new https.Agent(); +// empty argument +assert.strictEqual( + agent.getName(), + 'localhost::::::::::::::::::::::' +); + // empty options assert.strictEqual( agent.getName({}), diff --git a/test/parallel/test-https-server-headers-timeout.js b/test/parallel/test-https-server-headers-timeout.js new file mode 100644 index 00000000000000..45457e39425127 --- /dev/null +++ b/test/parallel/test-https-server-headers-timeout.js @@ -0,0 +1,21 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const { createServer } = require('https'); +const fixtures = require('../common/fixtures'); + +const options = { + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem'), +}; + +const server = createServer(options); + +// 60000 seconds is the default +assert.strictEqual(server.headersTimeout, 60000); +const headersTimeout = common.platformTimeout(1000); +server.headersTimeout = headersTimeout; +assert.strictEqual(server.headersTimeout, headersTimeout); diff --git a/test/parallel/test-https-server-request-timeout.js b/test/parallel/test-https-server-request-timeout.js index 66a1cb9f25f82e..00bac8ea3991fa 100644 --- a/test/parallel/test-https-server-request-timeout.js +++ b/test/parallel/test-https-server-request-timeout.js @@ -14,8 +14,8 @@ const options = { const server = createServer(options); -// 0 seconds is the default -assert.strictEqual(server.requestTimeout, 0); +// 300 seconds is the default +assert.strictEqual(server.requestTimeout, 300000); const requestTimeout = common.platformTimeout(1000); server.requestTimeout = requestTimeout; assert.strictEqual(server.requestTimeout, requestTimeout); diff --git a/test/parallel/test-https-slow-headers.js b/test/parallel/test-https-slow-headers.js deleted file mode 100644 index 95f0caa9878c5e..00000000000000 --- a/test/parallel/test-https-slow-headers.js +++ /dev/null @@ -1,63 +0,0 @@ -'use strict'; - -const common = require('../common'); -const { readKey } = require('../common/fixtures'); - -if (!common.hasCrypto) - common.skip('missing crypto'); - -const assert = require('assert'); -const { createServer } = require('https'); -const { connect } = require('tls'); -const { finished } = require('stream'); - -// This test validates that the 'timeout' event fires -// after server.headersTimeout. - -const headers = - 'GET / HTTP/1.1\r\n' + - 'Host: localhost\r\n' + - 'Agent: node\r\n'; - -const server = createServer({ - key: readKey('agent1-key.pem'), - cert: readKey('agent1-cert.pem'), - ca: readKey('ca1-cert.pem'), -}, common.mustNotCall()); - -let sendCharEvery = 1000; - -// 60 seconds is the default -assert.strictEqual(server.headersTimeout, 60 * 1000); - -// Pass a REAL env variable to shortening up the default -// value which is 40s otherwise -// this is useful for manual testing -if (!process.env.REAL) { - sendCharEvery = common.platformTimeout(10); - server.headersTimeout = 2 * sendCharEvery; -} - -server.once('timeout', common.mustCall((socket) => { - socket.destroy(); -})); - -server.listen(0, common.mustCall(() => { - const client = connect({ - port: server.address().port, - rejectUnauthorized: false - }); - client.write(headers); - client.write('X-CRASH: '); - - const interval = setInterval(() => { - client.write('a'); - }, sendCharEvery); - - client.resume(); - - finished(client, common.mustCall((err) => { - clearInterval(interval); - server.close(); - })); -})); diff --git a/test/parallel/test-net-listen-invalid-port.js b/test/parallel/test-net-listen-invalid-port.js index 844878036646dd..9e4dab1d4e5970 100644 --- a/test/parallel/test-net-listen-invalid-port.js +++ b/test/parallel/test-net-listen-invalid-port.js @@ -12,7 +12,7 @@ const invalidPort = -1 >>> 0; net.Server().listen(0, function() { const address = this.address(); - const key = `${address.family.slice(-1)}:${address.address}:0`; + const key = `${address.family}:${address.address}:0`; assert.strictEqual(this._connectionKey, key); this.close(); diff --git a/test/parallel/test-net-perf_hooks.js b/test/parallel/test-net-perf_hooks.js new file mode 100644 index 00000000000000..cbaaac135b92d8 --- /dev/null +++ b/test/parallel/test-net-perf_hooks.js @@ -0,0 +1,60 @@ +'use strict'; + +const common = require('../common'); +const tmpdir = require('../common/tmpdir'); +const assert = require('assert'); +const net = require('net'); + +tmpdir.refresh(); + +const { PerformanceObserver } = require('perf_hooks'); + +const entries = []; + +const obs = new PerformanceObserver(common.mustCallAtLeast((items) => { + entries.push(...items.getEntries()); +})); + +obs.observe({ type: 'net' }); + +{ + const server = net.createServer(common.mustCall((socket) => { + socket.destroy(); + })); + + server.listen(8080, common.mustCall(async () => { + await new Promise((resolve, reject) => { + const socket = net.connect(server.address().port); + socket.on('end', resolve); + socket.on('error', reject); + }); + server.close(); + })); +} + +{ + const server = net.createServer(common.mustCall((socket) => { + socket.destroy(); + })); + + server.listen(common.PIPE, common.mustCall(async () => { + await new Promise((resolve, reject) => { + const socket = net.connect(common.PIPE); + socket.on('end', resolve); + socket.on('error', reject); + }); + server.close(); + })); +} + +process.on('exit', () => { + assert.strictEqual(entries.length, 1); + entries.forEach((entry) => { + assert.strictEqual(entry.name, 'connect'); + assert.strictEqual(entry.entryType, 'net'); + assert.strictEqual(typeof entry.startTime, 'number'); + assert.strictEqual(typeof entry.duration, 'number'); + assert.strictEqual(!!entry.detail.host, true); + assert.strictEqual(!!entry.detail.port, true); + }); +}); diff --git a/test/parallel/test-net-socket-connect-without-cb.js b/test/parallel/test-net-socket-connect-without-cb.js index 468b29a3a486d7..a6a8db8b633159 100644 --- a/test/parallel/test-net-socket-connect-without-cb.js +++ b/test/parallel/test-net-socket-connect-without-cb.js @@ -16,5 +16,11 @@ const server = net.createServer(common.mustCall(function(conn) { client.end(); })); - client.connect(server.address()); + const address = server.address(); + if (!common.hasIPv6 && address.family === 6) { + // Necessary to pass CI running inside containers. + client.connect(address.port); + } else { + client.connect(address); + } })); diff --git a/test/parallel/test-net-socket-ready-without-cb.js b/test/parallel/test-net-socket-ready-without-cb.js index 1ad5db4760dc1e..29da68e173c193 100644 --- a/test/parallel/test-net-socket-ready-without-cb.js +++ b/test/parallel/test-net-socket-ready-without-cb.js @@ -9,7 +9,7 @@ const net = require('net'); const server = net.createServer(common.mustCall(function(conn) { conn.end(); server.close(); -})).listen(0, common.mustCall(function() { +})).listen(0, 'localhost', common.mustCall(function() { const client = new net.Socket(); client.on('ready', common.mustCall(function() { diff --git a/test/parallel/test-net-socket-timeout.js b/test/parallel/test-net-socket-timeout.js index 58906a25df42b9..2fc9b4e142c11e 100644 --- a/test/parallel/test-net-socket-timeout.js +++ b/test/parallel/test-net-socket-timeout.js @@ -53,9 +53,9 @@ for (let i = 0; i < validDelays.length; i++) { } for (let i = 0; i < invalidCallbacks.length; i++) { - [0, 1].forEach((mesc) => + [0, 1].forEach((msec) => assert.throws( - () => s.setTimeout(mesc, invalidCallbacks[i]), + () => s.setTimeout(msec, invalidCallbacks[i]), { code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError', diff --git a/test/parallel/test-os.js b/test/parallel/test-os.js index 80c32ea2a996c0..888a29539c9d18 100644 --- a/test/parallel/test-os.js +++ b/test/parallel/test-os.js @@ -138,7 +138,7 @@ switch (platform) { const expected = [{ address: '127.0.0.1', netmask: '255.0.0.0', - family: 'IPv4', + family: 4, mac: '00:00:00:00:00:00', internal: true, cidr: '127.0.0.1/8' @@ -154,7 +154,7 @@ switch (platform) { const expected = [{ address: '127.0.0.1', netmask: '255.0.0.0', - family: 'IPv4', + family: 4, mac: '00:00:00:00:00:00', internal: true, cidr: '127.0.0.1/8' diff --git a/test/parallel/test-performance-timeline.mjs b/test/parallel/test-performance-timeline.mjs index 18ef762ffd8d3b..57e0f6f0b7b6da 100644 --- a/test/parallel/test-performance-timeline.mjs +++ b/test/parallel/test-performance-timeline.mjs @@ -33,3 +33,18 @@ await setTimeout(50); performance.measure('a', 'one'); const entriesByName = performance.getEntriesByName('a'); assert.deepStrictEqual(entriesByName.map((x) => x.entryType), ['measure', 'mark', 'measure', 'mark']); + +// getEntriesBy[Name|Type](undefined) +performance.mark(undefined); +assert.strictEqual(performance.getEntriesByName(undefined).length, 1); +assert.strictEqual(performance.getEntriesByType(undefined).length, 0); +assert.throws(() => performance.getEntriesByName(), { + name: 'TypeError', + message: 'The "name" argument must be specified', + code: 'ERR_MISSING_ARGS' +}); +assert.throws(() => performance.getEntriesByType(), { + name: 'TypeError', + message: 'The "type" argument must be specified', + code: 'ERR_MISSING_ARGS' +}); diff --git a/test/parallel/test-process-env-delete.js b/test/parallel/test-process-env-delete.js new file mode 100644 index 00000000000000..3653a13911275e --- /dev/null +++ b/test/parallel/test-process-env-delete.js @@ -0,0 +1,13 @@ +'use strict'; +require('../common'); +const assert = require('assert'); + +process.env.foo = 'foo'; +assert.strictEqual(process.env.foo, 'foo'); +process.env.foo = undefined; +assert.strictEqual(process.env.foo, 'undefined'); + +process.env.foo = 'foo'; +assert.strictEqual(process.env.foo, 'foo'); +delete process.env.foo; +assert.strictEqual(process.env.foo, undefined); diff --git a/test/parallel/test-process-env-ignore-getter-setter.js b/test/parallel/test-process-env-ignore-getter-setter.js new file mode 100644 index 00000000000000..7368eb85684c5a --- /dev/null +++ b/test/parallel/test-process-env-ignore-getter-setter.js @@ -0,0 +1,67 @@ +'use strict'; +require('../common'); +const assert = require('assert'); + +assert.throws( + () => { + Object.defineProperty(process.env, 'foo', { + value: 'foo1' + }); + }, + { + code: 'ERR_INVALID_OBJECT_DEFINE_PROPERTY', + name: 'TypeError', + message: '\'process.env\' only accepts a ' + + 'configurable, writable,' + + ' and enumerable data descriptor' + } +); + +assert.strictEqual(process.env.foo, undefined); +process.env.foo = 'foo2'; +assert.strictEqual(process.env.foo, 'foo2'); + +assert.throws( + () => { + Object.defineProperty(process.env, 'goo', { + get() { + return 'goo'; + }, + set() {} + }); + }, + { + code: 'ERR_INVALID_OBJECT_DEFINE_PROPERTY', + name: 'TypeError', + message: '\'process.env\' does not accept an' + + 'accessor(getter/setter) descriptor' + } +); + +const attributes = ['configurable', 'writable', 'enumerable']; + +attributes.forEach((attribute) => { + assert.throws( + () => { + Object.defineProperty(process.env, 'goo', { + [attribute]: false + }); + }, + { + code: 'ERR_INVALID_OBJECT_DEFINE_PROPERTY', + name: 'TypeError', + message: '\'process.env\' only accepts a ' + + 'configurable, writable,' + + ' and enumerable data descriptor' + } + ); +}); + +assert.strictEqual(process.env.goo, undefined); +Object.defineProperty(process.env, 'goo', { + value: 'goo', + configurable: true, + writable: true, + enumerable: true +}); +assert.strictEqual(process.env.goo, 'goo'); diff --git a/test/parallel/test-process-env-tz.js b/test/parallel/test-process-env-tz.js index da716299c9f622..b0188ab9c267f8 100644 --- a/test/parallel/test-process-env-tz.js +++ b/test/parallel/test-process-env-tz.js @@ -31,19 +31,19 @@ if (date.toString().includes('(Central European Time)') || common.skip('tzdata too old'); } -assert.strictEqual( - date.toString().replace('Central European Summer Time', 'CEST'), - 'Sat Apr 14 2018 14:34:56 GMT+0200 (CEST)'); +assert.match( + date.toString(), + /^Sat Apr 14 2018 14:34:56 GMT\+0200 \(.+\)$/); process.env.TZ = 'Europe/London'; -assert.strictEqual( - date.toString().replace('British Summer Time', 'BST'), - 'Sat Apr 14 2018 13:34:56 GMT+0100 (BST)'); +assert.match( + date.toString(), + /^Sat Apr 14 2018 13:34:56 GMT\+0100 \(.+\)$/); process.env.TZ = 'Etc/UTC'; -assert.strictEqual( - date.toString().replace('Coordinated Universal Time', 'UTC'), - 'Sat Apr 14 2018 12:34:56 GMT+0000 (UTC)'); +assert.match( + date.toString(), + /^Sat Apr 14 2018 12:34:56 GMT\+0000 \(.+\)$/); // Just check that deleting the environment variable doesn't crash the process. // We can't really check the result of date.toString() because we don't know diff --git a/test/parallel/test-queue-microtask-uncaught-asynchooks.js b/test/parallel/test-queue-microtask-uncaught-asynchooks.js index ee64c6e68ab7ab..35b3d9fa309af9 100644 --- a/test/parallel/test-queue-microtask-uncaught-asynchooks.js +++ b/test/parallel/test-queue-microtask-uncaught-asynchooks.js @@ -11,7 +11,7 @@ let µtaskId; const events = []; async_hooks.createHook({ - init(id, type, triggerId, resoure) { + init(id, type, triggerId, resource) { if (type === 'Microtask') { µtaskId = id; events.push('init'); diff --git a/test/parallel/test-readline-interface.js b/test/parallel/test-readline-interface.js index ad7eee7c42e485..52cfdac2b8f88a 100644 --- a/test/parallel/test-readline-interface.js +++ b/test/parallel/test-readline-interface.js @@ -1092,6 +1092,40 @@ for (let i = 0; i < 12; i++) { rli.close(); } + // Call question after close + { + const [rli, fi] = getInterface({ terminal }); + rli.question('What\'s your name?', common.mustCall((name) => { + assert.strictEqual(name, 'Node.js'); + rli.close(); + assert.throws(() => { + rli.question('How are you?', common.mustNotCall()); + }, { + name: 'Error', + code: 'ERR_USE_AFTER_CLOSE' + }); + assert.notStrictEqual(rli.getPrompt(), 'How are you?'); + })); + fi.emit('data', 'Node.js\n'); + } + + // Call promisified question after close + { + const [rli, fi] = getInterface({ terminal }); + const question = util.promisify(rli.question).bind(rli); + question('What\'s your name?').then(common.mustCall((name) => { + assert.strictEqual(name, 'Node.js'); + rli.close(); + question('How are you?') + .then(common.mustNotCall(), common.expectsError({ + code: 'ERR_USE_AFTER_CLOSE', + name: 'Error' + })); + assert.notStrictEqual(rli.getPrompt(), 'How are you?'); + })); + fi.emit('data', 'Node.js\n'); + } + // Can create a new readline Interface with a null output argument { const [rli, fi] = getInterface({ output: null, terminal }); diff --git a/test/parallel/test-readline-promises-csi.mjs b/test/parallel/test-readline-promises-csi.mjs index 71a3ad7a816bda..9b0d0bb2b84008 100644 --- a/test/parallel/test-readline-promises-csi.mjs +++ b/test/parallel/test-readline-promises-csi.mjs @@ -4,6 +4,7 @@ import '../common/index.mjs'; import assert from 'assert'; import { Readline } from 'readline/promises'; +import { setImmediate } from 'timers/promises'; import { Writable } from 'stream'; import utils from 'internal/readline/utils'; @@ -161,3 +162,65 @@ class TestWritable extends Writable { await assert.rejects(readline.cursorTo(1).commit(), error); } + +{ + const writable = new TestWritable(); + const readline = new Readline(writable, { autoCommit: true }); + + await readline.clearScreenDown(); + await setImmediate(); // Wait for next tick as auto commit is asynchronous. + assert.deepStrictEqual(writable.data, CSI.kClearScreenDown); +} + +{ + const writable = new TestWritable(); + const readline = new Readline(writable, { autoCommit: true }); + for (const [dir, data] of + [ + [-1, CSI.kClearToLineBeginning], + [1, CSI.kClearToLineEnd], + [0, CSI.kClearLine], + ]) { + writable.data = ''; + readline.clearLine(dir); + await setImmediate(); // Wait for next tick as auto commit is asynchronous. + assert.deepStrictEqual(writable.data, data); + } +} + +{ + const writable = new TestWritable(); + const readline = new Readline(writable, { autoCommit: true }); + for (const [x, y, data] of + [ + [0, 0, ''], + [1, 0, '\x1b[1C'], + [-1, 0, '\x1b[1D'], + [0, 1, '\x1b[1B'], + [0, -1, '\x1b[1A'], + [1, 1, '\x1b[1C\x1b[1B'], + [-1, 1, '\x1b[1D\x1b[1B'], + [-1, -1, '\x1b[1D\x1b[1A'], + [1, -1, '\x1b[1C\x1b[1A'], + ]) { + writable.data = ''; + readline.moveCursor(x, y); + await setImmediate(); // Wait for next tick as auto commit is asynchronous. + assert.deepStrictEqual(writable.data, data); + } +} + +{ + const writable = new TestWritable(); + const readline = new Readline(writable, { autoCommit: true }); + for (const [x, y, data] of + [ + [1, undefined, '\x1b[2G'], + [1, 2, '\x1b[3;2H'], + ]) { + writable.data = ''; + readline.cursorTo(x, y); + await setImmediate(); // Wait for next tick as auto commit is asynchronous. + assert.deepStrictEqual(writable.data, data); + } +} diff --git a/test/parallel/test-readline-promises-interface.js b/test/parallel/test-readline-promises-interface.js index cf0543d7d255f7..455dabe6749bd5 100644 --- a/test/parallel/test-readline-promises-interface.js +++ b/test/parallel/test-readline-promises-interface.js @@ -952,6 +952,23 @@ for (let i = 0; i < 12; i++) { rli.close(); } + // Call question after close + { + const [rli, fi] = getInterface({ terminal }); + rli.question('What\'s your name?').then(common.mustCall((name) => { + assert.strictEqual(name, 'Node.js'); + rli.close(); + rli.question('How are you?') + .then(common.mustNotCall(), common.expectsError({ + code: 'ERR_USE_AFTER_CLOSE', + name: 'Error' + })); + assert.notStrictEqual(rli.getPrompt(), 'How are you?'); + })); + fi.emit('data', 'Node.js\n'); + } + + // Can create a new readline Interface with a null output argument { const [rli, fi] = getInterface({ output: null, terminal }); diff --git a/test/parallel/test-repl-tab-complete-import.js b/test/parallel/test-repl-tab-complete-import.js index 1968caa5accf54..e328d95db5986c 100644 --- a/test/parallel/test-repl-tab-complete-import.js +++ b/test/parallel/test-repl-tab-complete-import.js @@ -53,14 +53,18 @@ testMe.complete("import\t( 'n", common.mustCall((error, data) => { assert.strictEqual(data[1], 'n'); const completions = data[0]; // import(...) completions include `node:` URL modules: - publicModules.forEach((lib, index) => - assert.strictEqual(completions[index], `node:${lib}`)); - assert.strictEqual(completions[publicModules.length], ''); + let lastIndex = -1; + + publicModules.forEach((lib, index) => { + lastIndex = completions.indexOf(`node:${lib}`); + assert.notStrictEqual(lastIndex, -1); + }); + assert.strictEqual(completions[lastIndex + 1], ''); // There is only one Node.js module that starts with n: - assert.strictEqual(completions[publicModules.length + 1], 'net'); - assert.strictEqual(completions[publicModules.length + 2], ''); + assert.strictEqual(completions[lastIndex + 2], 'net'); + assert.strictEqual(completions[lastIndex + 3], ''); // It's possible to pick up non-core modules too - completions.slice(publicModules.length + 3).forEach((completion) => { + completions.slice(lastIndex + 4).forEach((completion) => { assert.match(completion, /^n/); }); })); diff --git a/test/parallel/test-repl-tab-complete.js b/test/parallel/test-repl-tab-complete.js index 270fb768b030cf..5b60c88dc712ce 100644 --- a/test/parallel/test-repl-tab-complete.js +++ b/test/parallel/test-repl-tab-complete.js @@ -261,14 +261,18 @@ testMe.complete("require\t( 'n", common.mustCall(function(error, data) { assert.strictEqual(data.length, 2); assert.strictEqual(data[1], 'n'); // require(...) completions include `node:`-prefixed modules: - publicModules.forEach((lib, index) => - assert.strictEqual(data[0][index], `node:${lib}`)); - assert.strictEqual(data[0][publicModules.length], ''); + let lastIndex = -1; + + publicModules.forEach((lib, index) => { + lastIndex = data[0].indexOf(`node:${lib}`); + assert.notStrictEqual(lastIndex, -1); + }); + assert.strictEqual(data[0][lastIndex + 1], ''); // There is only one Node.js module that starts with n: - assert.strictEqual(data[0][publicModules.length + 1], 'net'); - assert.strictEqual(data[0][publicModules.length + 2], ''); + assert.strictEqual(data[0][lastIndex + 2], 'net'); + assert.strictEqual(data[0][lastIndex + 3], ''); // It's possible to pick up non-core modules too - data[0].slice(publicModules.length + 3).forEach((completion) => { + data[0].slice(lastIndex + 4).forEach((completion) => { assert.match(completion, /^n/); }); })); diff --git a/test/parallel/test-runner-cli.js b/test/parallel/test-runner-cli.js new file mode 100644 index 00000000000000..7bd95372a2d68b --- /dev/null +++ b/test/parallel/test-runner-cli.js @@ -0,0 +1,107 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const { spawnSync } = require('child_process'); +const { join } = require('path'); +const fixtures = require('../common/fixtures'); +const testFixtures = fixtures.path('test-runner'); + +{ + // File not found. + const args = ['--test', 'a-random-file-that-does-not-exist.js']; + const child = spawnSync(process.execPath, args); + + assert.strictEqual(child.status, 1); + assert.strictEqual(child.signal, null); + assert.strictEqual(child.stdout.toString(), ''); + assert.match(child.stderr.toString(), /^Could not find/); +} + +{ + // Default behavior. node_modules is ignored. Files that don't match the + // pattern are ignored except in test/ directories. + const args = ['--test', testFixtures]; + const child = spawnSync(process.execPath, args); + + assert.strictEqual(child.status, 1); + assert.strictEqual(child.signal, null); + assert.strictEqual(child.stderr.toString(), ''); + const stdout = child.stdout.toString(); + assert.match(stdout, /ok 1 - .+index\.test\.js/); + assert.match(stdout, /not ok 2 - .+random\.test\.mjs/); + assert.match(stdout, /not ok 1 - this should fail/); + assert.match(stdout, /ok 3 - .+subdir.+subdir_test\.js/); + assert.match(stdout, /ok 4 - .+random\.cjs/); +} + +{ + // User specified files that don't match the pattern are still run. + const args = ['--test', testFixtures, join(testFixtures, 'index.js')]; + const child = spawnSync(process.execPath, args); + + assert.strictEqual(child.status, 1); + assert.strictEqual(child.signal, null); + assert.strictEqual(child.stderr.toString(), ''); + const stdout = child.stdout.toString(); + assert.match(stdout, /not ok 1 - .+index\.js/); + assert.match(stdout, /ok 2 - .+index\.test\.js/); + assert.match(stdout, /not ok 3 - .+random\.test\.mjs/); + assert.match(stdout, /not ok 1 - this should fail/); + assert.match(stdout, /ok 4 - .+subdir.+subdir_test\.js/); +} + +{ + // Searches node_modules if specified. + const args = ['--test', join(testFixtures, 'node_modules')]; + const child = spawnSync(process.execPath, args); + + assert.strictEqual(child.status, 1); + assert.strictEqual(child.signal, null); + assert.strictEqual(child.stderr.toString(), ''); + const stdout = child.stdout.toString(); + assert.match(stdout, /not ok 1 - .+test-nm\.js/); +} + +{ + // The current directory is used by default. + const args = ['--test']; + const options = { cwd: testFixtures }; + const child = spawnSync(process.execPath, args, options); + + assert.strictEqual(child.status, 1); + assert.strictEqual(child.signal, null); + assert.strictEqual(child.stderr.toString(), ''); + const stdout = child.stdout.toString(); + assert.match(stdout, /ok 1 - .+index\.test\.js/); + assert.match(stdout, /not ok 2 - .+random\.test\.mjs/); + assert.match(stdout, /not ok 1 - this should fail/); + assert.match(stdout, /ok 3 - .+subdir.+subdir_test\.js/); + assert.match(stdout, /ok 4 - .+random\.cjs/); +} + +{ + // Flags that cannot be combined with --test. + const flags = [ + ['--check', '--test'], + ['--interactive', '--test'], + ['--eval', 'console.log("should not print")', '--test'], + ['--print', 'console.log("should not print")', '--test'], + ]; + + if (process.features.inspector) { + flags.push( + ['--inspect', '--test'], + ['--inspect-brk', '--test'], + ); + } + + flags.forEach((args) => { + const child = spawnSync(process.execPath, args); + + assert.notStrictEqual(child.status, 0); + assert.strictEqual(child.signal, null); + assert.strictEqual(child.stdout.toString(), ''); + const stderr = child.stderr.toString(); + assert.match(stderr, /--test/); + }); +} diff --git a/test/parallel/test-runner-exit-code.js b/test/parallel/test-runner-exit-code.js new file mode 100644 index 00000000000000..0e72f77783e9a9 --- /dev/null +++ b/test/parallel/test-runner-exit-code.js @@ -0,0 +1,27 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const { spawnSync } = require('child_process'); + +if (process.argv[2] === 'child') { + const test = require('node:test'); + + if (process.argv[3] === 'pass') { + test('passing test', () => { + assert.strictEqual(true, true); + }); + } else { + assert.strictEqual(process.argv[3], 'fail'); + test('failing test', () => { + assert.strictEqual(true, false); + }); + } +} else { + let child = spawnSync(process.execPath, [__filename, 'child', 'pass']); + assert.strictEqual(child.status, 0); + assert.strictEqual(child.signal, null); + + child = spawnSync(process.execPath, [__filename, 'child', 'fail']); + assert.strictEqual(child.status, 1); + assert.strictEqual(child.signal, null); +} diff --git a/test/parallel/test-runner-import-no-scheme.js b/test/parallel/test-runner-import-no-scheme.js new file mode 100644 index 00000000000000..45dd83d0251988 --- /dev/null +++ b/test/parallel/test-runner-import-no-scheme.js @@ -0,0 +1,51 @@ +'use strict'; +const common = require('../common'); +const tmpdir = require('../common/tmpdir'); +const assert = require('assert'); +const { spawnSync } = require('child_process'); +const fs = require('fs'); +const path = require('path'); +const { createRequire } = require('module'); + +assert.throws( + () => require('test'), + common.expectsError({ code: 'MODULE_NOT_FOUND' }), +); + +(async () => { + await assert.rejects( + async () => import('test'), + common.expectsError({ code: 'ERR_MODULE_NOT_FOUND' }), + ); +})().then(common.mustCall()); + +assert.throws( + () => require.resolve('test'), + common.expectsError({ code: 'MODULE_NOT_FOUND' }), +); + +// Verify that files in node_modules can be resolved. +tmpdir.refresh(); + +const packageRoot = path.join(tmpdir.path, 'node_modules', 'test'); +const indexFile = path.join(packageRoot, 'index.js'); + +fs.mkdirSync(packageRoot, { recursive: true }); +fs.writeFileSync(indexFile, 'module.exports = { marker: 1 };'); + +function test(argv) { + const child = spawnSync(process.execPath, argv, { cwd: tmpdir.path }); + assert.strictEqual(child.status, 0); + assert.strictEqual(child.stdout.toString().trim(), '{ marker: 1 }'); +} + +test(['-e', 'console.log(require("test"))']); +test(['-e', 'import("test").then(m=>console.log(m.default))']); +test(['--input-type=module', '-e', 'import test from "test";console.log(test)']); +test(['--input-type=module', '-e', 'console.log((await import("test")).default)']); + +{ + const dummyFile = path.join(tmpdir.path, 'file.js'); + const require = createRequire(dummyFile); + assert.strictEqual(require.resolve('test'), indexFile); +} diff --git a/test/parallel/test-runner-test-filter.js b/test/parallel/test-runner-test-filter.js new file mode 100644 index 00000000000000..b6afba22a2e814 --- /dev/null +++ b/test/parallel/test-runner-test-filter.js @@ -0,0 +1,42 @@ +// Flags: --expose-internals +'use strict'; +require('../common'); +const assert = require('assert'); +const { doesPathMatchFilter } = require('internal/test_runner/utils'); + +// Paths expected to match +[ + 'test.js', + 'test.cjs', + 'test.mjs', + 'test-foo.js', + 'test-foo.cjs', + 'test-foo.mjs', + 'foo.test.js', + 'foo.test.cjs', + 'foo.test.mjs', + 'foo-test.js', + 'foo-test.cjs', + 'foo-test.mjs', + 'foo_test.js', + 'foo_test.cjs', + 'foo_test.mjs', +].forEach((p) => { + assert.strictEqual(doesPathMatchFilter(p), true); +}); + +// Paths expected not to match +[ + 'test', + 'test.djs', + 'test.cs', + 'test.mj', + 'foo.js', + 'test-foo.sj', + 'test.foo.js', + 'test_foo.js', + 'testfoo.js', + 'foo-test1.mjs', +].forEach((p) => { + assert.strictEqual(doesPathMatchFilter(p), false); +}); diff --git a/test/parallel/test-set-incoming-message-header.js b/test/parallel/test-set-incoming-message-header.js new file mode 100644 index 00000000000000..9ac05a8138d445 --- /dev/null +++ b/test/parallel/test-set-incoming-message-header.js @@ -0,0 +1,27 @@ +'use strict'; + +require('../common'); +const { IncomingMessage } = require('http'); +const assert = require('assert'); + +// Headers setter function set a header correctly +{ + const im = new IncomingMessage(); + im.headers = { key: 'value' }; + assert.deepStrictEqual(im.headers, { key: 'value' }); +} + +// Trailers setter function set a header correctly +{ + const im = new IncomingMessage(); + im.trailers = { key: 'value' }; + assert.deepStrictEqual(im.trailers, { key: 'value' }); +} + +// _addHeaderLines function set a header correctly +{ + const im = new IncomingMessage(); + im.headers = { key1: 'value1' }; + im._addHeaderLines(['key2', 'value2'], 2); + assert.deepStrictEqual(im.headers, { key1: 'value1', key2: 'value2' }); +} diff --git a/test/parallel/test-source-map-enable.js b/test/parallel/test-source-map-enable.js index 8e4f8e3a028062..dc00cf624ae8ea 100644 --- a/test/parallel/test-source-map-enable.js +++ b/test/parallel/test-source-map-enable.js @@ -343,6 +343,22 @@ function nextdir() { assert.ok(sourceMap); } +// Does not include null for async/await with esm +// Refs: https://github.com/nodejs/node/issues/42417 +{ + const output = spawnSync(process.execPath, [ + '--enable-source-maps', + require.resolve('../fixtures/source-map/throw-async.mjs'), + ]); + // Error in original context of source content: + assert.match( + output.stderr.toString(), + /throw new Error\(message\)\r?\n.*\^/ + ); + // Rewritten stack trace: + assert.match(output.stderr.toString(), /at Throw \([^)]+throw-async\.ts:4:9\)/); +} + function getSourceMapFromCache(fixtureFile, coverageDirectory) { const jsonFiles = fs.readdirSync(coverageDirectory); for (const jsonFile of jsonFiles) { diff --git a/test/parallel/test-stream-construct-async-error.js b/test/parallel/test-stream-construct-async-error.js deleted file mode 100644 index 3fe81b4ebe2d6b..00000000000000 --- a/test/parallel/test-stream-construct-async-error.js +++ /dev/null @@ -1,232 +0,0 @@ -'use strict'; - -const common = require('../common'); -const { - Duplex, - Writable, - Transform, -} = require('stream'); -const { setTimeout } = require('timers/promises'); -const assert = require('assert'); - -{ - class Foo extends Duplex { - async _destroy(err, cb) { - await setTimeout(common.platformTimeout(1)); - throw new Error('boom'); - } - } - - const foo = new Foo(); - foo.destroy(); - foo.on('error', common.expectsError({ - message: 'boom' - })); - foo.on('close', common.mustCall(() => { - assert(foo.destroyed); - })); -} - -{ - class Foo extends Duplex { - async _destroy(err, cb) { - await setTimeout(common.platformTimeout(1)); - } - } - - const foo = new Foo(); - foo.destroy(); - foo.on('close', common.mustCall(() => { - assert(foo.destroyed); - })); -} - -{ - class Foo extends Duplex { - async _construct() { - await setTimeout(common.platformTimeout(1)); - } - - _write = common.mustCall((chunk, encoding, cb) => { - cb(); - }); - - _read() {} - } - - const foo = new Foo(); - foo.write('test', common.mustCall()); -} - -{ - class Foo extends Duplex { - async _construct(callback) { - await setTimeout(common.platformTimeout(1)); - callback(); - } - - _write = common.mustCall((chunk, encoding, cb) => { - cb(); - }); - - _read() {} - } - - const foo = new Foo(); - foo.write('test', common.mustCall()); - foo.on('error', common.mustNotCall()); -} - -{ - class Foo extends Writable { - _write = common.mustCall((chunk, encoding, cb) => { - cb(); - }); - - async _final() { - await setTimeout(common.platformTimeout(1)); - } - } - - const foo = new Foo(); - foo.end('hello'); - foo.on('finish', common.mustCall()); -} - -{ - class Foo extends Writable { - _write = common.mustCall((chunk, encoding, cb) => { - cb(); - }); - - async _final(callback) { - await setTimeout(common.platformTimeout(1)); - callback(); - } - } - - const foo = new Foo(); - foo.end('hello'); - foo.on('finish', common.mustCall()); -} - -{ - class Foo extends Writable { - _write = common.mustCall((chunk, encoding, cb) => { - cb(); - }); - - async _final() { - await setTimeout(common.platformTimeout(1)); - throw new Error('boom'); - } - } - - const foo = new Foo(); - foo.end('hello'); - foo.on('error', common.expectsError({ - message: 'boom' - })); - foo.on('close', common.mustCall()); -} - -{ - const expected = ['hello', 'world']; - class Foo extends Transform { - async _flush() { - return 'world'; - } - - _transform(chunk, encoding, callback) { - callback(null, chunk); - } - } - - const foo = new Foo(); - foo.end('hello'); - foo.on('data', common.mustCall((chunk) => { - assert.strictEqual(chunk.toString(), expected.shift()); - }, 2)); -} - -{ - const expected = ['hello', 'world']; - class Foo extends Transform { - async _flush(callback) { - callback(null, 'world'); - } - - _transform(chunk, encoding, callback) { - callback(null, chunk); - } - } - - const foo = new Foo(); - foo.end('hello'); - foo.on('data', common.mustCall((chunk) => { - assert.strictEqual(chunk.toString(), expected.shift()); - }, 2)); -} - -{ - class Foo extends Transform { - async _flush(callback) { - throw new Error('boom'); - } - - _transform(chunk, encoding, callback) { - callback(null, chunk); - } - } - - const foo = new Foo(); - foo.end('hello'); - foo.on('data', common.mustCall()); - foo.on('error', common.expectsError({ - message: 'boom' - })); - foo.on('close', common.mustCall()); -} - -{ - class Foo extends Transform { - async _transform(chunk) { - return chunk.toString().toUpperCase(); - } - } - - const foo = new Foo(); - foo.end('hello'); - foo.on('data', common.mustCall((chunk) => { - assert.strictEqual(chunk.toString(), 'HELLO'); - })); -} - -{ - class Foo extends Transform { - async _transform(chunk, _, callback) { - callback(null, chunk.toString().toUpperCase()); - } - } - - const foo = new Foo(); - foo.end('hello'); - foo.on('data', common.mustCall((chunk) => { - assert.strictEqual(chunk.toString(), 'HELLO'); - })); -} - -{ - class Foo extends Transform { - async _transform() { - throw new Error('boom'); - } - } - - const foo = new Foo(); - foo.end('hello'); - foo.on('error', common.expectsError({ - message: 'boom' - })); - foo.on('close', common.mustCall()); -} diff --git a/test/parallel/test-stream-duplex-from.js b/test/parallel/test-stream-duplex-from.js index 07ef2f9ccf7c3c..afe4626ebbb9ae 100644 --- a/test/parallel/test-stream-duplex-from.js +++ b/test/parallel/test-stream-duplex-from.js @@ -155,10 +155,10 @@ const { Blob } = require('buffer'); // Ensure that Duplex.from works for blobs { const blob = new Blob(['blob']); - const expecteByteLength = blob.size; + const expectedByteLength = blob.size; const duplex = Duplex.from(blob); duplex.on('data', common.mustCall((arrayBuffer) => { - assert.strictEqual(arrayBuffer.byteLength, expecteByteLength); + assert.strictEqual(arrayBuffer.byteLength, expectedByteLength); })); } diff --git a/test/parallel/test-stream-iterator-helpers-test262-tests.mjs b/test/parallel/test-stream-iterator-helpers-test262-tests.mjs index d2e3a8bae02dbf..8a153fc2fc283e 100644 --- a/test/parallel/test-stream-iterator-helpers-test262-tests.mjs +++ b/test/parallel/test-stream-iterator-helpers-test262-tests.mjs @@ -1,4 +1,4 @@ -import '../common/index.mjs'; +import { mustCall } from '../common/index.mjs'; import { Readable } from 'stream'; import assert from 'assert'; @@ -68,7 +68,7 @@ import assert from 'assert'; ); assert.strictEqual(descriptor.enumerable, false); assert.strictEqual(descriptor.configurable, true); - assert.strictEqual(descriptor.writable, false); + assert.strictEqual(descriptor.writable, true); } { // drop/length @@ -79,7 +79,7 @@ import assert from 'assert'; ); assert.strictEqual(descriptor.enumerable, false); assert.strictEqual(descriptor.configurable, true); - assert.strictEqual(descriptor.writable, false); + assert.strictEqual(descriptor.writable, true); // drop/limit-equals-total const iterator = Readable.from([1, 2]).drop(2); const result = await iterator[Symbol.asyncIterator]().next(); @@ -111,5 +111,58 @@ import assert from 'assert'; // drop/proto const proto = Object.getPrototypeOf(Readable.prototype.drop); assert.strictEqual(proto, Function.prototype); +} +{ + // every/abrupt-iterator-close + const stream = Readable.from([1, 2, 3]); + const e = new Error(); + await assert.rejects(stream.every(mustCall(() => { + throw e; + }, 1)), e); +} +{ + // every/callable-fn + await assert.rejects(Readable.from([1, 2]).every({}), TypeError); +} +{ + // every/callable + Readable.prototype.every.call(Readable.from([]), () => {}); + // eslint-disable-next-line array-callback-return + Readable.from([]).every(() => {}); + assert.throws(() => { + const r = Readable.from([]); + new r.every(() => {}); + }, TypeError); +} +{ + // every/false + const iterator = Readable.from([1, 2, 3]); + const result = await iterator.every((v) => v === 1); + assert.strictEqual(result, false); +} +{ + // every/every + const iterator = Readable.from([1, 2, 3]); + const result = await iterator.every((v) => true); + assert.strictEqual(result, true); +} + +{ + // every/is-function + assert.strictEqual(typeof Readable.prototype.every, 'function'); +} +{ + // every/length + assert.strictEqual(Readable.prototype.every.length, 1); + // every/name + assert.strictEqual(Readable.prototype.every.name, 'every'); + // every/propdesc + const descriptor = Object.getOwnPropertyDescriptor( + Readable.prototype, + 'every' + ); + assert.strictEqual(descriptor.enumerable, false); + assert.strictEqual(descriptor.configurable, true); + assert.strictEqual(descriptor.writable, true); } diff --git a/test/parallel/test-stream-pipeline-listeners.js b/test/parallel/test-stream-pipeline-listeners.js index c2a72fc01c8b8d..81e287b77c7589 100644 --- a/test/parallel/test-stream-pipeline-listeners.js +++ b/test/parallel/test-stream-pipeline-listeners.js @@ -8,7 +8,7 @@ process.on('uncaughtException', common.mustCall((err) => { assert.strictEqual(err.message, 'no way'); }, 2)); -// Ensure that listeners is removed if last stream is readble +// Ensure that listeners is removed if last stream is readable // And other stream's listeners unchanged const a = new PassThrough(); a.end('foobar'); diff --git a/test/parallel/test-stream-pipeline.js b/test/parallel/test-stream-pipeline.js index 1948eeef8af257..eecf836b5bb77f 100644 --- a/test/parallel/test-stream-pipeline.js +++ b/test/parallel/test-stream-pipeline.js @@ -1511,3 +1511,18 @@ const tsp = require('timers/promises'); assert.strictEqual(s.destroyed, true); })); } + +{ + const s = new PassThrough({ objectMode: true }); + pipeline(async function*() { + await Promise.resolve(); + yield 'hello'; + yield 'world'; + yield 'world'; + }, s, async function(source) { + return null; + }, common.mustCall((err, val) => { + assert.strictEqual(err, undefined); + assert.strictEqual(val, null); + })); +} diff --git a/test/parallel/test-stream-readable-async-iterators.js b/test/parallel/test-stream-readable-async-iterators.js index 87184662139ae5..e8b69612014ca5 100644 --- a/test/parallel/test-stream-readable-async-iterators.js +++ b/test/parallel/test-stream-readable-async-iterators.js @@ -789,6 +789,20 @@ async function tests() { } ); } + + // Check for dangling listeners + (async function() { + const readable = createReadable(); + const opts = { destroyOnReturn: false }; + while (readable.readable) { + // eslint-disable-next-line no-unused-vars + for await (const chunk of readable.iterator(opts)) { + break; + } + } + + assert.deepStrictEqual(readable.eventNames(), []); + })().then(common.mustCall()); } { diff --git a/test/parallel/test-stream-writable-change-default-encoding.js b/test/parallel/test-stream-writable-change-default-encoding.js index 3fb9796251c16d..94a892567c1b21 100644 --- a/test/parallel/test-stream-writable-change-default-encoding.js +++ b/test/parallel/test-stream-writable-change-default-encoding.js @@ -68,7 +68,7 @@ assert.throws(() => { message: 'Unknown encoding: {}' }); -(function checkVairableCaseEncoding() { +(function checkVariableCaseEncoding() { const m = new MyWritable(function(isBuffer, type, enc) { assert.strictEqual(enc, 'ascii'); }, { decodeStrings: false }); diff --git a/test/parallel/test-string-decoder.js b/test/parallel/test-string-decoder.js index be876f46e5af02..02f0a3a718bdec 100644 --- a/test/parallel/test-string-decoder.js +++ b/test/parallel/test-string-decoder.js @@ -210,6 +210,13 @@ if (common.enoughTestMem) { ); } +assert.throws( + () => new StringDecoder('utf8').__proto__.write(Buffer.from('abc')), // eslint-disable-line no-proto + { + code: 'ERR_INVALID_THIS', + } +); + // Test verifies that StringDecoder will correctly decode the given input // buffer with the given encoding to the expected output. It will attempt all // possible ways to write() the input buffer, see writeSequences(). The diff --git a/test/parallel/test-tcp-wrap-listen.js b/test/parallel/test-tcp-wrap-listen.js index 0cac545e81d785..a19aed15071613 100644 --- a/test/parallel/test-tcp-wrap-listen.js +++ b/test/parallel/test-tcp-wrap-listen.js @@ -18,9 +18,9 @@ const r = (common.hasIPv6 ? server.bind6('::', 0) : server.bind('0.0.0.0', 0)); assert.strictEqual(r, 0); -let port = {}; + +const port = {}; server.getsockname(port); -port = port.port; server.listen(128); diff --git a/test/parallel/test-tls-passphrase.js b/test/parallel/test-tls-passphrase.js index ce77dd18a6e620..8d802400f6ee3b 100644 --- a/test/parallel/test-tls-passphrase.js +++ b/test/parallel/test-tls-passphrase.js @@ -223,8 +223,7 @@ server.listen(0, common.mustCall(function() { }, onSecureConnect()); })).unref(); -const errMessagePassword = common.hasOpenSSL3 ? - /Error: error:1400006B:UI routines::processing error/ : /bad decrypt/; +const errMessageDecrypt = /bad decrypt/; // Missing passphrase assert.throws(function() { @@ -234,7 +233,7 @@ assert.throws(function() { cert: cert, rejectUnauthorized: false }); -}, errMessagePassword); +}, errMessageDecrypt); assert.throws(function() { tls.connect({ @@ -243,7 +242,7 @@ assert.throws(function() { cert: cert, rejectUnauthorized: false }); -}, errMessagePassword); +}, errMessageDecrypt); assert.throws(function() { tls.connect({ @@ -252,9 +251,7 @@ assert.throws(function() { cert: cert, rejectUnauthorized: false }); -}, errMessagePassword); - -const errMessageDecrypt = /bad decrypt/; +}, errMessageDecrypt); // Invalid passphrase assert.throws(function() { diff --git a/test/parallel/test-tls-securepair-leak.js b/test/parallel/test-tls-securepair-leak.js index 4cd927d64ac088..98bdcde76ec034 100644 --- a/test/parallel/test-tls-securepair-leak.js +++ b/test/parallel/test-tls-securepair-leak.js @@ -20,9 +20,9 @@ setImmediate(() => { global.gc(); const after = process.memoryUsage().external; - // It's not an exact science but a SecurePair grows .external by about 45 kB. + // It's not an exact science but a SecurePair grows .external by about 45 KiB. // Unless AdjustAmountOfExternalAllocatedMemory() is called on destruction, - // 10,000 instances make it grow by well over 400 MB. Allow for some slop + // 10,000 instances make it grow by well over 400 MiB. Allow for some slop // because objects like buffers also affect the external limit. assert(after - before < 25 << 20); }); diff --git a/test/parallel/test-trace-events-all.js b/test/parallel/test-trace-events-all.js index b9f9b70a61b81b..8e836561ff7091 100644 --- a/test/parallel/test-trace-events-all.js +++ b/test/parallel/test-trace-events-all.js @@ -27,7 +27,7 @@ proc.once('exit', common.mustCall(() => { return false; if (trace.cat !== 'v8') return false; - if (trace.name !== 'V8.GCScavenger') + if (!trace.name.startsWith('V8.')) return false; return true; })); diff --git a/test/parallel/test-trace-events-v8.js b/test/parallel/test-trace-events-v8.js index 5d06124d6879b5..e5fc8b6c726600 100644 --- a/test/parallel/test-trace-events-v8.js +++ b/test/parallel/test-trace-events-v8.js @@ -29,7 +29,7 @@ proc.once('exit', common.mustCall(() => { return false; if (trace.cat !== 'v8') return false; - if (trace.name !== 'V8.GCScavenger') + if (!trace.name.startsWith('V8.')) return false; return true; })); diff --git a/test/parallel/test-url-null-char.js b/test/parallel/test-url-null-char.js new file mode 100644 index 00000000000000..468080844d534b --- /dev/null +++ b/test/parallel/test-url-null-char.js @@ -0,0 +1,8 @@ +'use strict'; +require('../common'); +const assert = require('assert'); + +assert.throws( + () => { new URL('a\0b'); }, + { input: 'a\0b' } +); diff --git a/test/parallel/test-url-parse-format.js b/test/parallel/test-url-parse-format.js index e1cf80a2778abd..99a6ace23a2fb3 100644 --- a/test/parallel/test-url-parse-format.js +++ b/test/parallel/test-url-parse-format.js @@ -977,6 +977,21 @@ const parseTests = { path: '/everybody', href: '//fhqwhgads@example.com/everybody#to-the-limit' }, + + '\bhttp://example.com/\b': { + protocol: 'http:', + slashes: true, + auth: null, + host: 'example.com', + port: null, + hostname: 'example.com', + hash: null, + search: null, + query: null, + pathname: '/', + path: '/', + href: 'http://example.com/' + } }; for (const u in parseTests) { diff --git a/test/parallel/test-url-parse-invalid-input.js b/test/parallel/test-url-parse-invalid-input.js index 45d6ff94307381..794a756524925b 100644 --- a/test/parallel/test-url-parse-invalid-input.js +++ b/test/parallel/test-url-parse-invalid-input.js @@ -37,6 +37,10 @@ assert.throws(() => { url.parse('http://%E0%A4%A@fail'); }, return e.code === undefined; }); +assert.throws(() => { url.parse('http://[127.0.0.1\x00c8763]:8000/'); }, + { code: 'ERR_INVALID_URL', input: 'http://[127.0.0.1\x00c8763]:8000/' } +); + if (common.hasIntl) { // An array of Unicode code points whose Unicode NFKD contains a "bad // character". diff --git a/test/parallel/test-url-relative.js b/test/parallel/test-url-relative.js index 1eb2bdc58fc3a6..9a398a0e508f33 100644 --- a/test/parallel/test-url-relative.js +++ b/test/parallel/test-url-relative.js @@ -53,6 +53,7 @@ const relativeTests = [ ['/foo/bar/baz', '/../etc/passwd', '/etc/passwd'], ['http://localhost', 'file:///Users/foo', 'file:///Users/foo'], ['http://localhost', 'file://foo/Users', 'file://foo/Users'], + ['https://registry.npmjs.org', '@foo/bar', 'https://registry.npmjs.org/@foo/bar'], ]; relativeTests.forEach(function(relativeTest) { const a = url.resolve(relativeTest[0], relativeTest[1]); diff --git a/test/parallel/test-v8-serdes.js b/test/parallel/test-v8-serdes.js index 4dffedd3c32b4b..3c079e661c7886 100644 --- a/test/parallel/test-v8-serdes.js +++ b/test/parallel/test-v8-serdes.js @@ -155,18 +155,41 @@ const hostObject = new (internalBinding('js_stream').JSStream)(); } { + // Test that an old serialized value can still be deserialized. const buf = Buffer.from('ff0d6f2203666f6f5e007b01', 'hex'); const des = new v8.DefaultDeserializer(buf); des.readHeader(); + assert.strictEqual(des.getWireFormatVersion(), 0x0d); + + const value = des.readValue(); + assert.strictEqual(value, value.foo); +} + +{ + const message = `New serialization format. + + This test is expected to fail when V8 changes its serialization format. + When that happens, the "desStr" variable must be updated to the new value + and the change should be mentioned in the release notes, as it is semver-major. + + Consider opening an issue as a heads up at https://github.com/nodejs/node/issues/new + `; + + const desStr = 'ff0f6f2203666f6f5e007b01'; + + const desBuf = Buffer.from(desStr, 'hex'); + const des = new v8.DefaultDeserializer(desBuf); + des.readHeader(); + const value = des.readValue(); const ser = new v8.DefaultSerializer(); ser.writeHeader(); + ser.writeValue(value); - ser.writeValue(des.readValue()); - - assert.deepStrictEqual(buf, ser.releaseBuffer()); - assert.strictEqual(des.getWireFormatVersion(), 0x0d); + const serBuf = ser.releaseBuffer(); + const serStr = serBuf.toString('hex'); + assert.deepStrictEqual(serStr, desStr, message); } { diff --git a/test/parallel/test-v8-stats.js b/test/parallel/test-v8-stats.js index 2093343859f2af..7503a08c5a67fa 100644 --- a/test/parallel/test-v8-stats.js +++ b/test/parallel/test-v8-stats.js @@ -6,15 +6,18 @@ const v8 = require('v8'); const s = v8.getHeapStatistics(); const keys = [ 'does_zap_garbage', + 'external_memory', 'heap_size_limit', 'malloced_memory', 'number_of_detached_contexts', 'number_of_native_contexts', 'peak_malloced_memory', 'total_available_size', + 'total_global_handles_size', 'total_heap_size', 'total_heap_size_executable', 'total_physical_size', + 'used_global_handles_size', 'used_heap_size']; assert.deepStrictEqual(Object.keys(s).sort(), keys); keys.forEach(function(key) { diff --git a/test/parallel/test-validators.js b/test/parallel/test-validators.js index 6b0d49c6997a65..0bba9d13b20bf0 100644 --- a/test/parallel/test-validators.js +++ b/test/parallel/test-validators.js @@ -10,6 +10,8 @@ const { validateNumber, validateObject, validateString, + validateInt32, + validateUint32, } = require('internal/validators'); const { MAX_SAFE_INTEGER, MIN_SAFE_INTEGER } = Number; const outOfRangeError = { @@ -41,6 +43,34 @@ const invalidArgValueError = { // validateInteger() works with unsafe integers. validateInteger(MAX_SAFE_INTEGER + 1, 'foo', 0, MAX_SAFE_INTEGER + 1); validateInteger(MIN_SAFE_INTEGER - 1, 'foo', MIN_SAFE_INTEGER - 1); + + // validateInt32() and validateUint32() + [ + Symbol(), 1n, {}, [], false, true, undefined, null, () => {}, '', '1', + ].forEach((val) => assert.throws(() => validateInt32(val, 'name'), { + code: 'ERR_INVALID_ARG_TYPE' + })); + [ + 2147483647 + 1, -2147483648 - 1, NaN, + ].forEach((val) => assert.throws(() => validateInt32(val, 'name'), { + code: 'ERR_OUT_OF_RANGE' + })); + [ + 0, 1, -1, + ].forEach((val) => validateInt32(val, 'name')); + [ + Symbol(), 1n, {}, [], false, true, undefined, null, () => {}, '', '1', + ].forEach((val) => assert.throws(() => validateUint32(val, 'name'), { + code: 'ERR_INVALID_ARG_TYPE' + })); + [ + 4294967296, -1, NaN, + ].forEach((val) => assert.throws(() => validateUint32(val, 'name'), { + code: 'ERR_OUT_OF_RANGE' + })); + [ + 0, 1, + ].forEach((val) => validateUint32(val, 'name')); } { diff --git a/test/parallel/test-vm-module-errors.js b/test/parallel/test-vm-module-errors.js index 888250cef84f6f..bec8258a4145c7 100644 --- a/test/parallel/test-vm-module-errors.js +++ b/test/parallel/test-vm-module-errors.js @@ -139,20 +139,25 @@ async function checkLinking() { code: 'ERR_VM_MODULE_DIFFERENT_CONTEXT' }); + const error = new Error(); await assert.rejects(async () => { - const erroredModule = new SourceTextModule('import "foo";'); + globalThis.error = error; + const erroredModule = new SourceTextModule('throw error;'); + await erroredModule.link(common.mustNotCall()); try { - await erroredModule.link(common.mustCall(() => ({}))); + await erroredModule.evaluate(); } catch { // ignored - } finally { - assert.strictEqual(erroredModule.status, 'errored'); } + delete globalThis.error; + + assert.strictEqual(erroredModule.status, 'errored'); const rootModule = new SourceTextModule('import "errored";'); await rootModule.link(common.mustCall(() => erroredModule)); }, { - code: 'ERR_VM_MODULE_LINKING_ERRORED' + code: 'ERR_VM_MODULE_LINK_FAILURE', + cause: error, }); } diff --git a/test/parallel/test-wasm-web-api.js b/test/parallel/test-wasm-web-api.js new file mode 100644 index 00000000000000..9576e13d669582 --- /dev/null +++ b/test/parallel/test-wasm-web-api.js @@ -0,0 +1,226 @@ +'use strict'; + +const common = require('../common'); +const fixtures = require('../common/fixtures'); + +const assert = require('assert'); +const events = require('events'); +const fs = require('fs/promises'); +const { createServer } = require('http'); + +assert.strictEqual(typeof WebAssembly.compileStreaming, 'function'); +assert.strictEqual(typeof WebAssembly.instantiateStreaming, 'function'); + +const simpleWasmBytes = fixtures.readSync('simple.wasm'); + +// Sets up an HTTP server with the given response handler and calls fetch() to +// obtain a Response from the newly created server. +async function testRequest(handler) { + const server = createServer((_, res) => handler(res)).unref().listen(0); + await events.once(server, 'listening'); + const { port } = server.address(); + return fetch(`http://127.0.0.1:${port}/`); +} + +// Runs the given function both with the promise itself and as a continuation +// of the promise. We use this to test that the API accepts not just a Response +// but also a Promise that resolves to a Response. +function withPromiseAndResolved(makePromise, consume) { + return Promise.all([ + consume(makePromise()), + makePromise().then(consume), + ]); +} + +// The makeResponsePromise function must return a Promise that resolves to a +// Response. The checkResult function receives the Promise returned by +// WebAssembly.compileStreaming and must return a Promise itself. +function testCompileStreaming(makeResponsePromise, checkResult) { + return withPromiseAndResolved( + common.mustCall(makeResponsePromise, 2), + common.mustCall((response) => { + return checkResult(WebAssembly.compileStreaming(response)); + }, 2) + ); +} + +function testCompileStreamingSuccess(makeResponsePromise) { + return testCompileStreaming(makeResponsePromise, async (modPromise) => { + const mod = await modPromise; + assert.strictEqual(mod.constructor, WebAssembly.Module); + }); +} + +function testCompileStreamingRejection(makeResponsePromise, rejection) { + return testCompileStreaming(makeResponsePromise, (modPromise) => { + assert.strictEqual(modPromise.constructor, Promise); + return assert.rejects(modPromise, rejection); + }); +} + +function testCompileStreamingSuccessUsingFetch(responseCallback) { + return testCompileStreamingSuccess(() => testRequest(responseCallback)); +} + +function testCompileStreamingRejectionUsingFetch(responseCallback, rejection) { + return testCompileStreamingRejection(() => testRequest(responseCallback), + rejection); +} + +(async () => { + // A non-Response should cause a TypeError. + for (const invalid of [undefined, null, 0, true, 'foo', {}, [], Symbol()]) { + await withPromiseAndResolved(() => Promise.resolve(invalid), (arg) => { + return assert.rejects(() => WebAssembly.compileStreaming(arg), { + name: 'TypeError', + code: 'ERR_INVALID_ARG_TYPE', + message: /^The "source" argument .*$/ + }); + }); + } + + // When given a Promise, any rejection should be propagated as-is. + { + const err = new RangeError('foo'); + await assert.rejects(() => { + return WebAssembly.compileStreaming(Promise.reject(err)); + }, (actualError) => actualError === err); + } + + // A valid WebAssembly file with the correct MIME type. + await testCompileStreamingSuccessUsingFetch((res) => { + res.setHeader('Content-Type', 'application/wasm'); + res.end(simpleWasmBytes); + }); + + // The same valid WebAssembly file with the same MIME type, but using a + // Response whose body is a Buffer instead of calling fetch(). + await testCompileStreamingSuccess(() => { + return Promise.resolve(new Response(simpleWasmBytes, { + status: 200, + headers: { 'Content-Type': 'application/wasm' } + })); + }); + + // The same valid WebAssembly file with the same MIME type, but using a + // Response whose body is a ReadableStream instead of calling fetch(). + await testCompileStreamingSuccess(async () => { + const handle = await fs.open(fixtures.path('simple.wasm')); + const stream = handle.readableWebStream(); + return Promise.resolve(new Response(stream, { + status: 200, + headers: { 'Content-Type': 'application/wasm' } + })); + }); + + // A larger valid WebAssembly file with the correct MIME type that causes the + // client to pass it to the compiler in many separate chunks. For this, we use + // the same WebAssembly file as in the previous test but insert useless custom + // sections into the WebAssembly module to increase the file size without + // changing the relevant contents. + await testCompileStreamingSuccessUsingFetch((res) => { + res.setHeader('Content-Type', 'application/wasm'); + + // Send the WebAssembly magic and version first. + res.write(simpleWasmBytes.slice(0, 8), common.mustCall()); + + // Construct a 4KiB custom section. + const customSection = Buffer.concat([ + Buffer.from([ + 0, // Custom section. + 134, 32, // (134 & 0x7f) + 0x80 * 32 = 6 + 4096 bytes in this section. + 5, // The length of the following section name. + ]), + Buffer.from('?'.repeat(5)), // The section name + Buffer.from('\0'.repeat(4096)), // The actual section data + ]); + + // Now repeatedly send useless custom sections. These have no use for the + // WebAssembly compiler but they are syntactically valid. The client has to + // keep reading the stream until the very end to obtain the relevant + // sections within the module. This adds up to a few hundred kibibytes. + (function next(i) { + if (i < 100) { + while (res.write(customSection)); + res.once('drain', () => next(i + 1)); + } else { + // End the response body with the actual module contents. + res.end(simpleWasmBytes.slice(8)); + } + })(0); + }); + + // A valid WebAssembly file with an empty parameter in the (otherwise valid) + // MIME type. + await testCompileStreamingRejectionUsingFetch((res) => { + res.setHeader('Content-Type', 'application/wasm;'); + res.end(simpleWasmBytes); + }, { + name: 'TypeError', + code: 'ERR_WEBASSEMBLY_RESPONSE', + message: 'WebAssembly response has unsupported MIME type ' + + "'application/wasm;'" + }); + + // A valid WebAssembly file with an invalid MIME type. + await testCompileStreamingRejectionUsingFetch((res) => { + res.setHeader('Content-Type', 'application/octet-stream'); + res.end(simpleWasmBytes); + }, { + name: 'TypeError', + code: 'ERR_WEBASSEMBLY_RESPONSE', + message: 'WebAssembly response has unsupported MIME type ' + + "'application/octet-stream'" + }); + + // HTTP status code indiciating an error. + await testCompileStreamingRejectionUsingFetch((res) => { + res.statusCode = 418; + res.setHeader('Content-Type', 'application/wasm'); + res.end(simpleWasmBytes); + }, { + name: 'TypeError', + code: 'ERR_WEBASSEMBLY_RESPONSE', + message: /^WebAssembly response has status code 418$/ + }); + + // HTTP status code indiciating an error, but using a Response whose body is + // a Buffer instead of calling fetch(). + await testCompileStreamingSuccess(() => { + return Promise.resolve(new Response(simpleWasmBytes, { + status: 200, + headers: { 'Content-Type': 'application/wasm' } + })); + }); + + // Extra bytes after the WebAssembly file. + await testCompileStreamingRejectionUsingFetch((res) => { + res.setHeader('Content-Type', 'application/wasm'); + res.end(Buffer.concat([simpleWasmBytes, Buffer.from('foo')])); + }, { + name: 'CompileError', + message: /^WebAssembly\.compileStreaming\(\): .*$/ + }); + + // Missing bytes at the end of the WebAssembly file. + await testCompileStreamingRejectionUsingFetch((res) => { + res.setHeader('Content-Type', 'application/wasm'); + res.end(simpleWasmBytes.subarray(0, simpleWasmBytes.length - 3)); + }, { + name: 'CompileError', + message: /^WebAssembly\.compileStreaming\(\): .*$/ + }); + + // Incomplete HTTP response body. The TypeError might come as a surprise, but + // it originates from within fetch(). + await testCompileStreamingRejectionUsingFetch((res) => { + res.setHeader('Content-Length', simpleWasmBytes.length); + res.setHeader('Content-Type', 'application/wasm'); + res.write(simpleWasmBytes.slice(0, 5), common.mustSucceed(() => { + res.destroy(); + })); + }, { + name: 'TypeError', + message: /terminated/ + }); +})().then(common.mustCall()); diff --git a/test/parallel/test-webcrypto-constructors.js b/test/parallel/test-webcrypto-constructors.js new file mode 100644 index 00000000000000..ddc4e8cb0796b1 --- /dev/null +++ b/test/parallel/test-webcrypto-constructors.js @@ -0,0 +1,159 @@ +// Flags: --experimental-global-webcrypto +'use strict'; + +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); + +// Test CryptoKey constructor +{ + assert.throws(() => new CryptoKey(), { + name: 'TypeError', message: 'Illegal constructor', code: 'ERR_ILLEGAL_CONSTRUCTOR' + }); +} + +// Test SubtleCrypto constructor +{ + assert.throws(() => new SubtleCrypto(), { + name: 'TypeError', message: 'Illegal constructor', code: 'ERR_ILLEGAL_CONSTRUCTOR' + }); +} + +// Test Crypto constructor +{ + assert.throws(() => new Crypto(), { + name: 'TypeError', message: 'Illegal constructor', code: 'ERR_ILLEGAL_CONSTRUCTOR' + }); +} + +const notCrypto = Reflect.construct(function() {}, [], Crypto); +const notSubtle = Reflect.construct(function() {}, [], SubtleCrypto); + +// Test Crypto.prototype.subtle +{ + assert.throws(() => notCrypto.subtle, { + name: 'TypeError', code: 'ERR_INVALID_THIS', + }); +} + +// Test Crypto.prototype.randomUUID +{ + assert.throws(() => notCrypto.randomUUID(), { + name: 'TypeError', code: 'ERR_INVALID_THIS', + }); +} + +// Test Crypto.prototype.getRandomValues +{ + assert.throws(() => notCrypto.getRandomValues(new Uint8Array(12)), { + name: 'TypeError', code: 'ERR_INVALID_THIS', + }); +} + +// Test SubtleCrypto.prototype.encrypt +{ + assert.rejects(() => notSubtle.encrypt(), { + name: 'TypeError', code: 'ERR_INVALID_THIS', + }).then(common.mustCall()); +} + +// Test SubtleCrypto.prototype.decrypt +{ + assert.rejects(() => notSubtle.decrypt(), { + name: 'TypeError', code: 'ERR_INVALID_THIS', + }).then(common.mustCall()); +} + +// Test SubtleCrypto.prototype.sign +{ + assert.rejects(() => notSubtle.sign(), { + name: 'TypeError', code: 'ERR_INVALID_THIS', + }).then(common.mustCall()); +} + +// Test SubtleCrypto.prototype.verify +{ + assert.rejects(() => notSubtle.verify(), { + name: 'TypeError', code: 'ERR_INVALID_THIS', + }).then(common.mustCall()); +} + +// Test SubtleCrypto.prototype.digest +{ + assert.rejects(() => notSubtle.digest(), { + name: 'TypeError', code: 'ERR_INVALID_THIS', + }).then(common.mustCall()); +} + +// Test SubtleCrypto.prototype.generateKey +{ + assert.rejects(() => notSubtle.generateKey(), { + name: 'TypeError', code: 'ERR_INVALID_THIS', + }).then(common.mustCall()); +} + +// Test SubtleCrypto.prototype.deriveKey +{ + assert.rejects(() => notSubtle.deriveKey(), { + name: 'TypeError', code: 'ERR_INVALID_THIS', + }).then(common.mustCall()); +} + +// Test SubtleCrypto.prototype.deriveBits +{ + assert.rejects(() => notSubtle.deriveBits(), { + name: 'TypeError', code: 'ERR_INVALID_THIS', + }).then(common.mustCall()); +} + +// Test SubtleCrypto.prototype.importKey +{ + assert.rejects(() => notSubtle.importKey(), { + name: 'TypeError', code: 'ERR_INVALID_THIS', + }).then(common.mustCall()); +} + +// Test SubtleCrypto.prototype.exportKey +{ + assert.rejects(() => notSubtle.exportKey(), { + name: 'TypeError', code: 'ERR_INVALID_THIS', + }).then(common.mustCall()); +} + +// Test SubtleCrypto.prototype.wrapKey +{ + assert.rejects(() => notSubtle.wrapKey(), { + name: 'TypeError', code: 'ERR_INVALID_THIS', + }).then(common.mustCall()); +} + +// Test SubtleCrypto.prototype.unwrapKey +{ + assert.rejects(() => notSubtle.unwrapKey(), { + name: 'TypeError', code: 'ERR_INVALID_THIS', + }).then(common.mustCall()); +} + +{ + globalThis.crypto.subtle.importKey( + 'raw', + globalThis.crypto.getRandomValues(new Uint8Array(4)), + 'PBKDF2', + false, + ['deriveKey'], + ).then((key) => { + globalThis.crypto.subtle.importKey = common.mustNotCall(); + return globalThis.crypto.subtle.deriveKey({ + name: 'PBKDF2', + hash: 'SHA-512', + salt: globalThis.crypto.getRandomValues(new Uint8Array()), + iterations: 5, + }, key, { + name: 'AES-GCM', + length: 256 + }, true, ['encrypt', 'decrypt']); + }).then(common.mustCall()); +} diff --git a/test/parallel/test-webcrypto-derivebits-ecdh.js b/test/parallel/test-webcrypto-derivebits-ecdh.js index 64cbae7cec6a03..166da81e3e4e6d 100644 --- a/test/parallel/test-webcrypto-derivebits-ecdh.js +++ b/test/parallel/test-webcrypto-derivebits-ecdh.js @@ -6,7 +6,8 @@ if (!common.hasCrypto) common.skip('missing crypto'); const assert = require('assert'); -const { subtle, getRandomValues } = require('crypto').webcrypto; +const { webcrypto } = require('crypto'); +const { subtle } = webcrypto; const kTests = [ { @@ -250,7 +251,7 @@ async function prepareKeys() { { // Public is a secret key - const keyData = getRandomValues(new Uint8Array(32)); + const keyData = webcrypto.getRandomValues(new Uint8Array(32)); const key = await subtle.importKey( 'raw', keyData, diff --git a/test/parallel/test-webcrypto-derivekey-ecdh.js b/test/parallel/test-webcrypto-derivekey-ecdh.js index bdd9bd7588a763..42c8d250f42b06 100644 --- a/test/parallel/test-webcrypto-derivekey-ecdh.js +++ b/test/parallel/test-webcrypto-derivekey-ecdh.js @@ -6,7 +6,8 @@ if (!common.hasCrypto) common.skip('missing crypto'); const assert = require('assert'); -const { subtle, getRandomValues } = require('crypto').webcrypto; +const { webcrypto } = require('crypto'); +const { subtle } = webcrypto; const kTests = [ { @@ -226,7 +227,7 @@ async function prepareKeys() { { // Public is a secret key - const keyData = getRandomValues(new Uint8Array(32)); + const keyData = webcrypto.getRandomValues(new Uint8Array(32)); const key = await subtle.importKey( 'raw', keyData, diff --git a/test/parallel/test-webcrypto-derivekey.js b/test/parallel/test-webcrypto-derivekey.js index ee48a61f4ac8f5..0c11d38af30dc6 100644 --- a/test/parallel/test-webcrypto-derivekey.js +++ b/test/parallel/test-webcrypto-derivekey.js @@ -7,7 +7,7 @@ if (!common.hasCrypto) common.skip('missing crypto'); const assert = require('assert'); -const { subtle } = require('crypto').webcrypto; +const { webcrypto: { subtle }, KeyObject } = require('crypto'); const { internalBinding } = require('internal/test/binding'); @@ -152,3 +152,35 @@ if (typeof internalBinding('crypto').ScryptJob === 'function') { tests.then(common.mustCall()); } + +// Test default key lengths +{ + const vectors = [ + ['PBKDF2', 'deriveKey', 528], + ['HKDF', 'deriveKey', 528], + [{ name: 'HMAC', hash: 'SHA-1' }, 'sign', 160], + [{ name: 'HMAC', hash: 'SHA-256' }, 'sign', 256], + [{ name: 'HMAC', hash: 'SHA-384' }, 'sign', 384], + [{ name: 'HMAC', hash: 'SHA-512' }, 'sign', 512], + ]; + + (async () => { + const keyPair = await subtle.generateKey({ name: 'ECDH', namedCurve: 'P-521' }, false, ['deriveKey']); + for (const [derivedKeyAlgorithm, usage, expected] of vectors) { + const derived = await subtle.deriveKey( + { name: 'ECDH', public: keyPair.publicKey }, + keyPair.privateKey, + derivedKeyAlgorithm, + false, + [usage]); + + if (derived.algorithm.name === 'HMAC') { + assert.strictEqual(derived.algorithm.length, expected); + } else { + // KDFs cannot be exportable and do not indicate their length + const secretKey = KeyObject.from(derived); + assert.strictEqual(secretKey.symmetricKeySize, expected / 8); + } + } + })().then(common.mustCall()); +} diff --git a/test/parallel/test-webcrypto-encrypt-decrypt-aes.js b/test/parallel/test-webcrypto-encrypt-decrypt-aes.js index 7adb18918d2205..885cded906b079 100644 --- a/test/parallel/test-webcrypto-encrypt-decrypt-aes.js +++ b/test/parallel/test-webcrypto-encrypt-decrypt-aes.js @@ -6,7 +6,8 @@ if (!common.hasCrypto) common.skip('missing crypto'); const assert = require('assert'); -const { getRandomValues, subtle } = require('crypto').webcrypto; +const { webcrypto } = require('crypto'); +const { subtle } = webcrypto; async function testEncrypt({ keyBuffer, algorithm, plaintext, result }) { // Using a copy of plaintext to prevent tampering of the original @@ -213,8 +214,8 @@ async function testDecrypt({ keyBuffer, algorithm, result }) { ['encrypt', 'decrypt'], ); - const iv = getRandomValues(new Uint8Array(12)); - const aad = getRandomValues(new Uint8Array(32)); + const iv = webcrypto.getRandomValues(new Uint8Array(12)); + const aad = webcrypto.getRandomValues(new Uint8Array(32)); const encrypted = await subtle.encrypt( { @@ -224,7 +225,7 @@ async function testDecrypt({ keyBuffer, algorithm, result }) { tagLength: 128 }, secretKey, - getRandomValues(new Uint8Array(32)) + webcrypto.getRandomValues(new Uint8Array(32)) ); await subtle.decrypt( diff --git a/test/parallel/test-webcrypto-encrypt-decrypt.js b/test/parallel/test-webcrypto-encrypt-decrypt.js index 50fa99a999cd92..cc997e7d2e6d26 100644 --- a/test/parallel/test-webcrypto-encrypt-decrypt.js +++ b/test/parallel/test-webcrypto-encrypt-decrypt.js @@ -6,14 +6,15 @@ if (!common.hasCrypto) common.skip('missing crypto'); const assert = require('assert'); -const { subtle, getRandomValues } = require('crypto').webcrypto; +const { webcrypto } = require('crypto'); +const { subtle } = webcrypto; // This is only a partial test. The WebCrypto Web Platform Tests // will provide much greater coverage. // Test Encrypt/Decrypt RSA-OAEP { - const buf = getRandomValues(new Uint8Array(50)); + const buf = webcrypto.getRandomValues(new Uint8Array(50)); async function test() { const ec = new TextEncoder(); @@ -44,8 +45,8 @@ const { subtle, getRandomValues } = require('crypto').webcrypto; // Test Encrypt/Decrypt AES-CTR { - const buf = getRandomValues(new Uint8Array(50)); - const counter = getRandomValues(new Uint8Array(16)); + const buf = webcrypto.getRandomValues(new Uint8Array(50)); + const counter = webcrypto.getRandomValues(new Uint8Array(16)); async function test() { const key = await subtle.generateKey({ @@ -71,8 +72,8 @@ const { subtle, getRandomValues } = require('crypto').webcrypto; // Test Encrypt/Decrypt AES-CBC { - const buf = getRandomValues(new Uint8Array(50)); - const iv = getRandomValues(new Uint8Array(16)); + const buf = webcrypto.getRandomValues(new Uint8Array(50)); + const iv = webcrypto.getRandomValues(new Uint8Array(16)); async function test() { const key = await subtle.generateKey({ @@ -98,8 +99,8 @@ const { subtle, getRandomValues } = require('crypto').webcrypto; // Test Encrypt/Decrypt AES-GCM { - const buf = getRandomValues(new Uint8Array(50)); - const iv = getRandomValues(new Uint8Array(12)); + const buf = webcrypto.getRandomValues(new Uint8Array(50)); + const iv = webcrypto.getRandomValues(new Uint8Array(12)); async function test() { const key = await subtle.generateKey({ diff --git a/test/parallel/test-webcrypto-export-import.js b/test/parallel/test-webcrypto-export-import.js index d7db433b364011..b4fd26b0cda6ae 100644 --- a/test/parallel/test-webcrypto-export-import.js +++ b/test/parallel/test-webcrypto-export-import.js @@ -6,11 +6,12 @@ if (!common.hasCrypto) common.skip('missing crypto'); const assert = require('assert'); -const { subtle, getRandomValues } = require('crypto').webcrypto; +const { webcrypto } = require('crypto'); +const { subtle } = webcrypto; { async function test() { - const keyData = getRandomValues(new Uint8Array(32)); + const keyData = webcrypto.getRandomValues(new Uint8Array(32)); await Promise.all([1, null, undefined, {}, []].map((format) => assert.rejects( subtle.importKey(format, keyData, {}, false, ['wrapKey']), { @@ -82,7 +83,7 @@ const { subtle, getRandomValues } = require('crypto').webcrypto; // Import/Export HMAC Secret Key { async function test() { - const keyData = getRandomValues(new Uint8Array(32)); + const keyData = webcrypto.getRandomValues(new Uint8Array(32)); const key = await subtle.importKey( 'raw', keyData, { @@ -112,7 +113,7 @@ const { subtle, getRandomValues } = require('crypto').webcrypto; // Import/Export AES Secret Key { async function test() { - const keyData = getRandomValues(new Uint8Array(32)); + const keyData = webcrypto.getRandomValues(new Uint8Array(32)); const key = await subtle.importKey( 'raw', keyData, { diff --git a/test/parallel/test-webcrypto-getRandomValues.js b/test/parallel/test-webcrypto-getRandomValues.js new file mode 100644 index 00000000000000..049cdcc847feb1 --- /dev/null +++ b/test/parallel/test-webcrypto-getRandomValues.js @@ -0,0 +1,11 @@ +'use strict'; + +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { getRandomValues } = require('crypto').webcrypto; + +assert.throws(() => getRandomValues(new Uint8Array()), { code: 'ERR_INVALID_THIS' }); diff --git a/test/parallel/test-webcrypto-random.js b/test/parallel/test-webcrypto-random.js index e17cc834b6c2bf..c3fc6aaab2eb1e 100644 --- a/test/parallel/test-webcrypto-random.js +++ b/test/parallel/test-webcrypto-random.js @@ -7,7 +7,7 @@ if (!common.hasCrypto) const { Buffer } = require('buffer'); const assert = require('assert'); -const { getRandomValues } = require('crypto').webcrypto; +const { webcrypto } = require('crypto'); [ undefined, null, '', 1, {}, [], @@ -16,14 +16,14 @@ const { getRandomValues } = require('crypto').webcrypto; new DataView(new ArrayBuffer(1)), ].forEach((i) => { assert.throws( - () => getRandomValues(i), + () => webcrypto.getRandomValues(i), { name: 'TypeMismatchError', code: 17 }, ); }); { const buf = new Uint8Array(0); - getRandomValues(buf); + webcrypto.getRandomValues(buf); } const intTypedConstructors = [ @@ -41,7 +41,7 @@ const intTypedConstructors = [ for (const ctor of intTypedConstructors) { const buf = new ctor(10); const before = Buffer.from(buf.buffer).toString('hex'); - getRandomValues(buf); + webcrypto.getRandomValues(buf); const after = Buffer.from(buf.buffer).toString('hex'); assert.notStrictEqual(before, after); } @@ -49,7 +49,7 @@ for (const ctor of intTypedConstructors) { { const buf = new Uint16Array(10); const before = Buffer.from(buf).toString('hex'); - getRandomValues(buf); + webcrypto.getRandomValues(buf); const after = Buffer.from(buf).toString('hex'); assert.notStrictEqual(before, after); } @@ -63,7 +63,7 @@ for (const ctor of intTypedConstructors) { } if (kData !== undefined) { - assert.throws(() => getRandomValues(kData), { + assert.throws(() => webcrypto.getRandomValues(kData), { code: 22 }); } diff --git a/test/parallel/test-webcrypto-util.js b/test/parallel/test-webcrypto-util.js new file mode 100644 index 00000000000000..4bb14a7f91494f --- /dev/null +++ b/test/parallel/test-webcrypto-util.js @@ -0,0 +1,25 @@ +// Flags: --expose-internals +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); + +const { + normalizeAlgorithm, +} = require('internal/crypto/util'); + +{ + // Check that normalizeAlgorithm does not add an undefined hash property. + assert.strictEqual('hash' in normalizeAlgorithm({ name: 'ECDH' }), false); + assert.strictEqual('hash' in normalizeAlgorithm('ECDH'), false); +} + +{ + // Check that normalizeAlgorithm does not mutate object inputs. + const algorithm = { name: 'ECDH', hash: 'SHA-256' }; + assert.strictEqual(normalizeAlgorithm(algorithm) !== algorithm, true); + assert.deepStrictEqual(algorithm, { name: 'ECDH', hash: 'SHA-256' }); +} diff --git a/test/parallel/test-whatwg-readablestream.js b/test/parallel/test-whatwg-readablestream.js index 13261fe6b7ca84..cef3eca6ed2733 100644 --- a/test/parallel/test-whatwg-readablestream.js +++ b/test/parallel/test-whatwg-readablestream.js @@ -80,6 +80,36 @@ const { assert(r.locked); } +{ + // Throw error and return rejected promise in `cancel()` method + // would execute same cleanup code + const r1 = new ReadableStream({ + cancel: () => { + return Promise.reject('Cancel Error'); + }, + }); + r1.cancel().finally(common.mustCall(() => { + const controllerState = r1[kState].controller[kState]; + + assert.strictEqual(controllerState.pullAlgorithm, undefined); + assert.strictEqual(controllerState.cancelAlgorithm, undefined); + assert.strictEqual(controllerState.sizeAlgorithm, undefined); + })).catch(() => {}); + + const r2 = new ReadableStream({ + cancel() { + throw new Error('Cancel Error'); + } + }); + r2.cancel().finally(common.mustCall(() => { + const controllerState = r2[kState].controller[kState]; + + assert.strictEqual(controllerState.pullAlgorithm, undefined); + assert.strictEqual(controllerState.cancelAlgorithm, undefined); + assert.strictEqual(controllerState.sizeAlgorithm, undefined); + })).catch(() => {}); +} + { const source = { start: common.mustCall((controller) => { diff --git a/test/parallel/test-whatwg-url-custom-searchparams-inspect.js b/test/parallel/test-whatwg-url-custom-searchparams-inspect.js index c03890938d9cfe..7729a36eb8fd9f 100644 --- a/test/parallel/test-whatwg-url-custom-searchparams-inspect.js +++ b/test/parallel/test-whatwg-url-custom-searchparams-inspect.js @@ -9,12 +9,20 @@ const util = require('util'); const sp = new URLSearchParams('?a=a&b=b&b=c'); assert.strictEqual(util.inspect(sp), "URLSearchParams { 'a' => 'a', 'b' => 'b', 'b' => 'c' }"); +assert.strictEqual(util.inspect(sp, { depth: -1 }), '[Object]'); +assert.strictEqual( + util.inspect(sp, { breakLength: 1 }), + "URLSearchParams {\n 'a' => 'a',\n 'b' => 'b',\n 'b' => 'c' }" +); assert.strictEqual(util.inspect(sp.keys()), "URLSearchParams Iterator { 'a', 'b', 'b' }"); assert.strictEqual(util.inspect(sp.values()), "URLSearchParams Iterator { 'a', 'b', 'c' }"); assert.strictEqual(util.inspect(sp.keys(), { breakLength: 1 }), "URLSearchParams Iterator {\n 'a',\n 'b',\n 'b' }"); +assert.throws(() => sp[util.inspect.custom].call(), { + code: 'ERR_INVALID_THIS', +}); const iterator = sp.entries(); assert.strictEqual(util.inspect(iterator), @@ -27,3 +35,5 @@ iterator.next(); iterator.next(); assert.strictEqual(util.inspect(iterator), 'URLSearchParams Iterator { }'); +const emptySp = new URLSearchParams(); +assert.strictEqual(util.inspect(emptySp), 'URLSearchParams {}'); diff --git a/test/parallel/test-whatwg-webstreams-adapters-to-streamduplex.js b/test/parallel/test-whatwg-webstreams-adapters-to-streamduplex.js index 4567c53c1e89a0..15ac9f832714e9 100644 --- a/test/parallel/test-whatwg-webstreams-adapters-to-streamduplex.js +++ b/test/parallel/test-whatwg-webstreams-adapters-to-streamduplex.js @@ -147,3 +147,20 @@ const { finished(duplex, common.mustCall()); pipeline(readable, duplex, writable, common.mustCall()); } + +{ + const transform = new TransformStream(); + const duplex = newStreamDuplexFromReadableWritablePair(transform); + duplex.setEncoding('utf-8'); + duplex.on('data', common.mustCall((data) => { + assert.strictEqual(data, 'hello'); + }, 5)); + + duplex.write(Buffer.from('hello')); + duplex.write(Buffer.from('hello')); + duplex.write(Buffer.from('hello')); + duplex.write(Buffer.from('hello')); + duplex.write(Buffer.from('hello')); + + duplex.end(); +} diff --git a/test/parallel/test-whatwg-webstreams-adapters-to-streamwritable.js b/test/parallel/test-whatwg-webstreams-adapters-to-streamwritable.js index 8da73b4fe9f0c3..495eef73f79272 100644 --- a/test/parallel/test-whatwg-webstreams-adapters-to-streamwritable.js +++ b/test/parallel/test-whatwg-webstreams-adapters-to-streamwritable.js @@ -200,7 +200,7 @@ class TestSource { { const writableStream = new WritableStream({ - write: common.mustCall(2), + write: common.mustCall(5), close: common.mustCall(), }); const writable = newStreamWritableFromWritableStream(writableStream); @@ -208,6 +208,9 @@ class TestSource { finished(writable, common.mustCall()); writable.write('hello'); + writable.write('hello'); + writable.write('hello'); + writable.write('world'); writable.write('world'); writable.end(); } diff --git a/test/parallel/test-worker-debug.js b/test/parallel/test-worker-debug.js index 758d7acfc3a99f..da8e26b39ca453 100644 --- a/test/parallel/test-worker-debug.js +++ b/test/parallel/test-worker-debug.js @@ -6,7 +6,6 @@ common.skipIfInspectorDisabled(); const assert = require('assert'); const EventEmitter = require('events'); const { Session } = require('inspector'); -const { pathToFileURL } = require('url'); const { Worker, isMainThread, parentPort, workerData } = require('worker_threads'); @@ -106,7 +105,6 @@ class WorkerSession extends EventEmitter { this.post(command); const notification = await notificationPromise; const callFrame = notification.params.callFrames[0]; - assert.strictEqual(callFrame.url, pathToFileURL(script).toString()); assert.strictEqual(callFrame.location.lineNumber, line); } @@ -153,7 +151,7 @@ async function testBasicWorkerDebug(session, post) { await workerSession.waitForBreakAfterCommand( 'Runtime.runIfWaitingForDebugger', __filename, 1); await workerSession.waitForBreakAfterCommand( - 'Debugger.resume', __filename, 26); // V8 line number is zero-based + 'Debugger.resume', __filename, 25); // V8 line number is zero-based const msg = await consolePromise; assert.strictEqual(msg, workerMessage); workerSession.post('Debugger.resume'); diff --git a/test/parallel/test-worker-hasref.js b/test/parallel/test-worker-hasref.js new file mode 100644 index 00000000000000..51593b14725f5b --- /dev/null +++ b/test/parallel/test-worker-hasref.js @@ -0,0 +1,33 @@ +'use strict'; +const common = require('../common'); + +const { Worker } = require('worker_threads'); +const { createHook } = require('async_hooks'); +const { strictEqual } = require('assert'); + +let handle; + +createHook({ + init(asyncId, type, triggerAsyncId, resource) { + if (type === 'WORKER') { + handle = resource; + this.disable(); + } + } +}).enable(); + +const w = new Worker('', { eval: true }); + +strictEqual(handle.hasRef(), true); +w.unref(); +strictEqual(handle.hasRef(), false); +w.ref(); +strictEqual(handle.hasRef(), true); + +w.on('exit', common.mustCall((exitCode) => { + strictEqual(exitCode, 0); + strictEqual(handle.hasRef(), true); + setTimeout(common.mustCall(() => { + strictEqual(handle.hasRef(), undefined); + }), 0); +})); diff --git a/test/parallel/test-worker-message-port-close.js b/test/parallel/test-worker-message-port-close.js index 6abc01d1b7b568..6562824d6a9ed3 100644 --- a/test/parallel/test-worker-message-port-close.js +++ b/test/parallel/test-worker-message-port-close.js @@ -39,3 +39,11 @@ function dummy() {} message: 'Cannot send data on closed MessagePort' }); } + +// Refs: https://github.com/nodejs/node/issues/42296 +{ + const ch = new MessageChannel(); + ch.port1.onmessage = common.mustNotCall(); + ch.port2.close(); + ch.port2.postMessage('fhqwhgads'); +} diff --git a/test/parallel/test-worker-nearheaplimit-deadlock.js b/test/parallel/test-worker-nearheaplimit-deadlock.js index 1f38bc074da048..b22c86b211611f 100644 --- a/test/parallel/test-worker-nearheaplimit-deadlock.js +++ b/test/parallel/test-worker-nearheaplimit-deadlock.js @@ -1,3 +1,7 @@ +// Flags: --no-node-snapshot +// With node snapshot the OOM can occur during the deserialization of the +// context, so disable it since we want the OOM to occur during the creation of +// the message port. 'use strict'; const common = require('../common'); const assert = require('assert'); @@ -10,7 +14,7 @@ if (!process.env.HAS_STARTED_WORKER) { resourceLimits: { maxYoungGenerationSizeMb: 0, maxOldGenerationSizeMb: 0 - } + }, }; const worker = new Worker(__filename, opts); diff --git a/test/parallel/test-worker-process-env.js b/test/parallel/test-worker-process-env.js index 9680d685140f60..f43c2affa1f48b 100644 --- a/test/parallel/test-worker-process-env.js +++ b/test/parallel/test-worker-process-env.js @@ -39,8 +39,20 @@ if (!workerData && process.argv[2] !== 'child') { process.env.SET_IN_WORKER = 'set'; assert.strictEqual(process.env.SET_IN_WORKER, 'set'); - Object.defineProperty(process.env, 'DEFINED_IN_WORKER', { value: 42 }); - assert.strictEqual(process.env.DEFINED_IN_WORKER, '42'); + assert.throws( + () => { + Object.defineProperty(process.env, 'DEFINED_IN_WORKER', { + value: 42 + }); + }, + { + code: 'ERR_INVALID_OBJECT_DEFINE_PROPERTY', + name: 'TypeError', + message: '\'process.env\' only accepts a configurable, ' + + 'writable, and enumerable data descriptor' + } + ); + const { stderr } = child_process.spawnSync(process.execPath, [__filename, 'child']); diff --git a/test/parallel/test-worker-resource-limits.js b/test/parallel/test-worker-resource-limits.js index ffda452f6c6335..f79c31b2a18793 100644 --- a/test/parallel/test-worker-resource-limits.js +++ b/test/parallel/test-worker-resource-limits.js @@ -35,10 +35,10 @@ if (!process.env.HAS_STARTED_WORKER) { assert.deepStrictEqual(resourceLimits, testResourceLimits); const array = []; while (true) { - // Leave 10% wiggle room here, and 20% on debug builds. - const wiggleRoom = common.buildType === 'Release' ? 1.1 : 1.2; const usedMB = v8.getHeapStatistics().used_heap_size / 1024 / 1024; - assert(usedMB < resourceLimits.maxOldGenerationSizeMb * wiggleRoom); + const maxReservedSize = resourceLimits.maxOldGenerationSizeMb + + resourceLimits.maxYoungGenerationSizeMb; + assert(usedMB < maxReservedSize); let seenSpaces = 0; for (const { space_name, space_size } of v8.getHeapSpaceStatistics()) { diff --git a/test/parallel/test-x509-escaping.js b/test/parallel/test-x509-escaping.js index c3b88646899a2d..c31a6d21351dc9 100644 --- a/test/parallel/test-x509-escaping.js +++ b/test/parallel/test-x509-escaping.js @@ -88,22 +88,22 @@ const { hasOpenSSL3 } = common; // OpenSSL should not know it. 'Registered ID:1.3.9999.12.34', hasOpenSSL3 ? - 'othername: XmppAddr::abc123' : + 'othername:XmppAddr:abc123' : 'othername:', hasOpenSSL3 ? - 'othername:" XmppAddr::abc123\\u002c DNS:good.example.com"' : + 'othername:"XmppAddr:abc123\\u002c DNS:good.example.com"' : 'othername:', hasOpenSSL3 ? - 'othername:" XmppAddr::good.example.com\\u0000abc123"' : + 'othername:"XmppAddr:good.example.com\\u0000abc123"' : 'othername:', // This is unsupported because the OID is not recognized. 'othername:', - hasOpenSSL3 ? 'othername: SRVName::abc123' : 'othername:', + hasOpenSSL3 ? 'othername:SRVName:abc123' : 'othername:', // This is unsupported because it is an SRVName with a UTF8String value, // which is not allowed for SRVName. 'othername:', hasOpenSSL3 ? - 'othername:" SRVName::abc\\u0000def"' : + 'othername:"SRVName:abc\\u0000def"' : 'othername:', ]; @@ -173,14 +173,14 @@ const { hasOpenSSL3 } = common; }, }, hasOpenSSL3 ? { - text: 'OCSP - othername: XmppAddr::good.example.com\n' + + text: 'OCSP - othername:XmppAddr:good.example.com\n' + 'OCSP - othername:\n' + - 'OCSP - othername: SRVName::abc123', + 'OCSP - othername:SRVName:abc123', legacy: { 'OCSP - othername': [ - ' XmppAddr::good.example.com', + 'XmppAddr:good.example.com', '', - ' SRVName::abc123', + 'SRVName:abc123', ], }, } : { @@ -196,10 +196,10 @@ const { hasOpenSSL3 } = common; }, }, hasOpenSSL3 ? { - text: 'OCSP - othername:" XmppAddr::good.example.com\\u0000abc123"', + text: 'OCSP - othername:"XmppAddr:good.example.com\\u0000abc123"', legacy: { 'OCSP - othername': [ - ' XmppAddr::good.example.com\0abc123', + 'XmppAddr:good.example.com\0abc123', ], }, } : { @@ -241,6 +241,15 @@ const { hasOpenSSL3 } = common; assert.deepStrictEqual(peerCert.infoAccess, Object.assign(Object.create(null), expected.legacy)); + + // toLegacyObject() should also produce the same properties. However, + // the X509Certificate is not aware of the chain, so we need to add + // the circular issuerCertificate reference manually for the assertion + // to be true. + const obj = cert.toLegacyObject(); + assert.strictEqual(obj.issuerCertificate, undefined); + obj.issuerCertificate = obj; + assert.deepStrictEqual(peerCert, obj); }, }, common.mustCall()); })); @@ -350,6 +359,15 @@ const { hasOpenSSL3 } = common; // self-signed. Otherwise, OpenSSL would have already rejected the // certificate while connecting to the TLS server. assert.deepStrictEqual(peerCert.issuer, expectedObject); + + // toLegacyObject() should also produce the same properties. However, + // the X509Certificate is not aware of the chain, so we need to add + // the circular issuerCertificate reference manually for the assertion + // to be true. + const obj = cert.toLegacyObject(); + assert.strictEqual(obj.issuerCertificate, undefined); + obj.issuerCertificate = obj; + assert.deepStrictEqual(peerCert, obj); }, }, common.mustCall()); })); diff --git a/test/pummel/test-crypto-dh-hash-modp18.js b/test/pummel/test-crypto-dh-hash-modp18.js index e2a7f43c45074d..ceb4cbd885c464 100644 --- a/test/pummel/test-crypto-dh-hash-modp18.js +++ b/test/pummel/test-crypto-dh-hash-modp18.js @@ -26,8 +26,8 @@ if (!common.hasCrypto) { common.skip('node compiled without OpenSSL.'); } -if (process.config.variables.arm_version === '7') { - common.skip('Too slow for armv7 bots'); +if (common.isPi) { + common.skip('Too slow for Raspberry Pi devices'); } const assert = require('assert'); diff --git a/test/pummel/test-crypto-dh-hash.js b/test/pummel/test-crypto-dh-hash.js index a5932eb764792f..3ad974ff329efd 100644 --- a/test/pummel/test-crypto-dh-hash.js +++ b/test/pummel/test-crypto-dh-hash.js @@ -26,8 +26,8 @@ if (!common.hasCrypto) { common.skip('node compiled without OpenSSL.'); } -if (process.config.variables.arm_version === '7') { - common.skip('Too slow for armv7 bots'); +if (common.isPi) { + common.skip('Too slow for Raspberry Pi devices'); } const assert = require('assert'); diff --git a/test/pummel/test-crypto-dh-keys.js b/test/pummel/test-crypto-dh-keys.js index 99be0e517fd640..2caa4e244a9859 100644 --- a/test/pummel/test-crypto-dh-keys.js +++ b/test/pummel/test-crypto-dh-keys.js @@ -26,8 +26,8 @@ if (!common.hasCrypto) { common.skip('node compiled without OpenSSL.'); } -if (process.config.variables.arm_version === '7') { - common.skip('Too slow for armv7 bots'); +if (common.isPi) { + common.skip('Too slow for Raspberry Pi devices'); } const assert = require('assert'); diff --git a/test/pummel/test-dh-regr.js b/test/pummel/test-dh-regr.js index 87136a0afeb23d..76686d22a3c7e0 100644 --- a/test/pummel/test-dh-regr.js +++ b/test/pummel/test-dh-regr.js @@ -26,8 +26,8 @@ if (!common.hasCrypto) { common.skip('missing crypto'); } -if (process.config.variables.arm_version === '7') { - common.skip('Too slow for armv7 bots'); +if (common.isPi) { + common.skip('Too slow for Raspberry Pi devices'); } const assert = require('assert'); diff --git a/test/pummel/test-fs-watch-system-limit.js b/test/pummel/test-fs-watch-system-limit.js index 20995c514fa569..3486f5372b8d5d 100644 --- a/test/pummel/test-fs-watch-system-limit.js +++ b/test/pummel/test-fs-watch-system-limit.js @@ -9,8 +9,8 @@ if (!common.isLinux) { common.skip('The fs watch limit is OS-dependent'); } -if (process.config.variables.arm_version === '7') { - common.skip('Too slow for armv7 bots'); +if (common.isPi) { + common.skip('Too slow for Raspberry Pi devices'); } try { diff --git a/test/pummel/test-hash-seed.js b/test/pummel/test-hash-seed.js index 42b626b079e873..274183d8ce977e 100644 --- a/test/pummel/test-hash-seed.js +++ b/test/pummel/test-hash-seed.js @@ -3,8 +3,8 @@ // Check that spawn child doesn't create duplicated entries const common = require('../common'); -if (process.config.variables.arm_version === '7') { - common.skip('Too slow for armv7 bots'); +if (common.isPi) { + common.skip('Too slow for Raspberry Pi devices'); } const kRepetitions = 2; diff --git a/test/pummel/test-heapsnapshot-near-heap-limit-bounded.js b/test/pummel/test-heapsnapshot-near-heap-limit-bounded.js index faf5c4755aac75..dd7497d975ab92 100644 --- a/test/pummel/test-heapsnapshot-near-heap-limit-bounded.js +++ b/test/pummel/test-heapsnapshot-near-heap-limit-bounded.js @@ -2,8 +2,8 @@ const common = require('../common'); -if (process.config.variables.arm_version === '7') { - common.skip('Too slow for armv7 bots'); +if (common.isPi) { + common.skip('Too slow for Raspberry Pi devices'); } const tmpdir = require('../common/tmpdir'); diff --git a/test/pummel/test-heapsnapshot-near-heap-limit.js b/test/pummel/test-heapsnapshot-near-heap-limit.js index 420ba04205945b..1af4e61e08c028 100644 --- a/test/pummel/test-heapsnapshot-near-heap-limit.js +++ b/test/pummel/test-heapsnapshot-near-heap-limit.js @@ -2,8 +2,8 @@ const common = require('../common'); -if (process.config.variables.arm_version === '7') { - common.skip('Too slow for armv7 bots'); +if (common.isPi) { + common.skip('Too slow for Raspberry Pi devices'); } const tmpdir = require('../common/tmpdir'); diff --git a/test/pummel/test-net-bytes-per-incoming-chunk-overhead.js b/test/pummel/test-net-bytes-per-incoming-chunk-overhead.js index f556e9881f728c..b3613110ab5c54 100644 --- a/test/pummel/test-net-bytes-per-incoming-chunk-overhead.js +++ b/test/pummel/test-net-bytes-per-incoming-chunk-overhead.js @@ -7,8 +7,8 @@ if (process.config.variables.asan) { common.skip('ASAN messes with memory measurements'); } -if (process.config.variables.arm_version === '7') { - common.skip('Too slow for armv7 bots'); +if (common.isPi) { + common.skip('Too slow for Raspberry Pi devices'); } const assert = require('assert'); diff --git a/test/pummel/test-next-tick-infinite-calls.js b/test/pummel/test-next-tick-infinite-calls.js index bf837f5ebc92c0..d1131066977ea2 100644 --- a/test/pummel/test-next-tick-infinite-calls.js +++ b/test/pummel/test-next-tick-infinite-calls.js @@ -22,8 +22,8 @@ 'use strict'; const common = require('../common'); -if (process.config.variables.arm_version === '7') { - common.skip('Too slow for armv7 bots'); +if (common.isPi) { + common.skip('Too slow for Raspberry Pi devices'); } let complete = 0; diff --git a/test/pummel/test-policy-integrity-dep.js b/test/pummel/test-policy-integrity-dep.js index ec58462335cd56..02f24d02a23544 100644 --- a/test/pummel/test-policy-integrity-dep.js +++ b/test/pummel/test-policy-integrity-dep.js @@ -6,8 +6,8 @@ if (!common.hasCrypto) { common.skip('missing crypto'); } -if (process.config.variables.arm_version === '7') { - common.skip('Too slow for armv7 bots'); +if (common.isPi) { + common.skip('Too slow for Raspberry Pi devices'); } common.requireNoPackageJSONAbove(); diff --git a/test/pummel/test-policy-integrity-parent-commonjs.js b/test/pummel/test-policy-integrity-parent-commonjs.js index 39febab73ee419..425abe38ebd9fb 100644 --- a/test/pummel/test-policy-integrity-parent-commonjs.js +++ b/test/pummel/test-policy-integrity-parent-commonjs.js @@ -6,8 +6,8 @@ if (!common.hasCrypto) { common.skip('missing crypto'); } -if (process.config.variables.arm_version === '7') { - common.skip('Too slow for armv7 bots'); +if (common.isPi) { + common.skip('Too slow for Raspberry Pi devices'); } common.requireNoPackageJSONAbove(); diff --git a/test/pummel/test-policy-integrity-parent-module.js b/test/pummel/test-policy-integrity-parent-module.js index e60a606ea32bbe..dda800dc3652fc 100644 --- a/test/pummel/test-policy-integrity-parent-module.js +++ b/test/pummel/test-policy-integrity-parent-module.js @@ -6,8 +6,8 @@ if (!common.hasCrypto) { common.skip('missing crypto'); } -if (process.config.variables.arm_version === '7') { - common.skip('Too slow for armv7 bots'); +if (common.isPi) { + common.skip('Too slow for Raspberry Pi devices'); } common.requireNoPackageJSONAbove(); diff --git a/test/pummel/test-policy-integrity-parent-no-package-json.js b/test/pummel/test-policy-integrity-parent-no-package-json.js index f2208744447827..a722263e1e484a 100644 --- a/test/pummel/test-policy-integrity-parent-no-package-json.js +++ b/test/pummel/test-policy-integrity-parent-no-package-json.js @@ -6,8 +6,8 @@ if (!common.hasCrypto) { common.skip('missing crypto'); } -if (process.config.variables.arm_version === '7') { - common.skip('Too slow for armv7 bots'); +if (common.isPi) { + common.skip('Too slow for Raspberry Pi devices'); } common.requireNoPackageJSONAbove(); diff --git a/test/pummel/test-policy-integrity-worker-commonjs.js b/test/pummel/test-policy-integrity-worker-commonjs.js index 22a7d762466046..b5d49222a2f01c 100644 --- a/test/pummel/test-policy-integrity-worker-commonjs.js +++ b/test/pummel/test-policy-integrity-worker-commonjs.js @@ -6,8 +6,8 @@ if (!common.hasCrypto) { common.skip('missing crypto'); } -if (process.config.variables.arm_version === '7') { - common.skip('Too slow for armv7 bots'); +if (common.isPi) { + common.skip('Too slow for Raspberry Pi devices'); } common.requireNoPackageJSONAbove(); diff --git a/test/pummel/test-policy-integrity-worker-module.js b/test/pummel/test-policy-integrity-worker-module.js index e5d4e4cd45d300..a8a4fb2c29589f 100644 --- a/test/pummel/test-policy-integrity-worker-module.js +++ b/test/pummel/test-policy-integrity-worker-module.js @@ -6,8 +6,8 @@ if (!common.hasCrypto) { common.skip('missing crypto'); } -if (process.config.variables.arm_version === '7') { - common.skip('Too slow for armv7 bots'); +if (common.isPi) { + common.skip('Too slow for Raspberry Pi devices'); } common.requireNoPackageJSONAbove(); diff --git a/test/pummel/test-policy-integrity-worker-no-package-json.js b/test/pummel/test-policy-integrity-worker-no-package-json.js index 808687f40ea219..e5b3e3ccfabbf6 100644 --- a/test/pummel/test-policy-integrity-worker-no-package-json.js +++ b/test/pummel/test-policy-integrity-worker-no-package-json.js @@ -6,8 +6,8 @@ if (!common.hasCrypto) { common.skip('missing crypto'); } -if (process.config.variables.arm_version === '7') { - common.skip('Too slow for armv7 bots'); +if (common.isPi) { + common.skip('Too slow for Raspberry Pi devices'); } common.requireNoPackageJSONAbove(); diff --git a/test/pummel/test-webcrypto-derivebits-pbkdf2.js b/test/pummel/test-webcrypto-derivebits-pbkdf2.js index 512662025c66f7..e7ed4f6bd646dd 100644 --- a/test/pummel/test-webcrypto-derivebits-pbkdf2.js +++ b/test/pummel/test-webcrypto-derivebits-pbkdf2.js @@ -6,8 +6,8 @@ if (!common.hasCrypto) { common.skip('missing crypto'); } -if (process.config.variables.arm_version === '7') { - common.skip('Too slow for armv7 bots'); +if (common.isPi) { + common.skip('Too slow for Raspberry Pi devices'); } const assert = require('assert'); diff --git a/test/sequential/test-child-process-pass-fd.js b/test/sequential/test-child-process-pass-fd.js index ad4e5d693ee2e5..86092f56da8565 100644 --- a/test/sequential/test-child-process-pass-fd.js +++ b/test/sequential/test-child-process-pass-fd.js @@ -9,8 +9,8 @@ const common = require('../common'); // This test is basically `test-cluster-net-send` but creating lots of workers // so the issue reproduces on OS X consistently. -if (process.config.variables.arm_version === '7') { - common.skip('Too slow for armv7 bots'); +if (common.isPi) { + common.skip('Too slow for Raspberry Pi devices'); } const assert = require('assert'); diff --git a/test/sequential/test-heapdump.js b/test/sequential/test-heapdump.js index e18c6189332574..cb84bca4cd96da 100644 --- a/test/sequential/test-heapdump.js +++ b/test/sequential/test-heapdump.js @@ -25,13 +25,13 @@ process.chdir(tmpdir.path); } { - const readonlyFile = 'ro'; - fs.writeFileSync(readonlyFile, Buffer.alloc(0), { mode: 0o444 }); + const directory = 'directory'; + fs.mkdirSync(directory); assert.throws(() => { - writeHeapSnapshot(readonlyFile); + writeHeapSnapshot(directory); }, (e) => { assert.ok(e, 'writeHeapSnapshot should error'); - assert.strictEqual(e.code, 'EACCES'); + assert.strictEqual(e.code, 'EISDIR'); assert.strictEqual(e.syscall, 'open'); return true; }); diff --git a/test/sequential/test-net-server-address.js b/test/sequential/test-net-server-address.js index 4312ffd5a1fe78..07776470f13a46 100644 --- a/test/sequential/test-net-server-address.js +++ b/test/sequential/test-net-server-address.js @@ -26,7 +26,7 @@ const net = require('net'); // Test on IPv4 Server { - const family = 'IPv4'; + const family = 4; const server = net.createServer(); server.on('error', common.mustNotCall()); @@ -46,7 +46,7 @@ if (!common.hasIPv6) { return; } -const family6 = 'IPv6'; +const family6 = 6; const anycast6 = '::'; // Test on IPv6 Server diff --git a/test/sequential/test-net-server-bind.js b/test/sequential/test-net-server-bind.js index 56216d0ed60619..67e9503f4cc3cc 100644 --- a/test/sequential/test-net-server-bind.js +++ b/test/sequential/test-net-server-bind.js @@ -24,7 +24,7 @@ const net = require('net'); const address = server.address(); assert.strictEqual(address.port, common.PORT); - if (address.family === 'IPv6') + if (address.family === 6) assert.strictEqual(server._connectionKey, `6::::${address.port}`); else assert.strictEqual(server._connectionKey, `4:0.0.0.0:${address.port}`); diff --git a/test/v8-updates/test-trace-gc-flag.js b/test/v8-updates/test-trace-gc-flag.js new file mode 100644 index 00000000000000..d84e359525127c --- /dev/null +++ b/test/v8-updates/test-trace-gc-flag.js @@ -0,0 +1,40 @@ +'use strict'; + +// This test verifies that `--trace-gc` flag is well integrated. +// We'll check here, that the console outputs gc events properly. +require('../common'); + +const assert = require('assert'); +const { spawnSync } = require('child_process'); + +const fixtures = require('../common/fixtures'); + +{ + const childProcess = spawnSync(process.execPath, [ + '--trace-gc', + '--expose-gc', + fixtures.path('gc.js'), + ]); + const output = childProcess.stdout.toString().trim(); + const lines = splitByLine(output); + + const scavengeRegex = /\bScavenge\b/; + const expectedOutput = [ + scavengeRegex, + scavengeRegex, + scavengeRegex, + scavengeRegex, + /\bMark-sweep\b/, + ]; + lines.forEach((line, index) => { + assert.match(line, expectedOutput[index]); + }); +} + +/** + * HELPERS + */ + +function splitByLine(str) { + return str.split(/\n/); +} diff --git a/test/wpt/status/wasm/webapi.json b/test/wpt/status/wasm/webapi.json new file mode 100644 index 00000000000000..e631f5c0a95310 --- /dev/null +++ b/test/wpt/status/wasm/webapi.json @@ -0,0 +1,24 @@ +{ + "historical.any.js": { + "skip": "indexedDB is not defined" + }, + "origin.sub.any.js": { + "skip": "CORS not implemented" + }, + + "abort.any.js": { + "skip": "WPTRunner does not support fetch()" + }, + "contenttype.any.js": { + "skip": "WPTRunner does not support fetch()" + }, + "empty-body.any.js": { + "skip": "Bug in undici, see https://github.com/nodejs/undici/issues/1345" + }, + "idlharness.any.js": { + "skip": "not configured" + }, + "status.any.js": { + "skip": "WPTRunner does not support fetch()" + } +} diff --git a/test/wpt/test-atob.js b/test/wpt/test-atob.js index 8dc9c20303a7dc..b227ae4d20e105 100644 --- a/test/wpt/test-atob.js +++ b/test/wpt/test-atob.js @@ -5,13 +5,4 @@ const { WPTRunner } = require('../common/wpt'); const runner = new WPTRunner('html/webappapis/atob'); -// Needed to access to DOMException. -runner.setFlags(['--expose-internals']); - -// Set a script that will be executed in the worker before running the tests. -runner.setInitScript(` - const { internalBinding } = require('internal/test/binding'); - const { atob, btoa } = require('buffer'); -`); - runner.runJsTests(); diff --git a/test/wpt/test-streams.js b/test/wpt/test-streams.js index 987676d8c49125..43c212247c41e7 100644 --- a/test/wpt/test-streams.js +++ b/test/wpt/test-streams.js @@ -5,112 +5,8 @@ const { WPTRunner } = require('../common/wpt'); const runner = new WPTRunner('streams'); -// Set Node.js flags required for the tests. -runner.setFlags(['--expose-internals']); - // Set a script that will be executed in the worker before running the tests. runner.setInitScript(` - let { - ReadableStream, - ReadableStreamDefaultReader, - ReadableStreamBYOBReader, - ReadableStreamBYOBRequest, - ReadableByteStreamController, - ReadableStreamDefaultController, - TransformStream, - TransformStreamDefaultController, - WritableStream, - WritableStreamDefaultWriter, - WritableStreamDefaultController, - ByteLengthQueuingStrategy, - CountQueuingStrategy, - } = require('stream/web'); - - const { internalBinding } = require('internal/test/binding'); - const { DOMException } = internalBinding('messaging'); - global.DOMException = DOMException; - - Object.defineProperties(global, { - ReadableStream: { - value: ReadableStream, - configurable: true, - writable: true, - enumerable: false, - }, - ReadableStreamDefaultReader: { - value: ReadableStreamDefaultReader, - configurable: true, - writable: true, - enumerable: false, - }, - ReadableStreamBYOBReader: { - value: ReadableStreamBYOBReader, - configurable: true, - writable: true, - enumerable: false, - }, - ReadableStreamBYOBRequest: { - value: ReadableStreamBYOBRequest, - configurable: true, - writable: true, - enumerable: false, - }, - ReadableByteStreamController: { - value: ReadableByteStreamController, - configurable: true, - writable: true, - enumerable: false, - }, - ReadableStreamDefaultController: { - value: ReadableStreamDefaultController, - configurable: true, - writable: true, - enumerable: false, - }, - TransformStream: { - value: TransformStream, - configurable: true, - writable: true, - enumerable: false, - }, - TransformStreamDefaultController: { - value: TransformStreamDefaultController, - configurable: true, - writable: true, - enumerable: false, - }, - WritableStream: { - value: WritableStream, - configurable: true, - writable: true, - enumerable: false, - }, - WritableStreamDefaultWriter: { - value: WritableStreamDefaultWriter, - configurable: true, - writable: true, - enumerable: false, - }, - WritableStreamDefaultController: { - value: WritableStreamDefaultController, - configurable: true, - writable: true, - enumerable: false, - }, - ByteLengthQueuingStrategy: { - value: ByteLengthQueuingStrategy, - configurable: true, - writable: true, - enumerable: false, - }, - CountQueuingStrategy: { - value: CountQueuingStrategy, - configurable: true, - writable: true, - enumerable: false, - }, - }); - // Simulate global postMessage for enqueue-with-detached-buffer.window.js function postMessage(value, origin, transferList) { const mc = new MessageChannel(); diff --git a/test/wpt/test-wasm-webapi.js b/test/wpt/test-wasm-webapi.js new file mode 100644 index 00000000000000..fecc6e89a3fe1a --- /dev/null +++ b/test/wpt/test-wasm-webapi.js @@ -0,0 +1,7 @@ +'use strict'; + +require('../common'); +const { WPTRunner } = require('../common/wpt'); + +const runner = new WPTRunner('wasm/webapi'); +runner.runJsTests(); diff --git a/test/wpt/test-webcrypto.js b/test/wpt/test-webcrypto.js index e8707a464f434c..c1ee6402c5a75c 100644 --- a/test/wpt/test-webcrypto.js +++ b/test/wpt/test-webcrypto.js @@ -9,45 +9,6 @@ const { WPTRunner } = require('../common/wpt'); const runner = new WPTRunner('WebCryptoAPI'); // Set Node.js flags required for the tests. -runner.setFlags(['--expose-internals']); - -// Set a script that will be executed in the worker before running the tests. -runner.setInitScript(` - const { - Crypto, - SubtleCrypto, - crypto, - } = require('internal/crypto/webcrypto'); - const { internalBinding } = require('internal/test/binding'); - const { DOMException } = internalBinding('messaging'); - global.DOMException = DOMException; - - Object.defineProperties(global, { - Crypto: { - value: Crypto, - configurable: true, - writable: true, - enumerable: false, - }, - SubtleCrypto: { - value: SubtleCrypto, - configurable: true, - writable: true, - enumerable: false, - }, - CryptoKey: { - value: crypto.CryptoKey, - configurable: true, - writable: true, - enumerable: false, - }, - crypto: { - value: crypto, - configurable: true, - writable: true, - enumerable: false, - }, - }); -`); +runner.setFlags(['--experimental-global-webcrypto']); runner.runJsTests(); diff --git a/tools/.eslintrc.yaml b/tools/.eslintrc.yaml index de30cf6d123f33..d9a7929836d91f 100644 --- a/tools/.eslintrc.yaml +++ b/tools/.eslintrc.yaml @@ -12,4 +12,3 @@ rules: - error - args: after-used prefer-arrow-callback: error - no-var: error diff --git a/tools/actions/commit-queue.sh b/tools/actions/commit-queue.sh index 16fbc7f2ef15fb..309c1c5406b334 100755 --- a/tools/actions/commit-queue.sh +++ b/tools/actions/commit-queue.sh @@ -40,7 +40,7 @@ for pr in "$@"; do fi # Skip PR if CI is still running - if ncu-ci url "https://github.com/${OWNER}/${REPOSITORY}/pull/${pr}" 2>&1 | grep "^Result *PENDING"; then + if gh pr checks "$pr" | grep -q "\spending\s"; then echo "pr ${pr} skipped, CI still running" continue fi diff --git a/tools/clang-format/package-lock.json b/tools/clang-format/package-lock.json index 61f17967f0e4a5..1a080a750eaf86 100644 --- a/tools/clang-format/package-lock.json +++ b/tools/clang-format/package-lock.json @@ -1,18 +1,196 @@ { "name": "node-core-clang-format", "version": "1.0.0", - "lockfileVersion": 1, + "lockfileVersion": 2, "requires": true, + "packages": { + "": { + "name": "node-core-clang-format", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "clang-format": "^1.7.0" + } + }, + "node_modules/async": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", + "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/clang-format": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/clang-format/-/clang-format-1.7.0.tgz", + "integrity": "sha512-BNuK+rXAK/Fk0rOQ1DW6bpSQUAZz6tpbZHTQn6m4PsgEkE1SNr6AQ/hhFK/b4KJrl4zjcl68molP+rEaKSZRAQ==", + "dependencies": { + "async": "^3.2.3", + "glob": "^7.0.0", + "resolve": "^1.1.6" + }, + "bin": { + "check-clang-format": "bin/check-clang-format.js", + "clang-format": "index.js", + "git-clang-format": "bin/git-clang-format" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/is-core-module": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", + "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/resolve": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "dependencies": { + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + } + }, "dependencies": { "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", + "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==" }, "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "brace-expansion": { "version": "1.1.11", @@ -24,11 +202,11 @@ } }, "clang-format": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/clang-format/-/clang-format-1.2.3.tgz", - "integrity": "sha512-x90Hac4ERacGDcZSvHKK58Ga0STuMD+Doi5g0iG2zf7wlJef5Huvhs/3BvMRFxwRYyYSdl6mpQNrtfMxE8MQzw==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/clang-format/-/clang-format-1.7.0.tgz", + "integrity": "sha512-BNuK+rXAK/Fk0rOQ1DW6bpSQUAZz6tpbZHTQn6m4PsgEkE1SNr6AQ/hhFK/b4KJrl4zjcl68molP+rEaKSZRAQ==", "requires": { - "async": "^1.5.2", + "async": "^3.2.3", "glob": "^7.0.0", "resolve": "^1.1.6" } @@ -43,10 +221,15 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -56,6 +239,14 @@ "path-is-absolute": "^1.0.0" } }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -66,14 +257,22 @@ } }, "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "is-core-module": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", + "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "requires": { + "has": "^1.0.3" + } }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "requires": { "brace-expansion": "^1.1.7" } @@ -97,13 +296,20 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "resolve": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", - "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==", + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", "requires": { - "path-parse": "^1.0.5" + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" } }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/tools/clang-format/package.json b/tools/clang-format/package.json index 8432296ed6f1fc..50454249b52c10 100644 --- a/tools/clang-format/package.json +++ b/tools/clang-format/package.json @@ -4,6 +4,6 @@ "description": "Formatting C++ files for Node.js core", "license": "MIT", "dependencies": { - "clang-format": "1.2.3" + "clang-format": "^1.7.0" } } diff --git a/tools/code_cache/mkcodecache.cc b/tools/code_cache/mkcodecache.cc index 5593e14fae3b55..68690252a147cd 100644 --- a/tools/code_cache/mkcodecache.cc +++ b/tools/code_cache/mkcodecache.cc @@ -68,6 +68,7 @@ int main(int argc, char* argv[]) { } isolate->Dispose(); + v8::V8::Dispose(); v8::V8::DisposePlatform(); return 0; } diff --git a/tools/compress_json.py b/tools/compress_json.py index dfe64063aeaead..fdb3d536cf3e3c 100644 --- a/tools/compress_json.py +++ b/tools/compress_json.py @@ -21,7 +21,7 @@ # To make decompression a little easier, we prepend the compressed data # with the size of the uncompressed data as a 24 bits BE unsigned integer. - assert len(text) < 1 << 24, 'Uncompressed JSON must be < 16 MB.' + assert len(text) < 1 << 24, 'Uncompressed JSON must be < 16 MiB.' data = struct.pack('>I', len(text))[1:4] + data step = 20 diff --git a/tools/cpplint.py b/tools/cpplint.py index b4a4586caaf80c..06fc45abf1a0eb 100755 --- a/tools/cpplint.py +++ b/tools/cpplint.py @@ -41,6 +41,11 @@ same line, but it is far from perfect (in either direction). """ +# cpplint predates fstrings +# pylint: disable=consider-using-f-string + +# pylint: disable=invalid-name + import codecs import copy import getopt @@ -59,7 +64,7 @@ # if empty, use defaults _valid_extensions = set([]) -__VERSION__ = '1.5.5' +__VERSION__ = '1.6.0' try: xrange # Python 2 @@ -1928,6 +1933,7 @@ def __init__(self, lines): self.raw_lines = lines self.num_lines = len(lines) self.lines_without_raw_strings = CleanseRawStrings(lines) + # # pylint: disable=consider-using-enumerate for linenum in range(len(self.lines_without_raw_strings)): self.lines.append(CleanseComments( self.lines_without_raw_strings[linenum])) @@ -5164,10 +5170,12 @@ def CheckIncludeLine(filename, clean_lines, linenum, include_state, error): # # We also make an exception for Lua headers, which follow google # naming convention but not the include convention. - match = Match(r'#include\s*"([^/]+\.h)"', line) - if match and not _THIRD_PARTY_HEADERS_PATTERN.match(match.group(1)): - error(filename, linenum, 'build/include_subdir', 4, - 'Include the directory when naming .h files') + match = Match(r'#include\s*"([^/]+\.(.*))"', line) + if match: + if (IsHeaderExtension(match.group(2)) and + not _THIRD_PARTY_HEADERS_PATTERN.match(match.group(1))): + error(filename, linenum, 'build/include_subdir', 4, + 'Include the directory when naming header files') # we shouldn't include a file more than once. actually, there are a # handful of instances where doing so is okay, but in general it's @@ -6620,7 +6628,7 @@ def ProcessConfigOverrides(filename): continue try: - with open(cfg_file) as file_handle: + with open(cfg_file, encoding='utf-8') as file_handle: for line in file_handle: line, _, _ = line.partition('#') # Remove comments. if not line.strip(): diff --git a/tools/doc/html.mjs b/tools/doc/html.mjs index 8d356836eb5667..1c5ad61182bc79 100644 --- a/tools/doc/html.mjs +++ b/tools/doc/html.mjs @@ -98,8 +98,10 @@ export function toHTML({ input, content, filename, nodeVersion, versions }) { .replace(/__FILENAME__/g, filename) .replace('__SECTION__', content.section) .replace(/__VERSION__/g, nodeVersion) - .replace('__TOC__', content.toc) - .replace('__GTOC__', gtocHTML.replace( + .replace(/__TOC__/g, content.toc) + .replace(/__TOC_PICKER__/g, tocPicker(id, content)) + .replace(/__GTOC_PICKER__/g, gtocPicker(id)) + .replace(/__GTOC__/g, gtocHTML.replace( `class="nav-${id}"`, `class="nav-${id} active"`)) .replace('__EDIT_ON_GITHUB__', editOnGitHub(filename)) .replace('__CONTENT__', processContent(content)); @@ -442,17 +444,18 @@ export function buildToc({ filename, apilinks }) { }); if (toc !== '') { - file.toc = '
Table of contents' + - unified() - .use(markdown) - .use(gfm) - .use(remark2rehype, { allowDangerousHtml: true }) - .use(raw) - .use(htmlStringify) - .processSync(toc).toString() + - '
'; + const inner = unified() + .use(markdown) + .use(gfm) + .use(remark2rehype, { allowDangerousHtml: true }) + .use(raw) + .use(htmlStringify) + .processSync(toc).toString(); + + file.toc = `
Table of contents${inner}
`; + file.tocPicker = `
${inner}
`; } else { - file.toc = ''; + file.toc = file.tocPicker = ''; } }; } @@ -508,9 +511,12 @@ function altDocs(filename, docCreated, versions) { const list = versions.filter(isDocInVersion).map(wrapInListItem).join('\n'); return list ? ` -
  • - View another version -
      ${list}
    +
  • + + + Other versions + +
      ${list}
  • ` : ''; } @@ -518,3 +524,47 @@ function altDocs(filename, docCreated, versions) { function editOnGitHub(filename) { return `
  • Edit on GitHub
  • `; } + +function gtocPicker(id) { + if (id === 'index') { + return ''; + } + + // Highlight the current module and add a link to the index + const gtoc = gtocHTML.replace( + `class="nav-${id}"`, `class="nav-${id} active"` + ).replace('', ` +
  • + Index +
  • + + `); + + return ` +
  • + + + Index + + +
    ${gtoc}
    +
  • + `; +} + +function tocPicker(id, content) { + if (id === 'index') { + return ''; + } + + return ` +
  • + + + Table of contents + + +
    ${content.tocPicker}
    +
  • + `; +} diff --git a/tools/doc/package-lock.json b/tools/doc/package-lock.json index ac1745798cab1f..cb7d7bcfad59be 100644 --- a/tools/doc/package-lock.json +++ b/tools/doc/package-lock.json @@ -11,7 +11,7 @@ "node-doc-generator": "generate.js" }, "devDependencies": { - "highlight.js": "^11.4.0", + "highlight.js": "^11.5.1", "js-yaml": "^4.1.0", "rehype-raw": "^6.1.1", "rehype-stringify": "^9.0.3", @@ -21,7 +21,7 @@ "remark-parse": "^10.0.1", "remark-rehype": "^10.1.0", "to-vfile": "^7.2.3", - "unified": "^10.1.1", + "unified": "^10.1.2", "unist-util-select": "^4.0.1", "unist-util-visit": "^4.1.0" }, @@ -159,9 +159,9 @@ "dev": true }, "node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "dependencies": { "ms": "2.1.2" @@ -416,9 +416,9 @@ } }, "node_modules/highlight.js": { - "version": "11.4.0", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.4.0.tgz", - "integrity": "sha512-nawlpCBCSASs7EdvZOYOYVkJpGmAOKMYZgZtUqSRqodZE0GRVcFKwo1RcpeOemqh9hyttTdd5wDBwHkuSyUfnA==", + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.5.1.tgz", + "integrity": "sha512-LKzHqnxr4CrD2YsNoIf/o5nJ09j4yi/GcH5BnYz9UnVpZdS4ucMgvP61TDty5xJcFGRjnH4DpujkS9bHT3hq0Q==", "dev": true, "engines": { "node": ">=12.0.0" @@ -599,16 +599,18 @@ } }, "node_modules/mdast-util-gfm": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-2.0.0.tgz", - "integrity": "sha512-wMwejlTN3EQADPFuvxe8lmGsay3+f6gSJKdAHR6KBJzpcxvsjJSILB9K6u6G7eQLC7iOTyVIHYGui9uBc9r1Tg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-2.0.1.tgz", + "integrity": "sha512-42yHBbfWIFisaAfV1eixlabbsa6q7vHeSPY+cg+BBjX51M8xhgMacqH9g6TftB/9+YkcI0ooV4ncfrJslzm/RQ==", "dev": true, "dependencies": { + "mdast-util-from-markdown": "^1.0.0", "mdast-util-gfm-autolink-literal": "^1.0.0", "mdast-util-gfm-footnote": "^1.0.0", "mdast-util-gfm-strikethrough": "^1.0.0", "mdast-util-gfm-table": "^1.0.0", - "mdast-util-gfm-task-list-item": "^1.0.0" + "mdast-util-gfm-task-list-item": "^1.0.0", + "mdast-util-to-markdown": "^1.0.0" }, "funding": { "type": "opencollective", @@ -661,12 +663,13 @@ } }, "node_modules/mdast-util-gfm-table": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-1.0.3.tgz", - "integrity": "sha512-B/tgpJjND1qIZM2WZst+NYnb0notPE6m0J+YOe3NOHXyEmvK38ytxaOsgz4BvrRPQQcNbRrTzSHMPnBkj1fCjg==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-1.0.4.tgz", + "integrity": "sha512-aEuoPwZyP4iIMkf2cLWXxx3EQ6Bmh2yKy9MVCg4i6Sd3cX80dcLEfXO/V4ul3pGH9czBK4kp+FAl+ZHmSUt9/w==", "dev": true, "dependencies": { "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^1.0.0", "mdast-util-to-markdown": "^1.3.0" }, "funding": { @@ -867,9 +870,9 @@ } }, "node_modules/micromark-extension-gfm-footnote": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-1.0.3.tgz", - "integrity": "sha512-bn62pC5y39rIo2g1RqZk1NhF7T7cJLuJlbevunQz41U0iPVCdVOFASe5/L1kke+DFKSgfCRhv24+o42cZ1+ADw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-1.0.4.tgz", + "integrity": "sha512-E/fmPmDqLiMUP8mLJ8NbJWJ4bTw6tS+FEQS8CcuDtZpILuOb2kjLqPEeAePF1djXROHXChM/wPJw0iS4kHCcIg==", "dev": true, "dependencies": { "micromark-core-commonmark": "^1.0.0", @@ -878,6 +881,7 @@ "micromark-util-normalize-identifier": "^1.0.0", "micromark-util-sanitize-uri": "^1.0.0", "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", "uvu": "^0.5.0" }, "funding": { @@ -1537,9 +1541,9 @@ } }, "node_modules/trough": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/trough/-/trough-2.0.2.tgz", - "integrity": "sha512-FnHq5sTMxC0sk957wHDzRnemFnNBvt/gSY99HzK8F7UP5WAbvP70yX5bd7CjEQkN+TjdxwI7g7lJ6podqrG2/w==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.1.0.tgz", + "integrity": "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==", "dev": true, "funding": { "type": "github", @@ -1547,9 +1551,9 @@ } }, "node_modules/unified": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.1.tgz", - "integrity": "sha512-v4ky1+6BN9X3pQrOdkFIPWAaeDsHPE1svRDxq7YpTc2plkIqFMwukfqM+l0ewpP9EfwARlt9pPFAeWYhHm8X9w==", + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz", + "integrity": "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==", "dev": true, "dependencies": { "@types/unist": "^2.0.0", @@ -1599,10 +1603,13 @@ } }, "node_modules/unist-util-position": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-4.0.1.tgz", - "integrity": "sha512-mgy/zI9fQ2HlbOtTdr2w9lhVaiFUHWQnZrFF2EUoVOqtAUdzqMtNiD99qA5a1IcjWVR8O6aVYE9u7Z2z1v0SQA==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-4.0.3.tgz", + "integrity": "sha512-p/5EMGIa1qwbXjA+QgcBXaPWjSnZfQ2Sc3yBEEfgPwsEmJd8Qh+DSk3LGnmOM4S1bY2C0AjmMnB8RuEYxpPwXQ==", "dev": true, + "dependencies": { + "@types/unist": "^2.0.0" + }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" @@ -1626,9 +1633,9 @@ } }, "node_modules/unist-util-stringify-position": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.0.tgz", - "integrity": "sha512-SdfAl8fsDclywZpfMDTVDxA2V7LjtRDTOFd44wUJamgl6OlVngsqWjxvermMYf60elWHbxhuRCZml7AnuXCaSA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.2.tgz", + "integrity": "sha512-7A6eiDCs9UtjcwZOcCpM4aPII3bAAGv13E96IkawkOAW0OhH+yRxtY0lzo8KiHpzEMfH7Q+FizUmwp8Iqy5EWg==", "dev": true, "dependencies": { "@types/unist": "^2.0.0" @@ -1700,9 +1707,9 @@ } }, "node_modules/vfile": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.0.tgz", - "integrity": "sha512-Tj44nY/48OQvarrE4FAjUfrv7GZOYzPbl5OD65HxVKwLJKMPU7zmfV8cCgCnzKWnSfYG2f3pxu+ALqs7j22xQQ==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.2.tgz", + "integrity": "sha512-w0PLIugRY3Crkgw89TeMvHCzqCs/zpreR31hl4D92y6SOE07+bfJe+dK5Q2akwS+i/c801kzjoOr9gMcTe6IAA==", "dev": true, "dependencies": { "@types/unist": "^2.0.0", @@ -1730,9 +1737,9 @@ } }, "node_modules/vfile-message": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.0.tgz", - "integrity": "sha512-4QJbBk+DkPEhBXq3f260xSaWtjE4gPKOfulzfMFF8ZNwaPZieWsg3iVlcmF04+eebzpcpeXOOFMfrYzJHVYg+g==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.2.tgz", + "integrity": "sha512-QjSNP6Yxzyycd4SVOtmKKyTsSvClqBPJcd00Z0zuPj3hOIjg0rUPG6DbFGPvUKRgYyaIWLPKpuEclcuvb3H8qA==", "dev": true, "dependencies": { "@types/unist": "^2.0.0", @@ -1871,9 +1878,9 @@ "dev": true }, "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "requires": { "ms": "2.1.2" @@ -2057,9 +2064,9 @@ } }, "highlight.js": { - "version": "11.4.0", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.4.0.tgz", - "integrity": "sha512-nawlpCBCSASs7EdvZOYOYVkJpGmAOKMYZgZtUqSRqodZE0GRVcFKwo1RcpeOemqh9hyttTdd5wDBwHkuSyUfnA==", + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.5.1.tgz", + "integrity": "sha512-LKzHqnxr4CrD2YsNoIf/o5nJ09j4yi/GcH5BnYz9UnVpZdS4ucMgvP61TDty5xJcFGRjnH4DpujkS9bHT3hq0Q==", "dev": true }, "html-void-elements": { @@ -2178,16 +2185,18 @@ } }, "mdast-util-gfm": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-2.0.0.tgz", - "integrity": "sha512-wMwejlTN3EQADPFuvxe8lmGsay3+f6gSJKdAHR6KBJzpcxvsjJSILB9K6u6G7eQLC7iOTyVIHYGui9uBc9r1Tg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-2.0.1.tgz", + "integrity": "sha512-42yHBbfWIFisaAfV1eixlabbsa6q7vHeSPY+cg+BBjX51M8xhgMacqH9g6TftB/9+YkcI0ooV4ncfrJslzm/RQ==", "dev": true, "requires": { + "mdast-util-from-markdown": "^1.0.0", "mdast-util-gfm-autolink-literal": "^1.0.0", "mdast-util-gfm-footnote": "^1.0.0", "mdast-util-gfm-strikethrough": "^1.0.0", "mdast-util-gfm-table": "^1.0.0", - "mdast-util-gfm-task-list-item": "^1.0.0" + "mdast-util-gfm-task-list-item": "^1.0.0", + "mdast-util-to-markdown": "^1.0.0" } }, "mdast-util-gfm-autolink-literal": { @@ -2224,12 +2233,13 @@ } }, "mdast-util-gfm-table": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-1.0.3.tgz", - "integrity": "sha512-B/tgpJjND1qIZM2WZst+NYnb0notPE6m0J+YOe3NOHXyEmvK38ytxaOsgz4BvrRPQQcNbRrTzSHMPnBkj1fCjg==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-1.0.4.tgz", + "integrity": "sha512-aEuoPwZyP4iIMkf2cLWXxx3EQ6Bmh2yKy9MVCg4i6Sd3cX80dcLEfXO/V4ul3pGH9czBK4kp+FAl+ZHmSUt9/w==", "dev": true, "requires": { "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^1.0.0", "mdast-util-to-markdown": "^1.3.0" } }, @@ -2378,9 +2388,9 @@ } }, "micromark-extension-gfm-footnote": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-1.0.3.tgz", - "integrity": "sha512-bn62pC5y39rIo2g1RqZk1NhF7T7cJLuJlbevunQz41U0iPVCdVOFASe5/L1kke+DFKSgfCRhv24+o42cZ1+ADw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-1.0.4.tgz", + "integrity": "sha512-E/fmPmDqLiMUP8mLJ8NbJWJ4bTw6tS+FEQS8CcuDtZpILuOb2kjLqPEeAePF1djXROHXChM/wPJw0iS4kHCcIg==", "dev": true, "requires": { "micromark-core-commonmark": "^1.0.0", @@ -2389,6 +2399,7 @@ "micromark-util-normalize-identifier": "^1.0.0", "micromark-util-sanitize-uri": "^1.0.0", "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", "uvu": "^0.5.0" } }, @@ -2785,15 +2796,15 @@ } }, "trough": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/trough/-/trough-2.0.2.tgz", - "integrity": "sha512-FnHq5sTMxC0sk957wHDzRnemFnNBvt/gSY99HzK8F7UP5WAbvP70yX5bd7CjEQkN+TjdxwI7g7lJ6podqrG2/w==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.1.0.tgz", + "integrity": "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==", "dev": true }, "unified": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.1.tgz", - "integrity": "sha512-v4ky1+6BN9X3pQrOdkFIPWAaeDsHPE1svRDxq7YpTc2plkIqFMwukfqM+l0ewpP9EfwARlt9pPFAeWYhHm8X9w==", + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz", + "integrity": "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==", "dev": true, "requires": { "@types/unist": "^2.0.0", @@ -2827,10 +2838,13 @@ "dev": true }, "unist-util-position": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-4.0.1.tgz", - "integrity": "sha512-mgy/zI9fQ2HlbOtTdr2w9lhVaiFUHWQnZrFF2EUoVOqtAUdzqMtNiD99qA5a1IcjWVR8O6aVYE9u7Z2z1v0SQA==", - "dev": true + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-4.0.3.tgz", + "integrity": "sha512-p/5EMGIa1qwbXjA+QgcBXaPWjSnZfQ2Sc3yBEEfgPwsEmJd8Qh+DSk3LGnmOM4S1bY2C0AjmMnB8RuEYxpPwXQ==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0" + } }, "unist-util-select": { "version": "4.0.1", @@ -2846,9 +2860,9 @@ } }, "unist-util-stringify-position": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.0.tgz", - "integrity": "sha512-SdfAl8fsDclywZpfMDTVDxA2V7LjtRDTOFd44wUJamgl6OlVngsqWjxvermMYf60elWHbxhuRCZml7AnuXCaSA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.2.tgz", + "integrity": "sha512-7A6eiDCs9UtjcwZOcCpM4aPII3bAAGv13E96IkawkOAW0OhH+yRxtY0lzo8KiHpzEMfH7Q+FizUmwp8Iqy5EWg==", "dev": true, "requires": { "@types/unist": "^2.0.0" @@ -2900,9 +2914,9 @@ } }, "vfile": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.0.tgz", - "integrity": "sha512-Tj44nY/48OQvarrE4FAjUfrv7GZOYzPbl5OD65HxVKwLJKMPU7zmfV8cCgCnzKWnSfYG2f3pxu+ALqs7j22xQQ==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.2.tgz", + "integrity": "sha512-w0PLIugRY3Crkgw89TeMvHCzqCs/zpreR31hl4D92y6SOE07+bfJe+dK5Q2akwS+i/c801kzjoOr9gMcTe6IAA==", "dev": true, "requires": { "@types/unist": "^2.0.0", @@ -2922,9 +2936,9 @@ } }, "vfile-message": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.0.tgz", - "integrity": "sha512-4QJbBk+DkPEhBXq3f260xSaWtjE4gPKOfulzfMFF8ZNwaPZieWsg3iVlcmF04+eebzpcpeXOOFMfrYzJHVYg+g==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.2.tgz", + "integrity": "sha512-QjSNP6Yxzyycd4SVOtmKKyTsSvClqBPJcd00Z0zuPj3hOIjg0rUPG6DbFGPvUKRgYyaIWLPKpuEclcuvb3H8qA==", "dev": true, "requires": { "@types/unist": "^2.0.0", diff --git a/tools/doc/package.json b/tools/doc/package.json index af39f927b4dc56..7cc4d7ec85bd9b 100644 --- a/tools/doc/package.json +++ b/tools/doc/package.json @@ -7,7 +7,7 @@ "node": ">=14.8.0" }, "devDependencies": { - "highlight.js": "^11.4.0", + "highlight.js": "^11.5.1", "js-yaml": "^4.1.0", "rehype-raw": "^6.1.1", "rehype-stringify": "^9.0.3", @@ -17,7 +17,7 @@ "remark-parse": "^10.0.1", "remark-rehype": "^10.1.0", "to-vfile": "^7.2.3", - "unified": "^10.1.1", + "unified": "^10.1.2", "unist-util-select": "^4.0.1", "unist-util-visit": "^4.1.0" }, diff --git a/tools/doc/type-parser.mjs b/tools/doc/type-parser.mjs index e17bf077d70f3c..7d76d072c34033 100644 --- a/tools/doc/type-parser.mjs +++ b/tools/doc/type-parser.mjs @@ -148,6 +148,7 @@ const customTypesMap = { 'http.Agent': 'http.html#class-httpagent', 'http.ClientRequest': 'http.html#class-httpclientrequest', 'http.IncomingMessage': 'http.html#class-httpincomingmessage', + 'http.OutgoingMessage': 'http.html#class-httpoutgoingmessage', 'http.Server': 'http.html#class-httpserver', 'http.ServerResponse': 'http.html#class-httpserverresponse', @@ -241,35 +242,35 @@ const customTypesMap = { 'zlib options': 'zlib.html#class-options', 'ReadableStream': - 'webstreams.md#class-readablestream', + 'webstreams.html#class-readablestream', 'ReadableStreamDefaultReader': - 'webstreams.md#class-readablestreamdefaultreader', + 'webstreams.html#class-readablestreamdefaultreader', 'ReadableStreamBYOBReader': - 'webstreams.md#class-readablestreambyobreader', + 'webstreams.html#class-readablestreambyobreader', 'ReadableStreamDefaultController': - 'webstreams.md#class-readablestreamdefaultcontroller', + 'webstreams.html#class-readablestreamdefaultcontroller', 'ReadableByteStreamController': - 'webstreams.md#class-readablebytestreamcontroller', + 'webstreams.html#class-readablebytestreamcontroller', 'ReadableStreamBYOBRequest': - 'webstreams.md#class-readablestreambyobrequest', + 'webstreams.html#class-readablestreambyobrequest', 'WritableStream': - 'webstreams.md#class-writablestream', + 'webstreams.html#class-writablestream', 'WritableStreamDefaultWriter': - 'webstreams.md#class-writablestreamdefaultwriter', + 'webstreams.html#class-writablestreamdefaultwriter', 'WritableStreamDefaultController': - 'webstreams.md#class-writablestreamdefaultcontroller', + 'webstreams.html#class-writablestreamdefaultcontroller', 'TransformStream': - 'webstreams.md#class-transformstream', + 'webstreams.html#class-transformstream', 'TransformStreamDefaultController': - 'webstreams.md#class-transformstreamdefaultcontroller', + 'webstreams.html#class-transformstreamdefaultcontroller', 'ByteLengthQueuingStrategy': - 'webstreams.md#class-bytelengthqueuingstrategy', + 'webstreams.html#class-bytelengthqueuingstrategy', 'CountQueuingStrategy': - 'webstreams.md#class-countqueuingstrategy', + 'webstreams.html#class-countqueuingstrategy', 'TextEncoderStream': - 'webstreams.md#class-textencoderstream', + 'webstreams.html#class-textencoderstream', 'TextDecoderStream': - 'webstreams.md#class-textdecoderstream', + 'webstreams.html#class-textdecoderstream', 'FormData': 'https://developer.mozilla.org/en-US/docs/Web/API/FormData', 'Headers': 'https://developer.mozilla.org/en-US/docs/Web/API/Headers', diff --git a/tools/gyp/.github/workflows/Python_tests.yml b/tools/gyp/.github/workflows/Python_tests.yml index 40ff521a6fd0b5..76e536826b9181 100644 --- a/tools/gyp/.github/workflows/Python_tests.yml +++ b/tools/gyp/.github/workflows/Python_tests.yml @@ -15,7 +15,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} - name: Install dependencies diff --git a/tools/gyp/.github/workflows/node-gyp.yml b/tools/gyp/.github/workflows/node-gyp.yml index 0c26a3d7de3d0e..c8d2455f13e5eb 100644 --- a/tools/gyp/.github/workflows/node-gyp.yml +++ b/tools/gyp/.github/workflows/node-gyp.yml @@ -21,10 +21,10 @@ jobs: with: repository: nodejs/node-gyp path: node-gyp - - uses: actions/setup-node@v2 + - uses: actions/setup-node@v3 with: node-version: 14.x - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v3 with: python-version: ${{ matrix.python }} - name: Install dependencies diff --git a/tools/gyp/CHANGELOG.md b/tools/gyp/CHANGELOG.md index 57d691c1812d38..09ceed8d41c245 100644 --- a/tools/gyp/CHANGELOG.md +++ b/tools/gyp/CHANGELOG.md @@ -1,5 +1,26 @@ # Changelog +### [0.12.1](https://www.github.com/nodejs/gyp-next/compare/v0.12.0...v0.12.1) (2022-04-06) + + +### Bug Fixes + +* **msvs:** avoid fixing path for arguments with "=" ([#143](https://www.github.com/nodejs/gyp-next/issues/143)) ([7e8f16e](https://www.github.com/nodejs/gyp-next/commit/7e8f16eb165e042e64bec98fa6c2a0232a42c26b)) + +## [0.12.0](https://www.github.com/nodejs/gyp-next/compare/v0.11.0...v0.12.0) (2022-04-04) + + +### Features + +* support building shared libraries on z/OS ([#137](https://www.github.com/nodejs/gyp-next/issues/137)) ([293bcfa](https://www.github.com/nodejs/gyp-next/commit/293bcfa4c25c6adb743377adafc45a80fee492c6)) + +## [0.11.0](https://www.github.com/nodejs/gyp-next/compare/v0.10.1...v0.11.0) (2022-03-04) + + +### Features + +* Add proper support for IBM i ([#140](https://www.github.com/nodejs/gyp-next/issues/140)) ([fdda4a3](https://www.github.com/nodejs/gyp-next/commit/fdda4a3038b8a7042ad960ce7a223687c24a21b1)) + ### [0.10.1](https://www.github.com/nodejs/gyp-next/compare/v0.10.0...v0.10.1) (2021-11-24) diff --git a/tools/gyp/pylib/gyp/common.py b/tools/gyp/pylib/gyp/common.py index 9213fcc5e82bb7..0847cdabc718d8 100644 --- a/tools/gyp/pylib/gyp/common.py +++ b/tools/gyp/pylib/gyp/common.py @@ -454,6 +454,8 @@ def GetFlavor(params): return "aix" if sys.platform.startswith(("os390", "zos")): return "zos" + if sys.platform == "os400": + return "os400" return "linux" @@ -463,9 +465,13 @@ def CopyTool(flavor, out_path, generator_flags={}): to |out_path|.""" # aix and solaris just need flock emulation. mac and win use more complicated # support scripts. - prefix = {"aix": "flock", "solaris": "flock", "mac": "mac", "win": "win"}.get( - flavor, None - ) + prefix = { + "aix": "flock", + "os400": "flock", + "solaris": "flock", + "mac": "mac", + "win": "win", + }.get(flavor, None) if not prefix: return diff --git a/tools/gyp/pylib/gyp/flock_tool.py b/tools/gyp/pylib/gyp/flock_tool.py index 1cb98152638b81..0754aff26fe7c0 100755 --- a/tools/gyp/pylib/gyp/flock_tool.py +++ b/tools/gyp/pylib/gyp/flock_tool.py @@ -41,7 +41,7 @@ def ExecFlock(self, lockfile, *cmd_list): # with EBADF, that's why we use this F_SETLK # hack instead. fd = os.open(lockfile, os.O_WRONLY | os.O_NOCTTY | os.O_CREAT, 0o666) - if sys.platform.startswith("aix"): + if sys.platform.startswith("aix") or sys.platform == "os400": # Python on AIX is compiled with LARGEFILE support, which changes the # struct size. op = struct.pack("hhIllqq", fcntl.F_WRLCK, 0, 0, 0, 0, 0, 0) diff --git a/tools/gyp/pylib/gyp/generator/make.py b/tools/gyp/pylib/gyp/generator/make.py index b15b1b83320235..97ca56075675f9 100644 --- a/tools/gyp/pylib/gyp/generator/make.py +++ b/tools/gyp/pylib/gyp/generator/make.py @@ -99,6 +99,8 @@ def CalculateVariables(default_variables, params): default_variables.setdefault("OS", operating_system) if flavor == "aix": default_variables.setdefault("SHARED_LIB_SUFFIX", ".a") + elif flavor == "zos": + default_variables.setdefault("SHARED_LIB_SUFFIX", ".x") else: default_variables.setdefault("SHARED_LIB_SUFFIX", ".so") default_variables.setdefault("SHARED_LIB_DIR", "$(builddir)/lib.$(TOOLSET)") @@ -286,6 +288,24 @@ def CalculateGeneratorInputInfo(params): """ # noqa: E501 +LINK_COMMANDS_OS400 = """\ +quiet_cmd_alink = AR($(TOOLSET)) $@ +cmd_alink = rm -f $@ && $(AR.$(TOOLSET)) -X64 crs $@ $(filter %.o,$^) + +quiet_cmd_alink_thin = AR($(TOOLSET)) $@ +cmd_alink_thin = rm -f $@ && $(AR.$(TOOLSET)) -X64 crs $@ $(filter %.o,$^) + +quiet_cmd_link = LINK($(TOOLSET)) $@ +cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(LD_INPUTS) $(LIBS) + +quiet_cmd_solink = SOLINK($(TOOLSET)) $@ +cmd_solink = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(LD_INPUTS) $(LIBS) + +quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@ +cmd_solink_module = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(filter-out FORCE_DO_CMD, $^) $(LIBS) +""" # noqa: E501 + + LINK_COMMANDS_OS390 = """\ quiet_cmd_alink = AR($(TOOLSET)) $@ cmd_alink = rm -f $@ && $(AR.$(TOOLSET)) crs $@ $(filter %.o,$^) @@ -297,10 +317,10 @@ def CalculateGeneratorInputInfo(params): cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(LD_INPUTS) $(LIBS) quiet_cmd_solink = SOLINK($(TOOLSET)) $@ -cmd_solink = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(LD_INPUTS) $(LIBS) -Wl,DLL +cmd_solink = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,DLL -o $(patsubst %.x,%.so,$@) $(LD_INPUTS) $(LIBS) && if [ -f $(notdir $@) ]; then /bin/cp $(notdir $@) $@; else true; fi quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@ -cmd_solink_module = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(filter-out FORCE_DO_CMD, $^) $(LIBS) -Wl,DLL +cmd_solink_module = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(filter-out FORCE_DO_CMD, $^) $(LIBS) """ # noqa: E501 @@ -449,6 +469,9 @@ def CalculateGeneratorInputInfo(params): # send stderr to /dev/null to ignore messages when linking directories. cmd_copy = ln -f "$<" "$@" 2>/dev/null || (rm -rf "$@" && cp %(copy_archive_args)s "$<" "$@") +quiet_cmd_symlink = SYMLINK $@ +cmd_symlink = ln -sf "$<" "$@" + %(link_commands)s """ # noqa: E501 r""" @@ -1030,12 +1053,20 @@ def WriteActions( # libraries, but until everything is made cross-compile safe, also use # target libraries. # TODO(piman): when everything is cross-compile safe, remove lib.target - self.WriteLn( - "cmd_%s = LD_LIBRARY_PATH=$(builddir)/lib.host:" - "$(builddir)/lib.target:$$LD_LIBRARY_PATH; " - "export LD_LIBRARY_PATH; " - "%s%s" % (name, cd_action, command) - ) + if self.flavor == "zos" or self.flavor == "aix": + self.WriteLn( + "cmd_%s = LIBPATH=$(builddir)/lib.host:" + "$(builddir)/lib.target:$$LIBPATH; " + "export LIBPATH; " + "%s%s" % (name, cd_action, command) + ) + else: + self.WriteLn( + "cmd_%s = LD_LIBRARY_PATH=$(builddir)/lib.host:" + "$(builddir)/lib.target:$$LD_LIBRARY_PATH; " + "export LD_LIBRARY_PATH; " + "%s%s" % (name, cd_action, command) + ) self.WriteLn() outputs = [self.Absolutify(o) for o in outputs] # The makefile rules are all relative to the top dir, but the gyp actions @@ -1529,6 +1560,8 @@ def ComputeOutputBasename(self, spec): target_prefix = "lib" if self.flavor == "aix": target_ext = ".a" + elif self.flavor == "zos": + target_ext = ".x" else: target_ext = ".so" elif self.type == "none": @@ -1609,6 +1642,14 @@ def ComputeDeps(self, spec): # link_deps.extend(spec.get('libraries', [])) return (gyp.common.uniquer(deps), gyp.common.uniquer(link_deps)) + def GetSharedObjectFromSidedeck(self, sidedeck): + """Return the shared object files based on sidedeck""" + return re.sub(r"\.x$", ".so", sidedeck) + + def GetUnversionedSidedeckFromSidedeck(self, sidedeck): + """Return the shared object files based on sidedeck""" + return re.sub(r"\.\d+\.x$", ".x", sidedeck) + def WriteDependencyOnExtraOutputs(self, target, extra_outputs): self.WriteMakeRule( [self.output_binary], @@ -1861,6 +1902,11 @@ def WriteTarget( part_of_all, postbuilds=postbuilds, ) + # z/OS has a .so target as well as a sidedeck .x target + if self.flavor == "zos": + self.WriteLn('%s: %s' % ( + QuoteSpaces(self.GetSharedObjectFromSidedeck(self.output_binary)), + QuoteSpaces(self.output_binary))) elif self.type == "loadable_module": for link_dep in link_deps: assert " " not in link_dep, ( @@ -1918,7 +1964,9 @@ def WriteTarget( else: file_desc = "executable" install_path = self._InstallableTargetInstallPath() - installable_deps = [self.output] + installable_deps = [] + if self.flavor != "zos": + installable_deps.append(self.output) if ( self.flavor == "mac" and "product_dir" not in spec @@ -1943,7 +1991,23 @@ def WriteTarget( comment="Copy this to the %s output path." % file_desc, part_of_all=part_of_all, ) - installable_deps.append(install_path) + if self.flavor != "zos": + installable_deps.append(install_path) + if self.flavor == 'zos' and self.type == 'shared_library': + # lib.target/libnode.so has a dependency on $(obj).target/libnode.so + self.WriteDoCmd([self.GetSharedObjectFromSidedeck(install_path)], + [self.GetSharedObjectFromSidedeck(self.output)], 'copy', + comment='Copy this to the %s output path.' % + file_desc, part_of_all=part_of_all) + # Create a symlink of libnode.x to libnode.version.x + self.WriteDoCmd([self.GetUnversionedSidedeckFromSidedeck(install_path)], + [install_path], 'symlink', + comment='Symlnk this to the %s output path.' % + file_desc, part_of_all=part_of_all) + # Place libnode.version.so and libnode.x symlink in lib.target dir + installable_deps.append(self.GetSharedObjectFromSidedeck(install_path)) + installable_deps.append( + self.GetUnversionedSidedeckFromSidedeck(install_path)) if self.output != self.alias and self.alias != self.target: self.WriteMakeRule( [self.alias], @@ -1951,7 +2015,18 @@ def WriteTarget( comment="Short alias for building this %s." % file_desc, phony=True, ) - if part_of_all: + if self.flavor == 'zos' and self.type == 'shared_library': + # Make sure that .x symlink target is run + self.WriteMakeRule( + ['all'], + [ + self.GetUnversionedSidedeckFromSidedeck(install_path), + self.GetSharedObjectFromSidedeck(install_path) + ], + comment='Add %s to "all" target.' % file_desc, + phony=True, + ) + elif part_of_all: self.WriteMakeRule( ["all"], [install_path], @@ -2247,6 +2322,9 @@ def _InstallableTargetInstallPath(self): # # Install all shared libs into a common directory (per toolset) for # # convenient access with LD_LIBRARY_PATH. # return "$(builddir)/lib.%s/%s" % (self.toolset, self.alias) + if self.flavor == "zos" and self.type == "shared_library": + return "$(builddir)/lib.%s/%s" % (self.toolset, self.alias) + return "$(builddir)/" + self.alias @@ -2414,6 +2492,16 @@ def CalculateMakefilePath(build_file, base_name): "flock_index": 2, } ) + elif flavor == "os400": + copy_archive_arguments = "-pPRf" + header_params.update( + { + "copy_archive_args": copy_archive_arguments, + "link_commands": LINK_COMMANDS_OS400, + "flock": "./gyp-flock-tool flock", + "flock_index": 2, + } + ) build_file, _, _ = gyp.common.ParseQualifiedTarget(target_list[0]) make_global_settings_array = data[build_file].get("make_global_settings", []) diff --git a/tools/gyp/pylib/gyp/generator/msvs.py b/tools/gyp/pylib/gyp/generator/msvs.py index 8308fa8433352c..fd950057847980 100644 --- a/tools/gyp/pylib/gyp/generator/msvs.py +++ b/tools/gyp/pylib/gyp/generator/msvs.py @@ -423,12 +423,15 @@ def _BuildCommandLineForRuleRaw( command.insert(0, "call") # Fix the paths # TODO(quote): This is a really ugly heuristic, and will miss path fixing - # for arguments like "--arg=path" or "/opt:path". - # If the argument starts with a slash or dash, it's probably a command line - # switch + # for arguments like "--arg=path", arg=path, or "/opt:path". + # If the argument starts with a slash or dash, or contains an equal sign, + # it's probably a command line switch. # Return the path with forward slashes because the command using it might # not support backslashes. - arguments = [i if (i[:1] in "/-") else _FixPath(i, "/") for i in cmd[1:]] + arguments = [ + i if (i[:1] in "/-" or "=" in i) else _FixPath(i, "/") + for i in cmd[1:] + ] arguments = [i.replace("$(InputDir)", "%INPUTDIR%") for i in arguments] arguments = [MSVSSettings.FixVCMacroSlashes(i) for i in arguments] if quote_cmd: diff --git a/tools/gyp/pylib/gyp/generator/ninja.py b/tools/gyp/pylib/gyp/generator/ninja.py index d173bf22990116..3db3771ac97855 100644 --- a/tools/gyp/pylib/gyp/generator/ninja.py +++ b/tools/gyp/pylib/gyp/generator/ninja.py @@ -2112,8 +2112,8 @@ class MEMORYSTATUSEX(ctypes.Structure): ctypes.windll.kernel32.GlobalMemoryStatusEx(ctypes.byref(stat)) # VS 2015 uses 20% more working set than VS 2013 and can consume all RAM - # on a 64 GB machine. - mem_limit = max(1, stat.ullTotalPhys // (5 * (2 ** 30))) # total / 5GB + # on a 64 GiB machine. + mem_limit = max(1, stat.ullTotalPhys // (5 * (2 ** 30))) # total / 5GiB hard_cap = max(1, int(os.environ.get("GYP_LINK_CONCURRENCY_MAX", 2 ** 32))) return min(mem_limit, hard_cap) elif sys.platform.startswith("linux"): diff --git a/tools/gyp/setup.py b/tools/gyp/setup.py index 576880a7f7a9fc..fa2eefe39f5312 100644 --- a/tools/gyp/setup.py +++ b/tools/gyp/setup.py @@ -15,7 +15,7 @@ setup( name="gyp-next", - version="0.10.1", + version="0.12.1", description="A fork of the GYP build system for use in the Node.js projects", long_description=long_description, long_description_content_type="text/markdown", diff --git a/tools/gyp/test_gyp.py b/tools/gyp/test_gyp.py index 9ba264170f43ab..b7bb956b8ed585 100755 --- a/tools/gyp/test_gyp.py +++ b/tools/gyp/test_gyp.py @@ -116,6 +116,7 @@ def main(argv=None): else: format_list = { "aix5": ["make"], + "os400": ["make"], "freebsd7": ["make"], "freebsd8": ["make"], "openbsd5": ["make"], diff --git a/tools/gyp/tools/pretty_gyp.py b/tools/gyp/tools/pretty_gyp.py index 4ffa44455181c3..6eef3a1bbf02a5 100755 --- a/tools/gyp/tools/pretty_gyp.py +++ b/tools/gyp/tools/pretty_gyp.py @@ -90,7 +90,7 @@ def count_braces(line): """ open_braces = ["[", "(", "{"] close_braces = ["]", ")", "}"] - closing_prefix_re = re.compile(r"(.*?[^\s\]\}\)]+.*?)([\]\}\)],?)\s*$") + closing_prefix_re = re.compile(r"[^\s\]\}\)]\s*[\]\}\)]+,?\s*$") cnt = 0 stripline = COMMENT_RE.sub(r"", line) stripline = QUOTE_RE.sub(r"''", stripline) diff --git a/tools/icu/current_ver.dep b/tools/icu/current_ver.dep index d135e99146e041..9d2ca52eda4153 100644 --- a/tools/icu/current_ver.dep +++ b/tools/icu/current_ver.dep @@ -1,6 +1,6 @@ [ { - "url": "https://github.com/unicode-org/icu/releases/download/release-70-1/icu4c-70_1-src.tgz", - "md5": "65287befec8116d79af23a58aa50c60d" + "url": "https://github.com/unicode-org/icu/releases/download/release-71-1/icu4c-71_1-src.tgz", + "md5": "e06ffc96f59762bd3c929b217445aaec" } ] diff --git a/tools/inspector_protocol/encoding/encoding.h b/tools/inspector_protocol/encoding/encoding.h index 08596e9e1e43f0..14432484d55b9d 100644 --- a/tools/inspector_protocol/encoding/encoding.h +++ b/tools/inspector_protocol/encoding/encoding.h @@ -167,7 +167,7 @@ namespace cbor { // must use a 32 bit wide length. // - At the top level, a message must be an indefinite length map // wrapped by an envelope. -// - Maximal size for messages is 2^32 (4 GB). +// - Maximal size for messages is 2^32 (4 GiB). // - For scalars, we support only the int32_t range, encoded as // UNSIGNED/NEGATIVE (major types 0 / 1). // - UTF16 strings, including with unbalanced surrogate pairs, are encoded diff --git a/tools/inspector_protocol/lib/encoding_h.template b/tools/inspector_protocol/lib/encoding_h.template index 2c6cfc10d594c2..4d9874bfbd5cb4 100644 --- a/tools/inspector_protocol/lib/encoding_h.template +++ b/tools/inspector_protocol/lib/encoding_h.template @@ -176,7 +176,7 @@ namespace cbor { // must use a 32 bit wide length. // - At the top level, a message must be an indefinite length map // wrapped by an envelope. -// - Maximal size for messages is 2^32 (4 GB). +// - Maximal size for messages is 2^32 (4 GiB). // - For scalars, we support only the int32_t range, encoded as // UNSIGNED/NEGATIVE (major types 0 / 1). // - UTF16 strings, including with unbalanced surrogate pairs, are encoded diff --git a/tools/lint-md/lint-md.mjs b/tools/lint-md/lint-md.mjs index e4108212d9bc3c..b431525f9667f9 100644 --- a/tools/lint-md/lint-md.mjs +++ b/tools/lint-md/lint-md.mjs @@ -172,9 +172,9 @@ function wrap(middleware, callback) { parameters.push(done); } try { - result = middleware(...parameters); + result = middleware.apply(this, parameters); } catch (error) { - const exception = error; + const exception = (error); if (fnExpectsCallback && called) { throw exception } @@ -201,18 +201,17 @@ function wrap(middleware, callback) { } } -var own$8 = {}.hasOwnProperty; function stringifyPosition(value) { if (!value || typeof value !== 'object') { return '' } - if (own$8.call(value, 'position') || own$8.call(value, 'type')) { + if ('position' in value || 'type' in value) { return position(value.position) } - if (own$8.call(value, 'start') || own$8.call(value, 'end')) { + if ('start' in value || 'end' in value) { return position(value) } - if (own$8.call(value, 'line') || own$8.call(value, 'column')) { + if ('line' in value || 'column' in value) { return point$1(value) } return '' @@ -229,19 +228,18 @@ function index(value) { class VFileMessage extends Error { constructor(reason, place, origin) { - var parts = [null, null]; - var position = { + const parts = [null, null]; + let position = { start: {line: null, column: null}, end: {line: null, column: null} }; - var index; super(); if (typeof place === 'string') { origin = place; - place = null; + place = undefined; } if (typeof origin === 'string') { - index = origin.indexOf(':'); + const index = origin.indexOf(':'); if (index === -1) { parts[1] = origin; } else { @@ -484,7 +482,7 @@ function base() { continue } if (options[0] === true) { - options[1] = undefined; + options[0] = undefined; } const transformer = attacher.call(processor, ...options); if (typeof transformer === 'function') { @@ -12629,12 +12627,12 @@ const remarkLintListItemBulletIndent = lintRule( ); var remarkLintListItemBulletIndent$1 = remarkLintListItemBulletIndent; -var pointStart = point('start'); -var pointEnd = point('end'); +const pointStart = point('start'); +const pointEnd = point('end'); function point(type) { return point function point(node) { - var point = (node && node.position && node.position[type]) || {}; + const point = (node && node.position && node.position[type]) || {}; return { line: point.line || null, column: point.column || null, @@ -19299,13 +19297,13 @@ var jsYaml = { const SEMVER_SPEC_VERSION = '2.0.0'; const MAX_LENGTH$2 = 256; const MAX_SAFE_INTEGER$1 = Number.MAX_SAFE_INTEGER || - 9007199254740991; + 9007199254740991; const MAX_SAFE_COMPONENT_LENGTH = 16; var constants = { SEMVER_SPEC_VERSION, MAX_LENGTH: MAX_LENGTH$2, MAX_SAFE_INTEGER: MAX_SAFE_INTEGER$1, - MAX_SAFE_COMPONENT_LENGTH + MAX_SAFE_COMPONENT_LENGTH, }; var re$2 = {exports: {}}; @@ -19329,7 +19327,7 @@ const t = exports.t = {}; let R = 0; const createToken = (name, value, isGlobal) => { const index = R++; - debug(index, value); + debug(name, index, value); t[name] = index; src[index] = value; re[index] = new RegExp(value, isGlobal ? 'g' : undefined); @@ -19409,17 +19407,17 @@ createToken('HYPHENRANGELOOSE', `^\\s*(${src[t.XRANGEPLAINLOOSE]})` + `(${src[t.XRANGEPLAINLOOSE]})` + `\\s*$`); createToken('STAR', '(<|>)?=?\\s*\\*'); -createToken('GTE0', '^\\s*>=\\s*0\.0\.0\\s*$'); -createToken('GTE0PRE', '^\\s*>=\\s*0\.0\.0-0\\s*$'); +createToken('GTE0', '^\\s*>=\\s*0\\.0\\.0\\s*$'); +createToken('GTE0PRE', '^\\s*>=\\s*0\\.0\\.0-0\\s*$'); }(re$2, re$2.exports)); const opts = ['includePrerelease', 'loose', 'rtl']; const parseOptions$2 = options => !options ? {} : typeof options !== 'object' ? { loose: true } - : opts.filter(k => options[k]).reduce((options, k) => { - options[k] = true; - return options + : opts.filter(k => options[k]).reduce((o, k) => { + o[k] = true; + return o }, {}); var parseOptions_1 = parseOptions$2; @@ -19440,7 +19438,7 @@ const compareIdentifiers$1 = (a, b) => { const rcompareIdentifiers = (a, b) => compareIdentifiers$1(b, a); var identifiers = { compareIdentifiers: compareIdentifiers$1, - rcompareIdentifiers + rcompareIdentifiers, }; const debug = debug_1; @@ -19654,7 +19652,7 @@ class SemVer$2 { } } if (identifier) { - if (this.prerelease[0] === identifier) { + if (compareIdentifiers(this.prerelease[0], identifier) === 0) { if (isNaN(this.prerelease[1])) { this.prerelease = [identifier, 0]; } @@ -19673,7 +19671,7 @@ class SemVer$2 { } var semver = SemVer$2; -const {MAX_LENGTH} = constants; +const { MAX_LENGTH } = constants; const { re, t } = re$2.exports; const SemVer$1 = semver; const parseOptions = parseOptions_1; @@ -21228,29 +21226,33 @@ function stringWidth(string, options = {}) { if (typeof string !== 'string' || string.length === 0) { return 0; } + options = { + ambiguousIsNarrow: true, + ...options + }; string = stripAnsi(string); if (string.length === 0) { return 0; } string = string.replace(emojiRegex(), ' '); - const ambiguousCharWidth = options.ambiguousIsNarrow ? 1 : 2; + const ambiguousCharacterWidth = options.ambiguousIsNarrow ? 1 : 2; let width = 0; - for (let index = 0; index < string.length; index++) { - const codePoint = string.codePointAt(index); + for (const character of string) { + const codePoint = character.codePointAt(0); if (codePoint <= 0x1F || (codePoint >= 0x7F && codePoint <= 0x9F)) { continue; } if (codePoint >= 0x300 && codePoint <= 0x36F) { continue; } - const code = eastAsianWidth.eastAsianWidth(string.charAt(index)); + const code = eastAsianWidth.eastAsianWidth(character); switch (code) { case 'F': case 'W': width += 2; break; case 'A': - width += ambiguousCharWidth; + width += ambiguousCharacterWidth; break; default: width += 1; @@ -21444,11 +21446,11 @@ const supportsColor = { const color = supportsColor.stderr.hasBasic; +const platform = process$1.platform; + const own = {}.hasOwnProperty; const chars = - process.platform === 'win32' - ? {error: '×', warning: '‼'} - : {error: '✖', warning: '⚠'}; + platform === 'win32' ? {error: '×', warning: '‼'} : {error: '✖', warning: '⚠'}; const labels = { true: 'error', false: 'warning', diff --git a/tools/lint-md/package-lock.json b/tools/lint-md/package-lock.json index 9535e4d7ab8227..7b6bb055390ec2 100644 --- a/tools/lint-md/package-lock.json +++ b/tools/lint-md/package-lock.json @@ -12,20 +12,20 @@ "remark-preset-lint-node": "^3.3.1", "remark-stringify": "^10.0.2", "to-vfile": "^7.2.3", - "unified": "^10.1.1", - "vfile-reporter": "^7.0.3" + "unified": "^10.1.2", + "vfile-reporter": "^7.0.4" }, "devDependencies": { - "@rollup/plugin-commonjs": "^21.0.1", - "@rollup/plugin-node-resolve": "^13.1.3", - "rollup": "^2.67.3", + "@rollup/plugin-commonjs": "^21.1.0", + "@rollup/plugin-node-resolve": "^13.2.1", + "rollup": "^2.70.2", "rollup-plugin-cleanup": "^3.2.1" } }, "node_modules/@rollup/plugin-commonjs": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-21.0.1.tgz", - "integrity": "sha512-EA+g22lbNJ8p5kuZJUYyhhDK7WgJckW5g4pNN7n4mAFUM96VuwUnNT3xr2Db2iCZPI1pJPbGyfT5mS9T1dHfMg==", + "version": "21.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-21.1.0.tgz", + "integrity": "sha512-6ZtHx3VHIp2ReNNDxHjuUml6ur+WcQ28N1yHgCQwsbNkQg2suhxGMDQGJOn/KuDxKtd1xuZP5xSTwBA4GQ8hbA==", "dev": true, "dependencies": { "@rollup/pluginutils": "^3.1.0", @@ -44,9 +44,9 @@ } }, "node_modules/@rollup/plugin-node-resolve": { - "version": "13.1.3", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.1.3.tgz", - "integrity": "sha512-BdxNk+LtmElRo5d06MGY4zoepyrXX1tkzX2hrnPEZ53k78GuOMWLqmJDGIIOPwVRIFZrLQOo+Yr6KtCuLIA0AQ==", + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.2.1.tgz", + "integrity": "sha512-btX7kzGvp1JwShQI9V6IM841YKNPYjKCvUbNrQ2EcVYbULtUd/GH6wZ/qdqH13j9pOHBER+EZXNN2L8RSJhVRA==", "dev": true, "dependencies": { "@rollup/pluginutils": "^3.1.0", @@ -129,9 +129,9 @@ "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" }, "node_modules/@types/node": { - "version": "17.0.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.18.tgz", - "integrity": "sha512-eKj4f/BsN/qcculZiRSujogjvp5O/k4lOW5m35NopjZM/QwLOR075a8pJW5hD+Rtdm2DaCVPENS6KtSQnUD6BA==", + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.24.tgz", + "integrity": "sha512-aveCYRQbgTH9Pssp1voEP7HiuWlD2jW2BO56w+bVrJn04i61yh6mRfoKO6hEYQD9vF+W8Chkwc6j1M36uPkx4g==", "dev": true }, "node_modules/@types/resolve": { @@ -242,9 +242,9 @@ "dev": true }, "node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dependencies": { "ms": "2.1.2" }, @@ -514,12 +514,12 @@ } }, "node_modules/magic-string": { - "version": "0.25.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", - "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", "dev": true, "dependencies": { - "sourcemap-codec": "^1.4.4" + "sourcemap-codec": "^1.4.8" } }, "node_modules/markdown-table": { @@ -581,15 +581,17 @@ } }, "node_modules/mdast-util-gfm": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-2.0.0.tgz", - "integrity": "sha512-wMwejlTN3EQADPFuvxe8lmGsay3+f6gSJKdAHR6KBJzpcxvsjJSILB9K6u6G7eQLC7iOTyVIHYGui9uBc9r1Tg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-2.0.1.tgz", + "integrity": "sha512-42yHBbfWIFisaAfV1eixlabbsa6q7vHeSPY+cg+BBjX51M8xhgMacqH9g6TftB/9+YkcI0ooV4ncfrJslzm/RQ==", "dependencies": { + "mdast-util-from-markdown": "^1.0.0", "mdast-util-gfm-autolink-literal": "^1.0.0", "mdast-util-gfm-footnote": "^1.0.0", "mdast-util-gfm-strikethrough": "^1.0.0", "mdast-util-gfm-table": "^1.0.0", - "mdast-util-gfm-task-list-item": "^1.0.0" + "mdast-util-gfm-task-list-item": "^1.0.0", + "mdast-util-to-markdown": "^1.0.0" }, "funding": { "type": "opencollective", @@ -639,11 +641,12 @@ } }, "node_modules/mdast-util-gfm-table": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-1.0.3.tgz", - "integrity": "sha512-B/tgpJjND1qIZM2WZst+NYnb0notPE6m0J+YOe3NOHXyEmvK38ytxaOsgz4BvrRPQQcNbRrTzSHMPnBkj1fCjg==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-1.0.4.tgz", + "integrity": "sha512-aEuoPwZyP4iIMkf2cLWXxx3EQ6Bmh2yKy9MVCg4i6Sd3cX80dcLEfXO/V4ul3pGH9czBK4kp+FAl+ZHmSUt9/w==", "dependencies": { "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^1.0.0", "mdast-util-to-markdown": "^1.3.0" }, "funding": { @@ -822,9 +825,9 @@ } }, "node_modules/micromark-extension-gfm-footnote": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-1.0.3.tgz", - "integrity": "sha512-bn62pC5y39rIo2g1RqZk1NhF7T7cJLuJlbevunQz41U0iPVCdVOFASe5/L1kke+DFKSgfCRhv24+o42cZ1+ADw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-1.0.4.tgz", + "integrity": "sha512-E/fmPmDqLiMUP8mLJ8NbJWJ4bTw6tS+FEQS8CcuDtZpILuOb2kjLqPEeAePF1djXROHXChM/wPJw0iS4kHCcIg==", "dependencies": { "micromark-core-commonmark": "^1.0.0", "micromark-factory-space": "^1.0.0", @@ -832,6 +835,7 @@ "micromark-util-normalize-identifier": "^1.0.0", "micromark-util-sanitize-uri": "^1.0.0", "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", "uvu": "^0.5.0" }, "funding": { @@ -2193,9 +2197,9 @@ } }, "node_modules/rollup": { - "version": "2.67.3", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.67.3.tgz", - "integrity": "sha512-G/x1vUwbGtP6O5ZM8/sWr8+p7YfZhI18pPqMRtMYMWSbHjKZ/ajHGiM+GWNTlWyOR0EHIdT8LHU+Z4ciIZ1oBw==", + "version": "2.70.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.70.2.tgz", + "integrity": "sha512-EitogNZnfku65I1DD5Mxe8JYRUCy0hkK5X84IlDtUs+O6JRMpRciXTzyCUuX11b5L5pvjH+OmFXiQ3XjabcXgg==", "dev": true, "bin": { "rollup": "dist/bin/rollup" @@ -2250,9 +2254,9 @@ } }, "node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -2284,9 +2288,9 @@ "dev": true }, "node_modules/string-width": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.0.tgz", - "integrity": "sha512-7x54QnN21P+XL/v8SuNKvfgsUre6PXpN7mc77N3HlZv+f1SBRGmjxtOud2Z6FZ8DmdkD/IdjCaf9XXbnqmTZGQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -2314,9 +2318,9 @@ } }, "node_modules/supports-color": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.2.1.tgz", - "integrity": "sha512-Obv7ycoCTG51N7y175StI9BlAXrmgZrFhZOb0/PyjHBher/NmsdBgbbQ1Inhq+gIhz6+7Gb+jWF2Vqi7Mf1xnQ==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.2.2.tgz", + "integrity": "sha512-XC6g/Kgux+rJXmwokjm9ECpD6k/smUoS5LKlUCcsYr4IY3rW0XyAympon2RmxGrlnZURMpg5T18gWDP9CsHXFA==", "engines": { "node": ">=12" }, @@ -2350,18 +2354,18 @@ } }, "node_modules/trough": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/trough/-/trough-2.0.2.tgz", - "integrity": "sha512-FnHq5sTMxC0sk957wHDzRnemFnNBvt/gSY99HzK8F7UP5WAbvP70yX5bd7CjEQkN+TjdxwI7g7lJ6podqrG2/w==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.1.0.tgz", + "integrity": "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, "node_modules/unified": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.1.tgz", - "integrity": "sha512-v4ky1+6BN9X3pQrOdkFIPWAaeDsHPE1svRDxq7YpTc2plkIqFMwukfqM+l0ewpP9EfwARlt9pPFAeWYhHm8X9w==", + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz", + "integrity": "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==", "dependencies": { "@types/unist": "^2.0.0", "bail": "^2.0.0", @@ -2441,18 +2445,21 @@ } }, "node_modules/unist-util-position": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-4.0.1.tgz", - "integrity": "sha512-mgy/zI9fQ2HlbOtTdr2w9lhVaiFUHWQnZrFF2EUoVOqtAUdzqMtNiD99qA5a1IcjWVR8O6aVYE9u7Z2z1v0SQA==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-4.0.3.tgz", + "integrity": "sha512-p/5EMGIa1qwbXjA+QgcBXaPWjSnZfQ2Sc3yBEEfgPwsEmJd8Qh+DSk3LGnmOM4S1bY2C0AjmMnB8RuEYxpPwXQ==", + "dependencies": { + "@types/unist": "^2.0.0" + }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, "node_modules/unist-util-stringify-position": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.0.tgz", - "integrity": "sha512-SdfAl8fsDclywZpfMDTVDxA2V7LjtRDTOFd44wUJamgl6OlVngsqWjxvermMYf60elWHbxhuRCZml7AnuXCaSA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.2.tgz", + "integrity": "sha512-7A6eiDCs9UtjcwZOcCpM4aPII3bAAGv13E96IkawkOAW0OhH+yRxtY0lzo8KiHpzEMfH7Q+FizUmwp8Iqy5EWg==", "dependencies": { "@types/unist": "^2.0.0" }, @@ -2519,9 +2526,9 @@ } }, "node_modules/vfile": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.0.tgz", - "integrity": "sha512-Tj44nY/48OQvarrE4FAjUfrv7GZOYzPbl5OD65HxVKwLJKMPU7zmfV8cCgCnzKWnSfYG2f3pxu+ALqs7j22xQQ==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.2.tgz", + "integrity": "sha512-w0PLIugRY3Crkgw89TeMvHCzqCs/zpreR31hl4D92y6SOE07+bfJe+dK5Q2akwS+i/c801kzjoOr9gMcTe6IAA==", "dependencies": { "@types/unist": "^2.0.0", "is-buffer": "^2.0.0", @@ -2547,9 +2554,9 @@ } }, "node_modules/vfile-message": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.0.tgz", - "integrity": "sha512-4QJbBk+DkPEhBXq3f260xSaWtjE4gPKOfulzfMFF8ZNwaPZieWsg3iVlcmF04+eebzpcpeXOOFMfrYzJHVYg+g==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.2.tgz", + "integrity": "sha512-QjSNP6Yxzyycd4SVOtmKKyTsSvClqBPJcd00Z0zuPj3hOIjg0rUPG6DbFGPvUKRgYyaIWLPKpuEclcuvb3H8qA==", "dependencies": { "@types/unist": "^2.0.0", "unist-util-stringify-position": "^3.0.0" @@ -2560,9 +2567,9 @@ } }, "node_modules/vfile-reporter": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/vfile-reporter/-/vfile-reporter-7.0.3.tgz", - "integrity": "sha512-q+ruTWxFHbow359TDqoNJn5THdwRDeV+XUOtzdT/OESgaGw05CjL68ImlbzRzqS5xL62Y1IaIWb8x+RbaNjayA==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/vfile-reporter/-/vfile-reporter-7.0.4.tgz", + "integrity": "sha512-4cWalUnLrEnbeUQ+hARG5YZtaHieVK3Jp4iG5HslttkVl+MHunSGNAIrODOTLbtjWsNZJRMCkL66AhvZAYuJ9A==", "dependencies": { "@types/supports-color": "^8.0.0", "string-width": "^5.0.0", @@ -2632,9 +2639,9 @@ }, "dependencies": { "@rollup/plugin-commonjs": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-21.0.1.tgz", - "integrity": "sha512-EA+g22lbNJ8p5kuZJUYyhhDK7WgJckW5g4pNN7n4mAFUM96VuwUnNT3xr2Db2iCZPI1pJPbGyfT5mS9T1dHfMg==", + "version": "21.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-21.1.0.tgz", + "integrity": "sha512-6ZtHx3VHIp2ReNNDxHjuUml6ur+WcQ28N1yHgCQwsbNkQg2suhxGMDQGJOn/KuDxKtd1xuZP5xSTwBA4GQ8hbA==", "dev": true, "requires": { "@rollup/pluginutils": "^3.1.0", @@ -2647,9 +2654,9 @@ } }, "@rollup/plugin-node-resolve": { - "version": "13.1.3", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.1.3.tgz", - "integrity": "sha512-BdxNk+LtmElRo5d06MGY4zoepyrXX1tkzX2hrnPEZ53k78GuOMWLqmJDGIIOPwVRIFZrLQOo+Yr6KtCuLIA0AQ==", + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.2.1.tgz", + "integrity": "sha512-btX7kzGvp1JwShQI9V6IM841YKNPYjKCvUbNrQ2EcVYbULtUd/GH6wZ/qdqH13j9pOHBER+EZXNN2L8RSJhVRA==", "dev": true, "requires": { "@rollup/pluginutils": "^3.1.0", @@ -2722,9 +2729,9 @@ "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" }, "@types/node": { - "version": "17.0.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.18.tgz", - "integrity": "sha512-eKj4f/BsN/qcculZiRSujogjvp5O/k4lOW5m35NopjZM/QwLOR075a8pJW5hD+Rtdm2DaCVPENS6KtSQnUD6BA==", + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.24.tgz", + "integrity": "sha512-aveCYRQbgTH9Pssp1voEP7HiuWlD2jW2BO56w+bVrJn04i61yh6mRfoKO6hEYQD9vF+W8Chkwc6j1M36uPkx4g==", "dev": true }, "@types/resolve": { @@ -2811,9 +2818,9 @@ "dev": true }, "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "requires": { "ms": "2.1.2" } @@ -2998,12 +3005,12 @@ } }, "magic-string": { - "version": "0.25.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", - "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", "dev": true, "requires": { - "sourcemap-codec": "^1.4.4" + "sourcemap-codec": "^1.4.8" } }, "markdown-table": { @@ -3049,15 +3056,17 @@ } }, "mdast-util-gfm": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-2.0.0.tgz", - "integrity": "sha512-wMwejlTN3EQADPFuvxe8lmGsay3+f6gSJKdAHR6KBJzpcxvsjJSILB9K6u6G7eQLC7iOTyVIHYGui9uBc9r1Tg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-2.0.1.tgz", + "integrity": "sha512-42yHBbfWIFisaAfV1eixlabbsa6q7vHeSPY+cg+BBjX51M8xhgMacqH9g6TftB/9+YkcI0ooV4ncfrJslzm/RQ==", "requires": { + "mdast-util-from-markdown": "^1.0.0", "mdast-util-gfm-autolink-literal": "^1.0.0", "mdast-util-gfm-footnote": "^1.0.0", "mdast-util-gfm-strikethrough": "^1.0.0", "mdast-util-gfm-table": "^1.0.0", - "mdast-util-gfm-task-list-item": "^1.0.0" + "mdast-util-gfm-task-list-item": "^1.0.0", + "mdast-util-to-markdown": "^1.0.0" } }, "mdast-util-gfm-autolink-literal": { @@ -3091,11 +3100,12 @@ } }, "mdast-util-gfm-table": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-1.0.3.tgz", - "integrity": "sha512-B/tgpJjND1qIZM2WZst+NYnb0notPE6m0J+YOe3NOHXyEmvK38ytxaOsgz4BvrRPQQcNbRrTzSHMPnBkj1fCjg==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-1.0.4.tgz", + "integrity": "sha512-aEuoPwZyP4iIMkf2cLWXxx3EQ6Bmh2yKy9MVCg4i6Sd3cX80dcLEfXO/V4ul3pGH9czBK4kp+FAl+ZHmSUt9/w==", "requires": { "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^1.0.0", "mdast-util-to-markdown": "^1.3.0" } }, @@ -3222,9 +3232,9 @@ } }, "micromark-extension-gfm-footnote": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-1.0.3.tgz", - "integrity": "sha512-bn62pC5y39rIo2g1RqZk1NhF7T7cJLuJlbevunQz41U0iPVCdVOFASe5/L1kke+DFKSgfCRhv24+o42cZ1+ADw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-1.0.4.tgz", + "integrity": "sha512-E/fmPmDqLiMUP8mLJ8NbJWJ4bTw6tS+FEQS8CcuDtZpILuOb2kjLqPEeAePF1djXROHXChM/wPJw0iS4kHCcIg==", "requires": { "micromark-core-commonmark": "^1.0.0", "micromark-factory-space": "^1.0.0", @@ -3232,6 +3242,7 @@ "micromark-util-normalize-identifier": "^1.0.0", "micromark-util-sanitize-uri": "^1.0.0", "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", "uvu": "^0.5.0" } }, @@ -4167,9 +4178,9 @@ } }, "rollup": { - "version": "2.67.3", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.67.3.tgz", - "integrity": "sha512-G/x1vUwbGtP6O5ZM8/sWr8+p7YfZhI18pPqMRtMYMWSbHjKZ/ajHGiM+GWNTlWyOR0EHIdT8LHU+Z4ciIZ1oBw==", + "version": "2.70.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.70.2.tgz", + "integrity": "sha512-EitogNZnfku65I1DD5Mxe8JYRUCy0hkK5X84IlDtUs+O6JRMpRciXTzyCUuX11b5L5pvjH+OmFXiQ3XjabcXgg==", "dev": true, "requires": { "fsevents": "~2.3.2" @@ -4211,9 +4222,9 @@ } }, "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "requires": { "lru-cache": "^6.0.0" } @@ -4236,9 +4247,9 @@ "dev": true }, "string-width": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.0.tgz", - "integrity": "sha512-7x54QnN21P+XL/v8SuNKvfgsUre6PXpN7mc77N3HlZv+f1SBRGmjxtOud2Z6FZ8DmdkD/IdjCaf9XXbnqmTZGQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "requires": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -4254,9 +4265,9 @@ } }, "supports-color": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.2.1.tgz", - "integrity": "sha512-Obv7ycoCTG51N7y175StI9BlAXrmgZrFhZOb0/PyjHBher/NmsdBgbbQ1Inhq+gIhz6+7Gb+jWF2Vqi7Mf1xnQ==" + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.2.2.tgz", + "integrity": "sha512-XC6g/Kgux+rJXmwokjm9ECpD6k/smUoS5LKlUCcsYr4IY3rW0XyAympon2RmxGrlnZURMpg5T18gWDP9CsHXFA==" }, "supports-preserve-symlinks-flag": { "version": "1.0.0", @@ -4274,14 +4285,14 @@ } }, "trough": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/trough/-/trough-2.0.2.tgz", - "integrity": "sha512-FnHq5sTMxC0sk957wHDzRnemFnNBvt/gSY99HzK8F7UP5WAbvP70yX5bd7CjEQkN+TjdxwI7g7lJ6podqrG2/w==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.1.0.tgz", + "integrity": "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==" }, "unified": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.1.tgz", - "integrity": "sha512-v4ky1+6BN9X3pQrOdkFIPWAaeDsHPE1svRDxq7YpTc2plkIqFMwukfqM+l0ewpP9EfwARlt9pPFAeWYhHm8X9w==", + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz", + "integrity": "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==", "requires": { "@types/unist": "^2.0.0", "bail": "^2.0.0", @@ -4339,14 +4350,17 @@ "integrity": "sha512-F5CZ68eYzuSvJjGhCLPL3cYx45IxkqXSetCcRgUXtbcm50X2L9oOWQlfUfDdAf+6Pd27YDblBfdtmsThXmwpbQ==" }, "unist-util-position": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-4.0.1.tgz", - "integrity": "sha512-mgy/zI9fQ2HlbOtTdr2w9lhVaiFUHWQnZrFF2EUoVOqtAUdzqMtNiD99qA5a1IcjWVR8O6aVYE9u7Z2z1v0SQA==" + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-4.0.3.tgz", + "integrity": "sha512-p/5EMGIa1qwbXjA+QgcBXaPWjSnZfQ2Sc3yBEEfgPwsEmJd8Qh+DSk3LGnmOM4S1bY2C0AjmMnB8RuEYxpPwXQ==", + "requires": { + "@types/unist": "^2.0.0" + } }, "unist-util-stringify-position": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.0.tgz", - "integrity": "sha512-SdfAl8fsDclywZpfMDTVDxA2V7LjtRDTOFd44wUJamgl6OlVngsqWjxvermMYf60elWHbxhuRCZml7AnuXCaSA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.2.tgz", + "integrity": "sha512-7A6eiDCs9UtjcwZOcCpM4aPII3bAAGv13E96IkawkOAW0OhH+yRxtY0lzo8KiHpzEMfH7Q+FizUmwp8Iqy5EWg==", "requires": { "@types/unist": "^2.0.0" } @@ -4393,9 +4407,9 @@ } }, "vfile": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.0.tgz", - "integrity": "sha512-Tj44nY/48OQvarrE4FAjUfrv7GZOYzPbl5OD65HxVKwLJKMPU7zmfV8cCgCnzKWnSfYG2f3pxu+ALqs7j22xQQ==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.2.tgz", + "integrity": "sha512-w0PLIugRY3Crkgw89TeMvHCzqCs/zpreR31hl4D92y6SOE07+bfJe+dK5Q2akwS+i/c801kzjoOr9gMcTe6IAA==", "requires": { "@types/unist": "^2.0.0", "is-buffer": "^2.0.0", @@ -4413,18 +4427,18 @@ } }, "vfile-message": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.0.tgz", - "integrity": "sha512-4QJbBk+DkPEhBXq3f260xSaWtjE4gPKOfulzfMFF8ZNwaPZieWsg3iVlcmF04+eebzpcpeXOOFMfrYzJHVYg+g==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.2.tgz", + "integrity": "sha512-QjSNP6Yxzyycd4SVOtmKKyTsSvClqBPJcd00Z0zuPj3hOIjg0rUPG6DbFGPvUKRgYyaIWLPKpuEclcuvb3H8qA==", "requires": { "@types/unist": "^2.0.0", "unist-util-stringify-position": "^3.0.0" } }, "vfile-reporter": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/vfile-reporter/-/vfile-reporter-7.0.3.tgz", - "integrity": "sha512-q+ruTWxFHbow359TDqoNJn5THdwRDeV+XUOtzdT/OESgaGw05CjL68ImlbzRzqS5xL62Y1IaIWb8x+RbaNjayA==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/vfile-reporter/-/vfile-reporter-7.0.4.tgz", + "integrity": "sha512-4cWalUnLrEnbeUQ+hARG5YZtaHieVK3Jp4iG5HslttkVl+MHunSGNAIrODOTLbtjWsNZJRMCkL66AhvZAYuJ9A==", "requires": { "@types/supports-color": "^8.0.0", "string-width": "^5.0.0", diff --git a/tools/lint-md/package.json b/tools/lint-md/package.json index 6e2e2d2fa7ceca..41a73a084b0cfb 100644 --- a/tools/lint-md/package.json +++ b/tools/lint-md/package.json @@ -10,13 +10,13 @@ "remark-preset-lint-node": "^3.3.1", "remark-stringify": "^10.0.2", "to-vfile": "^7.2.3", - "unified": "^10.1.1", - "vfile-reporter": "^7.0.3" + "unified": "^10.1.2", + "vfile-reporter": "^7.0.4" }, "devDependencies": { - "@rollup/plugin-commonjs": "^21.0.1", - "@rollup/plugin-node-resolve": "^13.1.3", - "rollup": "^2.67.3", + "@rollup/plugin-commonjs": "^21.1.0", + "@rollup/plugin-node-resolve": "^13.2.1", + "rollup": "^2.70.2", "rollup-plugin-cleanup": "^3.2.1" } } diff --git a/tools/macos-firewall.sh b/tools/macos-firewall.sh index b6050aaf3450bd..4dfa849bc3ace8 100755 --- a/tools/macos-firewall.sh +++ b/tools/macos-firewall.sh @@ -3,16 +3,16 @@ # popups asking to accept incoming network connections when # running tests. SFW="/usr/libexec/ApplicationFirewall/socketfilterfw" -TOOLSDIR="`dirname \"$0\"`" -TOOLSDIR="`( cd \"$TOOLSDIR\" && pwd) `" -ROOTDIR="`( cd \"$TOOLSDIR/..\" && pwd) `" +TOOLSDIR="$(dirname "$0")" +TOOLSDIR="$(cd "$TOOLSDIR" && pwd)" +ROOTDIR="$(cd "$TOOLSDIR/.." && pwd)" OUTDIR="$TOOLSDIR/../out" # Using cd and pwd here so that the path used for socketfilterfw does not # contain a '..', which seems to cause the rules to be incorrectly added # and they are not removed when this script is re-run. Instead the new # rules are simply appended. By using pwd we can get the full path # without '..' and things work as expected. -OUTDIR="`( cd \"$OUTDIR\" && pwd) `" +OUTDIR="$(cd "$OUTDIR" && pwd)" NODE_RELEASE="$OUTDIR/Release/node" NODE_DEBUG="$OUTDIR/Debug/node" NODE_LINK="$ROOTDIR/node" diff --git a/tools/make-v8.sh b/tools/make-v8.sh index 978b9f00860a65..62cabc70a6eac7 100755 --- a/tools/make-v8.sh +++ b/tools/make-v8.sh @@ -9,7 +9,7 @@ cd deps/v8 || exit find . -type d -name .git -print0 | xargs -0 rm -rf ../../tools/v8/fetch_deps.py . -ARCH="`arch`" +ARCH=$(arch) if [ "$ARCH" = "s390x" ] || [ "$ARCH" = "ppc64le" ]; then TARGET_ARCH=$ARCH if [ "$ARCH" = "ppc64le" ]; then @@ -18,22 +18,34 @@ if [ "$ARCH" = "s390x" ] || [ "$ARCH" = "ppc64le" ]; then # set paths manually for now to use locally installed gn export BUILD_TOOLS=/home/iojs/build-tools export LD_LIBRARY_PATH="$BUILD_TOOLS:$LD_LIBRARY_PATH" - # Avoid linking to ccache symbolic links as ccache decides which - # binary to run based on the name of the link (we always name them gcc/g++). - # shellcheck disable=SC2154 - CC_PATH=`command -v "$CC" gcc | grep -v ccache | head -n 1` - # shellcheck disable=SC2154 - CXX_PATH=`command -v "$CXX" g++ | grep -v ccache | head -n 1` rm -f "$BUILD_TOOLS/g++" rm -f "$BUILD_TOOLS/gcc" - ln -s "$CXX_PATH" "$BUILD_TOOLS/g++" - ln -s "$CC_PATH" "$BUILD_TOOLS/gcc" + # V8's build config looks for binaries called `gcc` and `g++` if not using + # clang. Ensure that `gcc` and `g++` point to the compilers we want to + # invoke, creating symbolic links placed at the front of PATH, if needed. + # Avoid linking to ccache symbolic links as ccache decides which binary + # to run based on the name of the link (i.e. `gcc`/`g++` in our case). + # shellcheck disable=SC2154 + if [ "$CC" != "" ] && [ "$CC" != "gcc" ]; then + CC_PATH=$(command -v "$CC" gcc | grep -v ccache | head -n 1) + ln -s "$CC_PATH" "$BUILD_TOOLS/gcc" + fi + # shellcheck disable=SC2154 + if [ "$CXX" != "" ] && [ "$CXX" != "g++" ]; then + CXX_PATH=$(command -v "$CXX" g++ | grep -v ccache | head -n 1) + ln -s "$CXX_PATH" "$BUILD_TOOLS/g++" + fi export PATH="$BUILD_TOOLS:$PATH" + # Propagate ccache to gn. + case "$CXX" in + *ccache*) CC_WRAPPER="cc_wrapper=\"ccache\"" ;; + *) ;; + esac g++ --version gcc --version export PKG_CONFIG_PATH=$BUILD_TOOLS/pkg-config - gn gen -v "out.gn/$BUILD_ARCH_TYPE" --args="is_component_build=false is_debug=false use_goma=false goma_dir=\"None\" use_custom_libcxx=false v8_target_cpu=\"$TARGET_ARCH\" target_cpu=\"$TARGET_ARCH\" v8_enable_backtrace=true" + gn gen -v "out.gn/$BUILD_ARCH_TYPE" --args="is_component_build=false is_debug=false use_goma=false goma_dir=\"None\" use_custom_libcxx=false v8_target_cpu=\"$TARGET_ARCH\" target_cpu=\"$TARGET_ARCH\" v8_enable_backtrace=true $CC_WRAPPER" ninja -v -C "out.gn/$BUILD_ARCH_TYPE" d8 cctest inspector-test else DEPOT_TOOLS_DIR="$(cd _depot_tools && pwd)" diff --git a/tools/msvs/find_python.cmd b/tools/msvs/find_python.cmd index 06e41fe06cd6ab..00fb6c9afbf1fc 100644 --- a/tools/msvs/find_python.cmd +++ b/tools/msvs/find_python.cmd @@ -46,7 +46,7 @@ exit /b 1 :found-python echo Python found in %p%\python.exe -call :check-python %p%\python.exe +call :check-python "%p%\python.exe" if errorlevel 1 goto :no-python endlocal ^ & set "pt=%p%" ^ @@ -57,7 +57,7 @@ set "need_path_ext=" exit /b 0 :check-python -%~1 -V +%1 -V :: 9009 means error file not found if %errorlevel% equ 9009 ( echo Not an executable Python program diff --git a/tools/msvs/install_tools/install_tools.bat b/tools/msvs/install_tools/install_tools.bat index 46ba93c8b890c9..18f92a981003f2 100644 --- a/tools/msvs/install_tools/install_tools.bat +++ b/tools/msvs/install_tools/install_tools.bat @@ -13,7 +13,7 @@ echo This script will install Python and the Visual Studio Build Tools, necessar echo to compile Node.js native modules. Note that Chocolatey and required Windows echo updates will also be installed. echo. -echo This will require about 3 Gb of free disk space, plus any space necessary to +echo This will require about 3 GiB of free disk space, plus any space necessary to echo install Windows updates. This will take a while to run. echo. echo Please close all open programs for the duration of the installation. If the diff --git a/tools/node_modules/eslint/lib/cli-engine/cli-engine.js b/tools/node_modules/eslint/lib/cli-engine/cli-engine.js index 3ae8b685cf34e6..f7aa9e2242aa6f 100644 --- a/tools/node_modules/eslint/lib/cli-engine/cli-engine.js +++ b/tools/node_modules/eslint/lib/cli-engine/cli-engine.js @@ -56,6 +56,7 @@ const validFixTypes = new Set(["directive", "problem", "suggestion", "layout"]); /** @typedef {import("../shared/types").Plugin} Plugin */ /** @typedef {import("../shared/types").RuleConf} RuleConf */ /** @typedef {import("../shared/types").Rule} Rule */ +/** @typedef {import("../shared/types").FormatterFunction} FormatterFunction */ /** @typedef {ReturnType} ConfigArray */ /** @typedef {ReturnType} ExtractedConfig */ @@ -616,8 +617,8 @@ class CLIEngine { useEslintrc: options.useEslintrc, builtInRules, loadRules, - eslintRecommendedPath: path.resolve(__dirname, "../../conf/eslint-recommended.js"), - eslintAllPath: path.resolve(__dirname, "../../conf/eslint-all.js") + getEslintRecommendedConfig: () => require("../../conf/eslint-recommended.js"), + getEslintAllConfig: () => require("../../conf/eslint-all.js") }); const fileEnumerator = new FileEnumerator({ configArrayFactory, @@ -1002,7 +1003,7 @@ class CLIEngine { * @param {string} [format] The name of the format to load or the path to a * custom formatter. * @throws {any} As may be thrown by requiring of formatter - * @returns {(Function|null)} The formatter function or null if the `format` is not a string. + * @returns {(FormatterFunction|null)} The formatter function or null if the `format` is not a string. */ getFormatter(format) { diff --git a/tools/node_modules/eslint/lib/cli-engine/file-enumerator.js b/tools/node_modules/eslint/lib/cli-engine/file-enumerator.js index f1442d150b844d..674e83e540de3d 100644 --- a/tools/node_modules/eslint/lib/cli-engine/file-enumerator.js +++ b/tools/node_modules/eslint/lib/cli-engine/file-enumerator.js @@ -215,8 +215,8 @@ class FileEnumerator { cwd = process.cwd(), configArrayFactory = new CascadingConfigArrayFactory({ cwd, - eslintRecommendedPath: path.resolve(__dirname, "../../conf/eslint-recommended.js"), - eslintAllPath: path.resolve(__dirname, "../../conf/eslint-all.js") + getEslintRecommendedConfig: () => require("../../conf/eslint-recommended.js"), + getEslintAllConfig: () => require("../../conf/eslint-all.js") }), extensions = null, globInputPaths = true, diff --git a/tools/node_modules/eslint/lib/cli-engine/formatters/html.js b/tools/node_modules/eslint/lib/cli-engine/formatters/html.js index e28996f6cd2f42..6e40bbe16b7f03 100644 --- a/tools/node_modules/eslint/lib/cli-engine/formatters/html.js +++ b/tools/node_modules/eslint/lib/cli-engine/formatters/html.js @@ -39,6 +39,8 @@ function pageTemplate(it) { ESLint Report + +